From e4280361d18234224bbc3e7dfd9784feeed97862 Mon Sep 17 00:00:00 2001 From: Adam Goldsmith Date: Mon, 5 Aug 2024 22:48:36 -0400 Subject: [PATCH] membershipworks: Convert EventMeetingTime to subclass of Reservation --- membershipworks/admin.py | 5 +- ...ingtime_unique_event_start_end_and_more.py | 82 +++++++++++++++++++ membershipworks/models.py | 34 ++++---- 3 files changed, 102 insertions(+), 19 deletions(-) create mode 100644 membershipworks/migrations/0020_remove_eventmeetingtime_unique_event_start_end_and_more.py diff --git a/membershipworks/admin.py b/membershipworks/admin.py index da1253e..ba12a22 100644 --- a/membershipworks/admin.py +++ b/membershipworks/admin.py @@ -103,11 +103,12 @@ class TransactionAdmin(BaseMembershipWorksAdmin): class EventMeetingTimeInline(admin.TabularInline): model = EventMeetingTime + fields = ["start", "end", "duration", "resources"] + readonly_fields = ["duration"] + autocomplete_fields = ["resources"] extra = 0 min_num = 1 - readonly_fields = ["duration"] - @admin.register(EventInstructor) class EventInstructorAdmin(admin.ModelAdmin): diff --git a/membershipworks/migrations/0020_remove_eventmeetingtime_unique_event_start_end_and_more.py b/membershipworks/migrations/0020_remove_eventmeetingtime_unique_event_start_end_and_more.py new file mode 100644 index 0000000..934c3e5 --- /dev/null +++ b/membershipworks/migrations/0020_remove_eventmeetingtime_unique_event_start_end_and_more.py @@ -0,0 +1,82 @@ +# Generated by Django 5.0.7 on 2024-07-30 23:10 + +import django.db.models.deletion +from django.db import migrations, models + + +def convert_meetingtimes_to_reservations(apps, schema_editor): + Reservation = apps.get_model("reservations", "Reservation") + EventMeetingTime = apps.get_model("membershipworks", "EventMeetingTime") + for meeting_time in EventMeetingTime.objects.all(): + reservation = Reservation.objects.create( + id=meeting_time.id, + start=meeting_time.start, + end=meeting_time.end, + ) + meeting_time.reservation_ptr = reservation + meeting_time.save() + + +class Migration(migrations.Migration): + dependencies = [ + ("membershipworks", "0019_eventext_should_survey_eventext_survey_email_sent"), + ("reservations", "0001_initial"), + ] + + operations = [ + # add reservation field + migrations.AddField( + model_name="eventmeetingtime", + name="reservation_ptr", + field=models.OneToOneField( + auto_created=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + serialize=False, + to="reservations.reservation", + ), + preserve_default=False, + ), + migrations.RunPython(convert_meetingtimes_to_reservations, atomic=True), + # remove primary key + migrations.RemoveField( + model_name="eventmeetingtime", + name="id", + ), + # make reservation non-nullable + migrations.AlterField( + model_name="eventmeetingtime", + name="reservation_ptr", + field=models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="reservations.reservation", + ), + preserve_default=False, + ), + # delete old columns + migrations.RemoveConstraint( + model_name="eventmeetingtime", + name="unique_event_start_end", + ), + migrations.RemoveConstraint( + model_name="eventmeetingtime", + name="end_after_start", + ), + migrations.RemoveField( + model_name="eventmeetingtime", + name="duration", + ), + migrations.RemoveField( + model_name="eventmeetingtime", + name="end", + ), + migrations.RemoveField( + model_name="eventmeetingtime", + name="start", + ), + ] diff --git a/membershipworks/models.py b/membershipworks/models.py index 22636b8..aca367f 100644 --- a/membershipworks/models.py +++ b/membershipworks/models.py @@ -30,6 +30,8 @@ import nh3 from django_db_views.db_view import DBView from django_stubs_ext import WithAnnotations +from reservations.models import Reservation + class BaseModel(models.Model): _api_names_override: dict[str, str] = {} @@ -619,29 +621,27 @@ else: EventExtAnnotatedWithFinancials = WithAnnotations[EventExt] -class EventMeetingTime(models.Model): +class EventMeetingTime(Reservation): event = models.ForeignKey( EventExt, on_delete=models.CASCADE, related_name="meeting_times" ) - start = models.DateTimeField() - end = models.DateTimeField() - duration = models.GeneratedField( - expression=F("end") - F("start"), - output_field=models.DurationField(), - db_persist=False, - ) + # TODO: should probably do some validation in python to enforce + # - uniqueness and non-overlapping (per event) + # - min/max start/end time == event start end - class Meta: - constraints = [ - models.UniqueConstraint( - fields=["event", "start", "end"], name="unique_event_start_end" - ), - models.CheckConstraint(check=Q(end__gt=F("start")), name="end_after_start"), - ] + def make_google_calendar_event(self): + status = ( + "confirmed" + if self.event.cap > 0 and self.event.calendar != Event.EventCalendar.HIDDEN + else "cancelled" + ) - def __str__(self) -> str: - return f"{self.start} - {self.end}" + return super().make_google_calendar_event() | { + # TODO: add event description and links + "summary": self.event.unescaped_title, + "status": status, + } class EventInvoice(models.Model):