Skip to content

Commit

Permalink
Validate user sessions on bulk upload when f2f type is single
Browse files Browse the repository at this point in the history
  • Loading branch information
rhell4 committed Dec 1, 2024
1 parent 7d4e242 commit 31e3a3a
Show file tree
Hide file tree
Showing 4 changed files with 179 additions and 9 deletions.
46 changes: 46 additions & 0 deletions classes/booking_manager.php
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,9 @@ public function validate($timenow = null): array {
$sessioncapacitycache = [];
$timenow ??= time();

$userrows = [];
$usersessions = [];

// Break into rows and validate the multiple interdependant fields together.
foreach ($this->get_iterator() as $index => $entry) {
$row = $index + 1;
Expand Down Expand Up @@ -241,6 +244,20 @@ public function validate($timenow = null): array {
$sessioncapacitycache[$session->id]['capacity']--;
$sessioncapacitycache[$session->id]['rows'][] = $row;
}

if (isset($userid) && $session && $this->facetoface->signuptype != MOD_FACETOFACE_SIGNUP_MULTIPLE) {
if ($currusersessions = facetoface_get_user_submissions($this->f, $userid)) {
foreach ($currusersessions as $currusersession) {
if ($currusersession->sessionid != $session->id) {
$errors[] = [
$row,
new lang_string('error:addalreadysignedupattendee', 'mod_facetoface', $entry->email),
];
break;
}
}
}
}
}

// Check user enrolment into the course.
Expand Down Expand Up @@ -273,6 +290,35 @@ public function validate($timenow = null): array {
new lang_string('error:invalidstatusspecified', 'mod_facetoface', $entry->status),
];
}

if (isset($userid)) {
if (!isset($userrows[$userid])) {
$userrows[$userid] = [];
}
if (!isset($usersessions[$userid])) {
$usersessions[$userid] = [];
}

$userrows[$userid][$row] = $entry->email;

if ($session && !in_array($session->id, $usersessions[$userid])) {
$usersessions[$userid][] = $session->id;
}
}
}

if ($this->facetoface->signuptype != MOD_FACETOFACE_SIGNUP_MULTIPLE) {
foreach ($usersessions as $userid => $sessions) {
if (count($sessions) > 1) {
$rows = $userrows[$userid];
foreach ($rows as $row => $email) {
$errors[] = [
$row,
new lang_string('error:multipleusersessions', 'mod_facetoface', $email),
];
}
}
}
}

// For all sessions that went over capacity, report it.
Expand Down
1 change: 1 addition & 0 deletions lang/en/facetoface.php
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@
$string['error:sessionoverbooked'] = 'Session ID {$a->session} overbooked by {$a->amount} person(s).';
$string['error:sessiondoesnotexist'] = 'Session ID {$a} does not exist';
$string['error:userdoesnotexist'] = 'User {$a} does not exist';
$string['error:multipleusersessions'] = 'User {$a} has more than one session';
$string['error:multipleusersmatched'] = 'Multiple users matched to identifier {$a}';
$string['error:addalreadysignedupattendee'] = '{$a} is already signed-up for this Face-to-Face activity.';
$string['error:addattendee'] = 'Could not add {$a} to the session.';
Expand Down
137 changes: 130 additions & 7 deletions tests/upload_test.php
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,9 @@ public function test_user_validation() {

// Generate users.
$user = $this->getDataGenerator()->create_user();
$student = $this->getDataGenerator()->create_and_enrol($course, 'student');
$student1 = $this->getDataGenerator()->create_and_enrol($course, 'student');
$student2 = $this->getDataGenerator()->create_and_enrol($course, 'student');
$student3 = $this->getDataGenerator()->create_and_enrol($course, 'student');

$this->setCurrentTimeStart();
$now = time();
Expand All @@ -156,6 +158,20 @@ public function test_user_validation() {
],
]);

$secondsession = $generator->create_session([
'facetoface' => $facetoface->id,
'capacity' => '3',
'allowoverbook' => '0',
'details' => 'xyz',
'duration' => '1.5', // One and half hours.
'normalcost' => '111',
'discountcost' => '11',
'allowcancellations' => '0',
'sessiondates' => [
['timestart' => $now + 4 * DAYSECS, 'timefinish' => $now + 5 * DAYSECS],
],
]);

$remotesession = $generator->create_session([
'facetoface' => $anotherfacetoface->id,
'capacity' => '3',
Expand All @@ -170,6 +186,9 @@ public function test_user_validation() {
],
]);

facetoface_user_signup($session, $facetoface, $course, '',
MDL_F2F_TEXT, MDL_F2F_STATUS_BOOKED, $student3->id);

$bm = new booking_manager($facetoface->id);

