Skip to content

Migrate learner management start date to progress repositories#7933

Open
donnapep wants to merge 5 commits into
trunkfrom
add/hpps-learner-start-date-pr
Open

Migrate learner management start date to progress repositories#7933
donnapep wants to merge 5 commits into
trunkfrom
add/hpps-learner-start-date-pr

Conversation

@donnapep
Copy link
Copy Markdown
Member

@donnapep donnapep commented Apr 1, 2026

Summary

  • Adds set_started_at() to course and lesson progress model interfaces/abstracts (follows existing set_updated_at() pattern)
  • Migrates the Students AJAX handler (edit_date_started) from get/update_comment_meta to progress repository get()/set_started_at()/save()
  • Migrates the Students list table start date display from get_comment_meta to repository lookup

Note to self: Try ultraplan and ultrareview mode for HPPS in CC.

Test plan

HPPS disabled (comments storage)

  • Go to Sensei LMS → Settings → Experimental Features
  • Uncheck "High-Performance Progress Storage" and save
  • Navigate to Sensei LMS → Students (the top-level menu item)
  • In the students list, find a student enrolled in a course and click the course name in their row
  • Verify the Date Started column displays correctly
  • Click the date to edit it, change it, and save — verify the new date persists after page reload

HPPS enabled (tables storage)

  • Go to Sensei LMS → Settings → Experimental Features
  • Check "High-Performance Progress Storage"
  • Select "High-Performance progress storage (experimental)" for the repository option and save
  • Navigate to Sensei LMS → Students, find a student and click the course name in their row
  • Verify the Date Started column displays correctly
  • Click the date to edit it, change it, and save — verify the new date persists after page reload

Sync check

  • If sync is enabled: after editing a date in tables mode, switch back to comments mode (uncheck HPPS) and verify the edited date carried over

🤖 Generated with Claude Code

Copilot AI review requested due to automatic review settings April 1, 2026 18:44
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR migrates Learner Management “start date” read/edit functionality away from direct comment meta access and into the internal student progress repositories, adding a set_started_at() API to course/lesson progress models to support that change across storage backends (comments vs HPPS tables).

Changes:

  • Add set_started_at() to course + lesson progress interfaces/abstracts (mirroring the existing set_updated_at() pattern) and add unit tests for it.
  • Update the Learner Management AJAX handler (edit_date_started) to update started_at via the appropriate progress repository and persist via save().
  • Update the Learners list table “start date” display to read from the progress repository rather than get_comment_meta().

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
tests/unit-tests/internal/student-progress/lesson-progress/models/test-class-tables-based-lesson-progress.php Adds unit coverage for set_started_at() on tables-based lesson progress.
tests/unit-tests/internal/student-progress/lesson-progress/models/test-class-comments-based-lesson-progress.php Adds unit coverage for set_started_at() on comments-based lesson progress.
tests/unit-tests/internal/student-progress/course-progress/models/test-class-tables-based-course-progress.php Adds unit coverage for set_started_at() on tables-based course progress.
tests/unit-tests/internal/student-progress/course-progress/models/test-class-comments-based-course-progress.php Adds unit coverage for set_started_at() on comments-based course progress.
includes/internal/student-progress/lesson-progress/models/class-lesson-progress-interface.php Introduces set_started_at() to the lesson progress model contract.
includes/internal/student-progress/lesson-progress/models/class-lesson-progress-abstract.php Implements set_started_at() in the shared lesson progress base class.
includes/internal/student-progress/course-progress/models/class-course-progress-interface.php Introduces set_started_at() to the course progress model contract.
includes/internal/student-progress/course-progress/models/class-course-progress-abstract.php Implements set_started_at() in the shared course progress base class.
includes/admin/class-sensei-learners-main.php Reads started_at via progress repositories for display in Learner Management rows.
includes/admin/class-sensei-learner-management.php Writes started_at via progress repositories in the edit_date_started AJAX endpoint.
changelog/add-hpps-learner-management-start-date Changelog entry for the migration to repository-based start date handling.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

exit( '' );
}

$progress->set_started_at( $date );
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In edit_date_started(), the parsed $date is created in wp_timezone(), but tables-based progress repositories store and read started_at in UTC (e.g., Tables_Based_*_Progress_Repository::get() instantiates DateTimeImmutable with UTC). Saving a non-UTC DateTime here will write a local-time string into a UTC field, causing shifted instants once the value is later used as a timestamp. Consider normalizing the DateTime to UTC before calling set_started_at() when the loaded progress is tables-based / table-reading mode (while keeping local-time storage for comments-based mode).

Suggested change
$progress->set_started_at( $date );
$storage_date = $date;
if (
is_a( $repository, 'Sensei\Internal\Student_Progress\Repositories\Tables_Based_Course_Progress_Repository' ) ||
is_a( $repository, 'Sensei\Internal\Student_Progress\Repositories\Tables_Based_Lesson_Progress_Repository' )
) {
$storage_date = $date->setTimezone( new DateTimeZone( 'UTC' ) );
}
$progress->set_started_at( $storage_date );

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in c9d3191 — date is now converted to UTC via $date->setTimezone( new DateTimeZone( 'UTC' ) ) before calling set_started_at(). The formatted date returned to the browser still uses the original site-timezone date.

