From 92f0b464c3fcee85e7a885f4b9d7cf8fa80a58ac Mon Sep 17 00:00:00 2001 From: magicfelix <felix@felix-zauberer.de> Date: Sat, 17 May 2025 18:04:39 +0200 Subject: [PATCH 01/11] Migrate Event to CalendarEvent --- aleksis/apps/paweljong/forms.py | 4 +- .../migrations/0033_migrate_event.py | 44 +++++++++++++++++++ .../migrations/0034_event_remove_fields.py | 38 ++++++++++++++++ aleksis/apps/paweljong/models.py | 27 +++++++++--- aleksis/apps/paweljong/schema/event.py | 21 +++++++++ aleksis/apps/paweljong/tables.py | 2 +- .../templates/paweljong/event/detail.html | 2 +- .../templates/paweljong/event/full.html | 2 +- .../templates/paweljong/print/voucher.html | 2 +- .../templated_email/event_created.email | 4 +- aleksis/apps/paweljong/views.py | 4 +- 11 files changed, 135 insertions(+), 15 deletions(-) create mode 100644 aleksis/apps/paweljong/migrations/0033_migrate_event.py create mode 100644 aleksis/apps/paweljong/migrations/0034_event_remove_fields.py diff --git a/aleksis/apps/paweljong/forms.py b/aleksis/apps/paweljong/forms.py index 2c24b5e..c71478f 100644 --- a/aleksis/apps/paweljong/forms.py +++ b/aleksis/apps/paweljong/forms.py @@ -61,7 +61,7 @@ class EditEventForm(ExtensibleForm): "linked_group", Row("display_name", "slug", "description"), Row("place", "published"), - Fieldset(_("Date data"), Row("date_event", "date_registration", "date_retraction")), + Fieldset(_("Date data"), Row("date_start", "date_registration", "date_retraction")), Fieldset( _("Event details"), Row("cost", "min_cost", "max_cost", "max_participants"), @@ -83,7 +83,7 @@ class EditEventForm(ExtensibleForm): "slug", "place", "published", - "date_event", + "date_start", "date_registration", "date_retraction", "cost", diff --git a/aleksis/apps/paweljong/migrations/0033_migrate_event.py b/aleksis/apps/paweljong/migrations/0033_migrate_event.py new file mode 100644 index 0000000..7a384f5 --- /dev/null +++ b/aleksis/apps/paweljong/migrations/0033_migrate_event.py @@ -0,0 +1,44 @@ +# Generated by Django 5.1.7 on 2025-04-03 11:42 + +import aleksis.core.mixins +import django.db.models.deletion +from django.conf import settings +from django.contrib.contenttypes.models import ContentType +from django.db import migrations, models + + +def migrate_events(apps, schema_editor): + CalendarEvent = apps.get_model("core", "CalendarEvent") + Event = apps.get_model("paweljong", "Event") + event_ctype = ContentType.objects.get_for_model(Event) + + db_alias = schema_editor.connection.alias + + for event in Event.objects.using(db_alias).all(): + calendar_event = CalendarEvent.objects.create( + date_start=event.date_event, + date_end=event.date_event, + polymorphic_ctype_id=event_ctype.pk, + extended_data=event.extended_data, + managed_by_app_label=event.managed_by_app_label, + ) + + event.calendarevent_ptr_id = calendar_event.pk + event.save() + + +class Migration(migrations.Migration): + + dependencies = [ + ('paweljong', '0032_alter_event_contact_information_visible_fields'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.AddField( + model_name='event', + name='calendarevent_ptr', + field=models.OneToOneField(auto_created=True, null=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, serialize=False, to='core.calendarevent'), + ), + migrations.RunPython(migrate_events), + ] diff --git a/aleksis/apps/paweljong/migrations/0034_event_remove_fields.py b/aleksis/apps/paweljong/migrations/0034_event_remove_fields.py new file mode 100644 index 0000000..a753904 --- /dev/null +++ b/aleksis/apps/paweljong/migrations/0034_event_remove_fields.py @@ -0,0 +1,38 @@ +# Generated by Django 5.1.7 on 2025-04-03 11:42 + +import aleksis.core.mixins +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('paweljong', '0033_migrate_event'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.RemoveField( + model_name='event', + name='id', + ), + migrations.AlterField( + model_name='event', + name='calendarevent_ptr', + field=models.OneToOneField(auto_created=True, null=False, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='core.calendarevent'), + ), + migrations.RemoveField( + model_name='event', + name='extended_data', + ), + migrations.RemoveField( + model_name='event', + name='managed_by_app_label', + ), + migrations.RemoveField( + model_name='event', + name='date_event', + ), + ] diff --git a/aleksis/apps/paweljong/models.py b/aleksis/apps/paweljong/models.py index 35deb2b..3a77722 100644 --- a/aleksis/apps/paweljong/models.py +++ b/aleksis/apps/paweljong/models.py @@ -5,6 +5,7 @@ from django.contrib.contenttypes.models import ContentType from django.contrib.postgres.fields import ArrayField from django.core.exceptions import ValidationError from django.db import models +from django.http import HttpRequest from django.urls import reverse from django.utils.text import slugify from django.utils.timezone import now @@ -16,7 +17,7 @@ from colorfield.fields import ColorField from aleksis.apps.tezor.models.base import Client from aleksis.apps.tezor.models.invoice import CustomPurchasedItem, Invoice, InvoiceGroup from aleksis.core.mixins import ExtensibleModel, GlobalPermissionModel -from aleksis.core.models import Group, Person, PersonRelationship +from aleksis.core.models import CalendarEvent, Group, Person, PersonRelationship from aleksis.core.util.core_helpers import generate_random_code, get_site_preferences from aleksis.core.util.email import send_email @@ -151,7 +152,10 @@ class EventAdditionalField(ExtensibleModel): verbose_name_plural = _("Addtitional fields for events") -class Event(ExtensibleModel): +class Event(CalendarEvent): + _class_name = "paweljong_event" + dav_verbose_name = _("Event registrations") + data_checks = [EventMembersSyncDataCheck] # Event details @@ -165,7 +169,6 @@ class Event(ExtensibleModel): slug = models.SlugField(max_length=255, verbose_name=_("Slug"), blank=True) # Date details - date_event = models.DateField(verbose_name=_("Date of event")) date_registration = models.DateField(verbose_name=_("Registration deadline")) date_retraction = models.DateField(verbose_name=_("Retraction deadline")) @@ -211,6 +214,20 @@ class Event(ExtensibleModel): default=[], ) + @classmethod + def value_title( + cls, reference_object: "Event", request: HttpRequest | None = None + ) -> str: + """Return the title of the event.""" + return reference_object.display_name + + @classmethod + def value_description( + cls, reference_object: "Event", request: HttpRequest | None = None + ) -> str: + """Return the description of the event.""" + return reference_object.description + def save(self, *args, **kwargs): if not self.slug: if self.linked_group.short_name: @@ -250,7 +267,7 @@ class Event(ExtensibleModel): if self.date_registration: return self.date_registration >= now - return self.date_event > now + return self.date_start > now def get_absolute_url(self): return reverse("event_by_name", kwargs={"slug": self.slug}) @@ -281,7 +298,7 @@ class Event(ExtensibleModel): @classmethod def upcoming_published_events(cls): - return Event.objects.filter(published=True, date_event__gte=now()) + return Event.objects.filter(published=True, date_start=now()) class EventInfoMailingThrough(ExtensibleModel): diff --git a/aleksis/apps/paweljong/schema/event.py b/aleksis/apps/paweljong/schema/event.py index 539969c..33ea772 100644 --- a/aleksis/apps/paweljong/schema/event.py +++ b/aleksis/apps/paweljong/schema/event.py @@ -8,3 +8,24 @@ from ..models import Event class EventType(PermissionsTypeMixin, DjangoObjectType): class Meta: model = Event + fields = [ + "id", + "uuid", + "date_start", + "date_end", + "display_name", + "description", + "published", + "place", + "slug", + "cost", + "min_cost", + "max_cost", + "max_participants", + "date_registration", + "date_retraction", + "terms", + "information", + "additional_fields", + "contact_information_visible_fields", + ] diff --git a/aleksis/apps/paweljong/tables.py b/aleksis/apps/paweljong/tables.py index 98e870e..0643a46 100644 --- a/aleksis/apps/paweljong/tables.py +++ b/aleksis/apps/paweljong/tables.py @@ -10,7 +10,7 @@ class ManageEventsTable(tables.Table): attrs = {"class": "responsive-table highlight"} display_name = tables.Column(verbose_name=_("Event")) - date_event = tables.Column(verbose_name=_("Date")) + date_start = tables.Column(verbose_name=_("Date")) max_participants = tables.Column(verbose_name=_("Max. participants")) date_registration = tables.Column(verbose_name=_("Registration until")) diff --git a/aleksis/apps/paweljong/templates/paweljong/event/detail.html b/aleksis/apps/paweljong/templates/paweljong/event/detail.html index 433537a..1482b77 100644 --- a/aleksis/apps/paweljong/templates/paweljong/event/detail.html +++ b/aleksis/apps/paweljong/templates/paweljong/event/detail.html @@ -38,7 +38,7 @@ <table id="event-detail-table"> <tr> <td><i class="material-icons small">event</i></td> - <td>{{ event.date_event }}</td> + <td>{{ event.date_start }}</td> <td><i class="material-icons small">location_on</i></td> <td>{{ event.place }}</td> </tr> diff --git a/aleksis/apps/paweljong/templates/paweljong/event/full.html b/aleksis/apps/paweljong/templates/paweljong/event/full.html index 06b637b..28ccab4 100644 --- a/aleksis/apps/paweljong/templates/paweljong/event/full.html +++ b/aleksis/apps/paweljong/templates/paweljong/event/full.html @@ -22,7 +22,7 @@ <table> <tr> <td><i class="material-icons small">event</i></td> - <td>{{ event.date_event }}</td> + <td>{{ event.date_start }}</td> <td><i class="material-icons small">location_on</i></td> <td>{{ event.place }}</td> </tr> diff --git a/aleksis/apps/paweljong/templates/paweljong/print/voucher.html b/aleksis/apps/paweljong/templates/paweljong/print/voucher.html index 5648732..8500d1b 100644 --- a/aleksis/apps/paweljong/templates/paweljong/print/voucher.html +++ b/aleksis/apps/paweljong/templates/paweljong/print/voucher.html @@ -17,7 +17,7 @@ <img src="{% firstof request.site.preferences.theme__logo.url aleksis_banner %}" alt="Logo" class="max-size-600 center"> <h3>{{ voucher.event }}<h3> <h4>{{ voucher.code }}</h4> - <p>{% trans "Voucher for" %} {{ voucher.person.first_name }} {{ voucher.person.last_name }} {% trans "to visit event" %} {{ voucher.event }} {% trans "on" %} {{ voucher.event.date_event }} {% trans "at" %} {{ voucher.event.place }}!</p> + <p>{% trans "Voucher for" %} {{ voucher.person.first_name }} {{ voucher.person.last_name }} {% trans "to visit event" %} {{ voucher.event }} {% trans "on" %} {{ voucher.event.date_start }} {% trans "at" %} {{ voucher.event.place }}!</p> <p>{% trans "To use the voucher, register for the event " %} <a href="{% url 'register_event_by_slug' voucher.event.pk %}">{% trans "here" %}</a></p> </div> diff --git a/aleksis/apps/paweljong/templates/templated_email/event_created.email b/aleksis/apps/paweljong/templates/templated_email/event_created.email index 30ac030..77d97d2 100644 --- a/aleksis/apps/paweljong/templates/templated_email/event_created.email +++ b/aleksis/apps/paweljong/templates/templated_email/event_created.email @@ -9,7 +9,7 @@ * {% trans "Description" %}: {{ new_event.description }} * {% trans "Published" %}: {{ new_event.published }} * {% trans "Place" %}: {{ new_event.place }} - * {% trans "Date of event" %}: {{ new_event.date_event }} + * {% trans "Date of event" %}: {{ new_event.date_start }} * {% trans "Registration deadline" %}: {{ new_event.date_registration }} * {% trans "Retraction deadline" %}: {{ new_event.date_retraction }} * {% trans "Fees" %}: {{ new_event.cost }} @@ -39,7 +39,7 @@ <li> {% trans "Description" %}: {{ new_event.description }}</li> <li> {% trans "Published" %}: {{ new_event.published }}</li> <li> {% trans "Place" %}: {{ new_event.place }}</li> - <li> {% trans "Date of event" %}: {{ new_event.date_event }}</li> + <li> {% trans "Date of event" %}: {{ new_event.date_start }}</li> <li> {% trans "Registration deadline" %}: {{ new_event.date_registration }}</li> <li> {% trans "Retraction deadline" %}: {{ new_event.date_retraction }}</li> <li> {% trans "Fees" %}: {{ new_event.cost }}</li> diff --git a/aleksis/apps/paweljong/views.py b/aleksis/apps/paweljong/views.py index 353a0ab..09cecc6 100644 --- a/aleksis/apps/paweljong/views.py +++ b/aleksis/apps/paweljong/views.py @@ -823,10 +823,10 @@ class UpcomingEventsRSSFeed(Feed): return _("Announcement feed of all upcoming events") def ttl(self): - date_event = Event.upcoming_published_events().order_by("-date_event").first().date_event + date_start = Event.upcoming_published_events().order_by("-date_start").first().date_start date_now = timezone.now().date() - return (date_event - date_now).seconds + return (date_start - date_now).seconds def items(self): return Event.upcoming_published_events() -- GitLab From c1e8c57465eeccfb944beb7b1acf01893f6cff1b Mon Sep 17 00:00:00 2001 From: magicfelix <felix@felix-zauberer.de> Date: Sat, 17 May 2025 18:04:54 +0200 Subject: [PATCH 02/11] Adapt to UUIDs --- .../paweljong/migrations/0035_add_uuid.py | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 aleksis/apps/paweljong/migrations/0035_add_uuid.py diff --git a/aleksis/apps/paweljong/migrations/0035_add_uuid.py b/aleksis/apps/paweljong/migrations/0035_add_uuid.py new file mode 100644 index 0000000..8f901f6 --- /dev/null +++ b/aleksis/apps/paweljong/migrations/0035_add_uuid.py @@ -0,0 +1,53 @@ +# Generated by Django 5.2.1 on 2025-05-17 15:43 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('paweljong', '0034_event_remove_fields'), + ] + + operations = [ + migrations.AddField( + model_name='checkpoint', + name='uuid', + field=models.UUIDField(db_default=models.Func(function='gen_random_uuid'), editable=False, unique=True), + ), + migrations.AddField( + model_name='eventadditionalfield', + name='uuid', + field=models.UUIDField(db_default=models.Func(function='gen_random_uuid'), editable=False, unique=True), + ), + migrations.AddField( + model_name='eventinfomailingthrough', + name='uuid', + field=models.UUIDField(db_default=models.Func(function='gen_random_uuid'), editable=False, unique=True), + ), + migrations.AddField( + model_name='eventregistration', + name='uuid', + field=models.UUIDField(db_default=models.Func(function='gen_random_uuid'), editable=False, unique=True), + ), + migrations.AddField( + model_name='infomailing', + name='uuid', + field=models.UUIDField(db_default=models.Func(function='gen_random_uuid'), editable=False, unique=True), + ), + migrations.AddField( + model_name='registrationstate', + name='uuid', + field=models.UUIDField(db_default=models.Func(function='gen_random_uuid'), editable=False, unique=True), + ), + migrations.AddField( + model_name='terms', + name='uuid', + field=models.UUIDField(db_default=models.Func(function='gen_random_uuid'), editable=False, unique=True), + ), + migrations.AddField( + model_name='voucher', + name='uuid', + field=models.UUIDField(db_default=models.Func(function='gen_random_uuid'), editable=False, unique=True), + ), + ] -- GitLab From 5ceb73bd0b0fac041a5875d21b91dfccc98af3e8 Mon Sep 17 00:00:00 2001 From: magicfelix <felix@felix-zauberer.de> Date: Sun, 18 May 2025 16:01:37 +0200 Subject: [PATCH 03/11] Show only relevant event --- aleksis/apps/paweljong/models.py | 37 ++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/aleksis/apps/paweljong/models.py b/aleksis/apps/paweljong/models.py index 3a77722..f8a5623 100644 --- a/aleksis/apps/paweljong/models.py +++ b/aleksis/apps/paweljong/models.py @@ -1,10 +1,12 @@ from datetime import datetime, timedelta from decimal import Decimal +from typing import Optional from django.contrib.contenttypes.models import ContentType from django.contrib.postgres.fields import ArrayField from django.core.exceptions import ValidationError from django.db import models +from django.db.models import Q, QuerySet from django.http import HttpRequest from django.urls import reverse from django.utils.text import slugify @@ -228,6 +230,41 @@ class Event(CalendarEvent): """Return the description of the event.""" return reference_object.description + @classmethod + def get_objects( + cls, + request: HttpRequest | None = None, + params: dict[str, any] | None = None, + start: Optional[datetime] = None, + end: Optional[datetime] = None, + start_qs: QuerySet | None = None, + additional_filter: Q | None = None, + **kwargs, + ) -> QuerySet: + q = additional_filter if additional_filter is not None else Q() + if request: + q = q & ( + Q( + id__in=EventRegistration.objects.filter(person=request.user.person) + .values_list("event_id", flat=True) + .union( + Group.objects.filter(linked_event__isnull=False, owners=request.user.person) + .values_list("linked_event__id", flat=True) + ) + ) + ) + + qs = super().get_objects( + request=request, + params=params, + start=start, + end=end, + start_qs=start_qs, + additional_filter=q, + **kwargs, + ) + return qs + def save(self, *args, **kwargs): if not self.slug: if self.linked_group.short_name: -- GitLab From 6a7a5cbac27483d52fdf99bf77df35308a43218b Mon Sep 17 00:00:00 2001 From: magicfelix <felix@felix-zauberer.de> Date: Sun, 18 May 2025 16:29:52 +0200 Subject: [PATCH 04/11] Migrate event registration references --- aleksis/apps/paweljong/migrations/0033_migrate_event.py | 9 +++++++++ .../paweljong/migrations/0034_event_remove_fields.py | 5 +++++ 2 files changed, 14 insertions(+) diff --git a/aleksis/apps/paweljong/migrations/0033_migrate_event.py b/aleksis/apps/paweljong/migrations/0033_migrate_event.py index 7a384f5..ca511c6 100644 --- a/aleksis/apps/paweljong/migrations/0033_migrate_event.py +++ b/aleksis/apps/paweljong/migrations/0033_migrate_event.py @@ -23,6 +23,10 @@ def migrate_events(apps, schema_editor): managed_by_app_label=event.managed_by_app_label, ) + for registration in event.registrations.all(): + registration.event_id = calendar_event.pk + registration.save() + event.calendarevent_ptr_id = calendar_event.pk event.save() @@ -40,5 +44,10 @@ class Migration(migrations.Migration): name='calendarevent_ptr', field=models.OneToOneField(auto_created=True, null=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, serialize=False, to='core.calendarevent'), ), + migrations.AlterField( + model_name='eventregistration', + name='event', + field=models.ForeignKey(db_constraint=False, db_index=False, on_delete=django.db.models.deletion.CASCADE, related_name='registrations', to='paweljong.event', verbose_name='Event'), + ), migrations.RunPython(migrate_events), ] diff --git a/aleksis/apps/paweljong/migrations/0034_event_remove_fields.py b/aleksis/apps/paweljong/migrations/0034_event_remove_fields.py index a753904..a1f5c67 100644 --- a/aleksis/apps/paweljong/migrations/0034_event_remove_fields.py +++ b/aleksis/apps/paweljong/migrations/0034_event_remove_fields.py @@ -35,4 +35,9 @@ class Migration(migrations.Migration): model_name='event', name='date_event', ), + migrations.AlterField( + model_name='eventregistration', + name='event', + field=models.ForeignKey(db_constraint=False, db_index=False, on_delete=django.db.models.deletion.CASCADE, related_name='registrations', to='paweljong.event', verbose_name='Event'), + ), ] -- GitLab From 91b509f22c07d4872328bab2bbc94cade3c24767 Mon Sep 17 00:00:00 2001 From: magicfelix <felix@felix-zauberer.de> Date: Sun, 18 May 2025 22:32:20 +0200 Subject: [PATCH 05/11] Include attendees in ical --- aleksis/apps/paweljong/models.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/aleksis/apps/paweljong/models.py b/aleksis/apps/paweljong/models.py index f8a5623..e20256a 100644 --- a/aleksis/apps/paweljong/models.py +++ b/aleksis/apps/paweljong/models.py @@ -15,6 +15,7 @@ from django.utils.translation import gettext_lazy as _ from ckeditor.fields import RichTextField from colorfield.fields import ColorField +from icalendar import vCalAddress from aleksis.apps.tezor.models.base import Client from aleksis.apps.tezor.models.invoice import CustomPurchasedItem, Invoice, InvoiceGroup @@ -230,6 +231,21 @@ class Event(CalendarEvent): """Return the description of the event.""" return reference_object.description + @classmethod + def value_attendee( + cls, reference_object: "Event", request: HttpRequest | None = None + ) -> list[vCalAddress]: + """Return the attendees of the event.""" + owners = Person.objects.filter(owner_of=reference_object.linked_group) + participants = Person.objects.filter( + pk__in=reference_object.registrations.filter(retracted=False).values_list("person__id", flat=True) + ) + + owner_attendees = [p.get_vcal_address(request=request) for p in owners] + participant_attendees = [p.get_vcal_address(role="OPT-PARTICIPANT", request=request) for p in participants] + + return owner_attendees + participant_attendees + @classmethod def get_objects( cls, -- GitLab From b22724da056f68718196549b079737f7d04ea773 Mon Sep 17 00:00:00 2001 From: magicfelix <felix@felix-zauberer.de> Date: Sun, 18 May 2025 22:32:36 +0200 Subject: [PATCH 06/11] Set ical status based on event registration retraction --- aleksis/apps/paweljong/models.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/aleksis/apps/paweljong/models.py b/aleksis/apps/paweljong/models.py index e20256a..4b22639 100644 --- a/aleksis/apps/paweljong/models.py +++ b/aleksis/apps/paweljong/models.py @@ -246,6 +246,22 @@ class Event(CalendarEvent): return owner_attendees + participant_attendees + @classmethod + def value_status( + cls, reference_object: "Event", request: HttpRequest | None = None + ) -> str: + """Return the status of the event.""" + if request is None: + return "CONFIRMED" + try: + registration = EventRegistration.objects.get( + event=reference_object, + person=request.user.person, + ) + except EventRegistration.DoesNotExist: + return "CONFIRMED" + return "CANCELLED" if registration.retracted else "CONFIRMED" + @classmethod def get_objects( cls, -- GitLab From 00a80b58b98df64882ab87dd4267cd63bc010c95 Mon Sep 17 00:00:00 2001 From: magicfelix <felix@felix-zauberer.de> Date: Thu, 22 May 2025 09:54:44 +0200 Subject: [PATCH 07/11] Fix refactoring mistake --- aleksis/apps/paweljong/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aleksis/apps/paweljong/models.py b/aleksis/apps/paweljong/models.py index 4b22639..8964a1c 100644 --- a/aleksis/apps/paweljong/models.py +++ b/aleksis/apps/paweljong/models.py @@ -367,7 +367,7 @@ class Event(CalendarEvent): @classmethod def upcoming_published_events(cls): - return Event.objects.filter(published=True, date_start=now()) + return Event.objects.filter(published=True, date_start__gte=now()) class EventInfoMailingThrough(ExtensibleModel): -- GitLab From 4dd0379698de7811310edc1502f456cd1e5522d0 Mon Sep 17 00:00:00 2001 From: magicfelix <felix@felix-zauberer.de> Date: Thu, 22 May 2025 19:10:19 +0200 Subject: [PATCH 08/11] Show participants only to event owners --- aleksis/apps/paweljong/models.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/aleksis/apps/paweljong/models.py b/aleksis/apps/paweljong/models.py index 8964a1c..912dd22 100644 --- a/aleksis/apps/paweljong/models.py +++ b/aleksis/apps/paweljong/models.py @@ -236,6 +236,11 @@ class Event(CalendarEvent): cls, reference_object: "Event", request: HttpRequest | None = None ) -> list[vCalAddress]: """Return the attendees of the event.""" + if request is None: + return [] + is_owner = reference_object.linked_group.owners.filter(user_id=request.user.pk).exists() + if not is_owner: + return [] owners = Person.objects.filter(owner_of=reference_object.linked_group) participants = Person.objects.filter( pk__in=reference_object.registrations.filter(retracted=False).values_list("person__id", flat=True) -- GitLab From b90bc07499f30b7b8cef5603e06d93929778e015 Mon Sep 17 00:00:00 2001 From: magicfelix <felix@felix-zauberer.de> Date: Thu, 22 May 2025 19:20:09 +0200 Subject: [PATCH 09/11] Migrate references to event --- .../migrations/0033_migrate_event.py | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/aleksis/apps/paweljong/migrations/0033_migrate_event.py b/aleksis/apps/paweljong/migrations/0033_migrate_event.py index ca511c6..2a8e8fa 100644 --- a/aleksis/apps/paweljong/migrations/0033_migrate_event.py +++ b/aleksis/apps/paweljong/migrations/0033_migrate_event.py @@ -27,6 +27,26 @@ def migrate_events(apps, schema_editor): registration.event_id = calendar_event.pk registration.save() + for term in event.terms.all(): + term.event_id = calendar_event.pk + term.save() + + for info_mailing in event.info_mailings.all(): + info_mailing.event_id = calendar_event.pk + info_mailing.save() + + for additional_field in event.additional_fields.all(): + additional_field.event_id = calendar_event.pk + additional_field.save() + + for voucher in event.vouchers.all(): + voucher.event_id = calendar_event.pk + voucher.save() + + for checkpoint in event.checkpoints.all(): + checkpoint.event_id = calendar_event.pk + checkpoint.save() + event.calendarevent_ptr_id = calendar_event.pk event.save() -- GitLab From 8c5c52929eaf944d8fa49bcc103ce2fe966a7463 Mon Sep 17 00:00:00 2001 From: Jonathan Weth <git@jonathanweth.de> Date: Sun, 1 Jun 2025 13:12:00 +0200 Subject: [PATCH 10/11] Add date_end to event form --- aleksis/apps/paweljong/forms.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/aleksis/apps/paweljong/forms.py b/aleksis/apps/paweljong/forms.py index c71478f..0d12959 100644 --- a/aleksis/apps/paweljong/forms.py +++ b/aleksis/apps/paweljong/forms.py @@ -61,7 +61,10 @@ class EditEventForm(ExtensibleForm): "linked_group", Row("display_name", "slug", "description"), Row("place", "published"), - Fieldset(_("Date data"), Row("date_start", "date_registration", "date_retraction")), + Fieldset( + _("Date data"), + Row("date_start", "date_end", "date_registration", "date_retraction"), + ), Fieldset( _("Event details"), Row("cost", "min_cost", "max_cost", "max_participants"), @@ -84,6 +87,7 @@ class EditEventForm(ExtensibleForm): "place", "published", "date_start", + "date_end", "date_registration", "date_retraction", "cost", -- GitLab From c8ef29d26512126945166622bae8b85b809b8a67 Mon Sep 17 00:00:00 2001 From: Jonathan Weth <git@jonathanweth.de> Date: Sun, 1 Jun 2025 13:12:21 +0200 Subject: [PATCH 11/11] Reformat --- .../EventRegistrationForm.vue | 33 +++++++++------- aleksis/apps/paweljong/models.py | 22 +++++------ .../paweljong/schema/event_registration.py | 39 +++++++++++++++---- aleksis/apps/paweljong/views.py | 4 +- 4 files changed, 65 insertions(+), 33 deletions(-) diff --git a/aleksis/apps/paweljong/frontend/components/event_registration/EventRegistrationForm.vue b/aleksis/apps/paweljong/frontend/components/event_registration/EventRegistrationForm.vue index 558164c..5fe372d 100644 --- a/aleksis/apps/paweljong/frontend/components/event_registration/EventRegistrationForm.vue +++ b/aleksis/apps/paweljong/frontend/components/event_registration/EventRegistrationForm.vue @@ -320,7 +320,12 @@ import EventRegistrationHelpTextCards from "./EventRegistrationHelpTextCards.vue ) " required - :rules="$rules().required.build([ ...usernameRules.usernameAllowed, ...usernameRules.usernameASCII ])" + :rules=" + $rules().required.build([ + ...usernameRules.usernameAllowed, + ...usernameRules.usernameASCII, + ]) + " prepend-icon="mdi-account-outline" ></v-text-field> </div> @@ -702,8 +707,13 @@ import EventRegistrationHelpTextCards from "./EventRegistrationHelpTextCards.vue </v-col> </v-row> </v-card-text> - <v-card-actions v-if="index > 0 && !Object.hasOwn(guardian, 'id')"> - <cancel-button i18n-key="paweljong.event_registration.form.steps.guardians.remove" @click="removeGuardian(index)" /> + <v-card-actions + v-if="index > 0 && !Object.hasOwn(guardian, 'id')" + > + <cancel-button + i18n-key="paweljong.event_registration.form.steps.guardians.remove" + @click="removeGuardian(index)" + /> </v-card-actions> </v-card> </v-form> @@ -1046,13 +1056,8 @@ export default { query: whoAmI, result({ data }) { if (data && data.whoAmI && data.whoAmI.person) { - const { - id, - __typename, - addresses, - guardians, - ...filteredPerson - } = data.whoAmI.person; + const { id, __typename, addresses, guardians, ...filteredPerson } = + data.whoAmI.person; this.data.person = filteredPerson; const filteredGuardians = guardians.map( @@ -1071,7 +1076,9 @@ export default { ]; } - const defaultAddress = addresses.find((a) => a.addressTypes.some((at) => at.name === "default")); + const defaultAddress = addresses.find((a) => + a.addressTypes.some((at) => at.name === "default"), + ); if (defaultAddress) { this.data.person.address = { street: defaultAddress.street, @@ -1080,7 +1087,7 @@ export default { place: defaultAddress.place, country: defaultAddress.country, }; - } else { + } else { this.data.person.address = { street: "", housenumber: "", @@ -1088,7 +1095,7 @@ export default { place: "", country: "", }; - } + } } }, skip() { diff --git a/aleksis/apps/paweljong/models.py b/aleksis/apps/paweljong/models.py index 912dd22..833e037 100644 --- a/aleksis/apps/paweljong/models.py +++ b/aleksis/apps/paweljong/models.py @@ -218,9 +218,7 @@ class Event(CalendarEvent): ) @classmethod - def value_title( - cls, reference_object: "Event", request: HttpRequest | None = None - ) -> str: + def value_title(cls, reference_object: "Event", request: HttpRequest | None = None) -> str: """Return the title of the event.""" return reference_object.display_name @@ -243,18 +241,20 @@ class Event(CalendarEvent): return [] owners = Person.objects.filter(owner_of=reference_object.linked_group) participants = Person.objects.filter( - pk__in=reference_object.registrations.filter(retracted=False).values_list("person__id", flat=True) + pk__in=reference_object.registrations.filter(retracted=False).values_list( + "person__id", flat=True + ) ) owner_attendees = [p.get_vcal_address(request=request) for p in owners] - participant_attendees = [p.get_vcal_address(role="OPT-PARTICIPANT", request=request) for p in participants] + participant_attendees = [ + p.get_vcal_address(role="OPT-PARTICIPANT", request=request) for p in participants + ] return owner_attendees + participant_attendees @classmethod - def value_status( - cls, reference_object: "Event", request: HttpRequest | None = None - ) -> str: + def value_status(cls, reference_object: "Event", request: HttpRequest | None = None) -> str: """Return the status of the event.""" if request is None: return "CONFIRMED" @@ -285,8 +285,9 @@ class Event(CalendarEvent): id__in=EventRegistration.objects.filter(person=request.user.person) .values_list("event_id", flat=True) .union( - Group.objects.filter(linked_event__isnull=False, owners=request.user.person) - .values_list("linked_event__id", flat=True) + Group.objects.filter( + linked_event__isnull=False, owners=request.user.person + ).values_list("linked_event__id", flat=True) ) ) ) @@ -515,7 +516,6 @@ class EventRegistration(ExtensibleModel): }, ) - address = self.person.addresses.first() street = address.street if address is not None else "" housenumber = address.housenumber if address is not None else "" diff --git a/aleksis/apps/paweljong/schema/event_registration.py b/aleksis/apps/paweljong/schema/event_registration.py index 3c2d665..493cf12 100644 --- a/aleksis/apps/paweljong/schema/event_registration.py +++ b/aleksis/apps/paweljong/schema/event_registration.py @@ -14,7 +14,11 @@ from aleksis.apps.postbuero.models import MailAddress, MailDomain from aleksis.apps.postbuero.schema import MailAddressInputType from aleksis.core.models import Activity, Person from aleksis.core.schema.base import PermissionsTypeMixin -from aleksis.core.schema.person import PersonAddressMutationMixin, PersonGuardianMutationMixin, PersonInputType +from aleksis.core.schema.person import ( + PersonAddressMutationMixin, + PersonGuardianMutationMixin, + PersonInputType, +) from aleksis.core.schema.user import UserInputType from aleksis.core.util.auth_helpers import custom_username_validators from aleksis.core.util.core_helpers import get_site_preferences @@ -50,7 +54,9 @@ class EventRegistrationInputType(graphene.InputObjectType): retraction_consent = graphene.Boolean(required=True) -class SendEventRegistrationMutation(PersonAddressMutationMixin, PersonGuardianMutationMixin, graphene.Mutation): +class SendEventRegistrationMutation( + PersonAddressMutationMixin, PersonGuardianMutationMixin, graphene.Mutation +): class Arguments: event = graphene.ID(required=True) event_registration = EventRegistrationInputType(required=True) @@ -75,7 +81,13 @@ class SendEventRegistrationMutation(PersonAddressMutationMixin, PersonGuardianMu email = None - if event_registration["email"] is not None and event_registration["email"]["domain"] is not None and event_registration["email"]["domain"] != "" and event_registration["email"]["local_part"] is not None and event_registration["email"]["local_part"] != "": + if ( + event_registration["email"] is not None + and event_registration["email"]["domain"] is not None + and event_registration["email"]["domain"] != "" + and event_registration["email"]["local_part"] is not None + and event_registration["email"]["local_part"] != "" + ): try: domain = MailDomain.objects.get(pk=event_registration["email"]["domain"]) except IntegrityError: @@ -89,7 +101,11 @@ class SendEventRegistrationMutation(PersonAddressMutationMixin, PersonGuardianMu raise ValidationError(_("Mail address already in use.")) email = str(_mail_address) - elif event_registration["user"] is not None and event_registration["user"]["email"] is not None and event_registration["user"]["email"] != "": + elif ( + event_registration["user"] is not None + and event_registration["user"]["email"] is not None + and event_registration["user"]["email"] != "" + ): validate_email(event_registration["user"]["email"]) email = event_registration["user"]["email"] elif not info.context.user.is_authenticated: @@ -99,7 +115,10 @@ class SendEventRegistrationMutation(PersonAddressMutationMixin, PersonGuardianMu if info.context.user.is_authenticated: user = info.context.user elif event_registration["user"] is not None: - if event_registration["user"]["username"] is not None and event_registration["user"]["username"] != "": + if ( + event_registration["user"]["username"] is not None + and event_registration["user"]["username"] != "" + ): for validator in custom_username_validators: validator(event_registration["user"]["username"]) try: @@ -112,7 +131,10 @@ class SendEventRegistrationMutation(PersonAddressMutationMixin, PersonGuardianMu else: raise ValidationError(_("Username is required")) - if event_registration["user"]["password"] is not None and event_registration["user"]["password"] != "": + if ( + event_registration["user"]["password"] is not None + and event_registration["user"]["password"] != "" + ): user.set_password(event_registration["user"]["password"]) else: raise ValidationError(_("Password is required")) @@ -147,7 +169,10 @@ class SendEventRegistrationMutation(PersonAddressMutationMixin, PersonGuardianMu guardian_pks = [] # Store address information - if event_registration["person"] is not None and event_registration["person"]["address"] is not None: + if ( + event_registration["person"] is not None + and event_registration["person"]["address"] is not None + ): cls._handle_address(root, info, event_registration["person"]["address"], person) if event_registration["person"] is not None: diff --git a/aleksis/apps/paweljong/views.py b/aleksis/apps/paweljong/views.py index 09cecc6..cee8533 100644 --- a/aleksis/apps/paweljong/views.py +++ b/aleksis/apps/paweljong/views.py @@ -1,11 +1,11 @@ from typing import Optional -from django.db.models import Avg from django.conf import settings from django.contrib.auth import get_user_model from django.contrib.contenttypes.models import ContentType from django.contrib.syndication.views import Feed from django.core.exceptions import ValidationError +from django.db.models import Avg from django.http import HttpRequest, HttpResponse from django.shortcuts import redirect, render from django.urls import reverse, reverse_lazy @@ -1014,7 +1014,7 @@ class EventDetailView(PermissionRequiredMixin, DetailView): average_amount_paid = Invoice.objects.filter( for_content_type=ContentType.objects.get_for_model(EventRegistration), - for_object_id__in=active_registrations.values_list("id", flat=True) + for_object_id__in=active_registrations.values_list("id", flat=True), ).aggregate(Avg("total", default=0))["total__avg"] context["average_amount_paid"] = round(average_amount_paid, 2) context["average_amount_paid_percent"] = ( -- GitLab