420 lines
12 KiB
Python
420 lines
12 KiB
Python
import logging
|
|
from datetime import timedelta
|
|
|
|
from django.conf import settings
|
|
from django.contrib.auth.models import User
|
|
from django.core.validators import ValidationError
|
|
from django.db import models
|
|
from django.urls import reverse
|
|
from django.utils import timezone
|
|
from django.utils.text import slugify
|
|
from django.utils.translation import gettext_lazy as _
|
|
from taggit.managers import TaggableManager
|
|
|
|
from core.models import CustomFlatPage
|
|
from documents.api import (
|
|
ep_create_new_pad,
|
|
ep_get_html,
|
|
ep_get_url,
|
|
ep_pad_exists,
|
|
ep_set_html,
|
|
)
|
|
|
|
from .managers import (
|
|
AllEventManager,
|
|
ArticleManager,
|
|
EventManager,
|
|
FetMeetingManager,
|
|
NewsManager,
|
|
PostManager,
|
|
)
|
|
|
|
logger = logging.getLogger(__name__)
|
|
request_logger = logging.getLogger("django.request")
|
|
|
|
|
|
def create_pad_for_post(slug, item="agenda"):
|
|
"""
|
|
Create a Etherpad pad.
|
|
|
|
Return a Etherpad key.
|
|
"""
|
|
logger.info(f"Pad-Type: {item}")
|
|
|
|
pad_id = slug + "-" + item
|
|
if ep_create_new_pad(pad_id):
|
|
# Set template into the newly created pad if it exists.
|
|
if page := CustomFlatPage.objects.filter(title__iexact=item).first():
|
|
ep_set_html(pad_id, page.content)
|
|
logger.info(f"Template is set. Template: {page.title}")
|
|
|
|
return pad_id
|
|
|
|
return "#"
|
|
|
|
|
|
class Category(models.Model):
|
|
# Titel des Posts
|
|
title = models.CharField(max_length=200)
|
|
|
|
subtitle = models.CharField(max_length=500, null=True, blank=True)
|
|
# Slug = Text Basierter url bestandteil zb Fetsitzung 22.1.2020 --> fetsitzung_22_1_2020 für Url
|
|
slug = models.SlugField(unique=True, null=True, blank=True)
|
|
# Ein Haupt Bild für den Post
|
|
image = models.ImageField(null=True, blank=True)
|
|
|
|
tags = TaggableManager(blank=True)
|
|
|
|
class Meta:
|
|
verbose_name = "Category"
|
|
verbose_name_plural = "Categories"
|
|
|
|
|
|
class Post(models.Model):
|
|
# legacy id is for the posts from the old website
|
|
legacy_id = models.IntegerField(null=True, blank=True)
|
|
|
|
title = models.CharField(verbose_name="Titel", max_length=200)
|
|
subtitle = models.CharField(max_length=500, null=True, blank=True)
|
|
tags = TaggableManager(blank=True)
|
|
|
|
# Slug = Text Basierter url bestandteil zb Fetsitzung 22.1.2020 --> fetsitzung_22_1_2020 für Url
|
|
slug = models.SlugField(unique=True, blank=True)
|
|
|
|
body = models.TextField(null=True, blank=True)
|
|
image = models.ImageField(null=True, blank=True)
|
|
|
|
author = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)
|
|
public_date = models.DateField(
|
|
verbose_name="Veröffentlichung", null=True, blank=True, default=timezone.now
|
|
)
|
|
|
|
__choices = [("N", _("News")), ("E", _("Event")), ("F", _("FetMeeting"))]
|
|
post_type = models.CharField(max_length=1, choices=__choices, editable=True)
|
|
|
|
class Status(models.TextChoices):
|
|
DRAFT = "10", _("DRAFT")
|
|
ONLY_INTERN = "15", _("ONLY_INTERN")
|
|
PUBLIC = "20", _("PUBLIC")
|
|
|
|
status = models.CharField(
|
|
verbose_name="Status",
|
|
max_length=2,
|
|
choices=Status.choices,
|
|
default=Status.DRAFT,
|
|
)
|
|
|
|
# post is pinned at main page
|
|
is_pinned = models.BooleanField(verbose_name="ANGEHEFTET", default=False)
|
|
|
|
# addional infos for events
|
|
event_start = models.DateTimeField(verbose_name="Event Start", null=True, blank=True)
|
|
event_end = models.DateTimeField(verbose_name="Event Ende", null=True, blank=True)
|
|
event_place = models.CharField(max_length=200, null=True, blank=True)
|
|
|
|
# protocol for fet meeting
|
|
has_protocol = models.BooleanField(default=False)
|
|
has_agenda = models.BooleanField(default=False)
|
|
protocol_key = models.CharField(max_length=200, null=True, blank=True)
|
|
agenda_key = models.CharField(max_length=200, null=True, blank=True)
|
|
|
|
# TimeStamps
|
|
date_modified = models.DateTimeField(auto_now=True)
|
|
date_created = models.DateTimeField(auto_now_add=True)
|
|
|
|
# useless (-_-)
|
|
legacy_rubrik_id = models.IntegerField(null=True, blank=True)
|
|
imported_from = models.CharField(max_length=200, null=True, blank=True)
|
|
|
|
# Managers
|
|
objects = PostManager()
|
|
articles = ArticleManager()
|
|
|
|
def __str__(self):
|
|
return "Post (%s, %s): %s" % (
|
|
self.slug,
|
|
self.public_date.strftime("%d.%m.%Y"),
|
|
self.title,
|
|
)
|
|
|
|
def get_absolute_url(self):
|
|
return reverse("posts:post", kwargs={"slug": self.slug})
|
|
|
|
def save(self, *args, **kwargs):
|
|
# save the post with some defaults
|
|
if not self.public_date:
|
|
self.public_date = timezone.now().date()
|
|
|
|
if not self.slug:
|
|
self.slug = slugify(self.public_date) + "-" + slugify(self.title)
|
|
|
|
super().save(*args, **kwargs)
|
|
|
|
@property
|
|
def agenda_html(self) -> (str | None):
|
|
"Agenda HTML from Etherpad Pad"
|
|
if self.agenda_key in [None, "#"]:
|
|
return None
|
|
|
|
return ep_get_html(self.agenda_key)
|
|
|
|
@property
|
|
def protocol_html(self) -> (str | None):
|
|
"Protocol HTML from Etherpad Pad"
|
|
if self.protocol_key in [None, "#"]:
|
|
return None
|
|
|
|
return ep_get_html(self.protocol_key)
|
|
|
|
@agenda_html.setter
|
|
def agenda_html(self, value: str) -> (str | None):
|
|
if self.agenda_key is None:
|
|
self.create_agenda_key()
|
|
|
|
if value is None or self.agenda_key in [None, "#"]:
|
|
return None
|
|
|
|
ep_set_html(self.agenda_key, value)
|
|
request_logger.info(f"Set agenda etherpad. Post: {self.slug}. Key: {self.agenda_key}")
|
|
return value
|
|
|
|
@protocol_html.setter
|
|
def protocol_html(self, value: str) -> (str | None):
|
|
if self.protocol_key is None:
|
|
self.create_protocol_key()
|
|
|
|
if value is None or self.protocol_key in [None, "#"]:
|
|
return None
|
|
|
|
ep_set_html(self.protocol_key, value)
|
|
request_logger.info(f"Set protocol etherpad. Post: {self.slug}. Key: {self.protocol_key}")
|
|
return value
|
|
|
|
_agenda_filename = None
|
|
_agenda_url = None
|
|
|
|
@property
|
|
def agenda_url(self) -> (str | None):
|
|
if self.has_agenda is not True:
|
|
self._agenda_url = None
|
|
self._agenda_filename = None
|
|
return self._agenda_url
|
|
|
|
if self._agenda_url is not None:
|
|
return self._agenda_url
|
|
|
|
if (url := ep_get_url(self.agenda_key)) not in [None, "#"]:
|
|
self._agenda_url = url
|
|
self._agenda_filename = self.slug + "-agenda.pdf"
|
|
else:
|
|
self._agenda_url = None
|
|
self._agenda_filename = None
|
|
|
|
return self._agenda_url
|
|
|
|
@property
|
|
def agenda_filename(self) -> (str | None):
|
|
if self._agenda_filename is not None:
|
|
return self._agenda_filename
|
|
|
|
if self.has_agenda and self.agenda_url:
|
|
return self.slug + "-agenda.pdf"
|
|
|
|
return None
|
|
|
|
_protocol_filename = None
|
|
_protocol_url = None
|
|
|
|
@property
|
|
def protocol_url(self) -> (str | None):
|
|
if self.has_protocol is not True:
|
|
self._protocol_url = None
|
|
self._protocol_filename = None
|
|
return self._protocol_url
|
|
|
|
if self._protocol_url is not None:
|
|
return self._protocol_url
|
|
|
|
if (url := ep_get_url(self.protocol_key)) not in [None, "#"]:
|
|
self._protocol_url = url
|
|
self._protocol_filename = self.slug + "-protokoll.pdf"
|
|
else:
|
|
self._protocol_url = None
|
|
self._protocol_filename = None
|
|
|
|
return self._protocol_url
|
|
|
|
@property
|
|
def protocol_filename(self) -> (str | None):
|
|
if self._protocol_filename is not None:
|
|
return self._protocol_filename
|
|
|
|
if self.has_protocol and self.protocol_url:
|
|
return self.slug + "-protokoll.pdf"
|
|
|
|
return None
|
|
|
|
def create_agenda_key(self) -> None:
|
|
"""
|
|
Create a Etherpad Id for the Pad associated to this post.
|
|
Create the pad if it doesn't exist.
|
|
"""
|
|
if self.slug:
|
|
self.agenda_key = create_pad_for_post(self.slug, "agenda")
|
|
|
|
def create_protocol_key(self) -> None:
|
|
"""
|
|
Create a Etherpad Id for the Pad associated to this post.
|
|
Create the pad if it doesn't exist.
|
|
"""
|
|
if self.slug:
|
|
self.protocol_key = create_pad_for_post(self.slug, "protocol")
|
|
|
|
def get_model_name(self):
|
|
return self._meta.model_name
|
|
|
|
@property
|
|
def three_tag_names(self):
|
|
return self.tags.names()[:3]
|
|
|
|
@property
|
|
def tag_names(self):
|
|
return [t for t in self.tags.names()]
|
|
|
|
@property
|
|
def imageurl(self) -> str:
|
|
"""
|
|
returns the url to the image
|
|
"""
|
|
if self.image:
|
|
return self.image.url
|
|
|
|
# return default image
|
|
return settings.STATIC_URL + "img/FET-Logo-2014-quadrat.png"
|
|
|
|
def clean(self):
|
|
if self.event_end and self.event_end < self.event_start:
|
|
raise ValidationError("Das Ende des Events liegt vor dem Beginn.")
|
|
if self.event_start and self.post_type not in ["E", "F"]:
|
|
raise ValidationError("Für diesen Post Typ ist kein Event Start zulässig.")
|
|
|
|
@property
|
|
def published(self):
|
|
if self.status == self.Status.PUBLIC:
|
|
return True
|
|
|
|
return False
|
|
|
|
|
|
class News(Post):
|
|
objects = NewsManager()
|
|
|
|
class Meta:
|
|
proxy = True
|
|
|
|
verbose_name = "News"
|
|
verbose_name_plural = "News"
|
|
|
|
def save(self, *args, **kwargs):
|
|
if not self.post_type:
|
|
self.post_type = "N"
|
|
|
|
super().save(*args, **kwargs)
|
|
|
|
|
|
class Event(Post):
|
|
only_events = EventManager()
|
|
all_events = AllEventManager()
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
self.post_type = "E"
|
|
|
|
class Meta:
|
|
proxy = True
|
|
|
|
verbose_name = "Event"
|
|
verbose_name_plural = "Events"
|
|
|
|
def save(self, *args, **kwargs):
|
|
if not self.post_type:
|
|
self.post_type = "E"
|
|
if not self.event_end:
|
|
self.event_end = self.event_start + timedelta(hours=2)
|
|
|
|
super().save(*args, **kwargs)
|
|
|
|
def clean(self):
|
|
super().clean()
|
|
if not self.event_start:
|
|
raise ValidationError("Das Datum des Events fehlt.")
|
|
|
|
|
|
class FetMeeting(Event):
|
|
objects = FetMeetingManager()
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
self.post_type = "F"
|
|
|
|
class Meta:
|
|
proxy = True
|
|
|
|
verbose_name = "Fet Sitzung"
|
|
verbose_name_plural = "Fet Sitzungen"
|
|
|
|
def save(self, *args, **kwargs):
|
|
self.title = "Fachschaftssitzung"
|
|
if not self.slug:
|
|
self.slug = self.__get_slug()
|
|
|
|
if ep_pad_exists(self.agenda_key) is not True or self.slug not in self.agenda_key:
|
|
self.create_agenda_key()
|
|
if self.agenda_key not in [None, "#"]:
|
|
self.has_agenda = True
|
|
|
|
if ep_pad_exists(self.protocol_key) is not True or self.slug not in self.protocol_key:
|
|
self.create_protocol_key()
|
|
if self.protocol_key not in [None, "#"]:
|
|
self.has_protocol = True
|
|
|
|
if not self.post_type:
|
|
self.post_type = "F"
|
|
|
|
if not self.event_place:
|
|
self.event_place = "FET"
|
|
|
|
# make duration 2 hours if not specified otherwise
|
|
if not self.event_end:
|
|
self.event_end = self.event_start + timedelta(hours=2)
|
|
|
|
# set FET Meeting always public
|
|
self.status = self.Status.PUBLIC
|
|
|
|
super().save(*args, **kwargs)
|
|
|
|
def __get_slug(self) -> str:
|
|
slug = slugify(self.event_start.date()) + "-" + slugify("Fachschaftssitzung")
|
|
|
|
if Post.objects.filter(slug=slug).exists():
|
|
if Post.objects.get(slug=slug).id != self.id:
|
|
raise ValidationError("Es existiert bereits eine Sitzung mit demselben Datum.")
|
|
|
|
return slug
|
|
|
|
def clean(self):
|
|
super().clean()
|
|
if not self.slug:
|
|
self.slug = self.__get_slug()
|
|
|
|
|
|
class FileUpload(models.Model):
|
|
title = models.CharField(verbose_name="Titel", max_length=200)
|
|
file_field = models.FileField(verbose_name="Dokument", upload_to="uploads/posts/files/")
|
|
post = models.ForeignKey(Post, on_delete=models.CASCADE)
|
|
|
|
objects = models.Manager()
|
|
|
|
def __str__(self):
|
|
return self.title
|