Skip to content
Snippets Groups Projects
models.py 2.68 KiB
import arrow
import requests
from django.contrib.postgres.fields import JSONField
from django.core.serializers.json import DjangoJSONEncoder
from django.db import models

from .parser import process_ical


def _convert_arrow_to_datetime(event):
    event["begin"] = event["begin"].datetime
    event["end"] = event["end"].datetime
    return event


class EventsJSONField(JSONField):
    """
    JSONField for lists of events which converts `begin` and `end` to datetime
    on load from DB.
    """

    def from_db_value(self, value, expression, connection):
        if value:
            for event in value:
                event["begin"] = arrow.get(event["begin"]).datetime
                event["end"] = arrow.get(event["end"]).datetime
        return value


class Calendar(models.Model):
    ACTUAL_NUM = 6

    url = models.URLField()
    source = models.TextField(null=True)
    last_update = models.DateTimeField(null=True)
    past_events = EventsJSONField(encoder=DjangoJSONEncoder, null=True)
    future_events = EventsJSONField(encoder=DjangoJSONEncoder, null=True)

    def update_source(self):
        source = requests.get(self.url).text
        if self.source != source:
            self.source = source
            past, future = process_ical(source)
            self.past_events = list(map(_convert_arrow_to_datetime, past))
            self.future_events = list(map(_convert_arrow_to_datetime, future))
        self.last_update = arrow.utcnow().datetime
        self.save()

    def actual_events(self):
        events = self.future_events[-self.ACTUAL_NUM :]
        num = len(events)
        if num < 6:
            events += self.past_events[: self.ACTUAL_NUM - num]
        return events


class CalendarMixin(models.Model):
    """
    Mixin to be used in other models, like site settings, which adds relation
    to Calendar.
    """

    calendar_url = models.URLField(
        "URL kalendáře ve formátu iCal", blank=True, null=True
    )
    calendar = models.ForeignKey(Calendar, null=True, on_delete=models.PROTECT)

    class Meta:
        abstract = True

    def save(self, *args, **kwargs):
        # create or update related Calendar
        if self.calendar_url:
            if self.calendar:
                if self.calendar.url != self.calendar_url:
                    self.calendar.url = self.calendar_url
                    self.calendar.save()
            else:
                self.calendar = Calendar.objects.create(url=self.calendar_url)
            self.calendar.update_source()

        # delete related Calendar when URL is cleared
        if not self.calendar_url and self.calendar:
            self.calendar.delete()
            self.calendar = None

        super().save(*args, **kwargs)