diff --git a/program/models.py b/program/models.py index 0b634d0c51b848ebe319dd0113d4de35909c66a8..f11245b284d1a878abd38bb890dfbb08241cc019 100644 --- a/program/models.py +++ b/program/models.py @@ -323,25 +323,25 @@ class Schedule(models.Model): True if sdl.get("add_business_days_only") is True else False ) - # TODO: replace `dstart` with `first_date` when the dashboard is updated - first_date = parse_date(str(sdl["dstart"])) - # TODO: replace `tstart` with `start_time` when the dashboard is updated + first_date = parse_date(str(sdl["first_date"])) start_time = ( - sdl["tstart"] + ":00" if len(str(sdl["tstart"])) == 5 else sdl["tstart"] + sdl["start_time"] + ":00" + if len(str(sdl["start_time"])) == 5 + else sdl["start_time"] + ) + end_time = ( + sdl["end_time"] + ":00" + if len(str(sdl["end_time"])) == 5 + else sdl["end_time"] ) - # TODO: replace `tend` with `end_time` when the dashboard is updated - end_time = sdl["tend"] + ":00" if len(str(sdl["tend"])) == 5 else sdl["tend"] start_time = parse_time(str(start_time)) end_time = parse_time(str(end_time)) - # TODO: replace `until` with `last_date` when the dashboard is updated - if sdl["until"]: - last_date = parse_date(str(sdl["until"])) + if sdl["last_date"]: + last_date = parse_date(str(sdl["last_date"])) else: - # If no until date was set - # Set it to the end of the year - # Or add x days + # If last_date was not set, set it to the end of the year or add x days if AUTO_SET_UNTIL_DATE_TO_END_OF_YEAR: year = timezone.now().year last_date = parse_date(f"{year}-12-31") @@ -350,10 +350,9 @@ class Schedule(models.Model): days=+AUTO_SET_UNTIL_DATE_TO_DAYS_IN_FUTURE ) - # TODO: replace `byweekday` with `by_weekday` when the dashboard is updated schedule = Schedule( pk=pk, - by_weekday=sdl["byweekday"], + by_weekday=sdl["by_weekday"], rrule=rrule, first_date=first_date, start_time=start_time, @@ -373,7 +372,7 @@ class Schedule(models.Model): def generate_timeslots(schedule): """ Returns a list of timeslot objects based on a schedule and its rrule - Returns past timeslots as well, starting from dstart (not today) + Returns past timeslots as well, starting from first_date (not today) """ by_week_no = None @@ -696,7 +695,7 @@ class Schedule(models.Model): # Generate schedule to be saved schedule = Schedule.instantiate_upcoming(sdl, show_pk, schedule_pk) - # Copy if dstart changes for generating timeslots + # Copy if first_date changes for generating timeslots gen_schedule = schedule # Generate timeslots @@ -742,13 +741,13 @@ class Schedule(models.Model): if schedule.rrule.freq > 0 and schedule.first_date == schedule.last_date: raise ValidationError( - _("Start and until dates mustn't be the same"), + _("Start and end dates must not be the same."), code="no-same-day-start-and-end", ) if schedule.last_date < schedule.first_date: raise ValidationError( - _("Until date mustn't be before start"), + _("End date mustn't be before start."), code="no-start-after-end", ) diff --git a/program/serializers.py b/program/serializers.py index ee5488be3cac05a12c20d6893e753555247b2a37..3ef7853fe0607197e3d206588af14f90e83b9436 100644 --- a/program/serializers.py +++ b/program/serializers.py @@ -36,7 +36,6 @@ from program.models import ( MusicFocus, Note, NoteLink, - RRule, Schedule, Show, TimeSlot, @@ -405,35 +404,16 @@ class ShowSerializer(serializers.HyperlinkedModelSerializer): class ScheduleSerializer(serializers.ModelSerializer): - rrule = serializers.PrimaryKeyRelatedField( - queryset=RRule.objects.all(), - help_text=Schedule.rrule.field.help_text, - ) - show = serializers.PrimaryKeyRelatedField( - queryset=Show.objects.all(), - help_text=Schedule.show.field.help_text, - ) - # TODO: remove this when the dashboard is updated - byweekday = serializers.IntegerField( - source="by_weekday", - help_text=Schedule.by_weekday.field.help_text, - ) - dstart = serializers.DateField( - source="first_date", - help_text=Schedule.first_date.field.help_text, - ) - tstart = serializers.TimeField( - source="start_time", - help_text=Schedule.start_time.field.help_text, - ) - tend = serializers.TimeField( - source="end_time", - help_text=Schedule.end_time.field.help_text, - ) - until = serializers.DateField( - source="last_date", - help_text=Schedule.last_date.field.help_text, - ) + class Meta: + model = Schedule + fields = "__all__" + + +class UnsavedScheduleSerializer(ScheduleSerializer): + id = serializers.IntegerField(allow_null=True) + + +class ScheduleInRequestSerializer(ScheduleSerializer): dryrun = serializers.BooleanField( write_only=True, required=False, @@ -464,11 +444,11 @@ class ScheduleSerializer(serializers.ModelSerializer): def update(self, instance, validated_data): """Update and return an existing Schedule instance, given the validated data.""" - instance.by_weekday = validated_data.get("byweekday", instance.by_weekday) - instance.first_date = validated_data.get("dstart", instance.first_date) - instance.start_time = validated_data.get("tstart", instance.start_time) - instance.end_time = validated_data.get("tend", instance.end_time) - instance.last_date = validated_data.get("until", instance.last_date) + instance.by_weekday = validated_data.get("by_weekday", instance.by_weekday) + instance.first_date = validated_data.get("first_date", instance.first_date) + instance.start_time = validated_data.get("start_time", instance.start_time) + instance.end_time = validated_data.get("end_time", instance.end_time) + instance.last_date = validated_data.get("last_date", instance.last_date) instance.is_repetition = validated_data.get( "is_repetition", instance.is_repetition ) @@ -525,41 +505,24 @@ class DryRunTimeSlotSerializer(serializers.Serializer): class ScheduleCreateUpdateRequestSerializer(serializers.Serializer): - schedule = ScheduleSerializer() - solutions = serializers.DictField(child=serializers.ChoiceField(SOLUTION_CHOICES)) + schedule = ScheduleInRequestSerializer() + solutions = serializers.DictField( + child=serializers.ChoiceField(SOLUTION_CHOICES), required=False + ) notes = serializers.DictField(child=serializers.IntegerField(), required=False) playlists = serializers.DictField(child=serializers.IntegerField(), required=False) -# TODO: There shouldn’t be a separate ScheduleSerializer for use in responses. -# Instead the default serializer should be used. Unfortunately, the -# code that generates the data creates custom dicts with this particular format. -class ScheduleInResponseSerializer(serializers.Serializer): - # "Schedule schema type" is the rendered name of the ScheduleSerializer. - """ - For documentation on the individual fields see the - Schedule schema type. - """ - add_business_days_only = serializers.BooleanField() - add_days_no = serializers.IntegerField(allow_null=True) - by_weekday = serializers.IntegerField() - default_playlist_id = serializers.IntegerField(allow_null=True) - end_time = serializers.TimeField() - first_date = serializers.DateField() - id = serializers.PrimaryKeyRelatedField(queryset=Schedule.objects.all()) - is_repetition = serializers.BooleanField() - last_date = serializers.DateField() - rrule = serializers.PrimaryKeyRelatedField(queryset=RRule.objects.all()) - show = serializers.PrimaryKeyRelatedField(queryset=Note.objects.all()) - start_time = serializers.TimeField() - - -class ScheduleConflictResponseSerializer(serializers.Serializer): +class ScheduleResponseSerializer(serializers.Serializer): projected = ProjectedTimeSlotSerializer(many=True) solutions = serializers.DictField(child=serializers.ChoiceField(SOLUTION_CHOICES)) notes = serializers.DictField(child=serializers.IntegerField()) playlists = serializers.DictField(child=serializers.IntegerField()) - schedule = ScheduleInResponseSerializer() + schedule = ScheduleSerializer() + + +class ScheduleConflictResponseSerializer(ScheduleResponseSerializer): + schedule = UnsavedScheduleSerializer() class ScheduleDryRunResponseSerializer(serializers.Serializer): diff --git a/program/views.py b/program/views.py index e0f7b4d893f9c8d1ec21198f1b0b2f60441df75a..8d18a2db210c52fb59f1199458faf268c2e3d351 100644 --- a/program/views.py +++ b/program/views.py @@ -58,6 +58,7 @@ from program.serializers import ( ScheduleConflictResponseSerializer, ScheduleCreateUpdateRequestSerializer, ScheduleDryRunResponseSerializer, + ScheduleResponseSerializer, ScheduleSerializer, ShowSerializer, TimeSlotSerializer, @@ -344,9 +345,10 @@ class APIShowViewSet(DisabledObjectPermissionCheckMixin, viewsets.ModelViewSet): @extend_schema_view( create=extend_schema( summary="Create a new schedule.", + request=ScheduleCreateUpdateRequestSerializer, responses={ status.HTTP_201_CREATED: OpenApiResponse( - response=ScheduleConflictResponseSerializer, + response=ScheduleResponseSerializer, description=( "Signals the successful creation of the schedule and of the projected " "timeslots." @@ -366,9 +368,9 @@ class APIShowViewSet(DisabledObjectPermissionCheckMixin, viewsets.ModelViewSet): Returned in case the request contained invalid data. This may happen if: - * the until date is before the start date (`no-start-after-end`), + * the last date is before the start date (`no-start-after-end`), in which case you should correct either the start or until date. - * The start and until date are the same (`no-same-day-start-and-end`). + * The start and last date are the same (`no-same-day-start-and-end`). This is only allowed for single timeslots with the recurrence rule set to `once`. You should fix either the start or until date. * The number of conflicts and solutions aren’t the same @@ -410,8 +412,14 @@ class APIShowViewSet(DisabledObjectPermissionCheckMixin, viewsets.ModelViewSet): }, ), retrieve=extend_schema(summary="Retrieve a single schedule."), - update=extend_schema(summary="Update an existing schedule."), - partial_update=extend_schema(summary="Partially update an existing schedule."), + update=extend_schema( + summary="Update an existing schedule.", + request=ScheduleCreateUpdateRequestSerializer, + ), + partial_update=extend_schema( + summary="Partially update an existing schedule.", + request=ScheduleCreateUpdateRequestSerializer, + ), destroy=extend_schema(summary="Delete an existing schedule."), list=extend_schema(summary="List all schedules."), )