Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CTP-3412 & CTP-3413 Create overrides for ECs & SORAs #68

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions .github/workflows/moodle-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,6 @@ jobs:
- php: '8.1'
moodle-branch: 'MOODLE_404_STABLE'
database: 'mariadb'
- php: '8.1'
moodle-branch: 'MOODLE_403_STABLE'
database: 'mariadb'

steps:
- name: Check out repository code
Expand Down
14 changes: 14 additions & 0 deletions classes/assessment/activity.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

namespace local_sitsgradepush\assessment;

use core\context\module;
use grade_item;
use local_sitsgradepush\manager;

Expand All @@ -32,13 +33,17 @@ abstract class activity extends assessment {
/** @var \stdClass Course module object */
public \stdClass $coursemodule;

/** @var module Context module */
protected module $context;

/**
* Constructor.
*
* @param \stdClass $coursemodule
*/
public function __construct(\stdClass $coursemodule) {
$this->coursemodule = $coursemodule;
$this->context = \context_module::instance($coursemodule->id);
parent::__construct(assessmentfactory::SOURCETYPE_MOD, $coursemodule->id);
}

Expand Down Expand Up @@ -103,6 +108,15 @@ public function get_module_name(): string {
return $this->coursemodule->modname;
}

/**
* Get the module context.
*
* @return module
*/
public function get_module_context(): module {
return $this->context;
}

/**
* Get the course module id.
*
Expand Down
62 changes: 61 additions & 1 deletion classes/assessment/assessment.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@

namespace local_sitsgradepush\assessment;

use local_sitsgradepush\extension\ec;
use local_sitsgradepush\extension\extension;
use local_sitsgradepush\extension\sora;
use local_sitsgradepush\manager;

/**
Expand Down Expand Up @@ -52,6 +55,27 @@ public function __construct(string $sourcetype, int $sourceid) {
$this->set_instance();
}

/**
* Apply extension to the assessment.
*
* @param extension $extension
* @return void
* @throws \moodle_exception
*/
public function apply_extension(extension $extension): void {
$check = $this->is_valid_for_extension();
if (!$check->valid) {
throw new \moodle_exception($check->errorcode, 'local_sitsgradepush');
}

// Do extension base on the extension type.
if ($extension instanceof ec) {
$this->apply_ec_extension($extension);
} else if ($extension instanceof sora) {
$this->apply_sora_extension($extension);
}
}

/**
* Get the source id.
*
Expand Down Expand Up @@ -196,6 +220,19 @@ public function check_assessment_validity(): \stdClass {
return $result;
}

/**
* Check if the assessment is valid for EC or SORA extension.
*
* @return \stdClass
*/
public function is_valid_for_extension(): \stdClass {
if ($this->get_start_date() === null || $this->get_end_date() === null) {
return $this->set_validity_result(false, 'error:dates_not_set');
}

return $this->set_validity_result(true);
}

/**
* Set validity result.
*
Expand Down Expand Up @@ -224,11 +261,34 @@ protected function get_equivalent_grade_from_mark(float $marks): ?string {
return $equivalentgrade;
}

/**
* Apply EC extension.
*
* @param ec $ec
* @return void
* @throws \moodle_exception
*/
protected function apply_ec_extension(ec $ec): void {
// Default not supported. Override in child class if needed.
throw new \moodle_exception('error:ecextensionnotsupported', 'local_sitsgradepush');
}

/**
* Apply SORA extension.
*
* @param sora $sora
* @return void
* @throws \moodle_exception
*/
protected function apply_sora_extension(sora $sora): void {
// Default not supported. Override in child class if needed.
throw new \moodle_exception('error:soraextensionnotsupported', 'local_sitsgradepush');
}

/**
* Get all participants for the assessment.
*
* @return array
*/
abstract public function get_all_participants(): array;

}
181 changes: 179 additions & 2 deletions classes/assessment/assign.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@

namespace local_sitsgradepush\assessment;

use cache;
use local_sitsgradepush\extension\ec;
use local_sitsgradepush\extension\sora;

