From 444e22061cef65960ef19e203e5ac8d1e82fb002 Mon Sep 17 00:00:00 2001 From: Felix Blanke <s6feblan@uni-bonn.de> Date: Tue, 11 Feb 2025 11:20:04 +0000 Subject: [PATCH] Expand block info in JSON export --- AKModel/tests/test_json_export.py | 187 ++++++++++++++++++++---------- AKModel/views/ak.py | 27 ++++- 2 files changed, 150 insertions(+), 64 deletions(-) diff --git a/AKModel/tests/test_json_export.py b/AKModel/tests/test_json_export.py index 5a604dc7..48c8d7fd 100644 --- a/AKModel/tests/test_json_export.py +++ b/AKModel/tests/test_json_export.py @@ -248,7 +248,7 @@ class JSONExportTest(TestCase): ) self.assertEqual( self.export_dict["timeslots"]["info"].keys(), - {"duration"}, + {"duration", "blocknames"}, "timeslot info keys not as expected", ) self._check_type( @@ -257,6 +257,21 @@ class JSONExportTest(TestCase): "info/duration", item=item, ) + self._check_lst( + self.export_dict["timeslots"]["info"]["blocknames"], + "info/blocknames", + item=item, + contained_type=list, + ) + for blockname in self.export_dict["timeslots"]["info"]["blocknames"]: + self.assertEqual(len(blockname), 2) + self._check_lst( + blockname, + "info/blocknames/entry", + item=item, + contained_type=str, + ) + self._check_lst( self.export_dict["timeslots"]["blocks"], "blocks", @@ -710,72 +725,120 @@ class JSONExportTest(TestCase): self.set_up_event(event=event) cat_avails = self._get_cat_availability() - for timeslot in chain.from_iterable( + num_blocks = len(self.export_dict["timeslots"]["blocks"]) + for block_idx, block in enumerate( self.export_dict["timeslots"]["blocks"] ): - start, end = self._get_timeslot_start_end(timeslot) - timeslot_avail = Availability( - event=self.event, start=start, end=end - ) - - fulfilled_time_constraints = set() + for timeslot in block: + start, end = self._get_timeslot_start_end(timeslot) + timeslot_avail = Availability( + event=self.event, start=start, end=end + ) - # reso deadline - if self.event.reso_deadline is not None: - # timeslot ends before deadline - if end < self.event.reso_deadline.astimezone( - self.event.timezone - ): - fulfilled_time_constraints.add("resolution") - - # add category constraints - fulfilled_time_constraints |= ( - AKCategory.create_category_constraints( - [ - cat - for cat in AKCategory.objects.filter( - event=self.event - ).all() - if timeslot_avail.is_covered(cat_avails[cat.name]) - ] + fulfilled_time_constraints = set() + + # reso deadline + if self.event.reso_deadline is not None: + # timeslot ends before deadline + if end < self.event.reso_deadline.astimezone( + self.event.timezone + ): + fulfilled_time_constraints.add("resolution") + + # add category constraints + fulfilled_time_constraints |= ( + AKCategory.create_category_constraints( + [ + cat + for cat in AKCategory.objects.filter( + event=self.event + ).all() + if timeslot_avail.is_covered(cat_avails[cat.name]) + ] + ) ) - ) - # add owner constraints - fulfilled_time_constraints |= { - f"availability-person-{owner.id}" - for owner in AKOwner.objects.filter(event=self.event).all() - if self._is_restricted_and_contained_slot( - timeslot_avail, - Availability.union(owner.availabilities.all()), + # add owner constraints + fulfilled_time_constraints |= { + f"availability-person-{owner.id}" + for owner in AKOwner.objects.filter(event=self.event).all() + if self._is_restricted_and_contained_slot( + timeslot_avail, + Availability.union(owner.availabilities.all()), + ) + } + + # add room constraints + fulfilled_time_constraints |= { + f"availability-room-{room.id}" + for room in self.rooms + if self._is_restricted_and_contained_slot( + timeslot_avail, + Availability.union(room.availabilities.all()), + ) + } + + # add ak constraints + fulfilled_time_constraints |= { + f"availability-ak-{ak.id}" + for ak in AK.objects.filter(event=event) + if self._is_restricted_and_contained_slot( + timeslot_avail, + Availability.union(ak.availabilities.all()), + ) + } + fulfilled_time_constraints |= { + f"fixed-akslot-{slot.id}" + for slot in self.ak_slots + if self._is_ak_fixed_in_slot(slot, timeslot_avail) + } + + fulfilled_time_constraints |= { + f"notblock{idx}" + for idx in range(num_blocks) + if idx != block_idx + } + + self.assertEqual( + fulfilled_time_constraints, + set(timeslot["fulfilled_time_constraints"]), ) - } - - # add room constraints - fulfilled_time_constraints |= { - f"availability-room-{room.id}" - for room in self.rooms - if self._is_restricted_and_contained_slot( - timeslot_avail, - Availability.union(room.availabilities.all()), + + def test_timeslots_info(self): + """Test timeslots info dict""" + for event in Event.objects.all(): + with self.subTest(event=event): + self.set_up_event(event=event) + + self.assertAlmostEqual( + self.export_dict["timeslots"]["info"]["duration"], + float(self.event.export_slot), + ) + + block_names = [] + for block in self.export_dict["timeslots"]["blocks"]: + if not block: + continue + + block_start, _ = self._get_timeslot_start_end(block[0]) + _, block_end = self._get_timeslot_start_end(block[-1]) + + start_day = block_start.strftime("%A, %d. %b") + if block_start.date() == block_end.date(): + # same day + time_str = ( + block_start.strftime("%H:%M") + + " – " + + block_end.strftime("%H:%M") ) - } - - # add ak constraints - fulfilled_time_constraints |= { - f"availability-ak-{ak.id}" - for ak in AK.objects.filter(event=event) - if self._is_restricted_and_contained_slot( - timeslot_avail, Availability.union(ak.availabilities.all()) + else: + # different days + time_str = ( + block_start.strftime("%a %H:%M") + + " – " + + block_end.strftime("%a %H:%M") ) - } - fulfilled_time_constraints |= { - f"fixed-akslot-{slot.id}" - for slot in self.ak_slots - if self._is_ak_fixed_in_slot(slot, timeslot_avail) - } - - self.assertEqual( - fulfilled_time_constraints, - set(timeslot["fulfilled_time_constraints"]), - ) + block_names.append([start_day, time_str]) + self.assertEqual( + block_names, self.export_dict["timeslots"]["info"]["blocknames"] + ) diff --git a/AKModel/views/ak.py b/AKModel/views/ak.py index f6fd7932..f8b83f04 100644 --- a/AKModel/views/ak.py +++ b/AKModel/views/ak.py @@ -1,4 +1,5 @@ import json +from datetime import datetime from typing import List from django.contrib import messages @@ -109,11 +110,30 @@ class AKJSONExportView(AdminViewMixin, FilterByEventSlugMixin, ListView): for person in AKOwner.objects.filter(event=self.event) } - blocks = self.event.discretize_timeslots() + blocks = list(self.event.discretize_timeslots()) - for block in blocks: + block_names = [] + + for block_idx, block in enumerate(blocks): current_block = [] + if not block: + continue + + block_start = block[0].avail.start.astimezone(self.event.timezone) + block_end = block[-1].avail.end.astimezone(self.event.timezone) + + start_day = block_start.strftime("%A, %d. %b") + if block_start.date() == block_end.date(): + # same day + time_str = block_start.strftime("%H:%M") + " – " + block_end.strftime("%H:%M") + else: + # different days + time_str = block_start.strftime("%a %H:%M") + " – " + block_end.strftime("%a %H:%M") + block_names.append([start_day, time_str]) + + block_timeconstraints = [f"notblock{idx}" for idx in range(len(blocks)) if idx != block_idx] + for timeslot in block: time_constraints = [] # if reso_deadline is set and timeslot ends before it, @@ -145,6 +165,7 @@ class AKJSONExportView(AdminViewMixin, FilterByEventSlugMixin, ListView): ]) time_constraints.extend(timeslot.constraints) + time_constraints.extend(block_timeconstraints) current_block.append({ "id": str(timeslot.idx), @@ -157,6 +178,8 @@ class AKJSONExportView(AdminViewMixin, FilterByEventSlugMixin, ListView): timeslots["blocks"].append(current_block) + timeslots["info"]["blocknames"] = block_names + context["timeslots"] = json.dumps(timeslots) info_dict = { -- GitLab