From a96165a4316d9c2aba1fc256bf7e22222c31fb27 Mon Sep 17 00:00:00 2001 From: RiedleroD Date: Tue, 9 Apr 2024 19:25:40 +0200 Subject: [PATCH 01/19] first draft of slot booking table structure --- lbplanner/db/install.xml | 53 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/lbplanner/db/install.xml b/lbplanner/db/install.xml index f0d274fb..29794e5f 100644 --- a/lbplanner/db/install.xml +++ b/lbplanner/db/install.xml @@ -114,5 +114,58 @@ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + +
+ + + + + + + + + + + +
From 70ddc77c69c565548d0d2c75cda24c692c63d5d6 Mon Sep 17 00:00:00 2001 From: RiedleroD Date: Tue, 9 Apr 2024 23:01:21 +0200 Subject: [PATCH 02/19] WIP groundwork + WIP get_my_slots API endpoint --- lbplanner/classes/enums/WEEKDAY.php | 66 ++++++++++ lbplanner/classes/helpers/slot_helper.php | 113 +++++++++++++++++ lbplanner/classes/model/reservation.php | 62 ++++++++++ lbplanner/classes/model/slot.php | 142 ++++++++++++++++++++++ lbplanner/classes/model/slot_filter.php | 44 +++++++ lbplanner/db/install.xml | 4 +- lbplanner/services/slots/get_my_slots.php | 92 ++++++++++++++ 7 files changed, 521 insertions(+), 2 deletions(-) create mode 100644 lbplanner/classes/enums/WEEKDAY.php create mode 100644 lbplanner/classes/helpers/slot_helper.php create mode 100644 lbplanner/classes/model/reservation.php create mode 100644 lbplanner/classes/model/slot.php create mode 100644 lbplanner/classes/model/slot_filter.php create mode 100644 lbplanner/services/slots/get_my_slots.php diff --git a/lbplanner/classes/enums/WEEKDAY.php b/lbplanner/classes/enums/WEEKDAY.php new file mode 100644 index 00000000..024e9451 --- /dev/null +++ b/lbplanner/classes/enums/WEEKDAY.php @@ -0,0 +1,66 @@ +. +/** + * enum for weekdays + * (cringe, ik, but we need these defined concretely) + * + * @package local_lbplanner + * @subpackage enums + * @copyright 2024 NecodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace local_lbplanner\enums; + +// TODO: revert to native enums once we migrate to php8. + +use local_lbplanner\polyfill\Enum; + +/** + * All the days of the week. + * All seven of them. + * Yup. + */ +class WEEKDAY extends Enum { + /** + * monday + */ + const MONDAY = 1; + /** + * tuesday + */ + const TUESDAY = 2; + /** + * wednesday + */ + const WEDNESDAY = 3; + /** + * thursday + */ + const THURSDAY = 4; + /** + * friday + */ + const FRIDAY = 5; + /** + * saturday + */ + const SATURDAY = 6; + /** + * sunday + */ + const SUNDAY = 7; +} diff --git a/lbplanner/classes/helpers/slot_helper.php b/lbplanner/classes/helpers/slot_helper.php new file mode 100644 index 00000000..4a6717b4 --- /dev/null +++ b/lbplanner/classes/helpers/slot_helper.php @@ -0,0 +1,113 @@ +. +/** + * Provides helper classes for any tables related with the slot booking function of the app + * + * @package local_lbplanner + * @subpackage helpers + * @copyright 2024 NecodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace local_lbplanner\helpers; + +use local_lbplanner\model\{slot, reservation, slot_filter}; + +/** + * Provides helper methods for any tables related with the planning function of the app + */ +class slot_helper { + /** + * local_lbplanner_slots table. + */ + const TABLE_SLOTS = 'local_lbplanner_slots'; + /** + * local_lbplanner_reservations table. + */ + const TABLE_RESERVATIONS = 'local_lbplanner_reservations'; + /** + * local_lbplanner_slot_courses table. + */ + const TABLE_SLOT_FILTERS = 'local_lbplanner_slot_courses'; + /** + * local_lbplanner_supervisors table. + */ + const TABLE_SUPERVISORS = 'local_lbplanner_supervisors'; + + /** + * Returns a list of all slots. + * + * @return slot[] An array of the slots. + */ + public static function get_all_slots(): array { + global $DB; + $slots = $DB->get_records(self::TABLE_SLOTS, []); + + $slots_obj = []; + foreach($slots as $slot){ + array_push($slots_obj, new slot(...$slot)); + } + + return $slots_obj; + } + + /** + * Returns a singular slot. + * + * @return slot the requested slot + */ + public static function get_slot(int $slotid): slot { + global $DB; + $slot = $DB->get_record(self::TABLE_SLOTS, ['id'=>$slotid]); + + return new slot(...$slot); + } + + /** + * Returns reservations for a slot. + * + * @return reservation[] the requested reservations + */ + public static function get_reservations_for_slot(int $slotid): array { + global $DB; + $reservations = $DB->get_records(self::TABLE_RESERVATIONS, ['slotid'=>$slotid]); + + $reservations_obj = []; + foreach($reservations as $reservation){ + $reservation['date'] = new \DateTime($reservation['date']); + array_push($reservations_obj, new reservation(...$reservation)); + } + + return $reservations_obj; + } + + /** + * Returns filters for a slot. + * + * @return slot_filter[] the requested filters + */ + public static function get_filters_for_slot(int $slotid): array { + global $DB; + $filters = $DB->get_records(self::TABLE_SLOT_FILTERS, ['slotid'=>$slotid]); + + $filters_obj = []; + foreach($filters as $filter){ + array_push($filters_obj, new slot_filter(...$filter)); + } + + return $filters_obj; + } +} diff --git a/lbplanner/classes/model/reservation.php b/lbplanner/classes/model/reservation.php new file mode 100644 index 00000000..900190f2 --- /dev/null +++ b/lbplanner/classes/model/reservation.php @@ -0,0 +1,62 @@ +. +/** + * Model for a reservation + * + * @package local_lbplanner + * @subpackage helpers + * @copyright 2024 NecodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace local_lbplanner\model; + +use local_lbplanner\model\slot; +use local_lbplanner\helpers\slot_helper; + +/** + * Model class for reservation + */ +class reservation { + public int $id; + public int $slotid; + public \DateTimeImmutable $date; + public int $userid; + public int $reserverid; + private ?slot $slot; + + public function __construct(int $id, int $slotid, \DateTimeImmutable $date, int $userid, int $reserverid) { + $this->id = $id; + $this->slotid = $slotid; + $this->date = $date; + $this->userid = $userid; + $this->reserverid = $reserverid; + $this->slot = null; + } + + /** + * Returns the associated slot. + * + * @returns slot the associated slot + */ + public function get_slot(): slot { + if(is_null($this->slot)){ + $this->slot = slot_helper::get_slot($this->slotid); + } + + return $this->slot; + } +} diff --git a/lbplanner/classes/model/slot.php b/lbplanner/classes/model/slot.php new file mode 100644 index 00000000..b81c8058 --- /dev/null +++ b/lbplanner/classes/model/slot.php @@ -0,0 +1,142 @@ +. +/** + * Model for a slot + * + * @package local_lbplanner + * @subpackage helpers + * @copyright 2024 NecodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace local_lbplanner\model; + +use local_lbplanner\enums\WEEKDAY; +use local_lbplanner\helpers\slot_helper; + +use external_single_structure; +use external_value; + +/** + * Model class for slot + */ +class slot { + public int $id; + public int $startunit; + public int $duration; + public int $weekday; + public string $room; + public int $size; + private ?int $fullness; + private ?bool $for_curuser; + + public function __construct(int $id, int $startunit, int $duration, int $weekday, string $room, int $size) { + $this->id = $id; + assert($startunit > 0); + $this->startunit = $startunit; + assert($duration > 0); + $this->duration = $duration; + $this->weekday = WEEKDAY::from($weekday); + assert(strlen($room) > 0 && strlen($room) <= 7); + $this->room = $room; + assert($size >= 0); // Make it technically possible to not allow any students in a room to temporarily disable the slot. + $this->size = $size; + $this->fullness = null; + $this->for_curuser = null; + } + + /** + * Returns how many reservations there are for this slot. + * + * @return int fullness + */ + public function get_fullness(): int { + if(is_null($this->fullness)){ + $this->_check_reservations(); + } + + return $this->fullness; + } + + /** + * Returns whether the current user has a reservation for this slot. + * + * @return bool for_curuser + */ + public function get_for_curuser(): bool { + if(is_null($this->for_curuser)){ + $this->_check_reservations(); + } + + return $this->for_curuser; + } + + /** + * Prepares data for the API endpoint. + * + * @return array a representation of this slot and its data + */ + public function prepare_for_api(): array { + return [ + 'id' => $this->id, + 'startunit' => $this->startunit, + 'duration' => $this->duration, + 'weekday' => $this->weekday, + 'room' => $this->room, + 'size' => $this->size, + 'fullness' => $this->get_fullness(), + 'for_curuser' => $this->get_for_curuser(), + ]; + } + + /** + * Returns the data structure of a slot for the API. + * + * @return external_single_structure The data structure of a slot for the API. + */ + public static function api_structure(): external_single_structure { + return new external_single_structure( + [ + 'id' => new external_value(PARAM_INT, 'slot ID'), + 'startunit' => new external_value(PARAM_INT, 'unit this slot starts in (8:00 is unit 1)'), + 'duration' => new external_value(PARAM_INT, 'duration of the slot in units'), + 'weekday' => new external_value(PARAM_INT, 'The day this unit repeats weekly: '.WEEKDAY::format()), + 'room' => new external_value(PARAM_TEXT, 'The room this slot is for'), + 'size' => new external_value(PARAM_INT, 'total capacity of the slot'), + 'fullness' => new external_value(PARAM_INT, 'how many people have already reserved this slot'), + 'for_curuser' => new external_value(PARAM_BOOL, 'whether the current user has reserved this slot'), + ] + ); + } + + /** + * Queries reservations for this slot and fills in internal data with that info. + */ + private function _check_reservations(): void { + global $USER; + $reservations = slot_helper::get_reservations_for_slot($this->id); + + $this->fullness = sizeof($reservations); + + foreach($reservations as $reservation){ + if($reservation->userid == $USER['id']){ + $this->for_curuser = true; + return; + } + } + $this->for_curuser = false; + } +} diff --git a/lbplanner/classes/model/slot_filter.php b/lbplanner/classes/model/slot_filter.php new file mode 100644 index 00000000..e3334260 --- /dev/null +++ b/lbplanner/classes/model/slot_filter.php @@ -0,0 +1,44 @@ +. +/** + * Model for a filter for slots + * + * @package local_lbplanner + * @subpackage helpers + * @copyright 2024 NecodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace local_lbplanner\model; + +/** + * Model class for a filter for slots + */ +class slot_filter { + public int $id; + public int $slotid; + public ?int $courseid; + public ?string $vintage; + + public function __construct(int $id, int $slotid, ?int $courseid, ?string $vintage) { + $this->id = $id; + $this->slotid = $slotid; + $this->courseid = $courseid; + if(!is_null($vintage)) + assert(strlen($vintage) <= 7); + $this->vintage = $vintage; + } +} diff --git a/lbplanner/db/install.xml b/lbplanner/db/install.xml index 29794e5f..13c855e2 100644 --- a/lbplanner/db/install.xml +++ b/lbplanner/db/install.xml @@ -146,8 +146,8 @@ - - + + diff --git a/lbplanner/services/slots/get_my_slots.php b/lbplanner/services/slots/get_my_slots.php new file mode 100644 index 00000000..75aeff98 --- /dev/null +++ b/lbplanner/services/slots/get_my_slots.php @@ -0,0 +1,92 @@ +. + +namespace local_lbplanner_services; + +use external_api; +use external_function_parameters; +use local_lbplanner\helpers\slot_helper; +use local_lbplanner\model\slot; + +/** + * Returns all slots the user is supposed to see. + * + * @package local_lbplanner + * @subpackage services_plan + * @copyright 2024 necodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class get_slots extends external_api { + /** + * Parameters for get_my_slots. + * @return external_function_parameters + */ + public static function get_my_slots_parameters(): external_function_parameters { + return new external_function_parameters([]); + } + + /** + * Returns slots the current user is supposed to see + */ + public static function get_my_slots() { + global $USER; + // NOTE: could be better solved by applying filters within one complex SQL query. + // Oh well. + + $all_slots = slot_helper::get_all_slots(); + + $my_courses = self::call_external_function('local_lbplanner_courses_get_all_courses', ['userid' => $USER->id]); + $my_courseids = []; + foreach($my_courses as $course){ + array_push($my_courseids, $course->courseid); + } + + $my_slots = []; + foreach($all_slots as $slot){ + $filters = slot_helper::get_filters_for_slot($slot->id); + foreach($filters as $filter) { + // Checking for course ID. + if(!is_null($filter->courseid) and !in_array($filter->courseid, $my_courseids)) { + continue; + } + // Checking for vintage. + if(!is_null($filter->vintage) and $USER->address !== $filter->vintage) { + continue; + } + // If all filters passed, add slot to my slots and break. + array_push($my_slots, $slot); + break; + } + } + + // TODO: check for time, weekday, etc. + + $returnslots = []; + foreach($my_slots as $slot){ + array_push($returnslots, $slot->prepare_for_api()); + } + + return $returnslots; + } + + /** + * Returns the structure of the slot array + * @return external_single_structure + */ + public static function get_my_slots_returns() { + return slot::api_structure(); + } +} From c15c051e86e8dd9d644e30892f472fdaf5ff1025 Mon Sep 17 00:00:00 2001 From: RiedleroD Date: Wed, 17 Apr 2024 11:48:01 +0200 Subject: [PATCH 03/19] minor fix in Enum.php documentation --- lbplanner/classes/polyfill/Enum.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lbplanner/classes/polyfill/Enum.php b/lbplanner/classes/polyfill/Enum.php index 1d8b7b19..3e7d5f1a 100644 --- a/lbplanner/classes/polyfill/Enum.php +++ b/lbplanner/classes/polyfill/Enum.php @@ -76,7 +76,7 @@ public static function cases(): array { /** * Formats all possible enum values into a string * Example: - * (31=>RED,32=>GREEN,33=>YELLOW) + * [31=>RED,32=>GREEN,33=>YELLOW] * @return string the resulting string */ public static function format(): string { From f73518abb0ab5843520e8f2e7df229da43dc0ab1 Mon Sep 17 00:00:00 2001 From: RiedleroD Date: Wed, 17 Apr 2024 11:55:15 +0200 Subject: [PATCH 04/19] refactored parts of Enum.php code deduplication, added name lookup --- lbplanner/classes/polyfill/Enum.php | 58 +++++++++++++++++++++++------ 1 file changed, 47 insertions(+), 11 deletions(-) diff --git a/lbplanner/classes/polyfill/Enum.php b/lbplanner/classes/polyfill/Enum.php index 3e7d5f1a..a85422a4 100644 --- a/lbplanner/classes/polyfill/Enum.php +++ b/lbplanner/classes/polyfill/Enum.php @@ -35,15 +35,34 @@ class Enum { /** * tries to match the passed value to one of the enum values * @param mixed $value the value to be matched - * @return mixed either the matching enum value or null if not found + * @param bool $try whether to return null (true) or throw an error (false) if not found + * @return ?EnumCase the matching enum case or null if not found and $try==true + * @throws ValueError if not found and $try==false */ - public static function try_from(mixed $value): mixed { + private static function _find(mixed $value, bool $try): ?EnumCase { foreach (static::cases() as $case) { if ($case->value === $value) { - return $value; + return $case; } } - return null; + + if($try) + return null; + else + throw new ValueError("value {$value} cannot be represented as a value in enum ".static::class); + } + /** + * tries to match the passed value to one of the enum values + * @param mixed $value the value to be matched + * @return mixed either the matching enum value or null if not found + */ + public static function try_from(mixed $value): ?mixed { + // TODO: replace with nullsafe operator in php8. + $case = static::_find($value, true); + if(is_null($case)) + return null; + else + return $case->value; } /** * tries to match the passed value to one of the enum values @@ -52,13 +71,30 @@ public static function try_from(mixed $value): mixed { * @throws ValueError if not found */ public static function from(mixed $value): mixed { - foreach (static::cases() as $case) { - if ($case->value === $value) { - return $value; - } - } - - throw new ValueError("value {$value} cannot be represented as a value in enum ".static::class); + return static::_find($value, false)->value; + } + /** + * tries to match the passed value to one of the enum values + * @param mixed $value the value to be matched + * @return string the matching enum case name + * @throws mixed either the matching enum case name or null if not found + */ + public static function try_name_from(mixed $value): ?string { + // TODO: replace with nullsafe operator in php8. + $case = static::_find($value, true); + if(is_null($case)) + return null; + else + return $case->name; + } + /** + * tries to match the passed value to one of the enum values + * @param mixed $value the value to be matched + * @return string the matching enum case name + * @throws ValueError if not found + */ + public static function name_from(mixed $value): string { + return static::_find($value, false)->name; } /** * Returns an array of all the cases that exist in this enum From 5c5a5b8f872eeda39a9d7e986be2ad4ee2a87e68 Mon Sep 17 00:00:00 2001 From: RiedleroD Date: Wed, 17 Apr 2024 12:22:27 +0200 Subject: [PATCH 05/19] defined school units MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …crudely. but good enough --- lbplanner/classes/helpers/slot_helper.php | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/lbplanner/classes/helpers/slot_helper.php b/lbplanner/classes/helpers/slot_helper.php index 4a6717b4..f2fea2b5 100644 --- a/lbplanner/classes/helpers/slot_helper.php +++ b/lbplanner/classes/helpers/slot_helper.php @@ -30,6 +30,28 @@ * Provides helper methods for any tables related with the planning function of the app */ class slot_helper { + /** + * school units according to untis, in H:i format + */ + const SCHOOL_UNITS = [ + null, + '08:00', + '08:50', + '09:50', + '10:40', + '11:30', + '12:30', + '13:20', + '14:10', + '15:10', + '16:00', + '17:00', // All units after this point are 45min long instead of the usual 50. + '17:45', // We will assume 50min anyway because it's easier that way. + '18:45', + '19:30', + '20:15', + '21:00', + ]; /** * local_lbplanner_slots table. */ From d439ef5ea95f3d36ff14bd83031b2b84a7f29613 Mon Sep 17 00:00:00 2001 From: RiedleroD Date: Wed, 17 Apr 2024 12:23:10 +0200 Subject: [PATCH 06/19] finished get_my_slots API call --- lbplanner/services/slots/get_my_slots.php | 34 +++++++++++++++++++---- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/lbplanner/services/slots/get_my_slots.php b/lbplanner/services/slots/get_my_slots.php index 75aeff98..07ac4b53 100644 --- a/lbplanner/services/slots/get_my_slots.php +++ b/lbplanner/services/slots/get_my_slots.php @@ -16,13 +16,19 @@ namespace local_lbplanner_services; +use DateInterval; +use DateTime; +use DateTimeImmutable; use external_api; use external_function_parameters; +use external_single_structure; use local_lbplanner\helpers\slot_helper; use local_lbplanner\model\slot; +use local_lbplanner\enums\WEEKDAY; /** - * Returns all slots the user is supposed to see. + * Returns all slots the user can theoretically reserve. + * This does not include times the user has already reserved a slot for. * * @package local_lbplanner * @subpackage services_plan @@ -54,6 +60,9 @@ public static function get_my_slots() { array_push($my_courseids, $course->courseid); } + /** + * @var slot[] $my_slots + */ $my_slots = []; foreach($all_slots as $slot){ $filters = slot_helper::get_filters_for_slot($slot->id); @@ -62,6 +71,7 @@ public static function get_my_slots() { if(!is_null($filter->courseid) and !in_array($filter->courseid, $my_courseids)) { continue; } + // TODO: replace address with cohorts. // Checking for vintage. if(!is_null($filter->vintage) and $USER->address !== $filter->vintage) { continue; @@ -72,11 +82,25 @@ public static function get_my_slots() { } } - // TODO: check for time, weekday, etc. - + $now = new DateTimeImmutable(); + /** + * @var slot[] $returnslots + */ $returnslots = []; + // Calculate date and time each slot happens next, and add it to the return list if within reach from today. foreach($my_slots as $slot){ - array_push($returnslots, $slot->prepare_for_api()); + $slotdaytime = slot_helper::SCHOOL_UNITS[$slot->startunit]; + $slotdatetime = DateTime::createFromFormat('Y-m-d H:i',$now->format('Y-m-d ').$slotdaytime); + // Move to next day this weekday occurs (doesn't move if it's the same as today). + $slotdatetime->modify('this '.WEEKDAY::name_from($slot->weekday)); + + // Check if slot is before now (because time of day and such) and move it a week into the future if so. + if($now->diff($slotdatetime)->invert === 1) + $slotdatetime->add(new DateInterval('P1W')); + + // TODO: make setting of "3 days in advance" changeable. + if($now->diff($slotdatetime)->days <= 3) + array_push($returnslots, $slot->prepare_for_api()); } return $returnslots; @@ -86,7 +110,7 @@ public static function get_my_slots() { * Returns the structure of the slot array * @return external_single_structure */ - public static function get_my_slots_returns() { + public static function get_my_slots_returns(): external_single_structure { return slot::api_structure(); } } From 113a8078bd6d06f7d8b68269de4b373b9c8f3b00 Mon Sep 17 00:00:00 2001 From: RiedleroD Date: Wed, 17 Apr 2024 12:31:54 +0200 Subject: [PATCH 07/19] made moodle codesniffer happy --- lbplanner/classes/helpers/slot_helper.php | 3 ++ lbplanner/services/slots/get_my_slots.php | 36 ++++++++++------------- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/lbplanner/classes/helpers/slot_helper.php b/lbplanner/classes/helpers/slot_helper.php index f2fea2b5..637e3bf6 100644 --- a/lbplanner/classes/helpers/slot_helper.php +++ b/lbplanner/classes/helpers/slot_helper.php @@ -88,6 +88,7 @@ public static function get_all_slots(): array { /** * Returns a singular slot. + * @param int $slotid ID of the slot * * @return slot the requested slot */ @@ -100,6 +101,7 @@ public static function get_slot(int $slotid): slot { /** * Returns reservations for a slot. + * @param int $slotid ID of the slot * * @return reservation[] the requested reservations */ @@ -118,6 +120,7 @@ public static function get_reservations_for_slot(int $slotid): array { /** * Returns filters for a slot. + * @param int $slotid ID of the slot * * @return slot_filter[] the requested filters */ diff --git a/lbplanner/services/slots/get_my_slots.php b/lbplanner/services/slots/get_my_slots.php index 07ac4b53..ddb76a47 100644 --- a/lbplanner/services/slots/get_my_slots.php +++ b/lbplanner/services/slots/get_my_slots.php @@ -52,55 +52,51 @@ public static function get_my_slots() { // NOTE: could be better solved by applying filters within one complex SQL query. // Oh well. - $all_slots = slot_helper::get_all_slots(); + $allSlots = slot_helper::get_all_slots(); - $my_courses = self::call_external_function('local_lbplanner_courses_get_all_courses', ['userid' => $USER->id]); - $my_courseids = []; - foreach($my_courses as $course){ - array_push($my_courseids, $course->courseid); + $myCourses = self::call_external_function('local_lbplanner_courses_get_all_courses', ['userid' => $USER->id]); + $myCourseids = []; + foreach ($myCourses as $course) { + array_push($myCourseids, $course->courseid); } - /** - * @var slot[] $my_slots - */ - $my_slots = []; - foreach($all_slots as $slot){ + $mySlots = []; + foreach ($allSlots as $slot) { $filters = slot_helper::get_filters_for_slot($slot->id); foreach($filters as $filter) { // Checking for course ID. - if(!is_null($filter->courseid) and !in_array($filter->courseid, $my_courseids)) { + if (!is_null($filter->courseid) && !in_array($filter->courseid, $myCourseids)) { continue; } // TODO: replace address with cohorts. // Checking for vintage. - if(!is_null($filter->vintage) and $USER->address !== $filter->vintage) { + if (!is_null($filter->vintage) && $USER->address !== $filter->vintage) { continue; } // If all filters passed, add slot to my slots and break. - array_push($my_slots, $slot); + array_push($mySlots, $slot); break; } } $now = new DateTimeImmutable(); - /** - * @var slot[] $returnslots - */ $returnslots = []; // Calculate date and time each slot happens next, and add it to the return list if within reach from today. - foreach($my_slots as $slot){ + foreach ($mySlots as $slot) { $slotdaytime = slot_helper::SCHOOL_UNITS[$slot->startunit]; - $slotdatetime = DateTime::createFromFormat('Y-m-d H:i',$now->format('Y-m-d ').$slotdaytime); + $slotdatetime = DateTime::createFromFormat('Y-m-d H:i', $now->format('Y-m-d ').$slotdaytime); // Move to next day this weekday occurs (doesn't move if it's the same as today). $slotdatetime->modify('this '.WEEKDAY::name_from($slot->weekday)); // Check if slot is before now (because time of day and such) and move it a week into the future if so. - if($now->diff($slotdatetime)->invert === 1) + if ($now->diff($slotdatetime)->invert === 1) { $slotdatetime->add(new DateInterval('P1W')); + } // TODO: make setting of "3 days in advance" changeable. - if($now->diff($slotdatetime)->days <= 3) + if ($now->diff($slotdatetime)->days <= 3) { array_push($returnslots, $slot->prepare_for_api()); + } } return $returnslots; From fed9cffc9c87851cda0edd1f0d6c356f2170953a Mon Sep 17 00:00:00 2001 From: RiedleroD Date: Wed, 17 Apr 2024 12:39:42 +0200 Subject: [PATCH 08/19] made codesniffer even more happy --- lbplanner/classes/helpers/slot_helper.php | 28 +++++++++++------------ 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/lbplanner/classes/helpers/slot_helper.php b/lbplanner/classes/helpers/slot_helper.php index 637e3bf6..1add2a39 100644 --- a/lbplanner/classes/helpers/slot_helper.php +++ b/lbplanner/classes/helpers/slot_helper.php @@ -78,12 +78,12 @@ public static function get_all_slots(): array { global $DB; $slots = $DB->get_records(self::TABLE_SLOTS, []); - $slots_obj = []; - foreach($slots as $slot){ - array_push($slots_obj, new slot(...$slot)); + $slotsObj = []; + foreach ($slots as $slot) { + array_push($slotsObj, new slot(...$slot)); } - return $slots_obj; + return $slotsObj; } /** @@ -94,7 +94,7 @@ public static function get_all_slots(): array { */ public static function get_slot(int $slotid): slot { global $DB; - $slot = $DB->get_record(self::TABLE_SLOTS, ['id'=>$slotid]); + $slot = $DB->get_record(self::TABLE_SLOTS, ['id' => $slotid]); return new slot(...$slot); } @@ -107,15 +107,15 @@ public static function get_slot(int $slotid): slot { */ public static function get_reservations_for_slot(int $slotid): array { global $DB; - $reservations = $DB->get_records(self::TABLE_RESERVATIONS, ['slotid'=>$slotid]); + $reservations = $DB->get_records(self::TABLE_RESERVATIONS, ['slotid' => $slotid]); - $reservations_obj = []; - foreach($reservations as $reservation){ + $reservationsObj = []; + foreach ($reservations as $reservation) { $reservation['date'] = new \DateTime($reservation['date']); - array_push($reservations_obj, new reservation(...$reservation)); + array_push($reservationsObj, new reservation(...$reservation)); } - return $reservations_obj; + return $reservationsObj; } /** @@ -126,13 +126,13 @@ public static function get_reservations_for_slot(int $slotid): array { */ public static function get_filters_for_slot(int $slotid): array { global $DB; - $filters = $DB->get_records(self::TABLE_SLOT_FILTERS, ['slotid'=>$slotid]); + $filters = $DB->get_records(self::TABLE_SLOT_FILTERS, ['slotid' => $slotid]); - $filters_obj = []; + $filtersObj = []; foreach($filters as $filter){ - array_push($filters_obj, new slot_filter(...$filter)); + array_push($filtersObj, new slot_filter(...$filter)); } - return $filters_obj; + return $filtersObj; } } From eb101ef93421bbeedd5f1e8726a40f892acd3ef8 Mon Sep 17 00:00:00 2001 From: RiedleroD Date: Wed, 17 Apr 2024 13:15:15 +0200 Subject: [PATCH 09/19] FUCK MOODLE DEVS AMAB stands for All Moodledevs Are Bad --- lbplanner/classes/enums/WEEKDAY.php | 12 ++-- lbplanner/classes/helpers/slot_helper.php | 18 +++--- lbplanner/classes/model/reservation.php | 38 ++++++++++-- lbplanner/classes/model/slot.php | 72 +++++++++++++++++------ lbplanner/classes/model/slot_filter.php | 22 ++++++- lbplanner/classes/polyfill/Enum.php | 27 +++++---- lbplanner/services/slots/get_my_slots.php | 18 +++--- 7 files changed, 149 insertions(+), 58 deletions(-) diff --git a/lbplanner/classes/enums/WEEKDAY.php b/lbplanner/classes/enums/WEEKDAY.php index 024e9451..7d5a5b3c 100644 --- a/lbplanner/classes/enums/WEEKDAY.php +++ b/lbplanner/classes/enums/WEEKDAY.php @@ -39,27 +39,27 @@ class WEEKDAY extends Enum { * monday */ const MONDAY = 1; - /** + /** * tuesday */ const TUESDAY = 2; - /** + /** * wednesday */ const WEDNESDAY = 3; - /** + /** * thursday */ const THURSDAY = 4; - /** + /** * friday */ const FRIDAY = 5; - /** + /** * saturday */ const SATURDAY = 6; - /** + /** * sunday */ const SUNDAY = 7; diff --git a/lbplanner/classes/helpers/slot_helper.php b/lbplanner/classes/helpers/slot_helper.php index 1add2a39..1d23f615 100644 --- a/lbplanner/classes/helpers/slot_helper.php +++ b/lbplanner/classes/helpers/slot_helper.php @@ -78,12 +78,12 @@ public static function get_all_slots(): array { global $DB; $slots = $DB->get_records(self::TABLE_SLOTS, []); - $slotsObj = []; + $slotsobj = []; foreach ($slots as $slot) { - array_push($slotsObj, new slot(...$slot)); + array_push($slotsobj, new slot(...$slot)); } - return $slotsObj; + return $slotsobj; } /** @@ -109,13 +109,13 @@ public static function get_reservations_for_slot(int $slotid): array { global $DB; $reservations = $DB->get_records(self::TABLE_RESERVATIONS, ['slotid' => $slotid]); - $reservationsObj = []; + $reservationsobj = []; foreach ($reservations as $reservation) { $reservation['date'] = new \DateTime($reservation['date']); array_push($reservationsObj, new reservation(...$reservation)); } - return $reservationsObj; + return $reservationsobj; } /** @@ -128,11 +128,11 @@ public static function get_filters_for_slot(int $slotid): array { global $DB; $filters = $DB->get_records(self::TABLE_SLOT_FILTERS, ['slotid' => $slotid]); - $filtersObj = []; - foreach($filters as $filter){ - array_push($filtersObj, new slot_filter(...$filter)); + $filtersobj = []; + foreach ($filters as $filter) { + array_push($filtersobj, new slot_filter(...$filter)); } - return $filtersObj; + return $filtersobj; } } diff --git a/lbplanner/classes/model/reservation.php b/lbplanner/classes/model/reservation.php index 900190f2..d3af0ddb 100644 --- a/lbplanner/classes/model/reservation.php +++ b/lbplanner/classes/model/reservation.php @@ -24,6 +24,7 @@ namespace local_lbplanner\model; +use DateTimeImmutable; use local_lbplanner\model\slot; use local_lbplanner\helpers\slot_helper; @@ -31,14 +32,43 @@ * Model class for reservation */ class reservation { + /** + * @var int $id ID of reservation + */ public int $id; + /** + * @var int $slotid ID of the linked slot + * @link slot + */ public int $slotid; - public \DateTimeImmutable $date; + /** + * @var DateTimeImmutable $date date this reservation is on (time will be ignored) + */ + public DateTimeImmutable $date; + /** + * @var int $userid ID of the user this reservation is for + */ public int $userid; + /** + * @var int $reserverid ID of the user who submitted this reservation (either pupil or supervisor) + */ public int $reserverid; + /** + * @var ?slot $slot the linked slot (gets filled in by helper functions) + * @link slot + */ private ?slot $slot; - public function __construct(int $id, int $slotid, \DateTimeImmutable $date, int $userid, int $reserverid) { + /** + * Constructs a reservation + * @param int $id ID of reservation + * @param int $slotid ID of the linked slot + * @param DateTimeImmutable $date date this reservation is on (time will be ignored) + * @param int $userid ID of the user this reservation is for + * @param int $reserverid ID of the user who submitted this reservation (either pupil or supervisor) + * @link slot + */ + public function __construct(int $id, int $slotid, DateTimeImmutable $date, int $userid, int $reserverid) { $this->id = $id; $this->slotid = $slotid; $this->date = $date; @@ -50,10 +80,10 @@ public function __construct(int $id, int $slotid, \DateTimeImmutable $date, int /** * Returns the associated slot. * - * @returns slot the associated slot + * @return slot the associated slot */ public function get_slot(): slot { - if(is_null($this->slot)){ + if (is_null($this->slot)) { $this->slot = slot_helper::get_slot($this->slotid); } diff --git a/lbplanner/classes/model/slot.php b/lbplanner/classes/model/slot.php index b81c8058..42e5db3c 100644 --- a/lbplanner/classes/model/slot.php +++ b/lbplanner/classes/model/slot.php @@ -34,15 +34,53 @@ * Model class for slot */ class slot { + /** + * @var int $id ID of slot + */ public int $id; + /** + * @var int $startunit Unit this slot starts in + * @link slot_helper::SCHOOL_UNITS + */ public int $startunit; + /** + * @var int $duration duration of slot in units + * @link slot_helper::SCHOOL_UNITS + */ public int $duration; + /** + * @var int $weekday weekday this slot occurs in + * @link WEEKDAY + */ public int $weekday; + /** + * @var string $room room this slot is for + */ public string $room; + /** + * @var int $size how many pupils fit in this slot + */ public int $size; + /** + * @var ?int $fullness how many pupils have already reserved this slot (gets filled in by helper functions) + */ private ?int $fullness; - private ?bool $for_curuser; + /** + * @var ?bool $forcuruser whether the current user has reserved this slot (gets filled in by helper functions) + */ + private ?bool $forcuruser; + /** + * Constructs a new Slot + * @param int $id ID of slot + * @param int $startunit Unit this slot starts in + * @param int $duration duration of slot in units + * @param int $weekday weekday this slot occurs in + * @param string $room room this slot is for + * @param int $size how many pupils fit in this slot + * @link slot_helper::SCHOOL_UNITS + * @link WEEKDAY + */ public function __construct(int $id, int $startunit, int $duration, int $weekday, string $room, int $size) { $this->id = $id; assert($startunit > 0); @@ -55,7 +93,7 @@ public function __construct(int $id, int $startunit, int $duration, int $weekday assert($size >= 0); // Make it technically possible to not allow any students in a room to temporarily disable the slot. $this->size = $size; $this->fullness = null; - $this->for_curuser = null; + $this->forcuruser = null; } /** @@ -64,8 +102,8 @@ public function __construct(int $id, int $startunit, int $duration, int $weekday * @return int fullness */ public function get_fullness(): int { - if(is_null($this->fullness)){ - $this->_check_reservations(); + if (is_null($this->fullness)) { + $this->check_reservations(); } return $this->fullness; @@ -74,14 +112,14 @@ public function get_fullness(): int { /** * Returns whether the current user has a reservation for this slot. * - * @return bool for_curuser + * @return bool forcuruser */ - public function get_for_curuser(): bool { - if(is_null($this->for_curuser)){ - $this->_check_reservations(); + public function get_forcuruser(): bool { + if (is_null($this->forcuruser)) { + $this->check_reservations(); } - return $this->for_curuser; + return $this->forcuruser; } /** @@ -98,7 +136,7 @@ public function prepare_for_api(): array { 'room' => $this->room, 'size' => $this->size, 'fullness' => $this->get_fullness(), - 'for_curuser' => $this->get_for_curuser(), + 'forcuruser' => $this->get_forcuruser(), ]; } @@ -117,7 +155,7 @@ public static function api_structure(): external_single_structure { 'room' => new external_value(PARAM_TEXT, 'The room this slot is for'), 'size' => new external_value(PARAM_INT, 'total capacity of the slot'), 'fullness' => new external_value(PARAM_INT, 'how many people have already reserved this slot'), - 'for_curuser' => new external_value(PARAM_BOOL, 'whether the current user has reserved this slot'), + 'forcuruser' => new external_value(PARAM_BOOL, 'whether the current user has reserved this slot'), ] ); } @@ -125,18 +163,18 @@ public static function api_structure(): external_single_structure { /** * Queries reservations for this slot and fills in internal data with that info. */ - private function _check_reservations(): void { + private function check_reservations(): void { global $USER; $reservations = slot_helper::get_reservations_for_slot($this->id); - $this->fullness = sizeof($reservations); + $this->fullness = count($reservations); - foreach($reservations as $reservation){ - if($reservation->userid == $USER['id']){ - $this->for_curuser = true; + foreach ($reservations as $reservation) { + if ($reservation->userid === $USER['id']) { + $this->forcuruser = true; return; } } - $this->for_curuser = false; + $this->forcuruser = false; } } diff --git a/lbplanner/classes/model/slot_filter.php b/lbplanner/classes/model/slot_filter.php index e3334260..d798f2c7 100644 --- a/lbplanner/classes/model/slot_filter.php +++ b/lbplanner/classes/model/slot_filter.php @@ -28,17 +28,37 @@ * Model class for a filter for slots */ class slot_filter { + /** + * @var int $id ID of filter + */ public int $id; + /** + * @var int $id ID of linked slot + */ public int $slotid; + /** + * @var ?int $id ID of linked course or null if any + */ public ?int $courseid; + /** + * @var ?string $vintage linked class or null if any + */ public ?string $vintage; + /** + * Constructs new slot_filter + * @param int $id ID of filter + * @param int $slotid ID of linked slot + * @param ?int $courseid ID of linked course or null if any + * @param ?string $vintage linked class or null if any + */ public function __construct(int $id, int $slotid, ?int $courseid, ?string $vintage) { $this->id = $id; $this->slotid = $slotid; $this->courseid = $courseid; - if(!is_null($vintage)) + if (!is_null($vintage)) { assert(strlen($vintage) <= 7); + } $this->vintage = $vintage; } } diff --git a/lbplanner/classes/polyfill/Enum.php b/lbplanner/classes/polyfill/Enum.php index a85422a4..e5c4d2a3 100644 --- a/lbplanner/classes/polyfill/Enum.php +++ b/lbplanner/classes/polyfill/Enum.php @@ -39,30 +39,32 @@ class Enum { * @return ?EnumCase the matching enum case or null if not found and $try==true * @throws ValueError if not found and $try==false */ - private static function _find(mixed $value, bool $try): ?EnumCase { + private static function find(mixed $value, bool $try): ?EnumCase { foreach (static::cases() as $case) { if ($case->value === $value) { return $case; } } - if($try) + if ($try) { return null; - else + } else { throw new ValueError("value {$value} cannot be represented as a value in enum ".static::class); + } } /** * tries to match the passed value to one of the enum values * @param mixed $value the value to be matched * @return mixed either the matching enum value or null if not found */ - public static function try_from(mixed $value): ?mixed { + public static function try_from(mixed $value): mixed { // TODO: replace with nullsafe operator in php8. - $case = static::_find($value, true); - if(is_null($case)) + $case = static::find($value, true); + if (is_null($case)) { return null; - else + } else { return $case->value; + } } /** * tries to match the passed value to one of the enum values @@ -71,7 +73,7 @@ public static function try_from(mixed $value): ?mixed { * @throws ValueError if not found */ public static function from(mixed $value): mixed { - return static::_find($value, false)->value; + return static::find($value, false)->value; } /** * tries to match the passed value to one of the enum values @@ -81,11 +83,12 @@ public static function from(mixed $value): mixed { */ public static function try_name_from(mixed $value): ?string { // TODO: replace with nullsafe operator in php8. - $case = static::_find($value, true); - if(is_null($case)) + $case = static::find($value, true); + if (is_null($case)) { return null; - else + } else { return $case->name; + } } /** * tries to match the passed value to one of the enum values @@ -94,7 +97,7 @@ public static function try_name_from(mixed $value): ?string { * @throws ValueError if not found */ public static function name_from(mixed $value): string { - return static::_find($value, false)->name; + return static::find($value, false)->name; } /** * Returns an array of all the cases that exist in this enum diff --git a/lbplanner/services/slots/get_my_slots.php b/lbplanner/services/slots/get_my_slots.php index ddb76a47..414a373c 100644 --- a/lbplanner/services/slots/get_my_slots.php +++ b/lbplanner/services/slots/get_my_slots.php @@ -52,20 +52,20 @@ public static function get_my_slots() { // NOTE: could be better solved by applying filters within one complex SQL query. // Oh well. - $allSlots = slot_helper::get_all_slots(); + $allslots = slot_helper::get_all_slots(); - $myCourses = self::call_external_function('local_lbplanner_courses_get_all_courses', ['userid' => $USER->id]); - $myCourseids = []; - foreach ($myCourses as $course) { - array_push($myCourseids, $course->courseid); + $mycourses = self::call_external_function('local_lbplanner_courses_get_all_courses', ['userid' => $USER->id]); + $mycourseids = []; + foreach ($mycourses as $course) { + array_push($mycourseids, $course->courseid); } $mySlots = []; - foreach ($allSlots as $slot) { + foreach ($allslots as $slot) { $filters = slot_helper::get_filters_for_slot($slot->id); - foreach($filters as $filter) { + foreach ($filters as $filter) { // Checking for course ID. - if (!is_null($filter->courseid) && !in_array($filter->courseid, $myCourseids)) { + if (!is_null($filter->courseid) && !in_array($filter->courseid, $mycourseids)) { continue; } // TODO: replace address with cohorts. @@ -74,7 +74,7 @@ public static function get_my_slots() { continue; } // If all filters passed, add slot to my slots and break. - array_push($mySlots, $slot); + array_push($myslots, $slot); break; } } From 46480f167d289dc5d0451b3f314984c7b841b0ab Mon Sep 17 00:00:00 2001 From: RiedleroD Date: Wed, 17 Apr 2024 13:22:08 +0200 Subject: [PATCH 10/19] KILL KILL KILL GRRR --- lbplanner/classes/helpers/slot_helper.php | 2 +- lbplanner/classes/model/reservation.php | 2 -- lbplanner/classes/model/slot.php | 3 --- lbplanner/services/slots/get_my_slots.php | 4 ++-- 4 files changed, 3 insertions(+), 8 deletions(-) diff --git a/lbplanner/classes/helpers/slot_helper.php b/lbplanner/classes/helpers/slot_helper.php index 1d23f615..7124b542 100644 --- a/lbplanner/classes/helpers/slot_helper.php +++ b/lbplanner/classes/helpers/slot_helper.php @@ -112,7 +112,7 @@ public static function get_reservations_for_slot(int $slotid): array { $reservationsobj = []; foreach ($reservations as $reservation) { $reservation['date'] = new \DateTime($reservation['date']); - array_push($reservationsObj, new reservation(...$reservation)); + array_push($reservationsobj, new reservation(...$reservation)); } return $reservationsobj; diff --git a/lbplanner/classes/model/reservation.php b/lbplanner/classes/model/reservation.php index d3af0ddb..513009b0 100644 --- a/lbplanner/classes/model/reservation.php +++ b/lbplanner/classes/model/reservation.php @@ -38,7 +38,6 @@ class reservation { public int $id; /** * @var int $slotid ID of the linked slot - * @link slot */ public int $slotid; /** @@ -55,7 +54,6 @@ class reservation { public int $reserverid; /** * @var ?slot $slot the linked slot (gets filled in by helper functions) - * @link slot */ private ?slot $slot; diff --git a/lbplanner/classes/model/slot.php b/lbplanner/classes/model/slot.php index 42e5db3c..8e2fa1e0 100644 --- a/lbplanner/classes/model/slot.php +++ b/lbplanner/classes/model/slot.php @@ -40,17 +40,14 @@ class slot { public int $id; /** * @var int $startunit Unit this slot starts in - * @link slot_helper::SCHOOL_UNITS */ public int $startunit; /** * @var int $duration duration of slot in units - * @link slot_helper::SCHOOL_UNITS */ public int $duration; /** * @var int $weekday weekday this slot occurs in - * @link WEEKDAY */ public int $weekday; /** diff --git a/lbplanner/services/slots/get_my_slots.php b/lbplanner/services/slots/get_my_slots.php index 414a373c..3c99f00f 100644 --- a/lbplanner/services/slots/get_my_slots.php +++ b/lbplanner/services/slots/get_my_slots.php @@ -60,7 +60,7 @@ public static function get_my_slots() { array_push($mycourseids, $course->courseid); } - $mySlots = []; + $myslots = []; foreach ($allslots as $slot) { $filters = slot_helper::get_filters_for_slot($slot->id); foreach ($filters as $filter) { @@ -82,7 +82,7 @@ public static function get_my_slots() { $now = new DateTimeImmutable(); $returnslots = []; // Calculate date and time each slot happens next, and add it to the return list if within reach from today. - foreach ($mySlots as $slot) { + foreach ($myslots as $slot) { $slotdaytime = slot_helper::SCHOOL_UNITS[$slot->startunit]; $slotdatetime = DateTime::createFromFormat('Y-m-d H:i', $now->format('Y-m-d ').$slotdaytime); // Move to next day this weekday occurs (doesn't move if it's the same as today). From d0a459c44160e1ce4f8f65ab70d6473e5267c0a5 Mon Sep 17 00:00:00 2001 From: RiedleroD Date: Wed, 17 Apr 2024 13:49:46 +0200 Subject: [PATCH 11/19] HOTFIX: oops I fucked up :) --- lbplanner/services/slots/get_my_slots.php | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/lbplanner/services/slots/get_my_slots.php b/lbplanner/services/slots/get_my_slots.php index 3c99f00f..1692b0c9 100644 --- a/lbplanner/services/slots/get_my_slots.php +++ b/lbplanner/services/slots/get_my_slots.php @@ -21,7 +21,7 @@ use DateTimeImmutable; use external_api; use external_function_parameters; -use external_single_structure; +use external_multiple_structure; use local_lbplanner\helpers\slot_helper; use local_lbplanner\model\slot; use local_lbplanner\enums\WEEKDAY; @@ -35,7 +35,7 @@ * @copyright 2024 necodeIT * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class get_slots extends external_api { +class get_my_slots extends external_api { /** * Parameters for get_my_slots. * @return external_function_parameters @@ -104,9 +104,11 @@ public static function get_my_slots() { /** * Returns the structure of the slot array - * @return external_single_structure + * @return external_multiple_structure */ - public static function get_my_slots_returns(): external_single_structure { - return slot::api_structure(); + public static function get_my_slots_returns(): external_multiple_structure { + return new external_multiple_structure( + slot::api_structure() + ); } } From 69aa697c3fcdd0f9bea14d52a4a94c962b089d65 Mon Sep 17 00:00:00 2001 From: RiedleroD Date: Wed, 17 Apr 2024 15:02:13 +0200 Subject: [PATCH 12/19] generalized parts of get_my_slots for/to slot_helper --- lbplanner/classes/helpers/slot_helper.php | 68 +++++++++++++++++++++++ lbplanner/services/slots/get_my_slots.php | 51 ++--------------- 2 files changed, 72 insertions(+), 47 deletions(-) diff --git a/lbplanner/classes/helpers/slot_helper.php b/lbplanner/classes/helpers/slot_helper.php index 7124b542..99a1633a 100644 --- a/lbplanner/classes/helpers/slot_helper.php +++ b/lbplanner/classes/helpers/slot_helper.php @@ -24,6 +24,12 @@ namespace local_lbplanner\helpers; +use DateInterval; +use DateTime; +use DateTimeImmutable; + +use external_api; +use local_lbplanner\enums\WEEKDAY; use local_lbplanner\model\{slot, reservation, slot_filter}; /** @@ -135,4 +141,66 @@ public static function get_filters_for_slot(int $slotid): array { return $filtersobj; } + + /** + * Filters an array of slots for the slots that the user can reserve. + * @param slot[] $allslots the slots to filter + * @param mixed $user a user object - e.g. $USER or a user object from the database + * @return slot[] the filtered slot array + */ + public static function filter_slots_for_user(array $allslots, mixed $user): array { + $mycourses = external_api::call_external_function('local_lbplanner_courses_get_all_courses', ['userid' => $user->id]); + $mycourseids = []; + foreach ($mycourses as $course) { + array_push($mycourseids, $course->courseid); + } + + $slots = []; + foreach ($allslots as $slot) { + $filters = slot_helper::get_filters_for_slot($slot->id); + foreach ($filters as $filter) { + // Checking for course ID. + if (!is_null($filter->courseid) && !in_array($filter->courseid, $mycourseids)) { + continue; + } + // TODO: replace address with cohorts. + // Checking for vintage. + if (!is_null($filter->vintage) && $user->address !== $filter->vintage) { + continue; + } + // If all filters passed, add slot to my slots and break. + array_push($slots, $slot); + break; + } + } + return $slots; + } + + /** + * Filters an array of slots for a timerange around now. + * @param slot[] $allslots the slots to filter + * @param DateTimeImmutable $now a point in time representing the point in time to start filtering at + * @param int $range how many days in the future the slot is allowed to be + * @return slot[] the filtered slot array + */ + public static function filter_slots_for_time(array $allslots, DateTimeImmutable $now, int $range): array { + $slots = []; + // Calculate date and time each slot happens next, and add it to the return list if within reach from today. + foreach ($allslots as $slot) { + $slotdaytime = slot_helper::SCHOOL_UNITS[$slot->startunit]; + $slotdatetime = DateTime::createFromFormat('Y-m-d H:i', $now->format('Y-m-d ').$slotdaytime); + // Move to next day this weekday occurs (doesn't move if it's the same as today). + $slotdatetime->modify('this '.WEEKDAY::name_from($slot->weekday)); + + // Check if slot is before now (because time of day and such) and move it a week into the future if so. + if ($now->diff($slotdatetime)->invert === 1) { + $slotdatetime->add(new DateInterval('P1W')); + } + + if ($now->diff($slotdatetime)->days <= $range) { + array_push($slots, $slot->prepare_for_api()); + } + } + return $slots; + } } diff --git a/lbplanner/services/slots/get_my_slots.php b/lbplanner/services/slots/get_my_slots.php index 1692b0c9..f8e9dc59 100644 --- a/lbplanner/services/slots/get_my_slots.php +++ b/lbplanner/services/slots/get_my_slots.php @@ -16,15 +16,13 @@ namespace local_lbplanner_services; -use DateInterval; -use DateTime; use DateTimeImmutable; + use external_api; use external_function_parameters; use external_multiple_structure; use local_lbplanner\helpers\slot_helper; use local_lbplanner\model\slot; -use local_lbplanner\enums\WEEKDAY; /** * Returns all slots the user can theoretically reserve. @@ -49,55 +47,14 @@ public static function get_my_slots_parameters(): external_function_parameters { */ public static function get_my_slots() { global $USER; - // NOTE: could be better solved by applying filters within one complex SQL query. - // Oh well. $allslots = slot_helper::get_all_slots(); - $mycourses = self::call_external_function('local_lbplanner_courses_get_all_courses', ['userid' => $USER->id]); - $mycourseids = []; - foreach ($mycourses as $course) { - array_push($mycourseids, $course->courseid); - } - - $myslots = []; - foreach ($allslots as $slot) { - $filters = slot_helper::get_filters_for_slot($slot->id); - foreach ($filters as $filter) { - // Checking for course ID. - if (!is_null($filter->courseid) && !in_array($filter->courseid, $mycourseids)) { - continue; - } - // TODO: replace address with cohorts. - // Checking for vintage. - if (!is_null($filter->vintage) && $USER->address !== $filter->vintage) { - continue; - } - // If all filters passed, add slot to my slots and break. - array_push($myslots, $slot); - break; - } - } + $myslots = slot_helper::filter_slots_for_user($allslots, $USER); $now = new DateTimeImmutable(); - $returnslots = []; - // Calculate date and time each slot happens next, and add it to the return list if within reach from today. - foreach ($myslots as $slot) { - $slotdaytime = slot_helper::SCHOOL_UNITS[$slot->startunit]; - $slotdatetime = DateTime::createFromFormat('Y-m-d H:i', $now->format('Y-m-d ').$slotdaytime); - // Move to next day this weekday occurs (doesn't move if it's the same as today). - $slotdatetime->modify('this '.WEEKDAY::name_from($slot->weekday)); - - // Check if slot is before now (because time of day and such) and move it a week into the future if so. - if ($now->diff($slotdatetime)->invert === 1) { - $slotdatetime->add(new DateInterval('P1W')); - } - - // TODO: make setting of "3 days in advance" changeable. - if ($now->diff($slotdatetime)->days <= 3) { - array_push($returnslots, $slot->prepare_for_api()); - } - } + // TODO: make setting of "3 days in advance" changeable. + $returnslots = slot_helper::filter_slots_for_time($myslots, $now, 3); return $returnslots; } From 4c00b96eaec4b158151a1594a2f8f11686f958ce Mon Sep 17 00:00:00 2001 From: RiedleroD Date: Wed, 17 Apr 2024 15:43:12 +0200 Subject: [PATCH 13/19] added get_student_slots API call + minor refactors --- lbplanner/classes/helpers/slot_helper.php | 36 ++++++++- lbplanner/services/slots/get_my_slots.php | 8 +- .../services/slots/get_student_slots.php | 74 +++++++++++++++++++ 3 files changed, 110 insertions(+), 8 deletions(-) create mode 100644 lbplanner/services/slots/get_student_slots.php diff --git a/lbplanner/classes/helpers/slot_helper.php b/lbplanner/classes/helpers/slot_helper.php index 99a1633a..38a196ac 100644 --- a/lbplanner/classes/helpers/slot_helper.php +++ b/lbplanner/classes/helpers/slot_helper.php @@ -36,6 +36,14 @@ * Provides helper methods for any tables related with the planning function of the app */ class slot_helper { + /** + * how far into the future a user can reserve a slot + */ + const RESERVATION_RANGE_USER = 3; + /** + * how far into the future a supervisor can reserve a slot for a user + */ + const RESERVATION_RANGE_SUPERVISOR = 7; /** * school units according to untis, in H:i format */ @@ -92,6 +100,30 @@ public static function get_all_slots(): array { return $slotsobj; } + /** + * Returns a list of all slots belonging to a supervisor. + * @param int $supervisorid userid of the supervisor in question + * + * @return slot[] An array of the slots. + */ + public static function get_supervisor_slots(int $supervisorid): array { + global $DB; + + $slots = $DB->get_records_sql( + 'SELECT slot.* FROM {'.self::TABLE_SLOTS.'} as slot'. + 'INNER JOIN '.self::TABLE_SUPERVISORS.' as supervisor ON supervisor.slotid=slot.id'. + 'WHERE supervisor.userid=?', + [$supervisorid] + ); + + $slotsobj = []; + foreach ($slots as $slot) { + array_push($slotsobj, new slot(...$slot)); + } + + return $slotsobj; + } + /** * Returns a singular slot. * @param int $slotid ID of the slot @@ -179,11 +211,11 @@ public static function filter_slots_for_user(array $allslots, mixed $user): arra /** * Filters an array of slots for a timerange around now. * @param slot[] $allslots the slots to filter - * @param DateTimeImmutable $now a point in time representing the point in time to start filtering at * @param int $range how many days in the future the slot is allowed to be * @return slot[] the filtered slot array */ - public static function filter_slots_for_time(array $allslots, DateTimeImmutable $now, int $range): array { + public static function filter_slots_for_time(array $allslots, int $range): array { + $now = new DateTimeImmutable(); $slots = []; // Calculate date and time each slot happens next, and add it to the return list if within reach from today. foreach ($allslots as $slot) { diff --git a/lbplanner/services/slots/get_my_slots.php b/lbplanner/services/slots/get_my_slots.php index f8e9dc59..84df1b3c 100644 --- a/lbplanner/services/slots/get_my_slots.php +++ b/lbplanner/services/slots/get_my_slots.php @@ -16,8 +16,6 @@ namespace local_lbplanner_services; -use DateTimeImmutable; - use external_api; use external_function_parameters; use external_multiple_structure; @@ -45,16 +43,14 @@ public static function get_my_slots_parameters(): external_function_parameters { /** * Returns slots the current user is supposed to see */ - public static function get_my_slots() { + public static function get_my_slots(): array { global $USER; $allslots = slot_helper::get_all_slots(); $myslots = slot_helper::filter_slots_for_user($allslots, $USER); - $now = new DateTimeImmutable(); - // TODO: make setting of "3 days in advance" changeable. - $returnslots = slot_helper::filter_slots_for_time($myslots, $now, 3); + $returnslots = slot_helper::filter_slots_for_time($myslots, slot_helper::RESERVATION_RANGE_USER); return $returnslots; } diff --git a/lbplanner/services/slots/get_student_slots.php b/lbplanner/services/slots/get_student_slots.php new file mode 100644 index 00000000..95201c43 --- /dev/null +++ b/lbplanner/services/slots/get_student_slots.php @@ -0,0 +1,74 @@ +. + +namespace local_lbplanner_services; + +use external_api; +use external_function_parameters; +use external_multiple_structure; +use external_value; +use local_lbplanner\helpers\slot_helper; +use local_lbplanner\model\slot; + +/** + * Returns all slots a supervisor can theoretically reserve for a user. + * This does not include times the user has already reserved a slot for. + * + * @package local_lbplanner + * @subpackage services_plan + * @copyright 2024 necodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class get_student_slots extends external_api { + /** + * Parameters for get_student_slots. + * @return external_function_parameters + */ + public static function get_my_slots_parameters(): external_function_parameters { + return new external_function_parameters([ + 'userid' => new external_value(PARAM_INT, 'ID of the user to query for', VALUE_REQUIRED, null, NULL_NOT_ALLOWED), + ]); + } + + /** + * Returns slots of a user the supervisor can see. + */ + public static function get_student_slots(int $userid): array { + global $USER; + self::validate_parameters( + self::get_my_slots_parameters(), + ['userid' => $userid] + ); + + $superslots = slot_helper::get_supervisor_slots($USER->id); + + $myslots = slot_helper::filter_slots_for_user($superslots, $userid); + + $returnslots = slot_helper::filter_slots_for_time($myslots, slot_helper::RESERVATION_RANGE_SUPERVISOR); + + return $returnslots; + } + + /** + * Returns the structure of the slot array + * @return external_multiple_structure + */ + public static function get_student_slots_returns(): external_multiple_structure { + return new external_multiple_structure( + slot::api_structure() + ); + } +} From febdce9b51c9b449e7cc4fffc3a3d4b2aae89aa8 Mon Sep 17 00:00:00 2001 From: RiedleroD Date: Wed, 17 Apr 2024 15:52:40 +0200 Subject: [PATCH 14/19] made codesniffer happy hopefully --- lbplanner/classes/helpers/slot_helper.php | 4 ++-- lbplanner/services/slots/get_my_slots.php | 2 +- lbplanner/services/slots/get_student_slots.php | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/lbplanner/classes/helpers/slot_helper.php b/lbplanner/classes/helpers/slot_helper.php index 38a196ac..576d88b8 100644 --- a/lbplanner/classes/helpers/slot_helper.php +++ b/lbplanner/classes/helpers/slot_helper.php @@ -189,7 +189,7 @@ public static function filter_slots_for_user(array $allslots, mixed $user): arra $slots = []; foreach ($allslots as $slot) { - $filters = slot_helper::get_filters_for_slot($slot->id); + $filters = self::get_filters_for_slot($slot->id); foreach ($filters as $filter) { // Checking for course ID. if (!is_null($filter->courseid) && !in_array($filter->courseid, $mycourseids)) { @@ -219,7 +219,7 @@ public static function filter_slots_for_time(array $allslots, int $range): array $slots = []; // Calculate date and time each slot happens next, and add it to the return list if within reach from today. foreach ($allslots as $slot) { - $slotdaytime = slot_helper::SCHOOL_UNITS[$slot->startunit]; + $slotdaytime = self::SCHOOL_UNITS[$slot->startunit]; $slotdatetime = DateTime::createFromFormat('Y-m-d H:i', $now->format('Y-m-d ').$slotdaytime); // Move to next day this weekday occurs (doesn't move if it's the same as today). $slotdatetime->modify('this '.WEEKDAY::name_from($slot->weekday)); diff --git a/lbplanner/services/slots/get_my_slots.php b/lbplanner/services/slots/get_my_slots.php index 84df1b3c..5198fc84 100644 --- a/lbplanner/services/slots/get_my_slots.php +++ b/lbplanner/services/slots/get_my_slots.php @@ -31,7 +31,7 @@ * @copyright 2024 necodeIT * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class get_my_slots extends external_api { +class slots_get_my_slots extends external_api { /** * Parameters for get_my_slots. * @return external_function_parameters diff --git a/lbplanner/services/slots/get_student_slots.php b/lbplanner/services/slots/get_student_slots.php index 95201c43..1cde91e2 100644 --- a/lbplanner/services/slots/get_student_slots.php +++ b/lbplanner/services/slots/get_student_slots.php @@ -32,7 +32,7 @@ * @copyright 2024 necodeIT * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class get_student_slots extends external_api { +class slots_get_student_slots extends external_api { /** * Parameters for get_student_slots. * @return external_function_parameters @@ -45,6 +45,7 @@ public static function get_my_slots_parameters(): external_function_parameters { /** * Returns slots of a user the supervisor can see. + * @param int $userid ID of the user in question (NOT the supervisor) */ public static function get_student_slots(int $userid): array { global $USER; From 1636705764c62137b5df5b21f863e21fadd00f4b Mon Sep 17 00:00:00 2001 From: RiedleroD Date: Wed, 17 Apr 2024 16:22:36 +0200 Subject: [PATCH 15/19] fixed oopsie --- lbplanner/services/slots/get_student_slots.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lbplanner/services/slots/get_student_slots.php b/lbplanner/services/slots/get_student_slots.php index 1cde91e2..2a9df0e1 100644 --- a/lbplanner/services/slots/get_student_slots.php +++ b/lbplanner/services/slots/get_student_slots.php @@ -37,7 +37,7 @@ class slots_get_student_slots extends external_api { * Parameters for get_student_slots. * @return external_function_parameters */ - public static function get_my_slots_parameters(): external_function_parameters { + public static function get_student_slots_parameters(): external_function_parameters { return new external_function_parameters([ 'userid' => new external_value(PARAM_INT, 'ID of the user to query for', VALUE_REQUIRED, null, NULL_NOT_ALLOWED), ]); @@ -50,7 +50,7 @@ public static function get_my_slots_parameters(): external_function_parameters { public static function get_student_slots(int $userid): array { global $USER; self::validate_parameters( - self::get_my_slots_parameters(), + self::get_student_slots_parameters(), ['userid' => $userid] ); From e70b78c040a782079ab5614bd874a80c23e03912 Mon Sep 17 00:00:00 2001 From: RiedleroD Date: Wed, 17 Apr 2024 16:24:33 +0200 Subject: [PATCH 16/19] implemented get_supervisor_slots --- .../services/slots/get_supervisor_slots.php | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 lbplanner/services/slots/get_supervisor_slots.php diff --git a/lbplanner/services/slots/get_supervisor_slots.php b/lbplanner/services/slots/get_supervisor_slots.php new file mode 100644 index 00000000..2a50347a --- /dev/null +++ b/lbplanner/services/slots/get_supervisor_slots.php @@ -0,0 +1,65 @@ +. + +namespace local_lbplanner_services; + +use external_api; +use external_function_parameters; +use external_multiple_structure; +use local_lbplanner\helpers\slot_helper; +use local_lbplanner\model\slot; + +/** + * Returns all slots a supervisor can see. + * + * @package local_lbplanner + * @subpackage services_plan + * @copyright 2024 necodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class slots_get_supervisor_slots extends external_api { + /** + * Parameters for get_supervisor_slots. + * @return external_function_parameters + */ + public static function get_supervisor_slots_parameters(): external_function_parameters { + return new external_function_parameters([]); + } + + /** + * Returns all slots a supervisor controls. + * @param int $userid ID of the user in question (NOT the supervisor) + */ + public static function get_supervisor_slots(int $userid): array { + global $USER; + self::validate_parameters( + self::get_supervisor_slots_parameters(), + ['userid' => $userid] + ); + + return slot_helper::get_supervisor_slots($USER->id); + } + + /** + * Returns the structure of the slot array + * @return external_multiple_structure + */ + public static function get_supervisor_slots_returns(): external_multiple_structure { + return new external_multiple_structure( + slot::api_structure() + ); + } +} From f60b499c23d1d4efe4b14bd79443d3756ff55c27 Mon Sep 17 00:00:00 2001 From: RiedleroD Date: Sat, 13 Jul 2024 15:20:26 +0200 Subject: [PATCH 17/19] feat: booking reservations --- lbplanner/classes/helpers/slot_helper.php | 56 +++++-- lbplanner/classes/model/reservation.php | 56 +++++++ lbplanner/db/services.php | 40 +++++ lbplanner/services/slots/book_reservation.php | 142 ++++++++++++++++++ 4 files changed, 282 insertions(+), 12 deletions(-) create mode 100644 lbplanner/services/slots/book_reservation.php diff --git a/lbplanner/classes/helpers/slot_helper.php b/lbplanner/classes/helpers/slot_helper.php index 576d88b8..f487d4b2 100644 --- a/lbplanner/classes/helpers/slot_helper.php +++ b/lbplanner/classes/helpers/slot_helper.php @@ -27,7 +27,7 @@ use DateInterval; use DateTime; use DateTimeImmutable; - +use DateTimeInterface; use external_api; use local_lbplanner\enums\WEEKDAY; use local_lbplanner\model\{slot, reservation, slot_filter}; @@ -149,7 +149,7 @@ public static function get_reservations_for_slot(int $slotid): array { $reservationsobj = []; foreach ($reservations as $reservation) { - $reservation['date'] = new \DateTime($reservation['date']); + $reservation['date'] = new DateTimeImmutable($reservation['date']); array_push($reservationsobj, new reservation(...$reservation)); } @@ -175,7 +175,8 @@ public static function get_filters_for_slot(int $slotid): array { } /** - * Filters an array of slots for the slots that the user can reserve. + * Filters an array of slots for the slots that the user can theoretically reserve + * NOTE: not taking into account time or fullness, only filters i.e. users' class and courses * @param slot[] $allslots the slots to filter * @param mixed $user a user object - e.g. $USER or a user object from the database * @return slot[] the filtered slot array @@ -219,15 +220,7 @@ public static function filter_slots_for_time(array $allslots, int $range): array $slots = []; // Calculate date and time each slot happens next, and add it to the return list if within reach from today. foreach ($allslots as $slot) { - $slotdaytime = self::SCHOOL_UNITS[$slot->startunit]; - $slotdatetime = DateTime::createFromFormat('Y-m-d H:i', $now->format('Y-m-d ').$slotdaytime); - // Move to next day this weekday occurs (doesn't move if it's the same as today). - $slotdatetime->modify('this '.WEEKDAY::name_from($slot->weekday)); - - // Check if slot is before now (because time of day and such) and move it a week into the future if so. - if ($now->diff($slotdatetime)->invert === 1) { - $slotdatetime->add(new DateInterval('P1W')); - } + $slotdatetime = self::calculate_slot_datetime($slot, $now); if ($now->diff($slotdatetime)->days <= $range) { array_push($slots, $slot->prepare_for_api()); @@ -235,4 +228,43 @@ public static function filter_slots_for_time(array $allslots, int $range): array } return $slots; } + + /** + * calculates when a slot is to happen next + * @param slot $slot the slot + * @param DateTimeInterface $now the point in time representing now + * @return DateTimeImmutable the next time this slot will occur + */ + public static function calculate_slot_datetime(slot $slot, DateTimeInterface $now): DateTimeImmutable { + $slotdaytime = self::SCHOOL_UNITS[$slot->startunit]; + // NOTE: format and fromFormat use different date formatting conventions + $slotdatetime = DateTime::createFromFormat('YY-MM-DD tHH:MM', $now->format('Y-m-d ').$slotdaytime); + // Move to next day this weekday occurs (doesn't move if it's the same as today). + $slotdatetime->modify('this '.WEEKDAY::name_from($slot->weekday)); + + // Check if slot is before now (because time of day and such) and move it a week into the future if so. + if ($now->diff($slotdatetime)->invert === 1) { + $slotdatetime->add(new DateInterval('P1W')); + } + + return new DateTimeImmutable($slotdatetime); + } + + /** + * Returns a list of all slots belonging to a supervisor. + * @param int $supervisorid userid of the supervisor in question + * + * @return slot[] An array of the slots. + */ + public static function check_slot_supervisor(int $supervisorid, int $slotid): bool { + global $DB; + + $result = $DB->get_record_sql( + 'SELECT supervisor.userid FROM '.self::TABLE_SUPERVISORS.' as supervisor'. + 'WHERE supervisor.userid=? AND supervisor.slotid=?', + [$supervisorid, $slotid] + ); + + return $result !== false; + } } diff --git a/lbplanner/classes/model/reservation.php b/lbplanner/classes/model/reservation.php index 513009b0..681fc3e4 100644 --- a/lbplanner/classes/model/reservation.php +++ b/lbplanner/classes/model/reservation.php @@ -25,6 +25,10 @@ namespace local_lbplanner\model; use DateTimeImmutable; + +use external_single_structure; +use external_value; + use local_lbplanner\model\slot; use local_lbplanner\helpers\slot_helper; @@ -56,6 +60,10 @@ class reservation { * @var ?slot $slot the linked slot (gets filled in by helper functions) */ private ?slot $slot; + /** + * @var ?DateTimeImmutable $datetime the date this reservation is for, with time filled in + */ + private ?DateTimeImmutable $datetime; /** * Constructs a reservation @@ -87,4 +95,52 @@ public function get_slot(): slot { return $this->slot; } + + /** + * Prepares data for the DB endpoint. + * + * @return object a representation of this reservation and its data + */ + public function prepare_for_db(): object { + $obj = new \stdClass(); + + $obj->slotid = $this->slotid; + $obj->date = $this->date; + $obj->userid = $this->userid; + $obj->reserverid = $this->reserverid; + + return $obj; + } + + /** + * Prepares data for the API endpoint. + * + * @return array a representation of this reservation and its data + */ + public function prepare_for_api(): array { + return [ + 'id' => $this->id, + 'slotid' => $this->slotid, + 'datetime' => $this->date->format('Y-m-d'), + 'userid' => $this->userid, + 'reserverid' => $this->reserverid, + ]; + } + + /** + * Returns the data structure of a reservation for the API. + * + * @return external_single_structure The data structure of a reservation for the API. + */ + public static function api_structure(): external_single_structure { + return new external_single_structure( + [ + 'id' => new external_value(PARAM_INT, 'reservation ID'), + 'slotid' => new external_value(PARAM_INT, 'ID of associated slot'), + 'date' => new external_value(PARAM_TEXT, 'date of the reservation in YYYY-MM-DD (as per ISO-8601)'), + 'userid' => new external_value(PARAM_INT, 'ID of the user this reservation is for'), + 'reserverid' => new external_value(PARAM_INT, 'ID of the user who submitted this reservation'), + ] + ); + } } diff --git a/lbplanner/db/services.php b/lbplanner/db/services.php index 2bf60516..1c5c5859 100644 --- a/lbplanner/db/services.php +++ b/lbplanner/db/services.php @@ -286,6 +286,42 @@ 'capabilities' => '', 'ajax' => true, ], + 'local_lbplanner_slots_get_my_slot' => [ + 'classname' => 'local_lbplanner_services\slots_get_my_slot', + 'methodname' => 'get_my_slot', + 'classpath' => 'local/lbplanner/services/slots/get_my_slot.php', + 'description' => 'Get all slots the user can theoretically reserve.', + 'type' => 'read', + 'capabilities' => 'local/lb_planner:student', + 'ajax' => true, + ], + 'local_lbplanner_slots_get_student_slots' => [ + 'classname' => 'local_lbplanner_services\slots_get_student_slots', + 'methodname' => 'get_student_slots', + 'classpath' => 'local/lbplanner/services/slots/get_student_slots.php', + 'description' => 'Get all slots a supervisor can theoretically reserve for a student.', + 'type' => 'read', + 'capabilities' => 'local/lb_planner:student', + 'ajax' => true, + ], + 'local_lbplanner_slots_get_supervisor_slots' => [ + 'classname' => 'local_lbplanner_services\slots_get_supervisor_slots', + 'methodname' => 'get_supervisor_slots', + 'classpath' => 'local/lbplanner/services/slots/get_supervisor_slots.php', + 'description' => 'Get all slots belonging to the supervisor.', + 'type' => 'read', + 'capabilities' => 'local/lb_planner:student', + 'ajax' => true, + ], + 'local_lbplanner_slots_book_reservation' => [ + 'classname' => 'local_lbplanner_services\slots_book_reservation', + 'methodname' => 'book_reservation', + 'classpath' => 'local/lbplanner/services/slots/book_reservation.php', + 'description' => 'Book a reservation', + 'type' => 'write', + 'capabilities' => 'local/lb_planner:student', + 'ajax' => true, + ], ]; $services = [ @@ -321,6 +357,10 @@ 'local_lbplanner_plan_accept_invite', 'local_lbplanner_plan_decline_invite', 'local_lbplanner_config_get_version', + 'local_lbplanner_slots_get_my_slot', + 'local_lbplanner_slots_get_student_slots', + 'local_lbplanner_slots_get_supervisor_slots', + 'local_lbplanner_slots_book_reservation', ], 'restrictedusers' => 0, 'enabled' => 1, diff --git a/lbplanner/services/slots/book_reservation.php b/lbplanner/services/slots/book_reservation.php new file mode 100644 index 00000000..dab33ab3 --- /dev/null +++ b/lbplanner/services/slots/book_reservation.php @@ -0,0 +1,142 @@ +. + +namespace local_lbplanner_services; + +use DateTimeImmutable; + +use core_user; +use external_api; +use external_function_parameters; +use external_single_structure; +use external_value; + +use local_lbplanner\helpers\slot_helper; +use local_lbplanner\model\reservation; + +/** + * Returns all slots the user can theoretically reserve. + * This does not include times the user has already reserved a slot for. + * + * @package local_lbplanner + * @subpackage services_plan + * @copyright 2024 necodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class slots_book_reservation extends external_api { + /** + * Parameters for book_reservation. + * @return external_function_parameters + */ + public static function book_reservation_parameters(): external_function_parameters { + global $USER; + return new external_function_parameters([ + 'slotid' => new external_value( + PARAM_INT, + 'ID of the slot for which a reservation is being requested', + VALUE_REQUIRED, + null, + NULL_NOT_ALLOWED + ), + 'date' => new external_value( + PARAM_TEXT, + 'date of the reservation in YYYY-MM-DD (as per ISO-8601)', + VALUE_REQUIRED, + null, + NULL_NOT_ALLOWED + ), + 'userid' => new external_value( + PARAM_INT, + 'the user to reserve this slot for', + VALUE_OPTIONAL, + $USER->id, + NULL_NOT_ALLOWED + ), + ]); + } + + /** + * Returns slots the current user is supposed to see + */ + public static function book_reservation(int $slotid, string $date, int $userid): array { + global $USER, $DB; + + $now = new DateTimeImmutable(); + $dateobj = DateTimeImmutable::createFromFormat("YY-MM-DD", $date); + $td = $dateobj->diff($now); + + if($td->invert){ + throw new \moodle_exception('Can\'t reserve date in the past'); + } + + $maxdays = null; + $student = null; + + if ($userid === $USER->id) { + // student reserving slot for themself + + $maxdays = slot_helper::RESERVATION_RANGE_USER; + $student = $USER; + } else { + // supervisor reserving slot for student + + if (!slot_helper::check_slot_supervisor($USER->id, $slotid)) { + throw new \moodle_exception('Forbidden: you\'re not a supervisor of this slot'); + } + + $maxdays = slot_helper::RESERVATION_RANGE_USER; + $student = core_user::get_user($userid, '*', MUST_EXIST); + } + + if ($td->days > $maxdays) { + throw new \moodle_exception("Date is past allowed date ({$maxdays} days in the future)"); + } + + $slot = slot_helper::get_slot($slotid); + + // check if user has access to slot + if (sizeof(slot_helper::filter_slots_for_user([$slot], $student)) === 0) { + throw new \moodle_exception('Student does not have access to this slot'); + } + + // check if user is already in slot + foreach (slot_helper::get_reservations_for_slot($slotid) as $_reservation) { + if ($_reservation->userid === $userid){ + throw new \moodle_exception('Student is already in slot'); + } + } + + // check if slot is full + if ($slot->get_fullness() > $slot->size){ + throw new \moodle_exception('Slot is already full'); + } + + $reservation = new reservation(0, $slotid, $dateobj, $userid, $USER->id); + + $id = $DB->insert_record(slot_helper::TABLE_RESERVATIONS, $reservation->prepare_for_db()); + $reservation->id = $id; + + return $reservation->prepare_for_api(); + } + + /** + * Returns the structure of the slot array + * @return external_multiple_structure + */ + public static function book_reservation_returns(): external_single_structure { + return reservation::api_structure(); + } +} From ab7f1710c9ccd39207140d1d460832204b51bf2c Mon Sep 17 00:00:00 2001 From: Riedler Date: Mon, 12 Aug 2024 13:53:20 +0200 Subject: [PATCH 18/19] fix: major typo --- lbplanner/services/plan/set_deadline.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lbplanner/services/plan/set_deadline.php b/lbplanner/services/plan/set_deadline.php index 9ad07a8f..aac4d86d 100644 --- a/lbplanner/services/plan/set_deadline.php +++ b/lbplanner/services/plan/set_deadline.php @@ -34,7 +34,7 @@ class plan_set_deadline extends external_api { * Parameters for set_deadline. * @return external_function_parameters */ - public static function set_deadline_parameters() { + public static function set_deadline_parameters(): external_function_parameters { return new external_function_parameters([ 'moduleid' => new external_value( PARAM_INT, @@ -69,7 +69,7 @@ public static function set_deadline_parameters() { * @return void * @throws \moodle_exception when access denied */ - public static function set_deadline(int $moduleid, int $deadlinestart, int $deadlineend): external_function_parameters { + public static function set_deadline(int $moduleid, int $deadlinestart, int $deadlineend) { global $DB, $USER; self::validate_parameters( From 98b14242677311c7379eae69a1e08a1b2931d25b Mon Sep 17 00:00:00 2001 From: Riedler Date: Mon, 12 Aug 2024 13:53:50 +0200 Subject: [PATCH 19/19] internal: add .kateproject --- .kateproject | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .kateproject diff --git a/.kateproject b/.kateproject new file mode 100644 index 00000000..b9ffc1c6 --- /dev/null +++ b/.kateproject @@ -0,0 +1,9 @@ +{ + "lspclient": { + "servers": { + "php" : { + "root": "../moodle/" + } + } + } +} \ No newline at end of file