Skip to content

Commit

Permalink
Merge pull request #165 from catalyst/164-attendance-fix-403
Browse files Browse the repository at this point in the history
164 attendance fix 403
  • Loading branch information
matthewhilton authored May 27, 2024
2 parents b0bf8c1 + d634455 commit 28ceea8
Show file tree
Hide file tree
Showing 3 changed files with 167 additions and 24 deletions.
70 changes: 48 additions & 22 deletions classes/booking_manager.php
Original file line number Diff line number Diff line change
Expand Up @@ -158,12 +158,14 @@ private function get_iterator(): \Generator {
* As there are multiple dependant data points (users, sessions, capacity)
* that are checked. They are all in this method.
*
* @param int $timenow The current time to use for validation.
* @return array An array of errors.
*/
public function validate(): array {
public function validate($timenow = null): array {
global $DB;
$errors = [];
$sessioncapacitycache = [];
$timenow ??= time();

// Break into rows and validate the multiple interdependant fields together.
foreach ($this->get_iterator() as $index => $entry) {
Expand Down Expand Up @@ -197,8 +199,6 @@ public function validate(): array {

// Check for session overbooking, that is, if it would go over session capacity.
if ($session) {
$timenow = time();

// If the session supplied does not link to the face-to-face module expected, then it's invalid.
if ($session->facetoface != $this->f) {
$errors[] = [
Expand All @@ -215,7 +215,7 @@ public function validate(): array {
}

if ($session->datetimeknown
&& $entry->status !== 'cancelled'
&& in_array($entry->status, ['', 'booked'])
&& facetoface_has_session_started($session, $timenow)) {
$inprogressstr = get_string('cannotsignupsessioninprogress', 'facetoface');
$overstr = get_string('cannotsignupsessionover', 'facetoface');
Expand Down Expand Up @@ -328,6 +328,7 @@ public function process() {

// Get signup type.
if ($entry->status === 'cancelled') {
// Handle cancellation.
if (facetoface_user_cancel($session, $user->id, true, $cancelerr)) {
// Notify the user of the cancellation if the session hasn't started yet.
$timenow = time();
Expand All @@ -339,26 +340,51 @@ public function process() {
}
} else {
// Map status to status code.
$statuscode = array_search($entry->status, facetoface_statuses());
if ($statuscode === false) {
// Defaults to booked if not found.
$statuscode = MDL_F2F_STATUS_BOOKED;
}
if ($statuscode === MDL_F2F_STATUS_BOOKED && !$session->datetimeknown) {
// If booked, ensures the status is waitlisted instead, if the datetime is unknown.
$statuscode = MDL_F2F_STATUS_WAITLISTED;
$statuscode = array_search($entry->status, facetoface_statuses()) ?: MDL_F2F_STATUS_BOOKED;

// Handle signups.
if (in_array($statuscode, [MDL_F2F_STATUS_BOOKED, MDL_F2F_STATUS_WAITLISTED])) {
if ($statuscode === MDL_F2F_STATUS_BOOKED && !$session->datetimeknown) {
// If booked, ensures the status is waitlisted instead, if the datetime is unknown.
$statuscode = MDL_F2F_STATUS_WAITLISTED;
}

facetoface_user_signup(
$session,
$this->facetoface,
$this->course,
$entry->discountcode,
$this->transform_notification_type($entry->notificationtype),
$statuscode,
$user->id,
true
);

continue;
}

facetoface_user_signup(
$session,
$this->facetoface,
$this->course,
$entry->discountcode,
$this->transform_notification_type($entry->notificationtype),
$statuscode,
$user->id,
true
);
// Handle attendance.
if (in_array($statuscode, [
MDL_F2F_STATUS_NO_SHOW,
MDL_F2F_STATUS_PARTIALLY_ATTENDED,
MDL_F2F_STATUS_FULLY_ATTENDED,
])) {
$attendees = facetoface_get_attendees($session->id);
// Get matching attendee.
foreach ($attendees as $attendee) {
if ($attendee->email === $entry->email) {
break;
}
}

$data = (object) [
's' => $session->id,
'submissionid_' . $attendee->submissionid => $statuscode,
];
facetoface_take_attendance($data);

continue;
}
}
}

Expand Down
117 changes: 117 additions & 0 deletions tests/upload_test.php
Original file line number Diff line number Diff line change
Expand Up @@ -426,4 +426,121 @@ public function test_processing_cancellation() {
$this->assertEquals($student->id, current($users)->id);
$this->assertNotEmpty(current($users)->timecancelled);
}

/**
* Updates via uploads can be done for previous sessions, only if they are to update attendance.
*
* Book someone in, then once the session is over, update their attendance. This should work.
*/
public function test_updates_for_previous_sessions() {
global $DB;
/** @var \mod_facetoface_generator $generator */
$generator = $this->getDataGenerator()->get_plugin_generator('mod_facetoface');

$course = $this->getDataGenerator()->create_course();
$facetoface = $generator->create_instance(['course' => $course->id]);

// Generate users.
$student = $this->getDataGenerator()->create_and_enrol($course, 'student');

$this->setCurrentTimeStart();
$now = time();
$session = $generator->create_session([
'facetoface' => $facetoface->id,
'capacity' => '3',
'allowoverbook' => '0',
'details' => 'xyz',
'duration' => '2', // One and half hours.
'normalcost' => '111',
'discountcost' => '11',
'allowcancellations' => '0',
'sessiondates' => [
['timestart' => $now + 5, 'timefinish' => $now + 10],
],
]);
$bm = new booking_manager($facetoface->id);

// Book the student.
$records = [
(object) [
'email' => $student->email,
'session' => $session->id,
'status' => 'booked',
],
];

$bm->load_from_array($records);
$errors = $bm->validate();
$this->assertFalse(
$this->check_row_validation_error_exists(
$errors,
1,
''
),
'Expecting user to be booked without issues.'
);
$bm->process();

$DB->update_record(
'facetoface_sessions_dates',
(object) [
'timestart' => 0,
'timefinish' => 1,
'id' => $session->sessiondates[0]->id,
],
);

// It should detect an error (e.g. cannot book a session in progress).
$errors = $bm->validate(time() + 1);
$this->assertTrue(
$this->check_row_validation_error_exists(
$errors,
1,
get_string('cannotsignupsessionover', 'facetoface')
),
'Expecting user to not be bookable since the session has started.'
);

// Update the student's attendance after the session finishes.
$attendanceupdates = [
(object) [
'email' => $student->email,
'session' => $session->id,
'status' => 'no_show',
'grade_expected' => 0,
],
(object) [
'email' => $student->email,
'session' => $session->id,
'status' => 'partially_attended',
'grade_expected' => 50,
],
(object) [
'email' => $student->email,
'session' => $session->id,
'status' => 'fully_attended',
'grade_expected' => 100,
],
];

$timenow = time() + 4 * DAYSECS; // Two days after the session started.
foreach ($attendanceupdates as $update) {
$bm->load_from_array([$update]);

$errors = $bm->validate($timenow);
$this->assertFalse(
$this->check_row_validation_error_exists(
$errors,
1,
''
),
'Expecting update to be valid (even though session has started or finished).'
);
$bm->process();

// Check to ensure the grade is as expected from the update.
$grade = facetoface_get_grade($student->id, $course->id, $facetoface->id);
$this->assertEquals($update->grade_expected, $grade->grade);
}
}
}
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 = 2024051700;
$plugin->release = 2024051700;
$plugin->version = 2024052700;
$plugin->release = 2024052700;
$plugin->requires = 2023100900; // Requires 4.3.
$plugin->component = 'mod_facetoface';
$plugin->maturity = MATURITY_STABLE;
Expand Down

0 comments on commit 28ceea8

Please sign in to comment.