/**
* Class for assignment assessment.
*
Expand All @@ -26,14 +30,23 @@
*/
class assign extends activity {

/**
* Is the user a participant in the assignment.
*
* @param int $userid
* @return bool
*/
public function is_user_a_participant(int $userid): bool {
return is_enrolled($this->get_module_context(), $userid, 'mod/assign:submit');
}

/**
* Get all participants.
*
* @return array
*/
public function get_all_participants(): array {
$context = \context_module::instance($this->coursemodule->id);
return get_enrolled_users($context, 'mod/assign:submit');
return get_enrolled_users($this->get_module_context(), 'mod/assign:submit');
}

/**
Expand All @@ -53,4 +66,168 @@ public function get_start_date(): ?int {
public function get_end_date(): ?int {
return $this->sourceinstance->duedate;
}

/**
* Apply EC extension to the assessment.
*
* @param ec $ec The EC extension.
* @return void
*/
protected function apply_ec_extension(ec $ec): void {
Copy link
Collaborator

Choose a reason for hiding this comment

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

just noting that if we need to implement this per activity, the other pull requests allowing push would only be providing partial support and would need follow up story to implement this in each

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes, that's correct, and the apply_sora_extension() function too.

global $CFG;
require_once($CFG->dirroot . '/mod/assign/locallib.php');
$originalduedate = $this->get_end_date();

// EC is using a new deadline without time. Extract the time part of the original due date.
$time = date('H:i:s', $originalduedate);

// Get the new date and time.
$newduedate = strtotime($ec->get_new_deadline() . ' ' . $time);

// Override the assignment settings for user.
$this->overrides_due_date($newduedate, $ec->get_userid());
}

/**
* Apply SORA extension to the assessment.
*
* @param sora $sora The SORA extension.
* @return void
* @throws \moodle_exception
*/
protected function apply_sora_extension(sora $sora): void {
global $CFG;
require_once($CFG->dirroot . '/group/lib.php');

// Get time extension in seconds.
$timeextensionperhour = $sora->get_time_extension();

// Calculate the new due date.
// Find the difference between the start and end date in hours. Multiply by the time extension per hour.
$actualextension = (($this->get_end_date() - $this->get_start_date()) / HOURSECS) * $timeextensionperhour;
$newduedate = $this->get_end_date() + round($actualextension);

// Get the group id, create if it doesn't exist and add the user to the group.
$groupid = $sora->get_sora_group_id($this->get_course_id(), $sora->get_userid());

if (!$groupid) {
throw new \moodle_exception('error:cannotgetsoragroupid', 'local_sitsgradepush');
}

$this->overrides_due_date($newduedate, $sora->get_userid(), $groupid);
}

/**
* Overrides the due date for the user or group.
*
* @param int $newduedate The new due date.
* @param int $userid The user id.
* @param int|null $groupid The group id.
* @return void
*/
private function overrides_due_date(int $newduedate, int $userid, ?int $groupid = null): void {
global $CFG, $DB;
require_once($CFG->dirroot . '/mod/assign/locallib.php');
require_once($CFG->dirroot . '/mod/assign/lib.php');

// It is a group override.
if ($groupid) {
$sql = 'SELECT * FROM {assign_overrides} WHERE assignid = :assignid AND groupid = :groupid AND userid IS NULL';
$params = [
'assignid' => $this->get_source_instance()->id,
'groupid' => $groupid,
];
} else {
// It is a user override.
$sql = 'SELECT * FROM {assign_overrides} WHERE assignid = :assignid AND userid = :userid AND groupid IS NULL';
$params = [
'assignid' => $this->get_source_instance()->id,
'userid' => $userid,
];
}

// Check if the override already exists.
$override = $DB->get_record_sql($sql, $params);
if ($override) {
// No need to update if the due date is the same.
if ($override->duedate == $newduedate) {
return;
}
$override->duedate = $newduedate;
$DB->update_record('assign_overrides', $override);
$newrecord = false;
} else {
// Create a new override.
$override = new \stdClass();
$override->assignid = $this->get_source_instance()->id;
$override->duedate = $newduedate;
$override->userid = $groupid ? null : $userid;
$override->groupid = $groupid ?: null;
$override->sortorder = $groupid ? 0 : null;
$override->id = $DB->insert_record('assign_overrides', $override);

// Reorder the group overrides.
if ($groupid) {
reorder_group_overrides($override->assignid);
}
$newrecord = true;
}

// Clear the cache.
$this->clear_override_cache($override);

// Trigger the event.
$this->trigger_override_event($override, $newrecord);

// Update the assign events.
assign_update_events(new \assign($this->context, $this->get_course_module(), null), $override);
}

/**
* Trigger the override event.
*
* @param \stdClass $override The override object.
* @param bool $newrecord Whether the override is a new record.
* @return void
* @throws \coding_exception
*/
private function trigger_override_event(\stdClass $override, bool $newrecord): void {
$params = [
'context' => $this->context,
'other' => [
'assignid' => $override->assignid,
],
];

$params['objectid'] = $override->id;
if (!$override->groupid) {
$params['relateduserid'] = $override->userid;
if ($newrecord) {
$event = \mod_assign\event\user_override_created::create($params);
} else {
$event = \mod_assign\event\user_override_updated::create($params);
}
} else {
$params['other']['groupid'] = $override->groupid;
if ($newrecord) {
$event = \mod_assign\event\group_override_created::create($params);
} else {
$event = \mod_assign\event\group_override_updated::create($params);
}
}
$event->trigger();
}

/**
* Clear the override cache.
*
* @param \stdClass $override The override object.
* @return void
* @throws \coding_exception
*/
private function clear_override_cache(\stdClass $override): void {
$cachekey = $override->groupid ?
"{$override->assignid}_g_{$override->groupid}" : "{$override->assignid}_u_{$override->userid}";
cache::make('mod_assign', 'overrides')->delete($cachekey);
}
}
Loading
Loading