Commit 384634c0 authored by Astor Bizard's avatar Astor Bizard 🐕
Browse files

Added tests for overrides. Refactored code a little to fit coding style.

parent c4e4848a
......@@ -28,6 +28,13 @@ require_once(__DIR__ . '/../vpl.class.php');
global $CFG;
require_once($CFG->libdir . '/formslib.php');
/**
* Return HTML fragment for buttons of a given override row.
* @param int $id VPL cmid.
* @param int $overrideid Override id for the current row.
* @param int $editing The id of the override being edited.
* @return string HTML fragment for buttons.
*/
function vpl_get_overrideactions($id, $overrideid, $editing) {
global $OUTPUT, $PAGE;
if ($editing == $overrideid) {
......@@ -181,11 +188,11 @@ if ($edit !== null || $update !== null) {
}
}
$availablegroups = array_filter($groups, function($group) use ($alreadyassignedgroups) {
return !in_array($group->id, $alreadyassignedgroups);
});
return !in_array($group->id, $alreadyassignedgroups);
});
$availablegroups = array_map(function($group) {
return $group->name;
}, $availablegroups);
return $group->name;
}, $availablegroups);
} else {
$availablegroups = null;
}
......@@ -204,7 +211,7 @@ if ($delete !== null) {
$override = $DB->get_record( VPL_OVERRIDES, array('id' => $overrideid) );
if ($override !== false) {
// Delete associated calendar events.
vpl_update_override_calendar_events($override, $vpl->get_instance(), null, true);
$vpl->update_override_calendar_events($override, null, true);
// Delete the override.
$DB->delete_records( VPL_OVERRIDES, array('id' => $overrideid) );
\mod_vpl\event\override_deleted::log($vpl, $overrideid);
......@@ -244,7 +251,7 @@ if ($update !== null) {
\mod_vpl\event\override_updated::log($vpl, $update);
}
// Create or update associated calendar events.
vpl_update_override_calendar_events($override, $vpl->get_instance(), $oldoverride);
$vpl->update_override_calendar_events($override, $oldoverride);
}
// Do not redirect if validation fails.
if ($optionsform->is_validated() || $optionsform->is_cancelled()) {
......@@ -313,7 +320,7 @@ foreach ($overrides as $override) {
if (!empty($override->groupids)) {
$users[] = implode(', ', array_map(function($groupid) {
return '<i class="fa fa-fw fa-group"></i>&nbsp;' . groups_get_group_name($groupid);
}, explode(',', $override->groupids)));
}, explode(',', $override->groupids)));
}
$users = implode(', ', $users);
if ($users == '') {
......
......@@ -986,83 +986,4 @@ function vpl_get_name_fields_display() {
function vpl_get_menu_action_link($str, $link, $comp = 'mod_vpl') {
$stri18n = get_string($str, $comp);
return new action_menu_link_secondary($link, new pix_icon($str, '', 'mod_vpl'), $stri18n);
}
/**
* Update calendar events for duedate overrides.
* @param stdClass $override The override being created / updated / deleted.
* @param stdClass $vplinstance The DB object of VPL instance.
* @param stdClass $oldoverride The old override data (in case of an update).
* @param boolean $delete If true, simply delete all related events.
*/
function vpl_update_override_calendar_events($override, $vplinstance, $oldoverride = null, $delete = false) {
global $DB, $CFG;
require_once($CFG->dirroot . '/calendar/lib.php');
$targets = array(
'userid' => CALENDAR_EVENT_USER_OVERRIDE_PRIORITY,
'groupid' => $override->id
);
foreach ($targets as $target => $priority) { // Process once for users and once for groups.
$params = array(
'modulename' => VPL,
'instance' => $vplinstance->id,
'priority' => $priority
);
if ($oldoverride !== null && !empty($oldoverride->{$target . 's'})) {
$oldtargets = array_fill_keys(explode(',', $oldoverride->{$target . 's'}), false);
} else {
$oldtargets = array();
}
if (!empty($override->{$target . 's'})) {
foreach (explode(',', $override->{$target . 's'}) as $userorgroupid) { // Loop over users or groups.
$params[$target] = $userorgroupid;
$currenteventid = $DB->get_field( 'event', 'id', $params ); // Get current calendar event.
if (isset($override->override_duedate) && !$delete) {
if ($target == 'userid') {
$userorgroupname = fullname($DB->get_record( 'user', array('id' => $userorgroupid) ));
} else {
$userorgroupname = groups_get_group($userorgroupid)->name;
}
$newevent = vpl_create_event($vplinstance, $vplinstance->id);
$newevent->name = get_string('overridefor', VPL, array(
'base' => $newevent->name,
'for' => $userorgroupname
));
if ($target == 'userid') {
// User overrides events do not show correctly if courseid is non zero.
$newevent->courseid = 0;
}
$newevent->timestart = $override->duedate;
$newevent->timesort = $override->duedate;
$newevent->{$target} = $userorgroupid;
$newevent->priority = $priority;
if ($currenteventid === false) {
// No event exist for current user or group, create a new one.
calendar_event::create( $newevent );
} else {
// An event already exists, update it.
calendar_event::load( $currenteventid )->update( $newevent );
}
} else {
if ($currenteventid !== false) {
calendar_event::load( $currenteventid )->delete();
}
}
// This user or group is in newly processed data (or has already been removed).
$oldtargets[$userorgroupid] = true;
}
}
// Discard events related to users or groups removed from override.
foreach ($oldtargets as $oldtarget => $tokeep) {
if (!$tokeep) {
$params[$target] = $oldtarget;
$eventid = $DB->get_field( 'event', 'id', $params );
if ($eventid !== false) {
calendar_event::load( $eventid )->delete();
}
}
}
}
}
\ No newline at end of file
......@@ -159,6 +159,7 @@ class mod_vpl_base_testcase extends advanced_testcase {
protected $vplonefile = null;
protected $vplmultifile = null;
protected $vplvariations = null;
protected $vploverrides = null;
protected $vplteamwork = null;
protected $vpls = null;
......@@ -169,6 +170,7 @@ class mod_vpl_base_testcase extends advanced_testcase {
$this->setup_onefile_instance();
$this->setup_multifile_instance();
$this->setup_variations_instance();
$this->setup_overrides_instance();
$this->setup_vplteamwork_instance();
$this->vpls = [
$this->vpldefault,
......@@ -176,6 +178,7 @@ class mod_vpl_base_testcase extends advanced_testcase {
$this->vplonefile,
$this->vplmultifile,
$this->vplvariations,
$this->vploverrides,
$this->vplteamwork,
];
return;
......@@ -218,7 +221,7 @@ class mod_vpl_base_testcase extends advanced_testcase {
'worktype' => 0,
);
$this->vplonefile = $this->create_instance($parms);
$rqfiles = $this->vplonefile->get_required_fgm();
$rqfiles = $this->vplonefile->get_fgm('required');
$rqfiles->addallfiles(array('a.c' => "int main(){\n}"));
// Add a submission.
$this->setUser($this->students[0]);
......@@ -285,7 +288,7 @@ class mod_vpl_base_testcase extends advanced_testcase {
);
$this->vplvariations = $this->create_instance($parms);
$instance = $this->vplvariations->get_instance();
for ( $i = 1; $i < 6; $i++) {
for ($i = 1; $i < 6; $i++) {
$parms = array(
'vpl' => $instance->id,
'identification' => '' . $i,
......@@ -323,6 +326,73 @@ class mod_vpl_base_testcase extends advanced_testcase {
}
}
protected function setup_overrides_instance() {
global $DB;
$this->setUser($this->editingteachers[0]);
$now = time();
$baseduedate = $now + DAYSECS;
$parms = array(
'name' => 'Overrides',
'startdate' => 0,
'duedate' => $baseduedate,
'freeevaluations' => 0,
'reductionbyevaluation' => 0,
'maxfiles' => 10,
'maxfilesize' => 1000,
'grade' => 10,
'worktype' => 0
);
$this->vploverrides = $this->create_instance($parms);
// Create overrides such that:
// - Student 0 has default settings,
// - Student 1 has everything (due date is postponed by 1 day) overriden (by user),
// - Student 2 has everything (due date is postponed by 1 day) overriden (by user),
// - Student 3 has due date (due date is postponed by 2 days) overriden (by user),
// - Teacher 0 has due date (due date is postponed by 2 days) overriden (by group),
// - Editing teacher 0 has due date (due date is postponed by 2 days) overriden (by group),
// - Teacher 1 has due date (due date is disabled) overriden (by group and by user, latter should prevail).
$override = new stdClass();
$override->vpl = $this->vploverrides->get_course_module()->id;
$override->userids = $this->students[1]->id . ',' . $this->students[2]->id;
$override->groupids = null;
$override->startdate = $now - 3600;
$override->duedate = $baseduedate + DAYSECS;
$override->freeevaluations = 10;
$override->reductionbyevaluation = 1;
$override->id = $DB->insert_record( VPL_OVERRIDES, $override );
$override->override_startdate = 1;
$override->override_duedate = 1;
$override->override_freeevaluations = 1;
$override->override_reductionbyevaluation = 1;
$this->vploverrides->update_override_calendar_events($override);
$override = new stdClass();
$override->vpl = $this->vploverrides->get_course_module()->id;
$override->userids = $this->students[3]->id;
$override->groupids = $this->groups[2]->id . ',' . $this->groups[3]->id;
$override->startdate = null;
$override->duedate = $baseduedate + 2 * DAYSECS;
$override->freeevaluations = null;
$override->reductionbyevaluation = null;
$override->id = $DB->insert_record( VPL_OVERRIDES, $override );
$override->override_duedate = 1;
$this->vploverrides->update_override_calendar_events($override);
$override = new stdClass();
$override->vpl = $this->vploverrides->get_course_module()->id;
$override->userids = $this->teachers[1]->id;
$override->groupids = null;
$override->startdate = null;
$override->duedate = 0;
$override->freeevaluations = null;
$override->reductionbyevaluation = null;
$override->id = $DB->insert_record( VPL_OVERRIDES, $override );
$override->override_duedate = 1;
$this->vploverrides->update_override_calendar_events($override);
}
protected function setup_vplteamwork_instance() {
global $DB;
$this->setUser($this->editingteachers[0]);
......
......@@ -41,7 +41,7 @@ class mod_vpl_lib_testcase extends mod_vpl_base_testcase {
/**
* Method to create lib test fixture
*/
protected function setup() {
protected function setup(): void {
parent::setup();
$this->setupinstances();
}
......@@ -198,14 +198,14 @@ class mod_vpl_lib_testcase extends mod_vpl_base_testcase {
foreach ($this->vpls as $vpl) {
$instance = $vpl->get_instance();
$instance->instance = $instance->id;
$sparms = array ('modulename' => VPL, 'instance' => $instance->id );
$sparms = array ('modulename' => VPL, 'instance' => $instance->id, 'priority' => null );
$event = $DB->get_record( 'event', $sparms );
$this->assertTrue(($event != false && $instance->duedate == $event->timestart) ||
($event == false && $instance->duedate == 0),
$instance->name);
$instance->duedate = time() + 1000;
vpl_update_instance($instance);
$sparms = array ('modulename' => VPL, 'instance' => $instance->id );
$sparms = array ('modulename' => VPL, 'instance' => $instance->id, 'priority' => null );
$event = $DB->get_record( 'event', $sparms );
$this->assertTrue(($event != false && $instance->duedate == $event->timestart) ||
($event == false && $instance->duedate == 0),
......@@ -305,7 +305,7 @@ class mod_vpl_lib_testcase extends mod_vpl_base_testcase {
FEATURE_GRADE_HAS_GRADE,
FEATURE_GRADE_OUTCOMES,
FEATURE_BACKUP_MOODLE2,
FEATURE_SHOW_DESCRIPTION,
FEATURE_SHOW_DESCRIPTION
];
foreach ($supp as $feature) {
$this->assertTrue(vpl_supports($feature));
......
......@@ -122,4 +122,196 @@ class mod_vpl_class_testcase extends mod_vpl_base_testcase {
// TODO Refactor code to test print submission.
}
/**
* Method to test mod_vpl::get_effective_setting
*/
public function test_get_effective_setting() {
$vpl = $this->vploverrides;
$instance = $vpl->get_instance();
$baseduedate = $instance->duedate;
// Check that student 0 has default settings.
$user = $this->students[0];
foreach (array('startdate', 'duedate', 'reductionbyevaluation', 'freeevaluations') as $field) {
$this->assertEquals(
$instance->$field,
$vpl->get_effective_setting($field, $user->id),
$instance->name . ': ' . $user->username . ' ' . $field
);
}
// Check that student 1 and student 2 have everything (due date is postponed by 1 day) overriden.
foreach (array($this->students[1], $this->students[2]) as $user) {
foreach (array('startdate', 'reductionbyevaluation', 'freeevaluations') as $field) {
$this->assertNotEquals(
$instance->$field,
$vpl->get_effective_setting($field, $user->id),
$instance->name . ': ' . $user->username . ' ' . $field
);
}
$this->assertEquals(
$baseduedate + DAYSECS,
$vpl->get_effective_setting('duedate', $user->id),
$instance->name . ': ' . $user->username . ' duedate'
);
}
// Check that student 3, teacher 0 and editing teacher 0 has due date (due date is postponed by 2 days) overriden.
foreach (array($this->students[3], $this->teachers[0], $this->editingteachers[0]) as $user) {
foreach (array('startdate', 'reductionbyevaluation', 'freeevaluations') as $field) {
$this->assertEquals(
$instance->$field,
$vpl->get_effective_setting($field, $user->id),
$instance->name . ': ' . $user->username . ' ' . $field
);
}
$this->assertEquals(
$baseduedate + 2 * DAYSECS,
$vpl->get_effective_setting('duedate', $user->id),
$instance->name . ': ' . $user->username . ' duedate'
);
}
// Check that teacher 1 has due date (due date is disabled) overriden.
$user = $this->teachers[1];
foreach (array('startdate', 'reductionbyevaluation', 'freeevaluations') as $field) {
$this->assertEquals(
$instance->$field,
$vpl->get_effective_setting($field, $user->id),
$instance->name . ': ' . $user->username . ' ' . $field
);
}
$this->assertEquals(
0,
$vpl->get_effective_setting('duedate', $user->id),
$instance->name . ': ' . $user->username . ' duedate'
);
// Check for any other vpl that settings are not overriden.
foreach ($this->vpls as $vpl) {
$instance = $vpl->get_instance();
if ($instance->name == $this->vploverrides->get_instance()->name) {
continue;
}
foreach ($this->users as $user) {
foreach (array('startdate', 'duedate', 'reductionbyevaluation', 'freeevaluations') as $field) {
$this->assertEquals(
$instance->$field,
$vpl->get_effective_setting($field, $user->id),
$instance->name. ': ' . $user->username . ' ' . $field
);
}
}
}
}
/**
* Method to test mod_vpl::update_override_calendar_events
*/
public function test_update_override_calendar_events() {
global $CFG;
require_once($CFG->dirroot . '/calendar/lib.php');
$vpl = $this->vploverrides;
$instance = $vpl->get_instance();
$baseduedate = $instance->duedate;
$start = $baseduedate - DAYSECS;
$end = $baseduedate + 3 * DAYSECS;
// Check that student 0 has default duedate event.
$user = $this->students[0];
$userevents = array_filter(calendar_get_events($start, $end, $user->id, false, $instance->course),
function($event) use ($instance) {
return $event->modulename == VPL && $event->instance == $instance->id;
}
);
$this->assertCount(
1,
$userevents,
$instance->name . ': events for ' . $user->username
);
$this->assertEquals(
$baseduedate,
reset($userevents)->timestart,
$instance->name . ': event for ' . $user->username
);
// Check that student 1 and student 2 have due date postponed by 1 day event.
foreach (array($this->students[1], $this->students[2]) as $user) {
$userevents = array_filter(calendar_get_events($start, $end, $user->id, false, $instance->course),
function($event) use ($instance) {
return $event->modulename == VPL && $event->instance == $instance->id
&& $event->priority !== null && $event->priority == CALENDAR_EVENT_USER_OVERRIDE_PRIORITY;
}
);
$this->assertCount(
1,
$userevents,
$instance->name . ': events for ' . $user->username
);
$this->assertEquals(
$baseduedate + DAYSECS,
reset($userevents)->timestart,
$instance->name . ': event for ' . $user->username
);
}
// Check that student 3 has due date postponed by 2 days (user) event.
$user = $this->students[3];
$userevents = array_filter(calendar_get_events($start, $end, $user->id, false, $instance->course),
function($event) use ($instance) {
return $event->modulename == VPL && $event->instance == $instance->id
&& $event->priority !== null && $event->priority == CALENDAR_EVENT_USER_OVERRIDE_PRIORITY;
}
);
$this->assertCount(
1,
$userevents,
$instance->name . ': events for ' . $user->username
);
$this->assertEquals(
$baseduedate + 2 * DAYSECS,
reset($userevents)->timestart,
$instance->name . ': event for ' . $user->username
);
// Check that teacher 0 and editing teacher 0 have due date postponed by 2 days (group) event.
foreach (array($this->groups[2], $this->groups[3]) as $group) {
$groupevents = array_filter(calendar_get_events($start, $end, false, $group->id, $instance->course),
function($event) use ($instance) {
return $event->modulename == VPL && $event->instance == $instance->id
&& $event->priority !== null && $event->priority > CALENDAR_EVENT_USER_OVERRIDE_PRIORITY;
}
);
$this->assertCount(
1,
$groupevents,
$instance->name . ': events for ' . $group->name
);
$this->assertEquals(
$baseduedate + 2 * DAYSECS,
reset($groupevents)->timestart,
$instance->name . ': event for ' . $group->name
);
}
// Check that teacher 1 has due date event disabled.
$user = $this->teachers[1];
$userevents = array_filter(calendar_get_events(0, $end, $user->id, false, $instance->course),
function($event) use ($instance) {
return $event->modulename == VPL && $event->instance == $instance->id
&& $event->priority !== null && $event->priority == CALENDAR_EVENT_USER_OVERRIDE_PRIORITY;
}
);
$this->assertCount(
1,
$userevents,
$instance->name . ': events for ' . $user->username
);
$this->assertEquals(
0,
reset($userevents)->timestart,
$instance->name . ': event for ' . $user->username
);
}
}
\ No newline at end of file
......@@ -124,7 +124,7 @@ class mod_vpl_webservice_testcase extends mod_vpl_base_testcase {
$instance = $vpl->get_instance();
$res = mod_vpl_webservice::info($vpl->get_course_module()->id, $instance->password);
$this->assertEquals($instance->name, $res['name']);
$rqfiles = $vpl->get_required_fgm();
$rqfiles = $vpl->get_fgm('required');
$this->internal_test_files($rqfiles->getallfiles(), $res['reqfiles']);
}
}
......
......@@ -1998,7 +1998,17 @@ class mod_vpl {
);
}
protected static $overridensettings = array ();
/**
* Cached settings of overrides, for get_effective_setting().
* @var array $overridensettings Array[ cmid => Array[ userid => {settings} ] ]
*/
protected static $overridensettings = array();
/**
* Return effective setting for this vpl instance (taking overrides into account).
* @param string $setting Setting name (field of database record).
* @param int $userid (optional) Get for given user, current user if null.
* @return mixed The effective setting, as a database field.
*/
public function get_effective_setting($setting, $userid = null) {
global $USER, $DB;
$fields = array('startdate', 'duedate', 'reductionbyevaluation', 'freeevaluations');
......@@ -2008,15 +2018,18 @@ class mod_vpl {
if (!$userid) {
$userid = $USER->id;
}
if (!isset(self::$overridensettings[$userid])) {
self::$overridensettings[$userid] = new stdClass();
if (!isset(self::$overridensettings[$this->cm->id])) {
self::$overridensettings[$this->cm->id] = array();
}
if (!isset(self::$overridensettings[$this->cm->id][$userid])) {
self::$overridensettings[$this->cm->id][$userid] = new stdClass();
$overrides = $DB->get_records( VPL_OVERRIDES, array('vpl' => $this->cm->id), 'id DESC' );
foreach ($overrides as $override) {
if (!empty($override->groupids)) {
foreach (explode(',', $override->groupids) as $groupid) {
if (groups_is_member($groupid, $userid)) {
foreach ($fields as $field) {
self::$overridensettings[$userid]->$field = $override->$field;
self::$overridensettings[$this->cm->id][$userid]->$field = $override->$field;
}
}
}
......@@ -2025,15 +2038,93 @@ class mod_vpl {
foreach ($overrides as $override) {
if (!empty($override->userids) && in_array($userid, explode(',', $override->userids))) {
foreach ($fields as $field) {
self::$overridensettings[$userid]->$field = $override->$field;
self::$overridensettings[$this->cm->id][$userid]->$field = $override->$field;
}
}
}
}
if (isset(self::$overridensettings[$userid]->$setting) && self::$overridensettings[$userid]->$setting !== null) {
return self::$overridensettings[$userid]->$setting;
if (isset(self::$overridensettings[$this->cm->id][$userid]->$setting) &&
self::$overridensettings[$this->cm->id][$userid]->$setting !== null) {
return self::$overridensettings[$this->cm->id][$userid]->$setting;
} else {
return $this->instance->$setting;
}
}
/**
* Update calendar events for duedate overrides.
* @param stdClass $override The override being created / updated / deleted.
* @param stdClass $oldoverride The old override data (in case of an update).
* @param boolean $delete If true, simply delete all related events.
*/
public function update_override_calendar_events($override, $oldoverride = null, $delete = false) {
global $DB, $CFG;
require_once($CFG->dirroot . '/calendar/lib.php');
$targets = array(
'userid' => CALENDAR_EVENT_USER_OVERRIDE_PRIORITY,
'groupid' => $override->id
);
foreach ($targets as $target => $priority) { // Process once for users and once for groups.
$params = array(
'modulename' => VPL,
'instance' => $this->instance->id,
'priority' => $priority
);
if ($oldoverride !== null && !empty($oldoverride->{$target . 's'})) {
$oldtargets = array_fill_keys(explode(',', $oldoverride->{$target . 's'}), false);
} else {
$oldtargets = array();
}
if (!empty($override->{$target . 's'})) {
foreach (explode(',', $override->{$target . 's'}) as $userorgroupid) { // Loop over users or groups.
$params[$target] = $userorgroupid;
$currenteventid = $DB->get_field( 'event', 'id', $params ); // Get current calendar event.
if (isset($override->override_duedate) && !$delete) {
if ($target == 'userid') {
$userorgroupname = fullname($DB->get_record( 'user', array('id' => $userorgroupid) ));
} else {
$userorgroupname = groups_get_group($userorgroupid)->name;
}
$newevent = vpl_create_event($this->instance, $this->instance->id);
$newevent->name = get_string('overridefor', VPL, array(
'base' => $newevent->name,
'for' => $userorgroupname
));
if ($target == 'userid') {
// User overrides events do not show correctly if courseid is non zero.