# 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"]