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

164 attendance fix 403 #165

Merged
merged 2 commits into from
May 27, 2024
Merged
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
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
Loading