Files
fet2020/fet2020/posts/models.py

394 lines
11 KiB
Python

import logging
import re
from datetime import timedelta
from taggit.managers import TaggableManager
from django.contrib.auth.models import User
from django.core.validators import ValidationError
from django.db import models
from django.template.loader import render_to_string
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 core.models import CustomFlatPage
from documents import create_pad, get_pad_html, set_pad_html
from .managers import (
PostManager,
ArticleManager,
NewsManager,
AllEventManager,
EventManager,
FetMeetingManager,
)
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}")
padID = create_pad(slug + "-" + item)
if padID:
# set template into the newly created pad if it exists
page = CustomFlatPage.objects.filter(title__iexact=item).first()
if page:
set_pad_html(padID, page.content)
logger.info(f"Template gesetzt von: {page.title}")
else:
padID = f"{slug}-{item}"
logger.info(f"PadID: {padID}")
return padID
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,
)
@property
def url(self):
# TODO: replace with get_absolute_url
return reverse("posts:posts.show", kwargs={"id": self.slug})
def get_absolute_url(self):
return self.url
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)
self.tags.set(
[
*re.findall(r"\#([\d\w-]+)", str(self.subtitle)),
*re.findall(r"\#([\d\w-]+)", str(self.title)),
]
)
@property
def agenda_html(self):
"Agenda HTML from Etherpad Pad"
if not self.agenda_key:
return None
return get_pad_html(self.agenda_key)
@property
def protocol_html(self):
"Protocol HTML from Etherpad Pad"
if not self.protocol_key:
return None
return get_pad_html(self.protocol_key)
@agenda_html.setter
def agenda_html(self, value):
if not self.agenda_key:
self.agenda_key = self.get_agenda_key()
if not value or not self.agenda_key:
return None
set_pad_html(self.agenda_key, value)
request_logger.info(f"set etherpad for post {self.slug} id: {self.agenda_key}")
return value
@protocol_html.setter
def protocol_html(self, value):
if not self.protocol_key:
self.protocol_key = self.get_protocol_key()
if not value or not self.protocol_key:
return None
set_pad_html(self.protocol_key, value)
request_logger.info(
f"set etherpad for post {self.slug} id: {self.protocol_key}"
)
return value
def get_agenda_key(self):
"""
Create a Etherpad Id for the Pad associated to this post.
Create the pad if it doesn't exist.
"""
if not self.slug:
return None
return create_pad_for_post(self.slug, "agenda")
def get_protocol_key(self):
"""
Create a Etherpad Id for the Pad associated to this post.
Create the pad if it doesn't exist.
"""
if not self.slug:
return None
return create_pad_for_post(self.slug, "protocol")
def get_tags(self):
"""
Returns assigned tags as a comma seperated list.
"""
return ",".join(self.tags.names())
@property
def get_tagnames(self):
return ["#%s" % t for t in self.tags.names()]
@property
def tag_string(self):
return self.get_tags()
@property
def imageurl(self):
"""
returns the url to the image
"""
if self.image:
return self.image.url
image = self.find_an_image()
if image:
return image.url
return ""
def find_an_image(self):
"""
find an image via another post
"""
# TODO: Explain why this image is selected on save of the image
# Query all posts that have a slug that equals one of the tags
posts1 = (
Post.objects.filter(slug__in=self.tags.names())
.filter(image__isnull=False)[0:1]
.all()
)
if len(posts1) > 0:
return posts1.get().image
return None
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")
)
super().clean()
@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):
if not self.event_start:
raise ValidationError(_("Das Datum des Events fehlt."))
super().clean()
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()
self.has_agenda = True
self.has_protocol = True
self.agenda_key = self.get_agenda_key()
self.protocol_key = self.get_protocol_key()
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):
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