Skip to content

Commit

Permalink
Merge pull request #7123 from Automattic/add/quiz-migration
Browse files Browse the repository at this point in the history
Add quiz migration
  • Loading branch information
m1r0 authored Sep 4, 2023
2 parents 0013028 + d8ec1c6 commit 10761b9
Show file tree
Hide file tree
Showing 15 changed files with 1,007 additions and 357 deletions.
20 changes: 12 additions & 8 deletions includes/class-sensei.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@

use Sensei\Internal\Action_Scheduler\Action_Scheduler;
use Sensei\Internal\Emails\Email_Customization;
use Sensei\Internal\Installer\Migrations\Student_Progress_Migration;
use Sensei\Internal\Installer\Updates_Factory;
use Sensei\Internal\Migration\Migration_Tool;
use Sensei\Internal\Migration\Migration_Job;
use Sensei\Internal\Migration\Migration_Job_Scheduler;
use Sensei\Internal\Migration\Migrations\Quiz_Migration;
use Sensei\Internal\Migration\Migrations\Student_Progress_Migration;
use Sensei\Internal\Quiz_Submission\Answer\Repositories\Answer_Repository_Factory;
use Sensei\Internal\Quiz_Submission\Answer\Repositories\Answer_Repository_Interface;
use Sensei\Internal\Quiz_Submission\Grade\Repositories\Grade_Repository_Factory;
Expand All @@ -12,8 +16,6 @@
use Sensei\Internal\Quiz_Submission\Submission\Repositories\Submission_Repository_Interface;
use Sensei\Internal\Student_Progress\Course_Progress\Repositories\Course_Progress_Repository_Factory;
use Sensei\Internal\Student_Progress\Course_Progress\Repositories\Course_Progress_Repository_Interface;
use Sensei\Internal\Student_Progress\Jobs\Migration_Job;
use Sensei\Internal\Student_Progress\Jobs\Migration_Job_Scheduler;
use Sensei\Internal\Student_Progress\Lesson_Progress\Repositories\Lesson_Progress_Repository_Factory;
use Sensei\Internal\Student_Progress\Lesson_Progress\Repositories\Lesson_Progress_Repository_Interface;
use Sensei\Internal\Student_Progress\Quiz_Progress\Repositories\Quiz_Progress_Repository_Factory;
Expand All @@ -22,7 +24,6 @@
use Sensei\Internal\Student_Progress\Services\Lesson_Deleted_Handler;
use Sensei\Internal\Student_Progress\Services\Quiz_Deleted_Handler;
use Sensei\Internal\Student_Progress\Services\User_Deleted_Handler;
use Sensei\Internal\Student_Progress\Tools\Migration_Tool;

