From 4b4ef80d236a99231e459aa83481bfec356db036 Mon Sep 17 00:00:00 2001 From: Ernesto Rico Schmidt <ernesto@helsinki.at> Date: Tue, 4 Apr 2023 16:13:21 -0400 Subject: [PATCH] Extract generate_conflicts as function --- program/models.py | 143 +------------------------------------------- program/services.py | 137 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 138 insertions(+), 142 deletions(-) diff --git a/program/models.py b/program/models.py index e2f317fa..2fb10313 100644 --- a/program/models.py +++ b/program/models.py @@ -24,15 +24,11 @@ from rest_framework.exceptions import ValidationError from versatileimagefield.fields import PPOIField, VersatileImageField from django.contrib.auth.models import User -from django.core.exceptions import ObjectDoesNotExist from django.db import models from django.db.models import Q, QuerySet from django.utils.translation import gettext_lazy as _ - from program.utils import parse_datetime -from steering.settings import ( - THUMBNAIL_SIZES, -) +from steering.settings import THUMBNAIL_SIZES class ScheduleConflictError(ValidationError): @@ -372,143 +368,6 @@ class Schedule(models.Model): class Meta: ordering = ("first_date", "start_time") - # FIXME: this does not belong here - @staticmethod - def generate_conflicts(timeslots): - """ - Tests a list of timeslot objects for colliding timeslots in the database - Returns a list of conflicts containing dicts of projected timeslots, collisions and - solutions - """ - - conflicts = {} - projected = [] - solutions = {} - - # Cycle each timeslot - for ts in timeslots: - # Contains collisions - collisions = [] - - # Contains possible solutions - solution_choices = set() - - # Get collisions for each timeslot - collision_list = list(TimeSlot.objects.get_colliding_timeslots(ts).order_by("start")) - - # Add the projected timeslot - projected_entry = { - "hash": ts.hash, - "start": str(ts.start), - "end": str(ts.end), - } - - for c in collision_list: - # Add the collision - collision = { - "id": c.id, - "start": str(c.start), - "end": str(c.end), - "playlist_id": c.playlist_id, - "show": c.show.id, - "show_name": c.show.name, - "schedule": c.schedule_id, - "memo": c.memo, - } - - # Get note - try: - note = Note.objects.get(timeslot=c.id) - collision["note_id"] = note.pk - except ObjectDoesNotExist: - collision["note_id"] = None - - collisions.append(collision) - - """Determine acceptable solutions""" - - if len(collision_list) > 1: - # If there is more than one collision: Only these two are supported at the - # moment - solution_choices.add("theirs") - solution_choices.add("ours") - else: - # These two are always possible: Either keep theirs and remove ours or vice - # versa - solution_choices.add("theirs") - solution_choices.add("ours") - - # Partly overlapping: projected starts earlier than existing and ends earlier - # - # ex. pr. - # +--+ - # | | - # +--+ | | - # | | +--+ - # | | - # +--+ - # - if ts.end > c.start > ts.start <= c.end: - solution_choices.add("theirs-end") - solution_choices.add("ours-end") - - # Partly overlapping: projected starts later than existing and ends later - # - # ex. pr. - # +--+ - # | | - # | | +--+ - # +--+ | | - # | | - # +--+ - # - if c.start <= ts.start < c.end < ts.end: - solution_choices.add("theirs-start") - solution_choices.add("ours-start") - - # Fully overlapping: projected starts earlier and ends later than existing - # - # ex. pr. - # +--+ - # +--+ | | - # | | | | - # +--+ | | - # +--+ - # - if ts.start < c.start and ts.end > c.end: - solution_choices.add("theirs-end") - solution_choices.add("theirs-start") - solution_choices.add("theirs-both") - - # Fully overlapping: projected starts later and ends earlier than existing - # - # ex. pr. - # +--+ - # | | +--+ - # | | | | - # | | +--+ - # +--+ - # - if ts.start > c.start and ts.end < c.end: - solution_choices.add("ours-end") - solution_choices.add("ours-start") - solution_choices.add("ours-both") - - if len(collisions) > 0: - solutions[ts.hash] = "" - - projected_entry["collisions"] = collisions - projected_entry["solution_choices"] = solution_choices - projected_entry["error"] = None - projected.append(projected_entry) - - conflicts["projected"] = projected - conflicts["solutions"] = solutions - conflicts["notes"] = {} - conflicts["playlists"] = {} - - return conflicts - class TimeSlotManager(models.Manager): @staticmethod diff --git a/program/services.py b/program/services.py index 09dc7295..eed89ef9 100644 --- a/program/services.py +++ b/program/services.py @@ -545,3 +545,140 @@ def generate_timeslots(schedule): ) return timeslots + + +# TODO: add type annotations +def generate_conflicts(timeslots): + """ + Tests a list of timeslot objects for colliding timeslots in the database + Returns a list of conflicts containing dicts of projected timeslots, collisions and + solutions + """ + + conflicts = {} + projected = [] + solutions = {} + + # Cycle each timeslot + for ts in timeslots: + # Contains collisions + collisions = [] + + # Contains possible solutions + solution_choices = set() + + # Get collisions for each timeslot + collision_list = list(TimeSlot.objects.get_colliding_timeslots(ts).order_by("start")) + + # Add the projected timeslot + projected_entry = { + "hash": ts.hash, + "start": str(ts.start), + "end": str(ts.end), + } + + for c in collision_list: + # Add the collision + collision = { + "id": c.id, + "start": str(c.start), + "end": str(c.end), + "playlist_id": c.playlist_id, + "show": c.show.id, + "show_name": c.show.name, + "schedule": c.schedule_id, + "memo": c.memo, + } + + # Get note + try: + note = Note.objects.get(timeslot=c.id) + collision["note_id"] = note.pk + except ObjectDoesNotExist: + collision["note_id"] = None + + collisions.append(collision) + + """Determine acceptable solutions""" + + if len(collision_list) > 1: + # If there is more than one collision: Only these two are supported at the + # moment + solution_choices.add("theirs") + solution_choices.add("ours") + else: + # These two are always possible: Either keep theirs and remove ours or vice + # versa + solution_choices.add("theirs") + solution_choices.add("ours") + + # Partly overlapping: projected starts earlier than existing and ends earlier + # + # ex. pr. + # +--+ + # | | + # +--+ | | + # | | +--+ + # | | + # +--+ + # + if ts.end > c.start > ts.start <= c.end: + solution_choices.add("theirs-end") + solution_choices.add("ours-end") + + # Partly overlapping: projected starts later than existing and ends later + # + # ex. pr. + # +--+ + # | | + # | | +--+ + # +--+ | | + # | | + # +--+ + # + if c.start <= ts.start < c.end < ts.end: + solution_choices.add("theirs-start") + solution_choices.add("ours-start") + + # Fully overlapping: projected starts earlier and ends later than existing + # + # ex. pr. + # +--+ + # +--+ | | + # | | | | + # +--+ | | + # +--+ + # + if ts.start < c.start and ts.end > c.end: + solution_choices.add("theirs-end") + solution_choices.add("theirs-start") + solution_choices.add("theirs-both") + + # Fully overlapping: projected starts later and ends earlier than existing + # + # ex. pr. + # +--+ + # | | +--+ + # | | | | + # | | +--+ + # +--+ + # + if ts.start > c.start and ts.end < c.end: + solution_choices.add("ours-end") + solution_choices.add("ours-start") + solution_choices.add("ours-both") + + if len(collisions) > 0: + solutions[ts.hash] = "" + + projected_entry["collisions"] = collisions + projected_entry["solution_choices"] = solution_choices + projected_entry["error"] = None + projected.append(projected_entry) + + conflicts["projected"] = projected + conflicts["solutions"] = solutions + conflicts["notes"] = {} + conflicts["playlists"] = {} + + return conflicts -- GitLab