from django import forms from django.db import models from django.db.models import Min, Q from django.utils import timezone from django.utils.translation import gettext_lazy as _ from grapple.helpers import register_query_field, register_singular_query_field from grapple.models import ( GraphQLBoolean, GraphQLCollection, GraphQLForeignKey, GraphQLImage, GraphQLInt, GraphQLStreamfield, GraphQLString, ) from modelcluster.fields import ParentalKey, ParentalManyToManyField from modelcluster.models import ClusterableModel from wagtail.admin.panels import ( FieldPanel, FieldRowPanel, HelpPanel, InlinePanel, MultiFieldPanel, PageChooserPanel, TitleFieldPanel, ) from wagtail.models import Orderable, Page, PageManager, PageQuerySet from wagtail.snippets.models import register_snippet from dnscms.fields import CommonStreamField from venues.models import VenuePage ALL_PIGS = [ ("logo", "Logogrisen"), ("music", "Musikergrisen"), ("drink", "Drikkegrisen"), ("dance", "Dansegrisen"), ("point", "Pekegrisen"), ("student", "Studentgrisen"), ("listen", "Lyttegrisen"), ("guard", "Vaktgrisen"), ("key", "Nøkkelgrisen"), ("chill", "Liggegrisen"), ("peek", "Tittegrisen"), ] @register_singular_query_field("eventIndex") class EventIndex(Page): max_count = 1 subpage_types = ["events.EventPage"] def future_events(self, info, **kwargs): return EventPage.objects.future().order_by("next_occurrence") graphql_fields = [ GraphQLCollection( GraphQLForeignKey, "future_events", "events.EventPage", required=True, item_required=True, is_queryset=True, ), ] @register_snippet @register_query_field("eventCategory", "eventCategories") class EventCategory(models.Model): name = models.CharField( max_length=100, null=False, blank=False, ) slug = models.SlugField( verbose_name=_("slug"), max_length=255, help_text=_("The name of the category as it will appear in URLs."), ) show_in_filters = models.BooleanField( default=False, help_text="Skal denne kategorien være mulig å filtrere på i programmet?" ) PIG_CHOICES = [ ("", "Ingen"), ] + ALL_PIGS pig = models.CharField( max_length=32, choices=PIG_CHOICES, default="", blank=True, help_text="Standardgris for arrangementer av denne typen.", ) panels = [ TitleFieldPanel("name"), FieldPanel("slug"), FieldPanel("show_in_filters"), FieldPanel("pig", heading="Gris"), ] graphql_fields = [ GraphQLString("name", required=True), GraphQLString("slug", required=True), GraphQLBoolean("show_in_filters", required=True), GraphQLString("pig", required=True), ] class Meta: verbose_name = "Event category" verbose_name_plural = "Event categories" ordering = ["name"] def __str__(self): return self.name @register_snippet @register_query_field("eventOrganizer", "eventOrganizers") class EventOrganizer(ClusterableModel): name = models.CharField( max_length=100, null=False, blank=False, ) slug = models.SlugField( verbose_name=_("slug"), max_length=255, help_text=_("The name of the organizer as it will appear in URLs."), ) association = models.ForeignKey( "associations.AssociationPage", null=True, blank=True, on_delete=models.PROTECT, related_name="organizers", ) panels = [ TitleFieldPanel("name"), FieldPanel("slug"), PageChooserPanel("association", ["associations.AssociationPage"]), ] class Meta: verbose_name = "Event organizer" verbose_name_plural = "Event organizers" ordering = ["name"] def __str__(self): return self.name class EventPageQuerySet(PageQuerySet): def future(self): today = timezone.localtime(timezone.now()).date() next_occurrence = Min("occurrences__start", filter=Q(occurrences__start__gte=today)) return self.filter(occurrences__start__gte=today).annotate(next_occurrence=next_occurrence) EventPageManager = PageManager.from_queryset(EventPageQuerySet) class EventPage(Page): subpage_types = [] parent_page_types = ["events.EventIndex"] show_in_menus = False objects = EventPageManager() featured_image = models.ForeignKey( "images.CustomImage", null=True, blank=True, on_delete=models.SET_NULL, related_name="+", help_text=( "Velg et bilde til bruk i programmet og andre visningsflater. " "Bør være et bilde eller en illustrasjon uten tekst " "– ikke gjenbruk et Facebook-cover ukritisk!" ), ) body = CommonStreamField categories = ParentalManyToManyField( "events.EventCategory", blank=True, ) organizers = ParentalManyToManyField( "events.EventOrganizer", blank=True, ) PIG_CHOICES = [ ("", "Ingen"), ("automatic", "Automatisk"), ] + ALL_PIGS pig = models.CharField( max_length=32, choices=PIG_CHOICES, default="automatic", blank=True, help_text=( "Grisen som henger på arrangementssiden. " "Automatisk fører til at en velges basert på arrangementets kategori." ), ) ticket_url = models.URLField( blank=True, max_length=512, help_text="Lenke direkte til billettkjøp, f.eks. TicketCo eller Ticketmaster", ) facebook_url = models.URLField( blank=True, max_length=512, help_text="Lenke direkte til arrangementet på Facebook", ) free = models.BooleanField(null=False, default=False) price_regular = models.IntegerField(null=True, blank=True) price_student = models.IntegerField(null=True, blank=True) price_member = models.IntegerField(null=True, blank=True) ticket_panels = [ FieldPanel("free", heading="Gratis", help_text="Er dette arrangementet gratis for alle?"), MultiFieldPanel( children=[ FieldRowPanel( children=[ FieldPanel("price_regular", heading="Ordinær pris"), FieldPanel("price_student", heading="Pris for studenter"), FieldPanel("price_member", heading="Pris for medlemmer av DNS"), ], help_text="", ), HelpPanel(content="La alle prisfeltene stå tomme om arrangementet er gratis."), ], attrs={"id": "specific_pricing_panel"}, ), FieldPanel("ticket_url", heading="Billettkjøpslenke"), ] content_panels = Page.content_panels + [ FieldPanel("featured_image"), FieldPanel("body"), FieldPanel("categories", widget=forms.CheckboxSelectMultiple), FieldPanel("organizers", widget=forms.SelectMultiple), FieldPanel("pig", heading="Gris"), FieldPanel( "facebook_url", heading="Facebook-lenke", help_text="Lenke direkte til arrangementet på Facebook.", ), MultiFieldPanel(heading="Priser og billettkjøp", children=ticket_panels), MultiFieldPanel( heading="Dato, tid og lokale", children=[ HelpPanel( content=( "Om arrangementet går over flere dager, " "legg inn hver dag som en egen forekomst." ), ), InlinePanel("occurrences", min_num=1, label="Forekomst"), ], ), ] graphql_fields = [ GraphQLImage("featured_image"), GraphQLStreamfield("body"), GraphQLString("pig"), GraphQLString("ticket_url"), GraphQLString("facebook_url"), GraphQLBoolean("free"), GraphQLInt("price_regular"), GraphQLInt("price_student"), GraphQLInt("price_member"), GraphQLCollection( GraphQLForeignKey, "categories", "events.EventCategory", required=True, item_required=True, ), GraphQLCollection( GraphQLForeignKey, "occurrences", "events.EventOccurrence", required=True, item_required=True, ), GraphQLCollection( GraphQLForeignKey, "organizers", "events.EventOrganizer", required=True, item_required=True, ), ] def clean(self): super().clean() # if the event is free, all specific pricing is unset if self.free: self.price_regular = None self.price_student = None self.price_member = None class EventOccurrence(Orderable): event = ParentalKey(EventPage, on_delete=models.CASCADE, related_name="occurrences") start = models.DateTimeField() end = models.DateTimeField(null=True, blank=True) venue = models.ForeignKey( VenuePage, on_delete=models.PROTECT, related_name="event_occurrences" ) panels = [ FieldRowPanel( children=[ FieldPanel("start", heading="Start"), FieldPanel("end", heading="Slutt"), ], ), FieldPanel("venue", heading="Lokale"), ] graphql_fields = [ GraphQLString("start", required=True), GraphQLString("end"), GraphQLForeignKey("venue", "venues.VenuePage"), ] def __str__(self): return f"{self.start}--{self.end}" class Meta: verbose_name = "Forekomst" verbose_name_plural = "Forekomster" sample_legacy_event_json = """ { "id": 64573, "date": "2023-12-27T11:28:34", "date_gmt": "2023-12-27T10:28:34", "guid": { "rendered": "https://studentersamfundet.no/?post_type=event&p=64573" }, "modified": "2023-12-27T11:44:11", "modified_gmt": "2023-12-27T10:44:11", "slug": "quiz-147-2-2-2-2-2-2-2-2-2-2-2-2-2-2-2-2-2-2-2-2-2-3-2-2-2-2-2-2-2-2-2-2-2-2-2-2-2-2-2-2-2-2-2-2-2-2-2-2-2-2-2-2-2-2-3-2-2-2-2-2-2-2-2-2-2-2-2-2", "status": "publish", "type": "event", "link": "https://studentersamfundet.no/arrangement/quiz-147-2-2-2-2-2-2-2-2-2-2-2-2-2-2-2-2-2-2-2-2-2-3-2-2-2-2-2-2-2-2-2-2-2-2-2-2-2-2-2-2-2-2-2-2-2-2-2-2-2-2-2-2-2-2-3-2-2-2-2-2-2-2-2-2-2-2-2-2/", "title": { "rendered": "QUIZ", "decoded": "QUIZ" }, "content": { "rendered": "\n
Det Norske Studentersamfund inviterer til quiz hver tirsdag kl. 19:00.
\n\n\n\nVi serverer 50 spørsmål som kan spenne seg fra one hit wonders fra 80-tallet, universets uendelighet, dyrelivets merkverdigheter og mye, mye mer!
\n\n\n\nQuiz på Chateau Neuf er åpent for alle. Vinnere og “lucky losers” vil bli utnevnt hver kveld. Lag som er over seks personer er tillatt, men da trekkes dere for ett poeng per deltaker per runde.
\n\n\n\nFor de som ønsker å være med på sammenlagtkonkurransen for høsten vil den regnes ut for de tolv beste prestasjonene laget leverer. Så det vil fremdeles være god sjanse for å vinne sammenlagt selv dere må droppe en quiz eller to for eksamener eller andre forpliktelser.
\n\n\n\nVelkommen quizglade mennesker!
\n\n\n\nGratis inngang!
\n", "protected": false }, "excerpt": { "rendered": "Det Norske Studentersamfund inviterer til quiz hver tirsdag kl. 19:00. Vi serverer 50 spørsmål som kan spenne seg fra one hit wonders fra 80-tallet, universets uendelighet, dyrelivets merkverdigheter og mye, mye mer! Quiz på Chateau Neuf er åpent for alle. Vinnere og “lucky losers” vil bli utnevnt hver kveld. Lag som er over seks personer […]
\n", "protected": false }, "author": 2150, "featured_media": 64585, "template": "", "meta": [], "event_types": [13], "event_organizers": [390, 322], "facebook_url": "https://fb.me/e/2RDR5pZdr", "ticket_url": "", "price_regular": "", "price_member": "", "start_time": "2024-05-07T17:00:00+00:00", "end_time": "2024-05-07T20:00:00+00:00", "venue": "Glassbaren", "venue_id": "55063", "thumbnail": { "thumbnail": "https://studentersamfundet.no/wp/wp-content/uploads/2023/12/quiz-header-1-150x150.png", "medium": "https://studentersamfundet.no/wp/wp-content/uploads/2023/12/quiz-header-1-300x169.png", "medium_large": "https://studentersamfundet.no/wp/wp-content/uploads/2023/12/quiz-header-1-768x433.png", "large": "https://studentersamfundet.no/wp/wp-content/uploads/2023/12/quiz-header-1-1280x720.png", "1536x1536": "https://studentersamfundet.no/wp/wp-content/uploads/2023/12/quiz-header-1-1536x865.png", "2048x2048": "https://studentersamfundet.no/wp/wp-content/uploads/2023/12/quiz-header-1.png", "four-column": "https://studentersamfundet.no/wp/wp-content/uploads/2023/12/quiz-header-1-393x342.png", "six-column": "https://studentersamfundet.no/wp/wp-content/uploads/2023/12/quiz-header-1-608x342.png", "extra-large": "https://studentersamfundet.no/wp/wp-content/uploads/2023/12/quiz-header-1-1600x901.png", "newsletter-half": "https://studentersamfundet.no/wp/wp-content/uploads/2023/12/quiz-header-1-320x190.png", "newsletter-third": "https://studentersamfundet.no/wp/wp-content/uploads/2023/12/quiz-header-1-213x126.png", "featured": "https://studentersamfundet.no/wp/wp-content/uploads/2023/12/quiz-header-1-1200x480.png" }, "_links": { "self": [ { "href": "https://studentersamfundet.no/wp-json/wp/v2/events/64573" } ], "collection": [ { "href": "https://studentersamfundet.no/wp-json/wp/v2/events" } ], "about": [ { "href": "https://studentersamfundet.no/wp-json/wp/v2/types/event" } ], "author": [ { "embeddable": true, "href": "https://studentersamfundet.no/wp-json/wp/v2/users/2150" } ], "version-history": [ { "count": 1, "href": "https://studentersamfundet.no/wp-json/wp/v2/events/64573/revisions" } ], "predecessor-version": [ { "id": 64574, "href": "https://studentersamfundet.no/wp-json/wp/v2/events/64573/revisions/64574" } ], "wp:featuredmedia": [ { "embeddable": true, "href": "https://studentersamfundet.no/wp-json/wp/v2/media/64585" } ], "wp:attachment": [ { "href": "https://studentersamfundet.no/wp-json/wp/v2/media?parent=64573" } ], "wp:term": [ { "taxonomy": "event_type", "embeddable": true, "href": "https://studentersamfundet.no/wp-json/wp/v2/event_types?post=64573" }, { "taxonomy": "event_organizer", "embeddable": true, "href": "https://studentersamfundet.no/wp-json/wp/v2/event_organizers?post=64573" } ], "curies": [ { "name": "wp", "href": "https://api.w.org/{rel}", "templated": true } ] } } """