if ( ! defined( 'ABSPATH' ) ) {
exit;
Expand Down Expand Up @@ -602,10 +603,13 @@ public function initialize_global_objects() {
$this->action_scheduler = new Action_Scheduler();
// Student progress migration.
if ( $use_tables ) {
$migration = new Student_Progress_Migration();
$migration_job = new Migration_Job( $migration );
$this->migration_scheduler = new Migration_Job_Scheduler( $this->action_scheduler, $migration_job );
$this->migration_scheduler->init();
$this->migration_scheduler = new Migration_Job_Scheduler( $this->action_scheduler );
$this->migration_scheduler->register_job(
new Migration_Job( 'student_progress_migration', new Student_Progress_Migration() )
);
$this->migration_scheduler->register_job(
new Migration_Job( 'quiz_migration', new Quiz_Migration() )
);
( new Migration_Tool( \Sensei_Tools::instance(), $this->migration_scheduler ) )->init();
}

Expand Down
42 changes: 0 additions & 42 deletions includes/internal/installer/class-migration.php

This file was deleted.

57 changes: 57 additions & 0 deletions includes/internal/migration/class-migration-abstract.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php
/**
* File containing the abstract class for migrations.
*
* @package sensei
* @since $$next-version$$
*/

namespace Sensei\Internal\Migration;

/**
* Migration abstract class.
*
* @since $$next-version$$
*/
abstract class Migration_Abstract {
/**
* The errors that occurred during the migration.
*
* @var array
*/
private $errors = array();

/**
* Run the migration.
*
* @since $$next-version$$
*
* @param bool $dry_run Whether to run the migration in dry-run mode.
*
* @return int The number of rows migrated.
*/
abstract public function run( bool $dry_run = true );

/**
* Return the errors that occurred during the migration.
*
* @since $$next-version$$
*
* @return array
*/
public function get_errors(): array {
return $this->errors;
}

/**
* Add an error message to the errors list unless it's there already.
*
* @param string $error The error message to add.
*/
protected function add_error( string $error ): void {
if ( ! in_array( $error, $this->errors, true ) ) {
$this->errors[] = $error;
}
}
}

209 changes: 209 additions & 0 deletions includes/internal/migration/class-migration-job-scheduler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
<?php
/**
* File containing the Migration_Job_Scheduler class.
*
* @package sensei
*/

namespace Sensei\Internal\Migration;

use Sensei\Internal\Action_Scheduler\Action_Scheduler;

if ( ! defined( 'ABSPATH' ) ) {
exit;
}

/**
* Class Migration_Job_Scheduler
*
* @internal
*
* @since $$next-version$$
*/
class Migration_Job_Scheduler {
/**
* Sensei jobs namespace.
*
* @var string
*/
private const HOOK_NAMESPACE = 'sensei_lms_migration_job_';

/**
* Migration errors option name.
*
* @var string
*/
public const ERRORS_OPTION_NAME = 'sensei_lms_migration_job_errors';

/**
* Migration job started option name.
*
* @var string
*/
public const STARTED_OPTION_NAME = 'sensei_lms_migration_job_started';

/**
* Migration job completed option name.
*
* @var string
*/
public const COMPLETED_OPTION_NAME = 'sensei_lms_migration_job_completed';

/**
* Action_Scheduler instance.
*
* @var Action_Scheduler
*/
private $action_scheduler;

/**
* Jobs to schedule.
*
* @var Migration_Job[]
*/
private $jobs = [];

/**
* Migration_Job_Scheduler constructor.
*
* @param Action_Scheduler $action_scheduler Action_Scheduler instance.
*/
public function __construct( Action_Scheduler $action_scheduler ) {
$this->action_scheduler = $action_scheduler;
}

/**
* Register a job to be scheduled.
*
* @param Migration_Job $job The migration job.
*/
public function register_job( Migration_Job $job ): void {
$this->jobs[ $job->get_name() ] = $job;

add_action( $this->get_job_hook_name( $job ), [ $this, 'run_job' ] );
}

/**
* Schedule all jobs.
*
* @internal
*
* @since $$next-version$$
* @throws \RuntimeException If no jobs to schedule.
*/
public function schedule(): void {
if ( ! $this->jobs ) {
throw new \RuntimeException( 'No jobs to schedule.' );
}

$first_job = reset( $this->jobs );

$this->schedule_job( $first_job );
}

/**
* Schedule a job.
*
* @param Migration_Job $job The migration job.
*/
private function schedule_job( Migration_Job $job ): void {
$this->action_scheduler->schedule_single_action(
$this->get_job_hook_name( $job ),
[ 'job_name' => $job->get_name() ],
false
);
}

/**
* Run the job.
*
* @internal
*
* @since $$next-version$$
*
* @param string $job_name The job name.
*/
public function run_job( string $job_name ): void {
if ( $this->is_first_run() ) {
$this->start();
}

$job = $this->jobs[ $job_name ];

$job->run();

if ( $job->get_errors() ) {
$migration_errors = (array) get_option( self::ERRORS_OPTION_NAME, [] );
$migration_errors = array_merge( $migration_errors, $job->get_errors() );
update_option( self::ERRORS_OPTION_NAME, $migration_errors );
}

if ( $job->is_complete() ) {
$next_job = $this->get_next_job( $job );
if ( $next_job ) {
$this->schedule_job( $next_job );
} else {
$this->complete();
}
} else {
$this->schedule_job( $job );
}
}

/**
* Get the next job.
*
* @param Migration_Job $job The migration job.
*
* @return Migration_Job|null
*/
private function get_next_job( Migration_Job $job ): ?Migration_Job {
$job_names = array_keys( $this->jobs );
$position = array_search( $job->get_name(), $job_names, true );
$has_next_job = false !== $position && isset( $job_names[ $position + 1 ] );

if ( ! $has_next_job ) {
return null;
}

return $this->jobs[ $job_names[ $position + 1 ] ];
}

/**
* Get the hook name for the job.
*
* @param Migration_Job $job The migration job.
*
* @return string
*/
private function get_job_hook_name( Migration_Job $job ): string {
return self::HOOK_NAMESPACE . $job->get_name();
}

/**
* Check if this is the first run of the job.
*
* @return bool
*/
private function is_first_run(): bool {
$started = get_option( self::STARTED_OPTION_NAME, 0 );
$completed = get_option( self::COMPLETED_OPTION_NAME, 0 );

return $started < $completed || 0 === $started;
}

/**
* Set start time.
*/
private function start(): void {
update_option( self::STARTED_OPTION_NAME, microtime( true ) );
delete_option( self::COMPLETED_OPTION_NAME );
}

/**
* Set completion time.
*/
private function complete(): void {
update_option( self::COMPLETED_OPTION_NAME, microtime( true ) );
}
}
Loading

0 comments on commit 10761b9

Please sign in to comment.