ruff check; update ValidationError params
This commit is contained in:
@@ -1,39 +1,39 @@
|
|||||||
# util functions for all apps
|
# util functions for all apps
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from django.contrib.admin.utils import construct_change_message
|
from django.contrib.admin.utils import construct_change_message
|
||||||
|
|
||||||
|
|
||||||
def add_log_action(request, form, app_label, model, add=True):
|
def add_log_action(request, form, app_label, model, add=True):
|
||||||
from django.contrib.admin.models import ADDITION, CHANGE, LogEntry
|
from django.contrib.admin.models import ADDITION, CHANGE, LogEntry
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
|
||||||
obj = form.save()
|
obj = form.save()
|
||||||
content_type = ContentType.objects.get(app_label=app_label, model=model)
|
content_type = ContentType.objects.get(app_label=app_label, model=model)
|
||||||
change_message = construct_change_message(form, None, add)
|
change_message = construct_change_message(form, None, add)
|
||||||
action_flag = ADDITION if add else CHANGE
|
action_flag = ADDITION if add else CHANGE
|
||||||
|
|
||||||
LogEntry.objects.log_action(
|
LogEntry.objects.log_action(
|
||||||
user_id=request.user.pk,
|
user_id=request.user.pk,
|
||||||
content_type_id=content_type.pk,
|
content_type_id=content_type.pk,
|
||||||
object_id=obj.pk,
|
object_id=obj.pk,
|
||||||
object_repr=str(obj),
|
object_repr=str(obj),
|
||||||
action_flag=action_flag,
|
action_flag=action_flag,
|
||||||
change_message=change_message,
|
change_message=change_message,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def create_perms(sender, **kwargs):
|
def create_perms(sender, **kwargs):
|
||||||
from django.contrib.auth.models import Group, Permission
|
from django.contrib.auth.models import Group, Permission
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
|
||||||
group, created = Group.objects.get_or_create(name=sender.label)
|
group, _ = Group.objects.get_or_create(name=sender.label)
|
||||||
|
|
||||||
content_types = ContentType.objects.filter(app_label=sender.label)
|
content_types = ContentType.objects.filter(app_label=sender.label)
|
||||||
for content_type in content_types:
|
for content_type in content_types:
|
||||||
permissions = Permission.objects.filter(content_type=content_type)
|
permissions = Permission.objects.filter(content_type=content_type)
|
||||||
[group.permissions.add(permission) for permission in permissions]
|
[group.permissions.add(permission) for permission in permissions]
|
||||||
|
|
||||||
|
|
||||||
def create_random_id():
|
def create_random_id():
|
||||||
return str(uuid.uuid4())[:8]
|
return str(uuid.uuid4())[:8]
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
import datetime
|
|
||||||
import decimal
|
import decimal
|
||||||
|
|
||||||
from dateutil.relativedelta import relativedelta
|
from dateutil.relativedelta import relativedelta
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.core.validators import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.db.models import Count, Q
|
from django.db.models import Count, Q
|
||||||
from django.forms import DateInput
|
from django.forms import DateInput
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
@@ -451,8 +450,8 @@ class ResolutionAdminForm(forms.ModelForm):
|
|||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs) # to get the self.fields set
|
super().__init__(*args, **kwargs) # to get the self.fields set
|
||||||
|
|
||||||
budget = decimal.Decimal(0.0)
|
budget = decimal.Decimal("0.0")
|
||||||
total = decimal.Decimal(0.0)
|
total = decimal.Decimal("0.0")
|
||||||
if resolution := kwargs.get("instance"):
|
if resolution := kwargs.get("instance"):
|
||||||
for elem in Bill.objects.filter(resolution=resolution):
|
for elem in Bill.objects.filter(resolution=resolution):
|
||||||
total += elem.amount
|
total += elem.amount
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
|
import logging
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from django.core.validators import FileExtensionValidator, ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
|
from django.core.validators import FileExtensionValidator
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
|
||||||
@@ -8,6 +10,8 @@ from members.models import Member
|
|||||||
|
|
||||||
from .validators import validate_bill_file_extension
|
from .validators import validate_bill_file_extension
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class BankData(models.Model):
|
class BankData(models.Model):
|
||||||
# members can be deleted but never their bank datas
|
# members can be deleted but never their bank datas
|
||||||
@@ -86,8 +90,18 @@ class Resolution(models.Model):
|
|||||||
if not Resolution.objects.filter(id=_id).exists():
|
if not Resolution.objects.filter(id=_id).exists():
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
|
msg = (
|
||||||
|
"Es wurden zu viele Beschlüsse in dieser Woche %(week)s vom Jahr %(year)s "
|
||||||
|
"erstellt."
|
||||||
|
)
|
||||||
|
logger.error(
|
||||||
|
"Too many resolutions created in week %(week)s of year %(year)s.",
|
||||||
|
extra={"week": week, "year": year},
|
||||||
|
)
|
||||||
raise ValidationError(
|
raise ValidationError(
|
||||||
f"Es wurden zu viele Beschlüsse in dieser Woche angelegt. (ID: {_id})"
|
msg,
|
||||||
|
code="too_many_resolutions",
|
||||||
|
params={"week": week, "year": year},
|
||||||
)
|
)
|
||||||
|
|
||||||
self.id = _id
|
self.id = _id
|
||||||
|
|||||||
@@ -27,11 +27,11 @@ def get_image_list(folder_name: str) -> list:
|
|||||||
|
|
||||||
Path(thumb_path).mkdir(exist_ok=True)
|
Path(thumb_path).mkdir(exist_ok=True)
|
||||||
|
|
||||||
for _file in os.listdir(image_path):
|
for _file in image_path.iterdir():
|
||||||
if Path(_file).suffix.lower()[1:] not in get_available_image_extensions():
|
if _file.suffix.lower()[1:] not in get_available_image_extensions():
|
||||||
continue
|
continue
|
||||||
|
|
||||||
thumb_file_path = Path(thumb_path) / f"thumb_{_file}"
|
thumb_file_path = Path(thumb_path) / f"thumb_{_file.name}"
|
||||||
if not Path(thumb_file_path).exists():
|
if not Path(thumb_file_path).exists():
|
||||||
with Image.open(Path(image_path) / _file, "r") as im:
|
with Image.open(Path(image_path) / _file, "r") as im:
|
||||||
if im._getexif() is not None:
|
if im._getexif() is not None:
|
||||||
|
|||||||
@@ -1,65 +1,67 @@
|
|||||||
import logging
|
import logging
|
||||||
from collections import deque
|
from collections import deque
|
||||||
|
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
from django.http import Http404
|
from django.http import Http404
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
from django.utils.text import slugify
|
from django.utils.text import slugify
|
||||||
from django.views.generic.detail import DetailView
|
from django.views.generic.detail import DetailView
|
||||||
|
|
||||||
from .models import Album
|
from .models import Album
|
||||||
from .utils import get_folder_list
|
from .utils import get_folder_list
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def index(request):
|
def index(request):
|
||||||
if request.user.is_authenticated:
|
if request.user.is_authenticated:
|
||||||
albums = deque(Album.objects.all())
|
albums = deque(Album.objects.all())
|
||||||
|
|
||||||
# Get albums that are in the server but not in the db.
|
# Get albums that are in the server but not in the db.
|
||||||
for folder in get_folder_list():
|
for folder in get_folder_list():
|
||||||
if not Album.objects.filter(folder_name=folder):
|
if not Album.objects.filter(folder_name=folder):
|
||||||
albums.append(
|
albums.append(
|
||||||
Album(title=folder, slug=slugify(folder), folder_name=folder, event_date=None)
|
Album(title=folder, slug=slugify(folder), folder_name=folder, event_date=None)
|
||||||
)
|
)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# Show only PUBLIC albums.
|
# Show only PUBLIC albums.
|
||||||
albums = Album.objects.public()
|
albums = Album.objects.public()
|
||||||
|
|
||||||
context = {"albums": albums}
|
context = {"albums": albums}
|
||||||
|
|
||||||
return render(request, "gallery/index.html", context)
|
return render(request, "gallery/index.html", context)
|
||||||
|
|
||||||
|
|
||||||
class AlbumDetailView(DetailView):
|
class AlbumDetailView(DetailView):
|
||||||
model = Album
|
model = Album
|
||||||
template_name = "gallery/album.html"
|
template_name = "gallery/album.html"
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
return (
|
return (
|
||||||
Album.objects.public()
|
Album.objects.public()
|
||||||
if not self.request.user.is_authenticated
|
if not self.request.user.is_authenticated
|
||||||
else Album.objects.all()
|
else Album.objects.all()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class DraftAlbumDetailView(LoginRequiredMixin, DetailView):
|
class DraftAlbumDetailView(LoginRequiredMixin, DetailView):
|
||||||
model = Album
|
model = Album
|
||||||
template_name = "gallery/album.html"
|
template_name = "gallery/album.html"
|
||||||
|
|
||||||
def get_object(self, queryset=None):
|
def get_object(self, queryset=None):
|
||||||
slug = self.kwargs.get(self.slug_url_kwarg)
|
slug = self.kwargs.get(self.slug_url_kwarg)
|
||||||
|
|
||||||
if not (album := Album.objects.filter(slug=slug).first()):
|
if not (album := Album.objects.filter(slug=slug).first()):
|
||||||
for folder in get_folder_list():
|
for folder in get_folder_list():
|
||||||
if slug == slugify(folder):
|
if slug == slugify(folder):
|
||||||
album = Album(
|
album = Album(
|
||||||
title=folder, slug=slugify(folder), folder_name=folder, event_date=None
|
title=folder, slug=slugify(folder), folder_name=folder, event_date=None
|
||||||
)
|
)
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
raise Http404("Album slug not found.")
|
msg = f"Album mit dem Slug '{slug}' nicht gefunden."
|
||||||
|
logger.error("Album with slug '%s' not found.", slug)
|
||||||
return album
|
raise Http404(msg)
|
||||||
|
|
||||||
|
return album
|
||||||
|
|||||||
@@ -1,205 +1,219 @@
|
|||||||
import logging
|
import logging
|
||||||
from datetime import date
|
from datetime import date
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from django.core.validators import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models.constraints import UniqueConstraint
|
from django.db.models.constraints import UniqueConstraint
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.text import slugify
|
from django.utils.text import slugify
|
||||||
|
|
||||||
from documents.api import ep_create_new_pad, ep_get_html, ep_get_url
|
from documents.api import ep_create_new_pad, ep_get_html, ep_get_url
|
||||||
from fet2020.utils import create_random_id
|
from fet2020.utils import create_random_id
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class TopicGroup(models.Model):
|
class TopicGroup(models.Model):
|
||||||
title = models.CharField(verbose_name="Titel", max_length=128)
|
title = models.CharField(verbose_name="Titel", max_length=128)
|
||||||
|
|
||||||
shortterm = models.CharField(max_length=128, unique=True, blank=True)
|
shortterm = models.CharField(max_length=128, unique=True, blank=True)
|
||||||
slug = models.SlugField(unique=True)
|
slug = models.SlugField(unique=True)
|
||||||
|
|
||||||
short_description = models.TextField(blank=True, default="")
|
short_description = models.TextField(blank=True, default="")
|
||||||
|
|
||||||
order = models.PositiveSmallIntegerField(
|
order = models.PositiveSmallIntegerField(
|
||||||
verbose_name="Reihenfolge",
|
verbose_name="Reihenfolge",
|
||||||
unique=True,
|
unique=True,
|
||||||
null=True,
|
null=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
objects = models.Manager()
|
objects = models.Manager()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = "Themenbereich"
|
verbose_name = "Themenbereich"
|
||||||
verbose_name_plural = "Themenbereiche"
|
verbose_name_plural = "Themenbereiche"
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.title
|
return self.title
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse("intern:index") + "#" + self.slug
|
return reverse("intern:index") + "#" + self.slug
|
||||||
|
|
||||||
def clean(self, *args, **kwargs):
|
def clean(self, *args, **kwargs):
|
||||||
if not self.shortterm:
|
if not self.shortterm:
|
||||||
self.shortterm = self.title
|
self.shortterm = self.title
|
||||||
self.slug = slugify(self.shortterm)
|
self.slug = slugify(self.shortterm)
|
||||||
|
|
||||||
|
|
||||||
class Topic(models.Model):
|
class Topic(models.Model):
|
||||||
title = models.CharField(max_length=128, verbose_name="Titel")
|
title = models.CharField(max_length=128, verbose_name="Titel")
|
||||||
slug = models.SlugField()
|
slug = models.SlugField()
|
||||||
|
|
||||||
archive = models.BooleanField(default=False, verbose_name="Archiv")
|
archive = models.BooleanField(default=False, verbose_name="Archiv")
|
||||||
|
|
||||||
description = models.TextField(blank=True, default="")
|
description = models.TextField(blank=True, default="")
|
||||||
|
|
||||||
topic_group = models.ForeignKey(
|
topic_group = models.ForeignKey(
|
||||||
TopicGroup,
|
TopicGroup,
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
verbose_name="Themenbereich",
|
verbose_name="Themenbereich",
|
||||||
)
|
)
|
||||||
|
|
||||||
objects = models.Manager()
|
objects = models.Manager()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = "Thema"
|
verbose_name = "Thema"
|
||||||
verbose_name_plural = "Themen"
|
verbose_name_plural = "Themen"
|
||||||
|
|
||||||
constraints = [
|
constraints = [
|
||||||
UniqueConstraint(fields=["slug", "topic_group"], name="unique_intern_slug_topic_group"),
|
UniqueConstraint(fields=["slug", "topic_group"], name="unique_intern_slug_topic_group"),
|
||||||
UniqueConstraint(
|
UniqueConstraint(
|
||||||
fields=["title", "topic_group"],
|
fields=["title", "topic_group"],
|
||||||
name="unique_intern_title_topic_group",
|
name="unique_intern_title_topic_group",
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.title
|
return self.title
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
kwargs = {
|
kwargs = {
|
||||||
"topic_group_slug": self.topic_group.slug,
|
"topic_group_slug": self.topic_group.slug,
|
||||||
"slug": self.slug,
|
"slug": self.slug,
|
||||||
}
|
}
|
||||||
return reverse("intern:topic", kwargs=kwargs)
|
return reverse("intern:topic", kwargs=kwargs)
|
||||||
|
|
||||||
def clean(self, *args, **kwargs):
|
def clean(self, *args, **kwargs):
|
||||||
self.slug = slugify(self.title)
|
self.slug = slugify(self.title)
|
||||||
|
|
||||||
|
|
||||||
class Attachment(models.Model):
|
class Attachment(models.Model):
|
||||||
title = models.CharField(max_length=128, verbose_name="Titel")
|
title = models.CharField(max_length=128, verbose_name="Titel")
|
||||||
|
|
||||||
slug = models.SlugField()
|
slug = models.SlugField()
|
||||||
|
|
||||||
description = models.TextField(blank=True, default="")
|
description = models.TextField(blank=True, default="")
|
||||||
|
|
||||||
topic = models.ForeignKey(Topic, on_delete=models.CASCADE, verbose_name="Thema")
|
topic = models.ForeignKey(Topic, on_delete=models.CASCADE, verbose_name="Thema")
|
||||||
|
|
||||||
objects = models.Manager()
|
objects = models.Manager()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = "Anhang Ordner"
|
verbose_name = "Anhang Ordner"
|
||||||
verbose_name_plural = "Anhang Ordner"
|
verbose_name_plural = "Anhang Ordner"
|
||||||
|
|
||||||
constraints = [
|
constraints = [
|
||||||
UniqueConstraint(fields=["slug", "topic"], name="unique_intern_slug_topic"),
|
UniqueConstraint(fields=["slug", "topic"], name="unique_intern_slug_topic"),
|
||||||
UniqueConstraint(fields=["title", "topic"], name="unique_intern_title_topic"),
|
UniqueConstraint(fields=["title", "topic"], name="unique_intern_title_topic"),
|
||||||
]
|
]
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.topic.title + " / " + self.title
|
return self.topic.title + " / " + self.title
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
kwargs = {
|
kwargs = {
|
||||||
"topic_group_slug": self.topic.topic_group.slug,
|
"topic_group_slug": self.topic.topic_group.slug,
|
||||||
"topic_slug": self.topic.slug,
|
"topic_slug": self.topic.slug,
|
||||||
"slug": self.slug,
|
"slug": self.slug,
|
||||||
}
|
}
|
||||||
return reverse("intern:attachment", kwargs=kwargs)
|
return reverse("intern:attachment", kwargs=kwargs)
|
||||||
|
|
||||||
def clean(self, *args, **kwargs):
|
def clean(self, *args, **kwargs):
|
||||||
self.slug = slugify(self.title)
|
self.slug = slugify(self.title)
|
||||||
|
|
||||||
|
|
||||||
class Etherpad(models.Model):
|
class Etherpad(models.Model):
|
||||||
title = models.CharField(max_length=128, verbose_name="Titel")
|
title = models.CharField(max_length=128, verbose_name="Titel")
|
||||||
|
|
||||||
slug_id = models.CharField(default=create_random_id, editable=False, max_length=8, unique=True)
|
slug_id = models.CharField(default=create_random_id, editable=False, max_length=8, unique=True)
|
||||||
etherpad_key = models.CharField(blank=True, max_length=50)
|
etherpad_key = models.CharField(blank=True, max_length=50)
|
||||||
date = models.DateField(default=date.today, verbose_name="Datum")
|
date = models.DateField(default=date.today, verbose_name="Datum")
|
||||||
|
|
||||||
attachment = models.ForeignKey(
|
attachment = models.ForeignKey(
|
||||||
Attachment,
|
Attachment,
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
verbose_name="Anhang Ordner",
|
verbose_name="Anhang Ordner",
|
||||||
)
|
)
|
||||||
|
|
||||||
objects = models.Manager()
|
objects = models.Manager()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = "Etherpad"
|
verbose_name = "Etherpad"
|
||||||
verbose_name_plural = "Etherpads"
|
verbose_name_plural = "Etherpads"
|
||||||
|
|
||||||
constraints = [
|
constraints = [
|
||||||
UniqueConstraint(fields=["title", "date", "attachment"], name="unique_intern_etherpad"),
|
UniqueConstraint(fields=["title", "date", "attachment"], name="unique_intern_etherpad"),
|
||||||
]
|
]
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.title
|
return self.title
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return ep_get_url(self.etherpad_key)
|
return ep_get_url(self.etherpad_key)
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
pad_name = slugify(str(self.slug_id) + "-" + self.title[:40])
|
pad_name = slugify(str(self.slug_id) + "-" + self.title[:40])
|
||||||
if len(pad_name) > 50:
|
if len(pad_name) > 50:
|
||||||
raise ValidationError(
|
msg = (
|
||||||
(
|
"Name ist zum Erstellen des Etherpads zu lange - max. 50 Zeichen. (Länge: "
|
||||||
"Name zum Erstellen des Etherpads ist zu lange - max. 50 Zeichen. ("
|
"%(length)s, Name: %(pad_name)s)"
|
||||||
"Länge: %(length)s, Name: %(pad_name)s)"
|
)
|
||||||
),
|
logger.error(
|
||||||
params={"length": len(pad_name), "pad_name": pad_name},
|
(
|
||||||
)
|
"Name '%(pad_name)s' is too long for creating a new etherpad - max. 50 "
|
||||||
|
"characters. (Length: %(length)s)",
|
||||||
if self.etherpad_key == "":
|
),
|
||||||
if ep_create_new_pad(pad_name):
|
extra={"length": len(pad_name), "pad_name": pad_name},
|
||||||
self.etherpad_key = pad_name
|
)
|
||||||
else:
|
raise ValidationError(
|
||||||
raise ValidationError(
|
msg,
|
||||||
f"Etherpad '{pad_name}' konnte nicht erstellt werden. This should never happen!"
|
code="pad_name_too_long",
|
||||||
)
|
params={"length": len(pad_name), "pad_name": pad_name},
|
||||||
|
)
|
||||||
@property
|
|
||||||
def etherpad_html(self):
|
if self.etherpad_key == "":
|
||||||
return ep_get_html(self.etherpad_key)
|
if ep_create_new_pad(pad_name):
|
||||||
|
self.etherpad_key = pad_name
|
||||||
def get_model_name(self):
|
else:
|
||||||
return self._meta.model_name
|
msg = "Etherpad '%(pad_name)s' konnte nicht erstellt werden."
|
||||||
|
logger.error(
|
||||||
|
"Etherpad '%(pad_name)s' could not be created. This should never happen!",
|
||||||
class FileUpload(models.Model):
|
extra={"pad_name": pad_name},
|
||||||
title = models.CharField(blank=True, max_length=128, verbose_name="Titel")
|
)
|
||||||
file_field = models.FileField(upload_to="uploads/intern/files/", verbose_name="Dokument")
|
raise ValidationError(
|
||||||
date = models.DateField(default=date.today, verbose_name="Datum")
|
msg, code="pad_creation_failed", params={"pad_name": pad_name}
|
||||||
|
)
|
||||||
attachment = models.ForeignKey(
|
|
||||||
Attachment,
|
@property
|
||||||
on_delete=models.CASCADE,
|
def etherpad_html(self):
|
||||||
verbose_name="Anhang Ordner",
|
return ep_get_html(self.etherpad_key)
|
||||||
)
|
|
||||||
|
def get_model_name(self):
|
||||||
objects = models.Manager()
|
return self._meta.model_name
|
||||||
|
|
||||||
class Meta:
|
|
||||||
verbose_name = "Datei"
|
class FileUpload(models.Model):
|
||||||
verbose_name_plural = "Dateien"
|
title = models.CharField(blank=True, max_length=128, verbose_name="Titel")
|
||||||
|
file_field = models.FileField(upload_to="uploads/intern/files/", verbose_name="Dokument")
|
||||||
def __str__(self):
|
date = models.DateField(default=date.today, verbose_name="Datum")
|
||||||
return self.title
|
|
||||||
|
attachment = models.ForeignKey(
|
||||||
def clean(self):
|
Attachment,
|
||||||
if not self.title:
|
on_delete=models.CASCADE,
|
||||||
self.title = Path(self.file_field.name).stem
|
verbose_name="Anhang Ordner",
|
||||||
|
)
|
||||||
|
|
||||||
|
objects = models.Manager()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = "Datei"
|
||||||
|
verbose_name_plural = "Dateien"
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.title
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
if not self.title:
|
||||||
|
self.title = Path(self.file_field.name).stem
|
||||||
|
|||||||
@@ -1,230 +1,232 @@
|
|||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
from .forms import (
|
from .forms import (
|
||||||
ActiveMemberForm,
|
ActiveMemberForm,
|
||||||
InactiveMemberForm,
|
InactiveMemberForm,
|
||||||
JobForm,
|
JobForm,
|
||||||
JobGroupForm,
|
JobGroupForm,
|
||||||
JobInlineForm,
|
JobInlineForm,
|
||||||
MemberForm,
|
MemberForm,
|
||||||
)
|
)
|
||||||
from .models import Job, JobGroup, JobMember, Member
|
from .models import Job, JobGroup, JobMember, Member
|
||||||
|
|
||||||
|
|
||||||
class MemberRoleFilter(admin.SimpleListFilter):
|
class MemberRoleFilter(admin.SimpleListFilter):
|
||||||
title = "Rolle"
|
title = "Rolle"
|
||||||
parameter_name = "role"
|
parameter_name = "role"
|
||||||
|
|
||||||
def lookups(self, request, model_admin):
|
def lookups(self, request, model_admin):
|
||||||
return (
|
return (
|
||||||
("A", "Aktiv"),
|
("A", "Aktiv"),
|
||||||
("P", "Pension"),
|
("P", "Pension"),
|
||||||
)
|
)
|
||||||
|
|
||||||
def queryset(self, request, queryset):
|
def queryset(self, request, queryset):
|
||||||
if self.value() in Member.MemberRole:
|
if self.value() in Member.MemberRole:
|
||||||
return queryset.filter(role=self.value())
|
return queryset.filter(role=self.value())
|
||||||
|
|
||||||
|
return queryset
|
||||||
class JobMemberInline(admin.TabularInline):
|
|
||||||
model = JobMember
|
|
||||||
extra = 0
|
class JobMemberInline(admin.TabularInline):
|
||||||
|
model = JobMember
|
||||||
|
extra = 0
|
||||||
class JobOverviewInline(JobMemberInline):
|
|
||||||
verbose_name = "Tätigkeit"
|
|
||||||
verbose_name_plural = "Tätigkeitsübersicht"
|
class JobOverviewInline(JobMemberInline):
|
||||||
|
verbose_name = "Tätigkeit"
|
||||||
def get_queryset(self, request):
|
verbose_name_plural = "Tätigkeitsübersicht"
|
||||||
return JobMember.members.get_all_jobs()
|
|
||||||
|
def get_queryset(self, request):
|
||||||
|
return JobMember.members.get_all_jobs()
|
||||||
class ActiveMemberInline(JobMemberInline):
|
|
||||||
form = ActiveMemberForm
|
|
||||||
verbose_name = "Mitglied"
|
class ActiveMemberInline(JobMemberInline):
|
||||||
verbose_name_plural = "Aktive Mitglieder Liste"
|
form = ActiveMemberForm
|
||||||
|
verbose_name = "Mitglied"
|
||||||
def get_queryset(self, request):
|
verbose_name_plural = "Aktive Mitglieder Liste"
|
||||||
return JobMember.active_member.get_queryset()
|
|
||||||
|
def get_queryset(self, request):
|
||||||
|
return JobMember.active_member.get_queryset()
|
||||||
class InactiveMemberInline(JobMemberInline):
|
|
||||||
form = InactiveMemberForm
|
|
||||||
verbose_name = "Mitglied"
|
class InactiveMemberInline(JobMemberInline):
|
||||||
verbose_name_plural = "Inaktive Mitglieder Liste"
|
form = InactiveMemberForm
|
||||||
|
verbose_name = "Mitglied"
|
||||||
def get_queryset(self, request):
|
verbose_name_plural = "Inaktive Mitglieder Liste"
|
||||||
return JobMember.inactive_member.get_queryset()
|
|
||||||
|
def get_queryset(self, request):
|
||||||
|
return JobMember.inactive_member.get_queryset()
|
||||||
class JobInline(admin.TabularInline):
|
|
||||||
form = JobInlineForm
|
|
||||||
model = Job
|
class JobInline(admin.TabularInline):
|
||||||
extra = 0
|
form = JobInlineForm
|
||||||
show_change_link = True
|
model = Job
|
||||||
|
extra = 0
|
||||||
|
show_change_link = True
|
||||||
@admin.register(Member)
|
|
||||||
class MemberAdmin(admin.ModelAdmin):
|
|
||||||
form = MemberForm
|
@admin.register(Member)
|
||||||
model = Member
|
class MemberAdmin(admin.ModelAdmin):
|
||||||
|
form = MemberForm
|
||||||
fieldsets = (
|
model = Member
|
||||||
(
|
|
||||||
None,
|
fieldsets = (
|
||||||
{
|
(
|
||||||
"fields": (
|
None,
|
||||||
(
|
{
|
||||||
"firstname",
|
"fields": (
|
||||||
"surname",
|
(
|
||||||
),
|
"firstname",
|
||||||
"nickname",
|
"surname",
|
||||||
"mailaccount",
|
),
|
||||||
"role",
|
"nickname",
|
||||||
"image",
|
"mailaccount",
|
||||||
"description",
|
"role",
|
||||||
),
|
"image",
|
||||||
},
|
"description",
|
||||||
),
|
),
|
||||||
)
|
},
|
||||||
inlines = (JobOverviewInline,)
|
),
|
||||||
|
)
|
||||||
list_display = ["nickname", "firstname", "surname", "mailaccount", "role"]
|
inlines = (JobOverviewInline,)
|
||||||
list_filter = [MemberRoleFilter]
|
|
||||||
ordering = ["firstname"]
|
list_display = ["nickname", "firstname", "surname", "mailaccount", "role"]
|
||||||
search_fields = ["firstname", "surname", "nickname", "mailaccount"]
|
list_filter = [MemberRoleFilter]
|
||||||
|
ordering = ["firstname"]
|
||||||
def add_view(self, request, form_url="", extra_context=None):
|
search_fields = ["firstname", "surname", "nickname", "mailaccount"]
|
||||||
extra_context = extra_context or {}
|
|
||||||
extra_context["help_text"] = "Fette Schriften sind Pflichtfelder."
|
def add_view(self, request, form_url="", extra_context=None):
|
||||||
return super().add_view(
|
extra_context = extra_context or {}
|
||||||
request,
|
extra_context["help_text"] = "Fette Schriften sind Pflichtfelder."
|
||||||
form_url,
|
return super().add_view(
|
||||||
extra_context=extra_context,
|
request,
|
||||||
)
|
form_url,
|
||||||
|
extra_context=extra_context,
|
||||||
def change_view(self, request, object_id, form_url="", extra_context=None):
|
)
|
||||||
extra_context = extra_context or {}
|
|
||||||
extra_context["help_text"] = "Fette Schriften sind Pflichtfelder."
|
def change_view(self, request, object_id, form_url="", extra_context=None):
|
||||||
return super().change_view(
|
extra_context = extra_context or {}
|
||||||
request,
|
extra_context["help_text"] = "Fette Schriften sind Pflichtfelder."
|
||||||
object_id,
|
return super().change_view(
|
||||||
form_url,
|
request,
|
||||||
extra_context=extra_context,
|
object_id,
|
||||||
)
|
form_url,
|
||||||
|
extra_context=extra_context,
|
||||||
def save_model(self, request, obj, form, change):
|
)
|
||||||
obj.author = request.user
|
|
||||||
super().save_model(request, obj, form, change)
|
def save_model(self, request, obj, form, change):
|
||||||
|
obj.author = request.user
|
||||||
|
super().save_model(request, obj, form, change)
|
||||||
@admin.register(Job)
|
|
||||||
class JobAdmin(admin.ModelAdmin):
|
|
||||||
form = JobForm
|
@admin.register(Job)
|
||||||
model = Job
|
class JobAdmin(admin.ModelAdmin):
|
||||||
|
form = JobForm
|
||||||
list_display = ["name", "job_group"]
|
model = Job
|
||||||
ordering = ["name"]
|
|
||||||
search_fields = ["name"]
|
list_display = ["name", "job_group"]
|
||||||
|
ordering = ["name"]
|
||||||
fieldsets = (
|
search_fields = ["name"]
|
||||||
(
|
|
||||||
None,
|
fieldsets = (
|
||||||
{
|
(
|
||||||
"fields": (
|
None,
|
||||||
"name",
|
{
|
||||||
"job_group",
|
"fields": (
|
||||||
),
|
"name",
|
||||||
},
|
"job_group",
|
||||||
),
|
),
|
||||||
(
|
},
|
||||||
"Permalink",
|
),
|
||||||
{
|
(
|
||||||
"fields": (
|
"Permalink",
|
||||||
"shortterm",
|
{
|
||||||
"slug",
|
"fields": (
|
||||||
),
|
"shortterm",
|
||||||
},
|
"slug",
|
||||||
),
|
),
|
||||||
)
|
},
|
||||||
inlines = (ActiveMemberInline, InactiveMemberInline)
|
),
|
||||||
readonly_fields = ["slug"]
|
)
|
||||||
|
inlines = (ActiveMemberInline, InactiveMemberInline)
|
||||||
def add_view(self, request, form_url="", extra_context=None):
|
readonly_fields = ["slug"]
|
||||||
extra_context = extra_context or {}
|
|
||||||
extra_context["help_text"] = "Fette Schriften sind Pflichtfelder."
|
def add_view(self, request, form_url="", extra_context=None):
|
||||||
return super().add_view(
|
extra_context = extra_context or {}
|
||||||
request,
|
extra_context["help_text"] = "Fette Schriften sind Pflichtfelder."
|
||||||
form_url,
|
return super().add_view(
|
||||||
extra_context=extra_context,
|
request,
|
||||||
)
|
form_url,
|
||||||
|
extra_context=extra_context,
|
||||||
def change_view(self, request, object_id, form_url="", extra_context=None):
|
)
|
||||||
extra_context = extra_context or {}
|
|
||||||
extra_context["help_text"] = "Fette Schriften sind Pflichfelder."
|
def change_view(self, request, object_id, form_url="", extra_context=None):
|
||||||
return super().change_view(
|
extra_context = extra_context or {}
|
||||||
request,
|
extra_context["help_text"] = "Fette Schriften sind Pflichfelder."
|
||||||
object_id,
|
return super().change_view(
|
||||||
form_url,
|
request,
|
||||||
extra_context=extra_context,
|
object_id,
|
||||||
)
|
form_url,
|
||||||
|
extra_context=extra_context,
|
||||||
def save_model(self, request, obj, form, change):
|
)
|
||||||
obj.author = request.user
|
|
||||||
super().save_model(request, obj, form, change)
|
def save_model(self, request, obj, form, change):
|
||||||
|
obj.author = request.user
|
||||||
|
super().save_model(request, obj, form, change)
|
||||||
@admin.register(JobGroup)
|
|
||||||
class JobGroupAdmin(admin.ModelAdmin):
|
|
||||||
form = JobGroupForm
|
@admin.register(JobGroup)
|
||||||
model = JobGroup
|
class JobGroupAdmin(admin.ModelAdmin):
|
||||||
|
form = JobGroupForm
|
||||||
list_display = ["name"]
|
model = JobGroup
|
||||||
ordering = ["name"]
|
|
||||||
search_fields = ["name"]
|
list_display = ["name"]
|
||||||
|
ordering = ["name"]
|
||||||
fieldsets = (
|
search_fields = ["name"]
|
||||||
(
|
|
||||||
None,
|
fieldsets = (
|
||||||
{
|
(
|
||||||
"fields": (
|
None,
|
||||||
"name",
|
{
|
||||||
"description",
|
"fields": (
|
||||||
),
|
"name",
|
||||||
},
|
"description",
|
||||||
),
|
),
|
||||||
(
|
},
|
||||||
"Permalink",
|
),
|
||||||
{
|
(
|
||||||
"fields": (
|
"Permalink",
|
||||||
"shortterm",
|
{
|
||||||
"slug",
|
"fields": (
|
||||||
),
|
"shortterm",
|
||||||
},
|
"slug",
|
||||||
),
|
),
|
||||||
)
|
},
|
||||||
inlines = (JobInline,)
|
),
|
||||||
readonly_fields = ["slug"]
|
)
|
||||||
|
inlines = (JobInline,)
|
||||||
def add_view(self, request, form_url="", extra_context=None):
|
readonly_fields = ["slug"]
|
||||||
extra_context = extra_context or {}
|
|
||||||
extra_context["help_text"] = "Fette Schriften sind Pflichtfelder."
|
def add_view(self, request, form_url="", extra_context=None):
|
||||||
return super().add_view(
|
extra_context = extra_context or {}
|
||||||
request,
|
extra_context["help_text"] = "Fette Schriften sind Pflichtfelder."
|
||||||
form_url,
|
return super().add_view(
|
||||||
extra_context=extra_context,
|
request,
|
||||||
)
|
form_url,
|
||||||
|
extra_context=extra_context,
|
||||||
def change_view(self, request, object_id, form_url="", extra_context=None):
|
)
|
||||||
extra_context = extra_context or {}
|
|
||||||
extra_context["help_text"] = "Fette Schriften sind Pflichfelder."
|
def change_view(self, request, object_id, form_url="", extra_context=None):
|
||||||
return super().change_view(
|
extra_context = extra_context or {}
|
||||||
request,
|
extra_context["help_text"] = "Fette Schriften sind Pflichfelder."
|
||||||
object_id,
|
return super().change_view(
|
||||||
form_url,
|
request,
|
||||||
extra_context=extra_context,
|
object_id,
|
||||||
)
|
form_url,
|
||||||
|
extra_context=extra_context,
|
||||||
def save_model(self, request, obj, form, change):
|
)
|
||||||
obj.author = request.user
|
|
||||||
super().save_model(request, obj, form, change)
|
def save_model(self, request, obj, form, change):
|
||||||
|
obj.author = request.user
|
||||||
|
super().save_model(request, obj, form, change)
|
||||||
|
|||||||
@@ -1,210 +1,207 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.core.validators import ValidationError, validate_email
|
from django.core.validators import validate_email
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models import F
|
from django.db.models import F
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.text import slugify
|
from django.utils.text import slugify
|
||||||
from easy_thumbnails.fields import ThumbnailerImageField
|
from easy_thumbnails.fields import ThumbnailerImageField
|
||||||
|
|
||||||
from .managers import (
|
from .managers import (
|
||||||
ActiveJobMemberManager,
|
ActiveJobMemberManager,
|
||||||
InactiveJobMemberManager,
|
InactiveJobMemberManager,
|
||||||
JobMemberManager,
|
JobMemberManager,
|
||||||
MemberManager,
|
MemberManager,
|
||||||
)
|
)
|
||||||
from .validators import (
|
from .validators import (
|
||||||
validate_domainonly_email,
|
validate_domainonly_email,
|
||||||
validate_file_size,
|
validate_file_size,
|
||||||
validate_image_dimension,
|
validate_image_dimension,
|
||||||
)
|
)
|
||||||
|
|
||||||
fet_logo_url = settings.STATIC_URL + "img/FET-Logo-2014-quadrat.png"
|
fet_logo_url = settings.STATIC_URL + "img/FET-Logo-2014-quadrat.png"
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class Member(models.Model):
|
class Member(models.Model):
|
||||||
firstname = models.CharField("Vorname", max_length=128)
|
firstname = models.CharField("Vorname", max_length=128)
|
||||||
surname = models.CharField("Nachname", max_length=128)
|
surname = models.CharField("Nachname", max_length=128)
|
||||||
nickname = models.CharField("Spitzname", max_length=128)
|
nickname = models.CharField("Spitzname", max_length=128)
|
||||||
|
|
||||||
# LDAP Username
|
# LDAP Username
|
||||||
username = models.CharField(max_length=128, blank=True)
|
username = models.CharField(max_length=128, blank=True)
|
||||||
|
|
||||||
# fet mail account
|
# fet mail account
|
||||||
mailaccount = models.CharField(
|
mailaccount = models.CharField(
|
||||||
"Mailadresse",
|
"Mailadresse",
|
||||||
unique=True,
|
unique=True,
|
||||||
max_length=128,
|
max_length=128,
|
||||||
validators=[validate_email, validate_domainonly_email],
|
validators=[validate_email, validate_domainonly_email],
|
||||||
error_messages={
|
error_messages={
|
||||||
"unique": "Diese Mailadresse existiert schon.",
|
"unique": "Diese Mailadresse existiert schon.",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
class MemberRole(models.TextChoices):
|
class MemberRole(models.TextChoices):
|
||||||
ACTIVE = "A", "Active"
|
ACTIVE = "A", "Active"
|
||||||
PENSION = "P", "Pension"
|
PENSION = "P", "Pension"
|
||||||
|
|
||||||
role = models.CharField(
|
role = models.CharField(
|
||||||
"Rolle",
|
"Rolle",
|
||||||
max_length=1,
|
max_length=1,
|
||||||
choices=MemberRole.choices,
|
choices=MemberRole.choices,
|
||||||
default=MemberRole.ACTIVE,
|
default=MemberRole.ACTIVE,
|
||||||
)
|
)
|
||||||
|
|
||||||
description = models.TextField(blank=True, default="")
|
description = models.TextField(blank=True, default="")
|
||||||
|
|
||||||
image = ThumbnailerImageField(
|
image = ThumbnailerImageField(
|
||||||
upload_to="uploads/members/image/",
|
upload_to="uploads/members/image/",
|
||||||
validators=[validate_file_size, validate_image_dimension],
|
validators=[validate_file_size, validate_image_dimension],
|
||||||
)
|
)
|
||||||
|
|
||||||
date_modified = models.DateTimeField(auto_now=True)
|
date_modified = models.DateTimeField(auto_now=True)
|
||||||
date_created = models.DateTimeField(auto_now_add=True)
|
date_created = models.DateTimeField(auto_now_add=True)
|
||||||
|
|
||||||
# Managers
|
# Managers
|
||||||
objects = models.Manager()
|
objects = models.Manager()
|
||||||
all_members = MemberManager()
|
all_members = MemberManager()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = "Mitglied"
|
verbose_name = "Mitglied"
|
||||||
verbose_name_plural = "Mitglieder"
|
verbose_name_plural = "Mitglieder"
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.firstname + " " + self.surname
|
return self.firstname + " " + self.surname
|
||||||
|
|
||||||
# need to have 'View on site' link in admin app
|
# need to have 'View on site' link in admin app
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse("members:member", kwargs={"pk": self.pk})
|
return reverse("members:member", kwargs={"pk": self.pk})
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
if not self.image:
|
if self.username:
|
||||||
raise ValidationError("Es fehlt das Profilbild.")
|
try:
|
||||||
|
user = User.objects.get(username=self.username.lower())
|
||||||
if self.username:
|
except User.DoesNotExist as e:
|
||||||
try:
|
logger.info("Username not found. Error: %s", e)
|
||||||
user = User.objects.get(username=self.username.lower())
|
else:
|
||||||
except User.DoesNotExist as e:
|
user.first_name = self.firstname
|
||||||
logger.info("Username not found. Error: %s", e)
|
user.save()
|
||||||
else:
|
|
||||||
user.first_name = self.firstname
|
def get_model_name(self):
|
||||||
user.save()
|
return self._meta.model_name
|
||||||
|
|
||||||
def get_model_name(self):
|
@property
|
||||||
return self._meta.model_name
|
def image_url(self):
|
||||||
|
return self.image.url if self.image else fet_logo_url
|
||||||
@property
|
|
||||||
def image_url(self):
|
@property
|
||||||
return self.image.url if self.image else fet_logo_url
|
def avatar_url(self):
|
||||||
|
return self.image["avatar"].url if self.image else fet_logo_url
|
||||||
@property
|
|
||||||
def avatar_url(self):
|
@property
|
||||||
return self.image["avatar"].url if self.image else fet_logo_url
|
def portrait_url(self):
|
||||||
|
return self.image["portrait"].url if self.image else fet_logo_url
|
||||||
@property
|
|
||||||
def portrait_url(self):
|
@property
|
||||||
return self.image["portrait"].url if self.image else fet_logo_url
|
def thumb_url(self):
|
||||||
|
return self.image["thumb"].url if self.image else fet_logo_url
|
||||||
@property
|
|
||||||
def thumb_url(self):
|
|
||||||
return self.image["thumb"].url if self.image else fet_logo_url
|
class JobGroup(models.Model):
|
||||||
|
name = models.CharField(max_length=128)
|
||||||
|
|
||||||
class JobGroup(models.Model):
|
shortterm = models.CharField(max_length=128, unique=True, blank=True)
|
||||||
name = models.CharField(max_length=128)
|
slug = models.SlugField(unique=True, null=True, blank=True)
|
||||||
|
|
||||||
shortterm = models.CharField(max_length=128, unique=True, blank=True)
|
description = models.TextField(blank=True, default="")
|
||||||
slug = models.SlugField(unique=True, null=True, blank=True)
|
|
||||||
|
# Managers
|
||||||
description = models.TextField(blank=True, default="")
|
objects = models.Manager()
|
||||||
|
|
||||||
# Managers
|
class Meta:
|
||||||
objects = models.Manager()
|
verbose_name = "Tätigkeitsbereich"
|
||||||
|
verbose_name_plural = "Tätigkeitsbereiche"
|
||||||
class Meta:
|
|
||||||
verbose_name = "Tätigkeitsbereich"
|
def __str__(self):
|
||||||
verbose_name_plural = "Tätigkeitsbereiche"
|
return self.name
|
||||||
|
|
||||||
def __str__(self):
|
# need to have 'View on site' link in admin app
|
||||||
return self.name
|
def get_absolute_url(self):
|
||||||
|
return reverse("members:jobs", kwargs={"slug": self.slug})
|
||||||
# need to have 'View on site' link in admin app
|
|
||||||
def get_absolute_url(self):
|
def clean(self):
|
||||||
return reverse("members:jobs", kwargs={"slug": self.slug})
|
if not self.shortterm:
|
||||||
|
self.shortterm = slugify(self.name)
|
||||||
def clean(self):
|
self.slug = slugify(self.shortterm)
|
||||||
if not self.shortterm:
|
|
||||||
self.shortterm = slugify(self.name)
|
|
||||||
self.slug = slugify(self.shortterm)
|
class Job(models.Model):
|
||||||
|
name = models.CharField(max_length=128)
|
||||||
|
|
||||||
class Job(models.Model):
|
shortterm = models.CharField(max_length=128, unique=True, blank=True)
|
||||||
name = models.CharField(max_length=128)
|
slug = models.SlugField(unique=True, null=True, blank=True)
|
||||||
|
|
||||||
shortterm = models.CharField(max_length=128, unique=True, blank=True)
|
order = models.PositiveSmallIntegerField(null=True, blank=True)
|
||||||
slug = models.SlugField(unique=True, null=True, blank=True)
|
|
||||||
|
job_group = models.ForeignKey(
|
||||||
order = models.PositiveSmallIntegerField(null=True, blank=True)
|
JobGroup,
|
||||||
|
on_delete=models.CASCADE,
|
||||||
job_group = models.ForeignKey(
|
verbose_name="Job Gruppe",
|
||||||
JobGroup,
|
)
|
||||||
on_delete=models.CASCADE,
|
|
||||||
verbose_name="Job Gruppe",
|
# Managers
|
||||||
)
|
objects = models.Manager()
|
||||||
|
|
||||||
# Managers
|
class Meta:
|
||||||
objects = models.Manager()
|
ordering = (F("order").asc(nulls_last=True), "name")
|
||||||
|
|
||||||
class Meta:
|
verbose_name = "Tätigkeit"
|
||||||
ordering = (F("order").asc(nulls_last=True), "name")
|
verbose_name_plural = "Tätigkeiten"
|
||||||
|
|
||||||
verbose_name = "Tätigkeit"
|
def __str__(self):
|
||||||
verbose_name_plural = "Tätigkeiten"
|
return self.name
|
||||||
|
|
||||||
def __str__(self):
|
def get_absolute_url(self):
|
||||||
return self.name
|
return reverse("members:jobs", kwargs={"slug": self.job_group.slug}) + "#" + self.slug
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def clean(self):
|
||||||
return reverse("members:jobs", kwargs={"slug": self.job_group.slug}) + "#" + self.slug
|
if not self.shortterm:
|
||||||
|
self.shortterm = slugify(self.name)
|
||||||
def clean(self):
|
self.slug = slugify(self.shortterm)
|
||||||
if not self.shortterm:
|
|
||||||
self.shortterm = slugify(self.name)
|
|
||||||
self.slug = slugify(self.shortterm)
|
class JobMember(models.Model):
|
||||||
|
member = models.ForeignKey(
|
||||||
|
Member,
|
||||||
class JobMember(models.Model):
|
on_delete=models.CASCADE,
|
||||||
member = models.ForeignKey(
|
verbose_name="Mitglied",
|
||||||
Member,
|
)
|
||||||
on_delete=models.CASCADE,
|
job = models.ForeignKey(
|
||||||
verbose_name="Mitglied",
|
Job,
|
||||||
)
|
on_delete=models.CASCADE,
|
||||||
job = models.ForeignKey(
|
verbose_name="Tätigkeit",
|
||||||
Job,
|
)
|
||||||
on_delete=models.CASCADE,
|
|
||||||
verbose_name="Tätigkeit",
|
job_start = models.DateField("Job Start")
|
||||||
)
|
job_end = models.DateField("Job Ende", null=True, blank=True)
|
||||||
|
|
||||||
job_start = models.DateField("Job Start")
|
class JobRole(models.TextChoices):
|
||||||
job_end = models.DateField("Job Ende", null=True, blank=True)
|
PRESIDENT = ("10", "Vorsitz")
|
||||||
|
VICE_PRESIDENT = ("20", "Stv. Vorsitz")
|
||||||
class JobRole(models.TextChoices):
|
SECOND_VICE_PRESIDENT = ("30", "2. Stv. Vorsitz")
|
||||||
PRESIDENT = ("10", "Vorsitz")
|
PERSON_RESPONSIBLE = ("40", "Verantwortliche_r")
|
||||||
VICE_PRESIDENT = ("20", "Stv. Vorsitz")
|
MEMBER = ("50", "Mitglied")
|
||||||
SECOND_VICE_PRESIDENT = ("30", "2. Stv. Vorsitz")
|
SUBSTITUTE_MEMBER = ("60", "Ersatzmitglied")
|
||||||
PERSON_RESPONSIBLE = ("40", "Verantwortliche_r")
|
|
||||||
MEMBER = ("50", "Mitglied")
|
job_role = models.CharField(max_length=2, choices=JobRole.choices, default=JobRole.MEMBER)
|
||||||
SUBSTITUTE_MEMBER = ("60", "Ersatzmitglied")
|
|
||||||
|
objects = models.Manager()
|
||||||
job_role = models.CharField(max_length=2, choices=JobRole.choices, default=JobRole.MEMBER)
|
members = JobMemberManager()
|
||||||
|
active_member = ActiveJobMemberManager()
|
||||||
objects = models.Manager()
|
inactive_member = InactiveJobMemberManager()
|
||||||
members = JobMemberManager()
|
|
||||||
active_member = ActiveJobMemberManager()
|
def __str__(self):
|
||||||
inactive_member = InactiveJobMemberManager()
|
return self.job.name
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.job.name
|
|
||||||
|
|||||||
@@ -1,33 +1,31 @@
|
|||||||
from collections import deque
|
from collections import deque
|
||||||
|
|
||||||
from django import template
|
from django import template
|
||||||
from django.db.models import F
|
from django.db.models import F
|
||||||
|
|
||||||
from members.models import JobGroup, JobMember
|
from members.models import JobGroup, JobMember
|
||||||
|
|
||||||
register = template.Library()
|
register = template.Library()
|
||||||
|
|
||||||
|
|
||||||
@register.inclusion_tag("members/partials/jobs_sidebar.html")
|
@register.inclusion_tag("members/partials/jobs_sidebar.html")
|
||||||
def get_jobs_sidebar(slug):
|
def get_jobs_sidebar(slug):
|
||||||
job_groups = deque(JobGroup.objects.all())
|
job_groups = deque(JobGroup.objects.all())
|
||||||
|
|
||||||
# remove job group if there is no longer an active member
|
# remove job group if there is no longer an active member
|
||||||
for elem in job_groups.copy():
|
for elem in job_groups.copy():
|
||||||
job_members = JobMember.active_member.get_all(slug=elem.slug)
|
job_members = JobMember.active_member.get_all(slug=elem.slug)
|
||||||
if not job_members:
|
if not job_members:
|
||||||
job_groups.remove(elem)
|
job_groups.remove(elem)
|
||||||
|
|
||||||
job_members = JobMember.active_member.get_all(slug=slug).order_by(
|
job_members = JobMember.active_member.get_all(slug=slug).order_by(
|
||||||
F("job__order").asc(nulls_last=True),
|
F("job__order").asc(nulls_last=True),
|
||||||
"job__name",
|
"job__name",
|
||||||
)
|
)
|
||||||
active_job_group = JobGroup.objects.filter(slug=slug).first()
|
active_job_group = JobGroup.objects.filter(slug=slug).first()
|
||||||
|
|
||||||
context = {
|
return {
|
||||||
"job_groups": job_groups,
|
"job_groups": job_groups,
|
||||||
"job_members": job_members,
|
"job_members": job_members,
|
||||||
"active_job_group": active_job_group,
|
"active_job_group": active_job_group,
|
||||||
}
|
}
|
||||||
|
|
||||||
return context
|
|
||||||
|
|||||||
@@ -1,22 +1,26 @@
|
|||||||
from django.core.validators import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
|
|
||||||
|
|
||||||
def validate_domainonly_email(value):
|
def validate_domainonly_email(value):
|
||||||
if "fet.at" not in value:
|
if "fet.at" not in value:
|
||||||
raise ValidationError("In der Mailadresse fehlt die richtige Domäne.")
|
msg = "In der Mailadresse fehlt die richtige Domäne."
|
||||||
|
raise ValidationError(msg, code="invalid_domain")
|
||||||
|
|
||||||
def validate_file_size(value):
|
|
||||||
if value.size > 10 * 1024 * 1024:
|
def validate_file_size(value):
|
||||||
raise ValidationError("Die maximale Dateigröße ist 10MB.")
|
if value.size > 10 * 1024 * 1024:
|
||||||
|
msg = "Die maximale Dateigröße ist 10MB."
|
||||||
|
raise ValidationError(msg, code="file_too_large")
|
||||||
def validate_image_dimension(value):
|
|
||||||
if value.height < 150 or value.width < 150:
|
|
||||||
raise ValidationError(
|
def validate_image_dimension(value):
|
||||||
"Das Bild ist zu klein. (Höhe: %(height)s, Breite: %(width)s)",
|
if value.height < 150 or value.width < 150:
|
||||||
params={
|
msg = "Das Bild ist zu klein. (Höhe: %(height)s, Breite: %(width)s)"
|
||||||
"height": value.height,
|
raise ValidationError(
|
||||||
"width": value.width,
|
msg,
|
||||||
},
|
code="image_too_small",
|
||||||
)
|
params={
|
||||||
|
"height": value.height,
|
||||||
|
"width": value.width,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|||||||
@@ -23,10 +23,7 @@ def create_token(username, masterpassword):
|
|||||||
|
|
||||||
@authenticated_user
|
@authenticated_user
|
||||||
def index(request):
|
def index(request):
|
||||||
context = {
|
context = {"mctoken": "", "valid_master_pwd": True}
|
||||||
"mctoken": "",
|
|
||||||
"valid_master_pwd": True
|
|
||||||
}
|
|
||||||
|
|
||||||
masterpassword = settings.MC_MASTERPASSWORD
|
masterpassword = settings.MC_MASTERPASSWORD
|
||||||
|
|
||||||
|
|||||||
@@ -179,8 +179,7 @@ class EventManager(PublishedManager, models.Manager):
|
|||||||
|
|
||||||
def past_events(self, public=True):
|
def past_events(self, public=True):
|
||||||
date_today = timezone.now().date()
|
date_today = timezone.now().date()
|
||||||
qs = self.published(public).filter(event_start__lt=date_today)
|
return self.published(public).filter(event_start__lt=date_today)
|
||||||
return qs
|
|
||||||
|
|
||||||
|
|
||||||
class FetMeetingManager(PublishedManager, models.Manager):
|
class FetMeetingManager(PublishedManager, models.Manager):
|
||||||
@@ -204,5 +203,4 @@ class FetMeetingManager(PublishedManager, models.Manager):
|
|||||||
|
|
||||||
def past_events(self):
|
def past_events(self):
|
||||||
date_today = timezone.now().date()
|
date_today = timezone.now().date()
|
||||||
qs = self.published().filter(event_start__lt=date_today)
|
return self.published().filter(event_start__lt=date_today)
|
||||||
return qs
|
|
||||||
|
|||||||
@@ -1,376 +1,384 @@
|
|||||||
import logging
|
import logging
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.core.validators import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.text import slugify
|
from django.utils.text import slugify
|
||||||
from taggit.managers import TaggableManager
|
from taggit.managers import TaggableManager
|
||||||
|
|
||||||
from core.models import CustomFlatPage
|
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 documents.api import ep_create_new_pad, ep_get_html, ep_get_url, ep_pad_exists, ep_set_html
|
||||||
|
|
||||||
from .choices import PostType, Status
|
from .choices import PostType, Status
|
||||||
from .managers import (
|
from .managers import (
|
||||||
AllEventManager,
|
AllEventManager,
|
||||||
ArticleManager,
|
ArticleManager,
|
||||||
EventManager,
|
EventManager,
|
||||||
FetMeetingManager,
|
FetMeetingManager,
|
||||||
NewsManager,
|
NewsManager,
|
||||||
PostManager,
|
PostManager,
|
||||||
)
|
)
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def create_pad_for_post(slug, item="agenda"):
|
def create_pad_for_post(slug, item="agenda"):
|
||||||
logger.info("Pad-Type: %s", item)
|
logger.info("Pad-Type: %s", item)
|
||||||
|
|
||||||
pad_id = slug + "-" + item
|
pad_id = slug + "-" + item
|
||||||
if not ep_create_new_pad(pad_id):
|
if not ep_create_new_pad(pad_id):
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
# Set template into the newly created pad if it exists.
|
# Set template into the newly created pad if it exists.
|
||||||
if page := CustomFlatPage.objects.filter(title__iexact=item).first():
|
if page := CustomFlatPage.objects.filter(title__iexact=item).first():
|
||||||
ep_set_html(pad_id, page.content)
|
ep_set_html(pad_id, page.content)
|
||||||
logger.info("Template '%s' is set.", page.title)
|
logger.info("Template '%s' is set.", page.title)
|
||||||
|
|
||||||
return pad_id
|
return pad_id
|
||||||
|
|
||||||
|
|
||||||
class Post(models.Model):
|
class Post(models.Model):
|
||||||
# legacy id is for the posts from the old website
|
# legacy id is for the posts from the old website
|
||||||
legacy_id = models.IntegerField(null=True, blank=True)
|
legacy_id = models.IntegerField(null=True, blank=True)
|
||||||
|
|
||||||
title = models.CharField(verbose_name="Titel", max_length=200)
|
title = models.CharField(verbose_name="Titel", max_length=200)
|
||||||
subtitle = models.CharField(max_length=500, blank=True, default="")
|
subtitle = models.CharField(max_length=500, blank=True, default="")
|
||||||
tags = TaggableManager(blank=True)
|
tags = TaggableManager(blank=True)
|
||||||
|
|
||||||
slug = models.SlugField(unique=True, blank=True)
|
slug = models.SlugField(unique=True, blank=True)
|
||||||
|
|
||||||
body = models.TextField(blank=True, default="")
|
body = models.TextField(blank=True, default="")
|
||||||
image = models.ImageField(null=True, blank=True)
|
image = models.ImageField(null=True, blank=True)
|
||||||
|
|
||||||
author = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)
|
author = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)
|
||||||
public_date = models.DateField(
|
public_date = models.DateField(
|
||||||
verbose_name="Veröffentlichung",
|
verbose_name="Veröffentlichung",
|
||||||
null=True,
|
null=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
default=timezone.now,
|
default=timezone.now,
|
||||||
)
|
)
|
||||||
|
|
||||||
post_type = models.CharField(max_length=1, choices=PostType.choices, editable=True)
|
post_type = models.CharField(max_length=1, choices=PostType.choices, editable=True)
|
||||||
|
|
||||||
status = models.CharField(
|
status = models.CharField(
|
||||||
verbose_name="Status", max_length=2, choices=Status.choices, default=Status.DRAFT
|
verbose_name="Status", max_length=2, choices=Status.choices, default=Status.DRAFT
|
||||||
)
|
)
|
||||||
|
|
||||||
# post is pinned at main page
|
# post is pinned at main page
|
||||||
is_pinned = models.BooleanField(verbose_name="ANGEHEFTET", default=False)
|
is_pinned = models.BooleanField(verbose_name="ANGEHEFTET", default=False)
|
||||||
|
|
||||||
# addional infos for events
|
# addional infos for events
|
||||||
event_start = models.DateTimeField(verbose_name="Event Start", null=True, blank=True)
|
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_end = models.DateTimeField(verbose_name="Event Ende", null=True, blank=True)
|
||||||
event_place = models.CharField(max_length=200, blank=True, default="")
|
event_place = models.CharField(max_length=200, blank=True, default="")
|
||||||
|
|
||||||
# protocol for fet meeting
|
# protocol for fet meeting
|
||||||
has_protocol = models.BooleanField(default=False)
|
has_protocol = models.BooleanField(default=False)
|
||||||
has_agenda = models.BooleanField(default=False)
|
has_agenda = models.BooleanField(default=False)
|
||||||
protocol_key = models.CharField(max_length=200, blank=True, default="")
|
protocol_key = models.CharField(max_length=200, blank=True, default="")
|
||||||
agenda_key = models.CharField(max_length=200, blank=True, default="")
|
agenda_key = models.CharField(max_length=200, blank=True, default="")
|
||||||
|
|
||||||
# TimeStamps
|
# TimeStamps
|
||||||
date_modified = models.DateTimeField(auto_now=True)
|
date_modified = models.DateTimeField(auto_now=True)
|
||||||
date_created = models.DateTimeField(auto_now_add=True)
|
date_created = models.DateTimeField(auto_now_add=True)
|
||||||
|
|
||||||
# Managers
|
# Managers
|
||||||
objects = PostManager()
|
objects = PostManager()
|
||||||
articles = ArticleManager()
|
articles = ArticleManager()
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "Post ({}, {}): {}".format(
|
return "Post ({}, {}): {}".format(
|
||||||
self.slug,
|
self.slug,
|
||||||
self.public_date.strftime("%d.%m.%Y"),
|
self.public_date.strftime("%d.%m.%Y"),
|
||||||
self.title,
|
self.title,
|
||||||
)
|
)
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
# save the post with some defaults
|
# save the post with some defaults
|
||||||
if not self.public_date:
|
if not self.public_date:
|
||||||
self.public_date = timezone.now().date()
|
self.public_date = timezone.now().date()
|
||||||
|
|
||||||
if not self.slug:
|
if not self.slug:
|
||||||
self.slug = slugify(self.public_date) + "-" + slugify(self.title)
|
self.slug = slugify(self.public_date) + "-" + slugify(self.title)
|
||||||
|
|
||||||
super().save(*args, **kwargs)
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse("posts:post", kwargs={"slug": self.slug})
|
return reverse("posts:post", kwargs={"slug": self.slug})
|
||||||
|
|
||||||
# "#" for backward compatibility
|
# "#" for backward compatibility
|
||||||
_possible_empty_key_value = ["#", "", None]
|
_possible_empty_key_value = ["#", "", None]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def agenda_html(self) -> str | None:
|
def agenda_html(self) -> str | None:
|
||||||
if self.agenda_key in self._possible_empty_key_value:
|
if self.agenda_key in self._possible_empty_key_value:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return ep_get_html(self.agenda_key)
|
return ep_get_html(self.agenda_key)
|
||||||
|
|
||||||
@agenda_html.setter
|
@agenda_html.setter
|
||||||
def agenda_html(self, value: str) -> str | None:
|
def agenda_html(self, value: str) -> str | None:
|
||||||
if self.agenda_key in self._possible_empty_key_value:
|
if self.agenda_key in self._possible_empty_key_value:
|
||||||
self.create_agenda_key()
|
self.create_agenda_key()
|
||||||
|
|
||||||
if not value or not self.agenda_key:
|
if not value or not self.agenda_key:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
ep_set_html(self.agenda_key, value)
|
ep_set_html(self.agenda_key, value)
|
||||||
logger.info("Set agenda etherpad '%s' for post '%s'.", self.agenda_key, self.slug)
|
logger.info("Set agenda etherpad '%s' for post '%s'.", self.agenda_key, self.slug)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def protocol_html(self) -> str | None:
|
def protocol_html(self) -> str | None:
|
||||||
if self.protocol_key in self._possible_empty_key_value:
|
if self.protocol_key in self._possible_empty_key_value:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return ep_get_html(self.protocol_key)
|
return ep_get_html(self.protocol_key)
|
||||||
|
|
||||||
@protocol_html.setter
|
@protocol_html.setter
|
||||||
def protocol_html(self, value: str) -> str | None:
|
def protocol_html(self, value: str) -> str | None:
|
||||||
if self.protocol_key in self._possible_empty_key_value:
|
if self.protocol_key in self._possible_empty_key_value:
|
||||||
self.create_protocol_key()
|
self.create_protocol_key()
|
||||||
|
|
||||||
if not value or not self.protocol_key:
|
if not value or not self.protocol_key:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
ep_set_html(self.protocol_key, value)
|
ep_set_html(self.protocol_key, value)
|
||||||
logger.info("Set protocol etherpad '%s' for post '%s'.", self.protocol_key, self.slug)
|
logger.info("Set protocol etherpad '%s' for post '%s'.", self.protocol_key, self.slug)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
_agenda_filename = None
|
_agenda_filename = None
|
||||||
_agenda_url = None
|
_agenda_url = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def agenda_url(self) -> str | None:
|
def agenda_url(self) -> str | None:
|
||||||
if not self.has_agenda:
|
if not self.has_agenda:
|
||||||
self._agenda_url = None
|
self._agenda_url = None
|
||||||
self._agenda_filename = None
|
self._agenda_filename = None
|
||||||
return self._agenda_url
|
return self._agenda_url
|
||||||
|
|
||||||
if self._agenda_url:
|
if self._agenda_url:
|
||||||
return self._agenda_url
|
return self._agenda_url
|
||||||
|
|
||||||
if url := ep_get_url(self.agenda_key):
|
if url := ep_get_url(self.agenda_key):
|
||||||
self._agenda_url = url
|
self._agenda_url = url
|
||||||
self._agenda_filename = self.slug + "-agenda.pdf"
|
self._agenda_filename = self.slug + "-agenda.pdf"
|
||||||
else:
|
else:
|
||||||
self._agenda_url = None
|
self._agenda_url = None
|
||||||
self._agenda_filename = None
|
self._agenda_filename = None
|
||||||
|
|
||||||
return self._agenda_url
|
return self._agenda_url
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def agenda_filename(self) -> str | None:
|
def agenda_filename(self) -> str | None:
|
||||||
# TODO: fix pdf render
|
# TODO: fix pdf render
|
||||||
# if self._agenda_filename:
|
# if self._agenda_filename:
|
||||||
# return self._agenda_filename
|
# return self._agenda_filename
|
||||||
|
|
||||||
# if self.has_agenda and self.agenda_url:
|
# if self.has_agenda and self.agenda_url:
|
||||||
# return self.slug + "-agenda.pdf"
|
# return self.slug + "-agenda.pdf"
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
_protocol_filename = None
|
_protocol_filename = None
|
||||||
_protocol_url = None
|
_protocol_url = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def protocol_url(self) -> str | None:
|
def protocol_url(self) -> str | None:
|
||||||
if not self.has_protocol:
|
if not self.has_protocol:
|
||||||
self._protocol_url = None
|
self._protocol_url = None
|
||||||
self._protocol_filename = None
|
self._protocol_filename = None
|
||||||
return self._protocol_url
|
return self._protocol_url
|
||||||
|
|
||||||
if self._protocol_url:
|
if self._protocol_url:
|
||||||
return self._protocol_url
|
return self._protocol_url
|
||||||
|
|
||||||
if url := ep_get_url(self.protocol_key):
|
if url := ep_get_url(self.protocol_key):
|
||||||
self._protocol_url = url
|
self._protocol_url = url
|
||||||
self._protocol_filename = self.slug + "-protokoll.pdf"
|
self._protocol_filename = self.slug + "-protokoll.pdf"
|
||||||
else:
|
else:
|
||||||
self._protocol_url = None
|
self._protocol_url = None
|
||||||
self._protocol_filename = None
|
self._protocol_filename = None
|
||||||
|
|
||||||
return self._protocol_url
|
return self._protocol_url
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def protocol_filename(self) -> str | None:
|
def protocol_filename(self) -> str | None:
|
||||||
# TODO: fix pdf render
|
# TODO: fix pdf render
|
||||||
# if self._protocol_filename:
|
# if self._protocol_filename:
|
||||||
# return self._protocol_filename
|
# return self._protocol_filename
|
||||||
|
|
||||||
# if self.has_protocol and self.protocol_url:
|
# if self.has_protocol and self.protocol_url:
|
||||||
# return self.slug + "-protokoll.pdf"
|
# return self.slug + "-protokoll.pdf"
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def create_agenda_key(self) -> None:
|
def create_agenda_key(self) -> None:
|
||||||
"""
|
"""
|
||||||
Create a Etherpad Id for the Pad associated to this post.
|
Create a Etherpad Id for the Pad associated to this post.
|
||||||
Create the pad if it doesn't exist.
|
Create the pad if it doesn't exist.
|
||||||
"""
|
"""
|
||||||
if self.slug:
|
if self.slug:
|
||||||
self.agenda_key = create_pad_for_post(self.slug, "agenda")
|
self.agenda_key = create_pad_for_post(self.slug, "agenda")
|
||||||
|
|
||||||
def create_protocol_key(self) -> None:
|
def create_protocol_key(self) -> None:
|
||||||
"""
|
"""
|
||||||
Create a Etherpad Id for the Pad associated to this post.
|
Create a Etherpad Id for the Pad associated to this post.
|
||||||
Create the pad if it doesn't exist.
|
Create the pad if it doesn't exist.
|
||||||
"""
|
"""
|
||||||
if self.slug:
|
if self.slug:
|
||||||
self.protocol_key = create_pad_for_post(self.slug, "protocol")
|
self.protocol_key = create_pad_for_post(self.slug, "protocol")
|
||||||
|
|
||||||
def get_model_name(self):
|
def get_model_name(self):
|
||||||
return self._meta.model_name
|
return self._meta.model_name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def three_tag_names(self):
|
def three_tag_names(self):
|
||||||
return self.tags.names()[:3]
|
return self.tags.names()[:3]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def tag_names(self):
|
def tag_names(self):
|
||||||
return [t for t in self.tags.names()]
|
return [t for t in self.tags.names()]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def imageurl(self) -> str:
|
def imageurl(self) -> str:
|
||||||
return (
|
return (
|
||||||
self.image.url if self.image else settings.STATIC_URL + "img/FET-Logo-2014-quadrat.png"
|
self.image.url if self.image else settings.STATIC_URL + "img/FET-Logo-2014-quadrat.png"
|
||||||
)
|
)
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
if self.event_end and self.event_end < self.event_start:
|
if self.event_end and self.event_end < self.event_start:
|
||||||
raise ValidationError("Das Ende des Events liegt vor dem Beginn.")
|
msg = "Das Ende des Events liegt vor dem Beginn."
|
||||||
if self.event_start and self.post_type not in ["E", "F"]:
|
raise ValidationError(msg, code="invalid_event_end")
|
||||||
raise ValidationError("Für diesen Post Typ ist kein Event Start zulässig.")
|
if self.event_start and self.post_type not in ["E", "F"]:
|
||||||
|
msg = "Für diesen Post Typ ist kein Event Start zulässig."
|
||||||
@property
|
raise ValidationError(msg, code="invalid_event_start")
|
||||||
def published(self):
|
|
||||||
return self.status == Status.PUBLIC
|
@property
|
||||||
|
def published(self):
|
||||||
|
return self.status == Status.PUBLIC
|
||||||
class News(Post):
|
|
||||||
objects = NewsManager()
|
|
||||||
|
class News(Post):
|
||||||
class Meta:
|
objects = NewsManager()
|
||||||
proxy = True
|
|
||||||
|
class Meta:
|
||||||
verbose_name = "News"
|
proxy = True
|
||||||
verbose_name_plural = "News"
|
|
||||||
|
verbose_name = "News"
|
||||||
def save(self, *args, **kwargs):
|
verbose_name_plural = "News"
|
||||||
if not self.post_type:
|
|
||||||
self.post_type = "N"
|
def save(self, *args, **kwargs):
|
||||||
|
if not self.post_type:
|
||||||
super().save(*args, **kwargs)
|
self.post_type = "N"
|
||||||
|
|
||||||
|
super().save(*args, **kwargs)
|
||||||
class Event(Post):
|
|
||||||
only_events = EventManager()
|
|
||||||
all_events = AllEventManager()
|
class Event(Post):
|
||||||
|
only_events = EventManager()
|
||||||
class Meta:
|
all_events = AllEventManager()
|
||||||
proxy = True
|
|
||||||
|
class Meta:
|
||||||
verbose_name = "Event"
|
proxy = True
|
||||||
verbose_name_plural = "Events"
|
|
||||||
|
verbose_name = "Event"
|
||||||
def __init__(self, *args, **kwargs):
|
verbose_name_plural = "Events"
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
self.post_type = "E"
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
def save(self, *args, **kwargs):
|
self.post_type = "E"
|
||||||
if not self.post_type:
|
|
||||||
self.post_type = "E"
|
def save(self, *args, **kwargs):
|
||||||
if not self.event_end:
|
if not self.post_type:
|
||||||
self.event_end = self.event_start + timedelta(hours=2)
|
self.post_type = "E"
|
||||||
|
if not self.event_end:
|
||||||
super().save(*args, **kwargs)
|
self.event_end = self.event_start + timedelta(hours=2)
|
||||||
|
|
||||||
def clean(self):
|
super().save(*args, **kwargs)
|
||||||
super().clean()
|
|
||||||
if not self.event_start:
|
def clean(self):
|
||||||
raise ValidationError("Das Datum des Events fehlt.")
|
super().clean()
|
||||||
|
if not self.event_start:
|
||||||
|
msg = "Das Datum des Events fehlt."
|
||||||
class FetMeeting(Event):
|
raise ValidationError(msg, code="missing_event_start")
|
||||||
objects = FetMeetingManager()
|
|
||||||
|
|
||||||
class Meta:
|
class FetMeeting(Event):
|
||||||
proxy = True
|
objects = FetMeetingManager()
|
||||||
|
|
||||||
verbose_name = "Fet Sitzung"
|
class Meta:
|
||||||
verbose_name_plural = "Fet Sitzungen"
|
proxy = True
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
verbose_name = "Fet Sitzung"
|
||||||
super().__init__(*args, **kwargs)
|
verbose_name_plural = "Fet Sitzungen"
|
||||||
self.post_type = "F"
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
def save(self, *args, **kwargs):
|
super().__init__(*args, **kwargs)
|
||||||
self.title = "Fachschaftssitzung"
|
self.post_type = "F"
|
||||||
if not self.slug:
|
|
||||||
self.slug = self.__get_slug()
|
def save(self, *args, **kwargs):
|
||||||
|
self.title = "Fachschaftssitzung"
|
||||||
if not ep_pad_exists(self.agenda_key) or self.agenda_key in self._possible_empty_key_value:
|
if not self.slug:
|
||||||
self.create_agenda_key()
|
self.slug = self.__get_slug()
|
||||||
if self.agenda_key:
|
|
||||||
self.has_agenda = True
|
if not ep_pad_exists(self.agenda_key) or self.agenda_key in self._possible_empty_key_value:
|
||||||
|
self.create_agenda_key()
|
||||||
if (
|
if self.agenda_key:
|
||||||
not ep_pad_exists(self.protocol_key)
|
self.has_agenda = True
|
||||||
or self.protocol_key in self._possible_empty_key_value
|
|
||||||
):
|
if (
|
||||||
self.create_protocol_key()
|
not ep_pad_exists(self.protocol_key)
|
||||||
if self.protocol_key:
|
or self.protocol_key in self._possible_empty_key_value
|
||||||
self.has_protocol = True
|
):
|
||||||
|
self.create_protocol_key()
|
||||||
if not self.post_type:
|
if self.protocol_key:
|
||||||
self.post_type = "F"
|
self.has_protocol = True
|
||||||
|
|
||||||
if not self.event_place:
|
if not self.post_type:
|
||||||
self.event_place = "FET"
|
self.post_type = "F"
|
||||||
|
|
||||||
# make duration 2 hours if not specified otherwise
|
if not self.event_place:
|
||||||
if not self.event_end:
|
self.event_place = "FET"
|
||||||
self.event_end = self.event_start + timedelta(hours=2)
|
|
||||||
|
# make duration 2 hours if not specified otherwise
|
||||||
# set FET Meeting always public
|
if not self.event_end:
|
||||||
self.status = Status.PUBLIC
|
self.event_end = self.event_start + timedelta(hours=2)
|
||||||
|
|
||||||
super().save(*args, **kwargs)
|
# set FET Meeting always public
|
||||||
|
self.status = Status.PUBLIC
|
||||||
def __get_slug(self) -> str:
|
|
||||||
slug = slugify(self.event_start.date()) + "-" + slugify("Fachschaftssitzung")
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
if Post.objects.filter(slug=slug).exists() and Post.objects.get(slug=slug).id != self.id:
|
def __get_slug(self) -> str:
|
||||||
raise ValidationError("Es existiert bereits eine Sitzung mit demselben Datum.")
|
slug = slugify(self.event_start.date()) + "-" + slugify("Fachschaftssitzung")
|
||||||
|
|
||||||
return slug
|
if Post.objects.filter(slug=slug).exists() and Post.objects.get(slug=slug).id != self.id:
|
||||||
|
msg = "Es existiert bereits eine Sitzung mit demselben Datum."
|
||||||
def clean(self):
|
logger.error(
|
||||||
super().clean()
|
"A fet meeting with same date (slug: %(slug)s) is already existing.",
|
||||||
if not self.slug:
|
extra={"slug": slug},
|
||||||
self.slug = self.__get_slug()
|
)
|
||||||
|
raise ValidationError(msg, code="duplicate_fet_meeting")
|
||||||
|
|
||||||
class FileUpload(models.Model):
|
return slug
|
||||||
title = models.CharField(verbose_name="Titel", max_length=200)
|
|
||||||
file_field = models.FileField(verbose_name="Dokument", upload_to="uploads/posts/files/")
|
def clean(self):
|
||||||
post = models.ForeignKey(Post, on_delete=models.CASCADE)
|
super().clean()
|
||||||
|
if not self.slug:
|
||||||
objects = models.Manager()
|
self.slug = self.__get_slug()
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.title
|
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
|
||||||
|
|||||||
@@ -1,36 +1,36 @@
|
|||||||
from haystack import indexes
|
from haystack import indexes
|
||||||
from html2text import html2text
|
from html2text import html2text
|
||||||
|
|
||||||
from .models import Post
|
from .models import Post
|
||||||
|
|
||||||
|
|
||||||
class PostIndex(indexes.SearchIndex, indexes.Indexable):
|
class PostIndex(indexes.SearchIndex, indexes.Indexable):
|
||||||
text = indexes.CharField(document=True)
|
text = indexes.CharField(document=True)
|
||||||
title = indexes.EdgeNgramField(model_attr="title")
|
title = indexes.EdgeNgramField(model_attr="title")
|
||||||
body = indexes.EdgeNgramField(model_attr="body", null=True)
|
body = indexes.EdgeNgramField(model_attr="body", null=True)
|
||||||
status = indexes.CharField(model_attr="status")
|
status = indexes.CharField(model_attr="status")
|
||||||
date = indexes.DateField()
|
date = indexes.DateField()
|
||||||
agenda = indexes.EdgeNgramField()
|
agenda = indexes.EdgeNgramField()
|
||||||
protocol = indexes.EdgeNgramField()
|
protocol = indexes.EdgeNgramField()
|
||||||
|
|
||||||
def get_model(self):
|
def get_model(self):
|
||||||
return Post
|
return Post
|
||||||
|
|
||||||
def index_queryset(self, using=None):
|
def index_queryset(self, using=None):
|
||||||
return self.get_model().objects.date_sorted(public=False)
|
return self.get_model().objects.date_sorted(public=False)
|
||||||
|
|
||||||
def prepare_date(self, obj):
|
def prepare_date(self, obj):
|
||||||
if obj.post_type == "N":
|
if obj.post_type == "E" or obj.post_type == "F":
|
||||||
return obj.public_date
|
return obj.event_start.date()
|
||||||
elif obj.post_type == "E" or obj.post_type == "F":
|
|
||||||
return obj.event_start.date()
|
return obj.public_date # obj.post_type == "N"
|
||||||
|
|
||||||
def prepare_agenda(self, obj):
|
def prepare_agenda(self, obj):
|
||||||
if obj.has_agenda and obj.agenda_html:
|
if obj.has_agenda and obj.agenda_html:
|
||||||
return html2text(obj.agenda_html)
|
return html2text(obj.agenda_html)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def prepare_protocol(self, obj):
|
def prepare_protocol(self, obj):
|
||||||
if obj.has_protocol and obj.protocol_html:
|
if obj.has_protocol and obj.protocol_html:
|
||||||
return html2text(obj.protocol_html)
|
return html2text(obj.protocol_html)
|
||||||
return None
|
return None
|
||||||
|
|||||||
@@ -117,15 +117,17 @@ class PostDetailView(DetailView):
|
|||||||
if not obj.published:
|
if not obj.published:
|
||||||
related_posts.remove(obj)
|
related_posts.remove(obj)
|
||||||
|
|
||||||
context = {
|
context.update(
|
||||||
"post": self.object,
|
{
|
||||||
"files": files,
|
"post": self.object,
|
||||||
"author": author,
|
"files": files,
|
||||||
"author_image": author_image,
|
"author": author,
|
||||||
"next": self.post_next(),
|
"author_image": author_image,
|
||||||
"prev": self.post_prev(),
|
"next": self.post_next(),
|
||||||
"related_posts": related_posts[:4],
|
"prev": self.post_prev(),
|
||||||
}
|
"related_posts": related_posts[:4],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
return context
|
return context
|
||||||
|
|
||||||
@@ -309,7 +311,8 @@ def show_pdf(request, html, filename):
|
|||||||
html = html[:idx] + rendered + html[idx:]
|
html = html[:idx] + rendered + html[idx:]
|
||||||
|
|
||||||
if not (pdf := render_to_pdf(html)):
|
if not (pdf := render_to_pdf(html)):
|
||||||
raise Http404("can't create pdf file.")
|
msg = "PDF konnte nicht erstellt werden."
|
||||||
|
raise Http404(msg)
|
||||||
|
|
||||||
response = HttpResponse(pdf, content_type="application/pdf")
|
response = HttpResponse(pdf, content_type="application/pdf")
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
|
from django.core.exceptions import ValidationError
|
||||||
from django.core.validators import FileExtensionValidator
|
from django.core.validators import FileExtensionValidator
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.forms import ValidationError
|
|
||||||
|
|
||||||
from .mails import send_mail_approved, send_mail_rejected, send_mail_returned
|
from .mails import send_mail_approved, send_mail_rejected, send_mail_returned
|
||||||
from .managers import RentalItemsManager
|
from .managers import RentalItemsManager
|
||||||
@@ -116,7 +116,8 @@ class Rental(models.Model):
|
|||||||
self.date_end = self.date_start
|
self.date_end = self.date_start
|
||||||
|
|
||||||
if self.date_start > self.date_end:
|
if self.date_start > self.date_end:
|
||||||
raise ValidationError("Das Abholdatum muss vor dem Rückgabedatum liegen.")
|
msg = "Das Abholdatum muss vor dem Rückgabedatum liegen."
|
||||||
|
raise ValidationError(msg, code="invalid_date_range")
|
||||||
|
|
||||||
def calc_total_deposit(self) -> int:
|
def calc_total_deposit(self) -> int:
|
||||||
total_deposit = 0
|
total_deposit = 0
|
||||||
|
|||||||
Reference in New Issue
Block a user