diff --git a/AKModel/models.py b/AKModel/models.py index 228eda45d66c95f2c2bcd2e5b573e3af9084d168..1cd3f1ba908d63d5f59ceb0c3d07b33322e500d5 100644 --- a/AKModel/models.py +++ b/AKModel/models.py @@ -872,7 +872,7 @@ class Room(models.Model): "time_constraints": time_constraints } - data["fulfilled_room_constraints"].append(f"availability-room-{self.pk}") + data["fulfilled_room_constraints"].append(f"fixed-room-{self.pk}") if not any(constr.startswith("proxy") for constr in data["fulfilled_room_constraints"]): data["fulfilled_room_constraints"].append("no-proxy") @@ -989,10 +989,12 @@ class AKSlot(models.Model): # check if ak resp. owner is available for the whole event # -> no time constraint needs to be introduced - if not self.fixed and Availability.is_event_covered(self.event, self.ak.availabilities.all()): - ak_time_constraints = [] - else: + if self.fixed and self.start is not None: + ak_time_constraints = [f"fixed-akslot-{self.id}"] + elif not Availability.is_event_covered(self.event, self.ak.availabilities.all()): ak_time_constraints = [f"availability-ak-{self.ak.pk}"] + else: + ak_time_constraints = [] def _owner_time_constraints(owner: AKOwner): if Availability.is_event_covered(self.event, owner.availabilities.all()): @@ -1001,6 +1003,7 @@ class AKSlot(models.Model): conflict_slots = AKSlot.objects.filter(ak__in=self.ak.conflicts.all()) dependency_slots = AKSlot.objects.filter(ak__in=self.ak.prerequisites.all()) + other_ak_slots = AKSlot.objects.filter(ak=self.ak).exclude(pk=self.pk) ceil_offet_eps = decimal.Decimal(1e-4) @@ -1009,7 +1012,9 @@ class AKSlot(models.Model): "id": str(self.pk), "duration": math.ceil(self.duration * self.slots_in_an_hour - ceil_offet_eps), "properties": { - "conflicts": [str(conflict.pk) for conflict in conflict_slots.all()], + "conflicts": + [str(conflict.pk) for conflict in conflict_slots.all()] + + [str(second_slot.pk) for second_slot in other_ak_slots.all()], "dependencies": [str(dep.pk) for dep in dependency_slots.all()], }, "room_constraints": [constraint.name @@ -1022,6 +1027,7 @@ class AKSlot(models.Model): "description": self.ak.description, "reso": self.ak.reso, "duration_in_hours": float(self.duration), + "django_ak_id": str(self.ak.pk), }, } @@ -1033,8 +1039,8 @@ class AKSlot(models.Model): category_constraints = AKCategory.create_category_constraints([self.ak.category]) data["time_constraints"].extend(category_constraints) - if self.room is not None and self.fixed: - data["room_constraints"].append(f"availability-room-{self.room.pk}") + if self.fixed and self.room is not None: + data["room_constraints"].append(f"fixed-room-{self.room.pk}") if not any(constr.startswith("proxy") for constr in data["room_constraints"]): data["room_constraints"].append("no-proxy") diff --git a/AKModel/views/ak.py b/AKModel/views/ak.py index 975af0f545a75393a194f8666e1417dc02aefc51..5e642dd5d054513947bc075835e925a1349b0bd3 100644 --- a/AKModel/views/ak.py +++ b/AKModel/views/ak.py @@ -59,13 +59,13 @@ class AKJSONExportView(AdminViewMixin, FilterByEventSlugMixin, ListView): """Test if event is not covered by availabilities.""" return not Availability.is_event_covered(self.event, availabilities) - def _test_ak_fixed_in_slot(self, ak_id, slot: Availability, ak_fixed: dict) -> bool: - """Test if AK defined by `ak_id` is fixed to happen during slot.""" - if ak_id not in ak_fixed: + def _test_akslot_fixed_in_timeslot(self, ak_slot: AKSlot, timeslot: Availability) -> bool: + """Test if an AKSlot is fixed to overlap a timeslot slot.""" + if not ak_slot.fixed or ak_slot.start is None: return False - fixed_slot = Availability(self.event, start=ak_fixed[ak_id].start, end=ak_fixed[ak_id].end) - return fixed_slot.overlaps(slot, strict=True) + fixed_avail = Availability(event=self.event, start=ak_slot.start, end=ak_slot.end) + return fixed_avail.overlaps(timeslot, strict=True) def _test_add_constraint(self, slot: Availability, availabilities: List[Availability]) -> bool: """Test if object is not available for whole event and may happen during slot.""" @@ -74,6 +74,19 @@ class AKJSONExportView(AdminViewMixin, FilterByEventSlugMixin, ListView): and self._test_slot_contained(slot, availabilities) ) + def _generate_time_constraints( + self, + avail_label: str, + avail_dict: dict, + timeslot_avail: Availability, + prefix: str = "availability", + ) -> list[str]: + return [ + f"{prefix}-{avail_label}-{pk}" + for pk, availabilities in avail_dict.items() + if self._test_add_constraint(timeslot_avail, availabilities) + ] + def get_queryset(self): return super().get_queryset().order_by("ak__track") @@ -96,8 +109,8 @@ class AKJSONExportView(AdminViewMixin, FilterByEventSlugMixin, ListView): slot.slots_in_an_hour = SLOTS_IN_AN_HOUR ak_availabilities = { - slot.ak.pk: Availability.union(slot.ak.availabilities.all()) - for slot in context["slots"] + ak.pk: Availability.union(ak.availabilities.all()) + for ak in AK.objects.filter(event=self.event).all() } room_availabilities = { room.pk: Availability.union(room.availabilities.all()) @@ -108,12 +121,6 @@ class AKJSONExportView(AdminViewMixin, FilterByEventSlugMixin, ListView): for person in AKOwner.objects.filter(event=self.event) } - ak_fixed = { - ak_id: values.get() - for ak_id in ak_availabilities.keys() - if (values := AKSlot.objects.select_related().filter(ak__pk=ak_id, fixed=True)).exists() - } - blocks = self.event.discretize_timeslots(slots_in_an_hour=SLOTS_IN_AN_HOUR) for block in blocks: @@ -127,26 +134,28 @@ class AKJSONExportView(AdminViewMixin, FilterByEventSlugMixin, ListView): time_constraints.append("resolution") # add fulfilled time constraints for all AKs that cannot happen during full event - time_constraints.extend([ - f"availability-ak-{ak_id}" - for ak_id, availabilities in ak_availabilities.items() - if ( - self._test_add_constraint(timeslot.avail, availabilities) - or self._test_ak_fixed_in_slot(ak_id, timeslot.avail, ak_fixed) - ) - ]) + time_constraints.extend( + self._generate_time_constraints("ak", ak_availabilities, timeslot.avail) + ) + # add fulfilled time constraints for all persons that are not available for full event - time_constraints.extend([ - f"availability-person-{person_id}" - for person_id, availabilities in person_availabilities.items() - if self._test_add_constraint(timeslot.avail, availabilities) - ]) + time_constraints.extend( + self._generate_time_constraints("person", person_availabilities, timeslot.avail) + ) + # add fulfilled time constraints for all rooms that are not available for full event + time_constraints.extend( + self._generate_time_constraints("room", room_availabilities, timeslot.avail) + ) + + # add fulfilled time constraints for all AKSlots fixed to happen during timeslot time_constraints.extend([ - f"availability-room-{room_id}" - for room_id, availabilities in room_availabilities.items() - if self._test_add_constraint(timeslot.avail, availabilities) + f"fixed-akslot-{slot.id}" + for slot in AKSlot.objects.filter(event=self.event, fixed=True) + .exclude(start__isnull=True) + if self._test_akslot_fixed_in_timeslot(slot, timeslot.avail) ]) + time_constraints.extend(timeslot.constraints) current_block.append({