Source code for eucrim.event.models

# SPDX-FileCopyrightText: 2024 Thomas Breitner <t.breitner@csl.mpg.de>
#
# SPDX-License-Identifier: EUPL-1.2

# from datetime import date
from datetime import timedelta
import urllib.parse

from django.http import HttpResponse
from django.db import models
from django.utils import timezone
from django.utils.html import format_html
from django.shortcuts import render

from wagtail.models import Page
from wagtail.fields import RichTextField
from wagtail.admin.panels import (
    FieldPanel,
    MultiFieldPanel,
    FieldRowPanel,
    TabbedInterface,
    ObjectList,
)
from wagtail.contrib.routable_page.models import RoutablePageMixin, route
from wagtail.search import index

from django_countries.fields import CountryField

from eucrim.core.models.mixins import HideShowInMenusField

# from .feeds import EventsFeed
from .utils import export_event


[docs] class EventIndexPage(HideShowInMenusField, RoutablePageMixin, Page): parent_page_types = ["core.HomePage"] subpage_types = ["event.EventPage"] max_count = 1 show_in_menus = True today = timezone.now().date() def _all_events(self): return EventPage.objects.live() @property def upcoming_events(self): return ( self._all_events() .filter(event_date_from__gte=self.today) .order_by("event_date_from") ) @property def past_events(self): return ( self._all_events() .filter(event_date_from__lt=self.today) .order_by("-event_date_from") )
[docs] @route(r"^$") def upcoming_events_route(self, request, *args, **kwargs): """ View function for the upcoming events page """ context = super().get_context(request) context["event_scope"] = "upcoming" return render(request, self.template, context, *args, **kwargs)
[docs] @route(r"^past/$") def past_events_route(self, request, *args, **kwargs): """ View function for the past events page """ context = super().get_context(request) context["event_scope"] = "past" return render(request, self.template, context, *args, **kwargs)
class Meta: verbose_name = "Events index page" verbose_name_plural = "Events index page"
[docs] class EventPage(HideShowInMenusField, Page): GERMAN = "GE" ENGLISH = "EN" FRENCH = "FR" LANGUAGE_CHOICES = ( (GERMAN, "German"), (ENGLISH, "English"), (FRENCH, "French"), ) parent_page_types = ["event.EventIndexPage"] subpage_types = [] show_in_menus = False language = models.CharField( max_length=2, choices=LANGUAGE_CHOICES, default=ENGLISH, ) subtitle = models.CharField( max_length=255, blank=True, help_text="Thats it, a subtitle" ) body = RichTextField( features=[ "h2", "h3", "h4", "h5", "h6", "bold", "italic", "ol", "ul", "link", "document-link", "image", ], verbose_name="Event body text", ) excerpt = models.TextField( verbose_name="Excerpt/Abstract", blank=True, max_length=500, help_text="Entry excerpt to be displayed on entries list. If this " "field is not filled, a truncate version of body text will " "be used. Please stick to no more than five lines.", ) event_organizer_name = models.CharField( max_length=255, blank=True, help_text="Whoever organizes this event: an institution, an " "individual etc.", ) event_organizer_www = models.URLField(blank=True) event_external_url = models.URLField(blank=True) event_external_url_linktext = models.CharField( max_length=100, blank=True, help_text="Human readable URL, in case the actual URL is very long.", ) event_venue = models.CharField( max_length=150, blank=True, help_text="Venue, just name the location (e.g. institution, hotel, " "conference center)", ) event_venue_city = models.CharField( max_length=100, blank=True, help_text='Only name the city, e.g. "Mumbai"', ) event_venue_country = CountryField(blank=True) event_contact_name = models.CharField(max_length=150, blank=True) event_contact_mail = models.EmailField(blank=True) event_contact_www = models.URLField(blank=True) event_date_from = models.DateField(verbose_name="Start date", null=True, blank=True) event_date_to = models.DateField( verbose_name="End date", null=True, blank=True, help_text="Not required if event is on a single day", ) event_time_from = models.TimeField(verbose_name="Start time", null=True, blank=True) event_time_to = models.TimeField(verbose_name="End time", null=True, blank=True) canceled = models.BooleanField( default=False, help_text='Set to true to display the label "Canceled" for this event.', ) is_online_event = models.BooleanField( default=False, help_text="Set to true if this event takes place only online. Be sure to set an external event url below.", ) @property def event_index_page(self): # Find closest ancestor which is an event index return self.get_ancestors().type(EventIndexPage).first() @property def get_event_contact_html(self): event_contact_html = "" if self.event_contact_name: event_contact_html = self.event_contact_name if self.event_contact_mail: event_contact_html = format_html( '<a href="mailto:{mail}"><i class="fas fa-envelope"></i> {mail}</a>', mail=self.event_contact_mail, ) if self.event_contact_mail and self.event_contact_name: event_contact_html = format_html( '<a href="mailto:{}"><i class="fas fa-envelope"></i> {}</a>', self.event_contact_mail, self.event_contact_name, ) if self.event_contact_www and self.event_contact_name: event_contact_html = format_html( '<a href="{}">{}</a>', self.event_contact_www, self.event_contact_name, ) if ( self.event_contact_www and self.event_contact_name and self.event_contact_mail ): event_contact_html = format_html( '<a href="{www}">{name}</a> (<a href="mailto:{mail}"><i class="fas fa-envelope"></i></a>)', www=self.event_contact_www, name=self.event_contact_name, mail=self.event_contact_mail, ) return event_contact_html @property def get_event_venue_city_country(self): return "{city}{delimiter}{country}".format( country=self.event_venue_country.name, city=self.event_venue_city, delimiter=", " if self.event_venue_city and self.event_venue_country else "", ) @property def get_event_location_googlemaps_url(self): google_maps_search_url = "{venue}{delimiter}{city}{delimiter}{country}".format( venue=self.event_venue, city=self.event_venue_city, country=self.event_venue_country.name, delimiter=", " if self.event_venue_city and self.event_venue_country else "", ) return " https://www.google.com/maps/search/?api=1&query={}".format( urllib.parse.quote_plus(google_maps_search_url) ) @property def has_been_modified(self): if self.last_published_at and self.first_published_at: if (self.last_published_at - self.first_published_at) > timedelta( minutes=60 ): return True @property def feed_description(self): return "{date_from} - {date_to}, Location: {location}. {description}".format( date_from=self.event_date_from, date_to=self.event_date_to, location=self.event_location, description=self.body, ) @property def get_obj_og_description(self): return self.excerpt
[docs] def serve(self, request): if "format" in request.GET: if request.GET["format"] == "ical": # Export to ical format response = HttpResponse( export_event(self, "ical"), content_type="text/calendar", ) response["Content-Disposition"] = ( "attachment; filename=" + self.slug + ".ics" ) return response else: # Unrecognised format error message = ( "Could not export event\n\nUnrecognised format: " + request.GET["format"] ) return HttpResponse(message, content_type="text/plain") else: # Display event page as usual return super().serve(request)
# get_absolute_url, needed by syndication framework, see feeds.py
[docs] def get_absolute_url(self): return self.full_url
# customizing wagtail editing interface content_panels = Page.content_panels + [ FieldPanel("subtitle"), FieldPanel("body", classname="full"), FieldPanel("excerpt"), MultiFieldPanel( [ FieldRowPanel( [ FieldPanel("event_date_from"), FieldPanel("event_date_to"), ] ), FieldRowPanel( [ FieldPanel("event_time_from"), FieldPanel("event_time_to"), ] ), FieldPanel("is_online_event"), FieldPanel("event_venue"), FieldRowPanel( [ FieldPanel("event_venue_city"), FieldPanel("event_venue_country"), ] ), FieldPanel("canceled"), ], heading="Date & location", classname="collapsible", ), MultiFieldPanel( [ FieldRowPanel( [ FieldPanel("event_external_url"), FieldPanel("event_external_url_linktext"), ] ), FieldRowPanel( [ FieldPanel("event_organizer_name"), FieldPanel("event_organizer_www"), ] ), FieldPanel("event_contact_name"), FieldRowPanel( [ FieldPanel("event_contact_mail"), FieldPanel("event_contact_www"), ] ), ], heading="Misc", classname="collapsible", ), ] # Auto-slug generation does not work if slug field was moved to # settings panel. # settings_panels = Page.settings_panels + [ # MultiFieldPanel([ # FieldPanel('slug'), # FieldPanel('search_description'), # ], heading='Page fields'), # ] search_fields = Page.search_fields + [ index.AutocompleteField("subtitle"), index.AutocompleteField("body"), index.AutocompleteField("excerpt"), index.AutocompleteField("search_description"), ] edit_handler = TabbedInterface( [ ObjectList(content_panels, heading="Content"), ObjectList(HideShowInMenusField.promote_panels, heading="Promote"), # ObjectList(settings_panels, heading='Settings', classname="settings"), ] ) class Meta: verbose_name = "Event" verbose_name_plural = "Events" ordering = ["-last_published_at"]