diff --git a/AKModel/models.py b/AKModel/models.py index d578645506693ab9e8560eb65fca4f774452860f..228eda45d66c95f2c2bcd2e5b573e3af9084d168 100644 --- a/AKModel/models.py +++ b/AKModel/models.py @@ -1,5 +1,7 @@ +import decimal import itertools import json +import math from dataclasses import dataclass from datetime import datetime, timedelta from typing import Iterable, Generator @@ -382,6 +384,23 @@ class Event(models.Model): constraints=category_constraints, ) + def discretize_timeslots(self, *, slots_in_an_hour: float = 1.0) -> Iterable[TimeslotBlock]: + """"Choose discretization scheme. + + Uses default_time_slots if the event has any DefaultSlot, otherwise uniform_time_slots. + + :param slots_in_an_hour: The percentage of an hour covered by a single slot. + Determines the discretization granularity. + :yield: Block of optimizer timeslots as the discretization result. + :ytype: list of TimeslotBlock + """ + + if DefaultSlot.objects.filter(event=self).exists(): + # discretize default slots if they exists + yield from merge_blocks(self.default_time_slots(slots_in_an_hour=slots_in_an_hour)) + else: + yield from self.uniform_time_slots(slots_in_an_hour=slots_in_an_hour) + @transaction.atomic def schedule_from_json(self, schedule: str) -> int: """Load AK schedule from a json string. @@ -397,7 +416,7 @@ class Event(models.Model): timeslot_dict = { timeslot.idx: timeslot - for block in merge_blocks(self.default_time_slots(slots_in_an_hour=slots_in_an_hour)) + for block in self.discretize_timeslots(slots_in_an_hour=slots_in_an_hour) for timeslot in block } @@ -980,11 +999,19 @@ class AKSlot(models.Model): return [] return [f"availability-person-{owner.pk}"] + conflict_slots = AKSlot.objects.filter(ak__in=self.ak.conflicts.all()) + dependency_slots = AKSlot.objects.filter(ak__in=self.ak.prerequisites.all()) + + ceil_offet_eps = decimal.Decimal(1e-4) + # self.slots_in_an_hour is set in AKJSONExportView data = { "id": str(self.pk), - "duration": round(self.duration * self.slots_in_an_hour), - "properties": {}, + "duration": math.ceil(self.duration * self.slots_in_an_hour - ceil_offet_eps), + "properties": { + "conflicts": [str(conflict.pk) for conflict in conflict_slots.all()], + "dependencies": [str(dep.pk) for dep in dependency_slots.all()], + }, "room_constraints": [constraint.name for constraint in self.ak.requirements.all()], "time_constraints": ["resolution"] if self.ak.reso else [], @@ -994,6 +1021,7 @@ class AKSlot(models.Model): for owner in self.ak.owners.all()]), "description": self.ak.description, "reso": self.ak.reso, + "duration_in_hours": float(self.duration), }, } diff --git a/AKModel/views/ak.py b/AKModel/views/ak.py index f180f9582325e3d3188b25fed902a99c4dd4c87a..975af0f545a75393a194f8666e1417dc02aefc51 100644 --- a/AKModel/views/ak.py +++ b/AKModel/views/ak.py @@ -9,7 +9,7 @@ from django.views.generic import ListView, DetailView from AKModel.availability.models import Availability from AKModel.metaviews.admin import AdminViewMixin, FilterByEventSlugMixin, EventSlugMixin, IntermediateAdminView, \ IntermediateAdminActionView -from AKModel.models import AKRequirement, AKSlot, DefaultSlot, Event, AKOrgaMessage, AK, Room, AKOwner, merge_blocks +from AKModel.models import AKRequirement, AKSlot, Event, AKOrgaMessage, AK, Room, AKOwner class AKRequirementOverview(AdminViewMixin, FilterByEventSlugMixin, ListView): @@ -114,11 +114,7 @@ class AKJSONExportView(AdminViewMixin, FilterByEventSlugMixin, ListView): if (values := AKSlot.objects.select_related().filter(ak__pk=ak_id, fixed=True)).exists() } - if DefaultSlot.objects.filter(event=self.event).exists(): - # discretize default slots if they exists - blocks = merge_blocks(self.event.default_time_slots(slots_in_an_hour=SLOTS_IN_AN_HOUR)) - else: - blocks = self.event.uniform_time_slots(slots_in_an_hour=SLOTS_IN_AN_HOUR) + blocks = self.event.discretize_timeslots(slots_in_an_hour=SLOTS_IN_AN_HOUR) for block in blocks: current_block = []