Comment on lines 536 to 556
@@ -535,12 +545,12 @@ public function edit_date_started() {
*
* @hook sensei_learners_learner_updated
*
* @param {bool} $updated A flag indicating if there was an update in the learner row.
* @param {int} $post_id Lesson or course id.
* @param {int} $comment_id The comment id which tracks the progress of the learner.
* @param {bool} $updated A flag indicating if there was an update in the learner row.
* @param {int} $post_id Lesson or course id.
* @param {int} $user_id The user id of the learner.
* @return {bool} False if there were no updates.
*/
$updated = apply_filters( 'sensei_learners_learner_updated', $updated, $post_id, $comment_id );
$updated = apply_filters( 'sensei_learners_learner_updated', true, $post_id, $user_id );

if ( false === $updated ) {
exit( '' );
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$updated is now always passed as true to the sensei_learners_learner_updated filter, regardless of whether anything actually changed or whether the repository write succeeded. This changes the semantics of the hook (it can no longer be used to detect no-ops/failures). Consider computing $updated by comparing the previous vs new started_at (e.g., timestamp equality) and passing that value through the filter.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in c9d3191$updated is now computed by comparing old vs new started_at timestamps instead of being hardcoded to true.

Comment on lines +541 to +553
@@ -535,12 +545,12 @@ public function edit_date_started() {
*
* @hook sensei_learners_learner_updated
*
* @param {bool} $updated A flag indicating if there was an update in the learner row.
* @param {int} $post_id Lesson or course id.
* @param {int} $comment_id The comment id which tracks the progress of the learner.
* @param {bool} $updated A flag indicating if there was an update in the learner row.
* @param {int} $post_id Lesson or course id.
* @param {int} $user_id The user id of the learner.
* @return {bool} False if there were no updates.
*/
$updated = apply_filters( 'sensei_learners_learner_updated', $updated, $post_id, $comment_id );
$updated = apply_filters( 'sensei_learners_learner_updated', true, $post_id, $user_id );
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The sensei_learners_learner_updated hook signature has changed from ( $updated, $post_id, $comment_id ) to ( $updated, $post_id, $user_id ) without changing the hook name. Since this is a public filter, this is a breaking change for any third-party code expecting a comment ID. Consider keeping the original third parameter (and optionally adding $user_id as an additional 4th parameter), or introducing a new hook name for the new signature.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in c9d3191 — preserved backward compatibility by keeping the deprecated $comment_id as the 3rd param (always 0, with deprecation note in docblock) and added $user_id as a new 4th parameter.

Comment thread includes/admin/class-sensei-learner-management.php
$learner_progress = $progress_repository->get( $post_id, $user_activity->user_id );
}
$learner_started_at = $learner_progress ? $learner_progress->get_started_at() : null;
$date_started = $learner_started_at ? $learner_started_at->format( 'Y-m-d H:i:s' ) : '';
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

get_row_data() formats started_at directly from the progress object. When tables storage is used, repositories return started_at in UTC (see Tables_Based_*_Progress_Repository::get()), so this will display UTC while date_completed is rendered from comment_date (site-local). Consider converting started_at to wp_timezone() (or using wp_date() with the site timezone) before formatting so the start date display matches the rest of the Learner Management UI.

Suggested change
$date_started = $learner_started_at ? $learner_started_at->format( 'Y-m-d H:i:s' ) : '';
$date_started = $learner_started_at ? wp_date( 'Y-m-d H:i:s', $learner_started_at->getTimestamp(), wp_timezone() ) : '';

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in c9d3191 — now uses wp_date( 'Y-m-d H:i:s', $learner_started_at->getTimestamp(), wp_timezone() ) to display in site-local time.

@donnapep donnapep force-pushed the add/hpps-learner-start-date-pr branch from c9d3191 to fe5abad Compare April 1, 2026 19:06
@donnapep donnapep added this to the 4.26.0 milestone Apr 1, 2026
@donnapep donnapep self-assigned this Apr 1, 2026
@donnapep donnapep added the HPPS label Apr 1, 2026
donnapep and others added 4 commits April 1, 2026 15:20
Add set_started_at() to course and lesson progress model interfaces and
abstract classes. Migrate edit_date_started() in learner management and
the start date display in learners-main to use progress repositories
instead of direct comment meta access. Update the
sensei_learners_learner_updated filter to pass user_id instead of
comment_id.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Convert date to UTC before set_started_at() for tables-based storage
- Compute $updated from old vs new started_at comparison
- Preserve hook backward compat: keep deprecated comment_id param (0), add user_id as 4th
- Use absint() instead of sanitize_key() for user_id
- Use wp_date() with wp_timezone() for start date display
- Fix equals sign alignment in learners-main

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Split course/lesson repo branches to avoid union type on save()
- Guard $post_id against false before passing to repo get()
- Cast wp_date() return to string to satisfy esc_attr() type

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@donnapep donnapep force-pushed the add/hpps-learner-start-date-pr branch from 04aadef to fc51a1f Compare April 1, 2026 19:22
…ment

The filter's $comment_id parameter is a storage implementation detail
that no longer applies across both storage backends.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants