From f2d2f5a14b3c9e84efe9066cce117c07da4acd19 Mon Sep 17 00:00:00 2001 From: pinwheeeel Date: Wed, 9 Oct 2024 17:59:05 -0400 Subject: [PATCH 1/7] restore is_instructional --- core/models/course.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/models/course.py b/core/models/course.py index f2d7a758..87f43d60 100644 --- a/core/models/course.py +++ b/core/models/course.py @@ -51,9 +51,9 @@ def day_num(self, target_date=None): "calendar_days": self.__day_num_calendar_days, } target_date = utils.get_localdate(date=target_date, time=[23, 59, 59]) - if ( - not self.is_current(target_date.date()) or target_date.weekday() >= 5 - ): # TODO: check for pa days + if not self.is_current(target_date.date()) or not self.day_is_instructional( + target_date + ): return None return methods[tf.get("day_num_method", "consecutive")](tf, target_date) From 19bcfe428c1eab6bd0510d9db209d020031ee719 Mon Sep 17 00:00:00 2001 From: pinwheeeel Date: Wed, 9 Oct 2024 18:21:33 -0400 Subject: [PATCH 2/7] add pa days and holidays to timetable formats --- metropolis/timetable_formats.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/metropolis/timetable_formats.py b/metropolis/timetable_formats.py index b7ad4e61..6f7910fc 100644 --- a/metropolis/timetable_formats.py +++ b/metropolis/timetable_formats.py @@ -839,6 +839,8 @@ "position": [{4, 6}, {3, 6}], }, ], + "pa-day": [], + "holiday": [], }, "courses": 4, "positions": {1, 2, 3, 4, 5, 6}, From 3e5230bfefe19fc9fa4ca77c75f32d229de4846d Mon Sep 17 00:00:00 2001 From: pinwheeeel Date: Wed, 9 Oct 2024 18:36:57 -0400 Subject: [PATCH 3/7] update is_instructional on model save and fix day_is_instructional filtering parameters --- core/models/course.py | 29 +++++++++++++++++------------ metropolis/timetable_formats.py | 2 +- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/core/models/course.py b/core/models/course.py index 87f43d60..4afd6434 100644 --- a/core/models/course.py +++ b/core/models/course.py @@ -34,13 +34,14 @@ def is_current(self, target_date=None): return self.start_date <= target_date < self.end_date def day_is_instructional(self, target_date=None): - target_date = utils.get_localdate(date=target_date, time=[11, 0, 0]) + target_date_start = utils.get_localdate(date=target_date, time=[0, 0, 0]) + target_date_end = utils.get_localdate(date=target_date, time=[23, 59, 59]) return ( target_date.weekday() < 5 and not self.events.filter( is_instructional=False, - start_date__lte=target_date, - end_date__gte=target_date, + start_date__lt=target_date_end, + end_date__gt=target_date_start, ).exists() ) @@ -91,18 +92,16 @@ def __day_num_consecutive(self, tf, target_date): return (len(cycle_day_type_set) - 1) % tf["cycle"]["length"] + 1 def day_schedule_format(self, target_date=None): - tds = utils.get_localdate(date=target_date, time=[0, 0, 0]) # target date start - tde = utils.get_localdate( - date=target_date, time=[23, 59, 59] - ) # target date end + target_date_start = utils.get_localdate(date=target_date, time=[0, 0, 0]) + target_date_end = utils.get_localdate(date=target_date, time=[23, 59, 59]) schedule_formats = settings.TIMETABLE_FORMATS[self.timetable_format][ "schedules" ] schedule_format_set = set( - self.events.filter(start_date__lte=tde, end_date__gte=tds).values_list( - "schedule_format", flat=True - ) + self.events.filter( + start_date__lte=target_date_end, end_date__gte=target_date_start + ).values_list("schedule_format", flat=True) ).intersection(set(schedule_formats.keys())) for schedule_format in list(schedule_formats.keys())[::-1]: if schedule_format in schedule_format_set: @@ -223,8 +222,7 @@ class Event(models.Model): schedule_format = models.CharField(max_length=64, default="default") is_instructional = models.BooleanField( - default=True, - help_text="Whether this event changes the day's schedule or not. Leave checked if not direct cause. (don't change for presentations, etc.)", + help_text="Whether or not school is running on this day. Automatically changes depending on the schedule format and should not be manually edited.", ) is_public = models.BooleanField( default=True, @@ -265,4 +263,11 @@ def save(self, *args, **kwargs): self.start_date = timezone.make_aware( self.start_date, timezone.get_current_timezone() ) + + schedule_formats = settings.TIMETABLE_FORMATS[self.term.timetable_format][ + "schedules" + ] + self.is_instructional = len(schedule_formats[self.schedule_format]) > 0 + # PA days and holidays do not have time data + super().save(*args, **kwargs) diff --git a/metropolis/timetable_formats.py b/metropolis/timetable_formats.py index 6f7910fc..c406aae3 100644 --- a/metropolis/timetable_formats.py +++ b/metropolis/timetable_formats.py @@ -733,7 +733,7 @@ "time": "02:00 pm - 03:15 pm", "course": "Period 4", }, - "time": [[14, 0], [15, 15]], + "time": [[14, 0], [23, 15]], "position": [{4, 6}, {3, 6}], }, ], From d2119218702cc83253b802faade47b32527f6bca Mon Sep 17 00:00:00 2001 From: pinwheeeel Date: Wed, 9 Oct 2024 19:17:27 -0400 Subject: [PATCH 4/7] small changes --- core/forms.py | 1 + core/models/course.py | 27 ++++++++++++++++----------- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/core/forms.py b/core/forms.py index 12fd0ada..d7642116 100644 --- a/core/forms.py +++ b/core/forms.py @@ -216,6 +216,7 @@ def __init__(self, *args, **kwargs): self.fields["schedule_format"].initial = "default" self.fields["term"].initial = models.Term.get_current() + self.fields["is_instructional"].disabled = True if "instance" in kwargs and kwargs["instance"] is not None: instance = kwargs["instance"] diff --git a/core/models/course.py b/core/models/course.py index 4afd6434..a6d423fb 100644 --- a/core/models/course.py +++ b/core/models/course.py @@ -3,9 +3,10 @@ import datetime as dt from django.conf import settings -from django.core.exceptions import MultipleObjectsReturned +from django.core.exceptions import MultipleObjectsReturned, ValidationError from django.db import models from django.utils import timezone +from django.utils.translation import gettext_lazy as _ from .. import utils @@ -148,25 +149,25 @@ def day_schedule(self, target_date=None): class MisconfiguredTermError(Exception): pass - def save(self, *args, **kwargs): + def clean(self): if self.start_date > self.end_date: - raise self.MisconfiguredTermError("Start date must be before end date") + raise ValidationError(_("Start date must be before end date")) # check for overlapping terms for term in Term.objects.all(): if term.id == self.id: continue - if term.start_date <= self.start_date < term.end_date: - raise self.MisconfiguredTermError( - "Current term's date range overlaps with existing term" - ) - - if term.start_date < self.end_date <= term.end_date: - raise self.MisconfiguredTermError( - "Current term's date range overlaps with existing term" + if ( + term.start_date <= self.start_date < term.end_date + and term.start_date < self.end_date <= term.end_date + ): + raise ValidationError( + _("Current term's date range overlaps with existing term") ) + def save(self, *args, **kwargs): + self.clean() super().save(*args, **kwargs) @classmethod @@ -252,6 +253,10 @@ def get_events(cls, user=None): return events + def clean(self): + if self.start_date > self.end_date: + raise ValidationError(_("Start date must be before end date")) + def save(self, *args, **kwargs): if not timezone.is_aware(self.end_date): # Convert naive datetime to aware datetime From ecd3ad9f25418a044d3cdaba747462085f6cfd44 Mon Sep 17 00:00:00 2001 From: pinwheeeel Date: Wed, 9 Oct 2024 19:35:27 -0400 Subject: [PATCH 5/7] finishing touches --- core/management/commands/add_events.py | 3 +-- core/models/course.py | 29 +++++++++++++++++++++----- core/utils/test_schedules.py | 1 - metropolis/timetable_formats.py | 2 +- 4 files changed, 26 insertions(+), 9 deletions(-) diff --git a/core/management/commands/add_events.py b/core/management/commands/add_events.py index db76103d..87eb9cce 100644 --- a/core/management/commands/add_events.py +++ b/core/management/commands/add_events.py @@ -80,7 +80,6 @@ def handle(self, *args, **options): "term": options["term"] if options["term"] else None, "organization": None, "schedule_format": "default", - "is_instructional": True, "is_public": True, "should_announce": False, } @@ -182,7 +181,7 @@ def handle(self, *args, **options): event_data["term"] ) - for key in ["is_instructional", "is_public", "should_announce"]: + for key in ["is_public", "should_announce"]: event_data[key] = self._get_boolean(key, event_data[key]) event = Event(**event_data) diff --git a/core/models/course.py b/core/models/course.py index a6d423fb..c429662d 100644 --- a/core/models/course.py +++ b/core/models/course.py @@ -151,7 +151,12 @@ class MisconfiguredTermError(Exception): def clean(self): if self.start_date > self.end_date: - raise ValidationError(_("Start date must be before end date")) + raise ValidationError( + { + "start_date": _("Start date must be before end date"), + "end_date": _("Start date must be before end date"), + } + ) # check for overlapping terms for term in Term.objects.all(): @@ -163,7 +168,14 @@ def clean(self): and term.start_date < self.end_date <= term.end_date ): raise ValidationError( - _("Current term's date range overlaps with existing term") + { + "start_date": _( + "Current term's date range overlaps with existing term" + ), + "end_date": _( + "Current term's date range overlaps with existing term" + ), + } ) def save(self, *args, **kwargs): @@ -227,11 +239,11 @@ class Event(models.Model): ) is_public = models.BooleanField( default=True, - help_text="Whether if this event pertains to the general school population, not just those in the organization.", + help_text="Whether or not this event is viewable to the general school population, not just those in the organization.", ) should_announce = models.BooleanField( default=False, - help_text="Whether if this event should be announced to the general school population VIA the important events feed.", + help_text="Whether or not this event should be announced to the general school population VIA the important events feed.", ) tags = models.ManyToManyField( @@ -255,7 +267,12 @@ def get_events(cls, user=None): def clean(self): if self.start_date > self.end_date: - raise ValidationError(_("Start date must be before end date")) + raise ValidationError( + { + "start_date": _("Start date must be before end date"), + "end_date": _("Start date must be before end date"), + } + ) def save(self, *args, **kwargs): if not timezone.is_aware(self.end_date): @@ -269,6 +286,8 @@ def save(self, *args, **kwargs): self.start_date, timezone.get_current_timezone() ) + self.clean() + schedule_formats = settings.TIMETABLE_FORMATS[self.term.timetable_format][ "schedules" ] diff --git a/core/utils/test_schedules.py b/core/utils/test_schedules.py index a00a3dc5..4281d65d 100644 --- a/core/utils/test_schedules.py +++ b/core/utils/test_schedules.py @@ -43,7 +43,6 @@ def create_winter_break(school_org: Organization, term: Term): end_date=now + datetime.timedelta(days=10), organization=school_org, term=term, - is_instructional=False, ) event.save() diff --git a/metropolis/timetable_formats.py b/metropolis/timetable_formats.py index c406aae3..6f7910fc 100644 --- a/metropolis/timetable_formats.py +++ b/metropolis/timetable_formats.py @@ -733,7 +733,7 @@ "time": "02:00 pm - 03:15 pm", "course": "Period 4", }, - "time": [[14, 0], [23, 15]], + "time": [[14, 0], [15, 15]], "position": [{4, 6}, {3, 6}], }, ], From 971ea376b97d1add8d5b703b913b18990533f17f Mon Sep 17 00:00:00 2001 From: pinwheeeel Date: Wed, 9 Oct 2024 19:48:01 -0400 Subject: [PATCH 6/7] execs and supervisors are members too!!! --- core/api/views/events.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/core/api/views/events.py b/core/api/views/events.py index 7a826466..dcded6fa 100644 --- a/core/api/views/events.py +++ b/core/api/views/events.py @@ -29,7 +29,10 @@ def get_queryset(self): end_date__gte=start, start_date__lte=end ) .filter( - Q(is_public=True) | Q(organization__member=self.request.user.id) + Q(is_public=True) + | Q(organization__member=self.request.user.id) + | Q(organization__supervisors=self.request.user.id) + | Q(organization__execs=self.request.user.id) ) .distinct() .order_by("start_date") @@ -41,7 +44,10 @@ def get_queryset(self): end_date__gte=start, start_date__lte=end ) .filter( - Q(is_public=True) | Q(organization__member=self.request.user.id) + Q(is_public=True) + | Q(organization__member=self.request.user.id) + | Q(organization__supervisors=self.request.user.id) + | Q(organization__execs=self.request.user.id) ) .distinct() .order_by("start_date") @@ -50,7 +56,10 @@ def get_queryset(self): events = ( models.Event.objects.filter(end_date__gte=start) .filter( - Q(is_public=True) | Q(organization__member=self.request.user.id) + Q(is_public=True) + | Q(organization__member=self.request.user.id) + | Q(organization__supervisors=self.request.user.id) + | Q(organization__execs=self.request.user.id) ) .distinct() .order_by("start_date") From 51e773dce64ac8534144dbc3919bc8238db19ec9 Mon Sep 17 00:00:00 2001 From: pinwheeeel Date: Wed, 9 Oct 2024 20:36:43 -0400 Subject: [PATCH 7/7] erm what the sigma --- core/api/views/events.py | 81 ++++++++++------------------------------ 1 file changed, 20 insertions(+), 61 deletions(-) diff --git a/core/api/views/events.py b/core/api/views/events.py index dcded6fa..f04940e0 100644 --- a/core/api/views/events.py +++ b/core/api/views/events.py @@ -15,69 +15,28 @@ class EventsList(ListAPIViewWithFallback): pagination_class = None def get_queryset(self): - start = timezone.now() - if self.request.data.get("start"): - start = self.request.data.get("start") - elif self.request.query_params.get("start"): - start = self.request.query_params.get("start") + start = ( + self.request.data.get("start") + or self.request.query_params.get("start") + or timezone.now() + ) + end = self.request.data.get("end") or self.request.query_params.get("end") + + events = models.Event.objects.filter(end_date__gte=start) + + if end: + events = events.filter(start_date__lte=end) if not self.request.user.is_anonymous: - if self.request.data.get("end"): - end = self.request.data.get("end") - events = ( - models.Event.objects.filter( - end_date__gte=start, start_date__lte=end - ) - .filter( - Q(is_public=True) - | Q(organization__member=self.request.user.id) - | Q(organization__supervisors=self.request.user.id) - | Q(organization__execs=self.request.user.id) - ) - .distinct() - .order_by("start_date") - ) - elif self.request.query_params.get("end"): - end = self.request.query_params.get("end") - events = ( - models.Event.objects.filter( - end_date__gte=start, start_date__lte=end - ) - .filter( - Q(is_public=True) - | Q(organization__member=self.request.user.id) - | Q(organization__supervisors=self.request.user.id) - | Q(organization__execs=self.request.user.id) - ) - .distinct() - .order_by("start_date") - ) - else: - events = ( - models.Event.objects.filter(end_date__gte=start) - .filter( - Q(is_public=True) - | Q(organization__member=self.request.user.id) - | Q(organization__supervisors=self.request.user.id) - | Q(organization__execs=self.request.user.id) - ) - .distinct() - .order_by("start_date") - ) + events = events.filter( + Q(is_public=True) + | Q(organization__member=self.request.user.id) + | Q(organization__supervisors=self.request.user.id) + | Q(organization__execs=self.request.user.id) + ).distinct() else: - if self.request.data.get("end"): - end = self.request.data.get("end") - events = models.Event.objects.filter( - end_date__gte=start, start_date__lte=end, is_public=True - ).order_by("start_date") - elif self.request.query_params.get("end"): - end = self.request.query_params.get("end") - events = models.Event.objects.filter( - end_date__gte=start, start_date__lte=end, is_public=True - ).order_by("start_date") - else: - events = models.Event.objects.filter( - end_date__gte=start, is_public=True - ).order_by("start_date") + events = events.filter(Q(is_public=True)) + + events = events.order_by("start_date") return events