$records = [
Expand All @@ -191,36 +210,52 @@ public function test_user_validation() {
],
// Test student who is enrolled into the course (no issues).
(object) [
'email' => $student->email,
'email' => $student1->email,
'session' => $session->id,
'status' => '',
'notificationtype' => '',
'discountcode' => '',
],
// Test invalid options.
(object) [
'email' => $student->email,
'email' => $student2->email,
'session' => $session->id,
'status' => 'helloworld',
'notificationtype' => 'phone',
'discountcode' => '',
],
// Test multiple rows for the same student when f2f signup type is single.
(object) [
'email' => $student2->email,
'session' => $secondsession->id,
'status' => '',
'notificationtype' => '',
'discountcode' => '',
],
// Test permissions (e.g. user not able to upload/process for a f2f activity loaded).
(object) [
'email' => $student->email,
'email' => $student2->email,
'session' => $remotesession->id,
'status' => '',
'notificationtype' => '',
'discountcode' => '',
],
// Test email does not match case-wise (in the default case sensitive mode).
(object) [
'email' => strtoupper($student->email),
'email' => strtoupper($student2->email),
'session' => $session->id,
'status' => '',
'notificationtype' => '',
'discountcode' => '',
],
// Test student already in a session when f2f signup type is single.
(object) [
'email' => $student3->email,
'session' => $secondsession->id,
'status' => '',
'notificationtype' => '',
'discountcode' => '',
],
];

$bm->load_from_array($records);
Expand Down Expand Up @@ -275,21 +310,40 @@ public function test_user_validation() {
$this->check_row_validation_error_exists(
$errors,
5,
new lang_string('error:multipleusersessions', 'mod_facetoface', $student2->email)
),
'Expecting multiple sessions error for user when f2f signup type is single.'
);

$this->assertTrue(
$this->check_row_validation_error_exists(
$errors,
6,
new lang_string('error:tryingtoupdatesessionfromanothermodule', 'mod_facetoface', (object) [
'session' => $remotesession->id,
'f' => $facetoface->id,
])
),
'Expecting permission check conflict due to session->facetoface + facetoface id mismatcherror.'
);

$this->assertTrue(
$this->check_row_validation_error_exists(
$errors,
6,
new lang_string('error:userdoesnotexist', 'mod_facetoface', strtoupper($student->email))
7,
new lang_string('error:userdoesnotexist', 'mod_facetoface', strtoupper($student2->email))
),
'Expecting user to not exist because email does not match case-wise.'
);

$this->assertTrue(
$this->check_row_validation_error_exists(
$errors,
8,
new lang_string('error:addalreadysignedupattendee', 'mod_facetoface', $student3->email)
),
'Expecting user already signed up to another session error.'
);
}

/**
Expand Down Expand Up @@ -758,4 +812,73 @@ public function test_email_suppression(string $status, bool $shouldsuppress) {
$this->assertNotEmpty($emails);
}
}

/**
* Test upload processing multiple sessions with signup type set to multiple.
*/
public function test_processing_signup_multiple() {
/** @var \mod_facetoface_generator $generator */
$generator = $this->getDataGenerator()->get_plugin_generator('mod_facetoface');

$course = $this->getDataGenerator()->create_course();
// Facetoface with signuptype set to multiple.
$facetoface = $generator->create_instance(['course' => $course->id, 'signuptype' => MOD_FACETOFACE_SIGNUP_MULTIPLE]);
$student = $this->getDataGenerator()->create_and_enrol($course, 'student');

$now = time();
$record = [
'facetoface' => $facetoface->id,
'capacity' => '3',
'allowoverbook' => '0',
'details' => 'xyz',
'duration' => '1.5', // One and half hours.
'normalcost' => '111',
'discountcost' => '11',
'allowcancellations' => '0',
'sessiondates' => [
['timestart' => $now + 3 * DAYSECS, 'timefinish' => $now + 4 * DAYSECS],
],
];
$session1 = $generator->create_session($record);
$session2 = $generator->create_session($record);
$session3 = $generator->create_session($record);

// Signup student to session3 to test already signed up validating.
facetoface_user_signup($session3, $facetoface, $course, '',
MDL_F2F_TEXT, MDL_F2F_STATUS_BOOKED, $student->id);

$bm = new booking_manager($facetoface->id);

$records = [
// Test user can be added to session 1.
(object) [
'email' => $student->email,
'session' => $session1->id,
'status' => '',
'notificationtype' => '',
'discountcode' => '',
],
// Test user can be added to session 2.
(object) [
'email' => $student->email,
'session' => $session2->id,
'status' => '',
'notificationtype' => '',
'discountcode' => '',
],
];

$bm->load_from_array($records);
$errors = $bm->validate();

$this->assertEmpty($errors);
$this->assertTrue($bm->process());

$sessions = facetoface_get_user_submissions($facetoface->id, $student->id);
$this->assertCount(3, $sessions);
$sessionids = array_column($sessions, 'sessionid');
$this->assertContains($session1->id, $sessionids);
$this->assertContains($session2->id, $sessionids);
$this->assertContains($session3->id, $sessionids);
}
}
4 changes: 2 additions & 2 deletions version.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@

defined('MOODLE_INTERNAL') || die();

$plugin->version = 2024101100;
$plugin->release = 2024101100;
$plugin->version = 2024101101;
$plugin->release = 2024101101;
$plugin->requires = 2023100900; // Requires 4.3.
$plugin->component = 'mod_facetoface';
$plugin->maturity = MATURITY_STABLE;
Expand Down

0 comments on commit 31e3a3a

Please sign in to comment.