10 Commits

14 changed files with 533 additions and 492 deletions

1
.gitignore vendored
View File

@@ -19,3 +19,4 @@ flowbite
gallery/* gallery/*
tailwind tailwind
whoosh_index whoosh_index
databases/django

View File

@@ -136,6 +136,10 @@ ckeditor -> django-prose-editor
## Version History ## Version History
2.2.1
* Fix rental (view, pdf file, sending mail)
2.2.0 2.2.0
* Add rental * Add rental

Binary file not shown.

Binary file not shown.

View File

@@ -9,7 +9,7 @@ services:
depends_on: depends_on:
- django-homepage - django-homepage
volumes: volumes:
- files-volume:/usr/src/app/files - ./files:/usr/src/app/files
- ./gallery:/usr/src/app/files/uploads/gallery - ./gallery:/usr/src/app/files/uploads/gallery
- ./assets:/usr/src/app/assets:ro - ./assets:/usr/src/app/assets:ro
networks: networks:
@@ -24,8 +24,8 @@ services:
SECRET_KEY: "sae34sADfrFr89E!Gl#f!34hdjGR#!jopi4qFEr#4R56rT56zT2#wE1!feGp" SECRET_KEY: "sae34sADfrFr89E!Gl#f!34hdjGR#!jopi4qFEr#4R56rT56zT2#wE1!feGp"
MYSQL_USER: "user" MYSQL_USER: "user"
MYSQL_PASSWORD: "hgu" MYSQL_PASSWORD: "hgu"
ETHERPAD_GROUP: "g.snlbqn7S6ksRbom3" ETHERPAD_GROUP: "g.snlbqn7S6ksRbom3"
EMAIL_HOST_USER: "verleih@fet.at" EMAIL_HOST_USER: "verleih@fet.at"
EMAIL_HOST_PASSWORD: "" EMAIL_HOST_PASSWORD: ""
depends_on: depends_on:
mysql: mysql:
@@ -47,7 +47,7 @@ services:
retries: 20 retries: 20
etherpad: etherpad:
container_name: etherpad-container container_name: etherpad-container
image: etherpad/etherpad:1.8.17 image: etherpad/etherpad:2.5.2
# ports: # ports:
# - 9001:9001 # - 9001:9001
environment: environment:
@@ -57,8 +57,9 @@ services:
DB_NAME: etherpaddb DB_NAME: etherpaddb
DB_USER: user DB_USER: user
DB_PASS: "hgu" DB_PASS: "hgu"
DB_CHARSET: utf8 DB_CHARSET: "utf8mb4"
TRUST_PROXY: false TRUST_PROXY: false
AUTHENTICATION_METHOD: "apikey"
depends_on: depends_on:
etherpadsql: etherpadsql:
condition: "service_healthy" condition: "service_healthy"
@@ -84,7 +85,8 @@ services:
MYSQL_CHARSET: utf8 MYSQL_CHARSET: utf8
MYSQL_ALLOW_EMPTY_PASSWORD: "yes" MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
volumes: volumes:
- mysql-volume:/docker-entrypoint-initdb.d/ - ./inits/django:/docker-entrypoint-initdb.d/
- ./databases/django:/var/lib/mysql:Z
networks: networks:
- django-db-network - django-db-network
healthcheck: healthcheck:
@@ -102,7 +104,8 @@ services:
MYSQL_CHARSET: utf8 MYSQL_CHARSET: utf8
MYSQL_ALLOW_EMPTY_PASSWORD: "yes" MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
volumes: volumes:
- etherpad-mysql-volume:/docker-entrypoint-initdb.d/ - ./init/etherpad:/docker-entrypoint-initdb.d/
- ./databases/etherpad:/var/lib/mysql:Z
networks: networks:
- etherpad-db-network - etherpad-db-network
healthcheck: healthcheck:

View File

@@ -1,6 +1,6 @@
from django.utils.version import get_version from django.utils.version import get_version
VERSION = (2, 2, 0, "final", 0) VERSION = (2, 2, 1, "final", 0)
BUILD = 0 BUILD = 0
__version__ = get_version(VERSION) __version__ = get_version(VERSION)

View File

@@ -204,7 +204,12 @@ class BillAdmin(admin.ModelAdmin):
actions = ["make_cleared", "make_finished"] actions = ["make_cleared", "make_finished"]
autocomplete_fields = ["resolution"] autocomplete_fields = ["resolution"]
list_filter = ["status", "affiliation", "payer", BillPeriodeFilter] list_filter = ["status", "affiliation", "payer", BillPeriodeFilter]
search_fields = ["purpose", "bankdata__name"] search_fields = [
"purpose",
"bankdata__name",
"bill_creator__firstname",
"bill_creator__surname",
]
show_facets = admin.ShowFacets.ALWAYS show_facets = admin.ShowFacets.ALWAYS
ordering = ["-id"] ordering = ["-id"]

View File

@@ -6,6 +6,7 @@ from django import forms
from django.core.validators import ValidationError from django.core.validators 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 members.models import Member from members.models import Member
@@ -412,7 +413,7 @@ class BillAdminForm(forms.ModelForm):
self.fields["resolution"].queryset = self.fields["resolution"].queryset.filter( self.fields["resolution"].queryset = self.fields["resolution"].queryset.filter(
( (
Q(option=Resolution.Option.FINANCE) Q(option=Resolution.Option.FINANCE)
& Q(date__gt=datetime.datetime.now(tz=datetime.UTC).date() - relativedelta(years=2)) & Q(date__gt=timezone.now().date() - relativedelta(years=2))
) )
| Q(option=Resolution.Option.PERMANENT) | Q(option=Resolution.Option.PERMANENT)
) )

View File

@@ -1,8 +1,8 @@
import datetime
import io import io
from pathlib import Path from pathlib import Path
from django.core.files import File from django.core.files import File
from django.utils import timezone
from pypdf import PdfReader, PdfWriter from pypdf import PdfReader, PdfWriter
from pypdf.constants import FieldDictionaryAttributes as FA # noqa: N814 from pypdf.constants import FieldDictionaryAttributes as FA # noqa: N814
@@ -34,7 +34,7 @@ def generate_pdf(wiref):
) )
# Get budget year # Get budget year
today = datetime.datetime.now(tz=datetime.UTC).date() today = timezone.now().date()
if today.month < 7: if today.month < 7:
budget_year = f"{today.year - 1}-{today.year}" budget_year = f"{today.year - 1}-{today.year}"
else: else:

View File

@@ -1,275 +1,275 @@
from ckeditor_uploader.widgets import CKEditorUploadingWidget from ckeditor_uploader.widgets import CKEditorUploadingWidget
from django import forms from django import forms
from django.forms.widgets import CheckboxInput from django.forms.widgets import CheckboxInput
from django.utils.dates import MONTHS from django.utils.dates import MONTHS
from taggit.models import Tag from taggit.models import Tag
from .models import Event, FetMeeting, News, Post from .models import Event, FetMeeting, News, Post
class PostForm(forms.ModelForm): class PostForm(forms.ModelForm):
class Meta: class Meta:
model = Post model = Post
fields = [ fields = [
"title", "title",
"subtitle", "subtitle",
"tags", "tags",
"image", "image",
"body", "body",
"slug", "slug",
"author", "author",
"public_date", "public_date",
] ]
widgets = {"body": CKEditorUploadingWidget(config_name="default")} widgets = {"body": CKEditorUploadingWidget(config_name="default")}
class Media: class Media:
js = ( js = (
"js/auto_slug.js", # automatic slag completion via ajax "js/auto_slug.js", # automatic slag completion via ajax
"js/tag_completion.js", # to get a list for tag autocompletion via ajax "js/tag_completion.js", # to get a list for tag autocompletion via ajax
) )
class NewsForm(PostForm): class NewsForm(PostForm):
class Meta: class Meta:
model = News model = News
fields = "__all__" fields = "__all__"
help_texts = { help_texts = {
"tags": ( "tags": (
"Die Hashtags ohne '#' eintragen, und mit Komma kann man mehrere Tags anfügen." "Die Hashtags ohne '#' eintragen, und mit Komma kann man mehrere Tags anfügen."
), ),
"image": "Verwendbare Formate: ...", "image": "Verwendbare Formate: ...",
"is_pinned": ( "is_pinned": (
"Der Post soll als erster auf der Startseite angeheftet werden und sich " "Der Post soll als erster auf der Startseite angeheftet werden und sich "
"automatisch einen Monat nach der Veröffentlichung wieder lösen." "automatisch einen Monat nach der Veröffentlichung wieder lösen."
), ),
} }
labels = { labels = {
"title": "Titel", "title": "Titel",
"subtitle": "Untertitel", "subtitle": "Untertitel",
"image": "Hintergrundbild", "image": "Hintergrundbild",
"body": "Text", "body": "Text",
"slug": "Permalink", "slug": "Permalink",
"author": "Autor", "author": "Autor",
"public_date": "Veröffentlichung", "public_date": "Veröffentlichung",
"is_pinned": "Post anheften", "is_pinned": "Post anheften",
} }
widgets = {"body": CKEditorUploadingWidget(config_name="default")} widgets = {"body": CKEditorUploadingWidget(config_name="default")}
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
author_qs = self.fields["author"].queryset.order_by("username") author_qs = self.fields["author"].queryset.order_by("username")
self.fields["author"].queryset = author_qs self.fields["author"].queryset = author_qs
class EventForm(PostForm): class EventForm(PostForm):
class Meta: class Meta:
model = Event model = Event
fields = "__all__" fields = "__all__"
help_texts = { help_texts = {
"tags": ( "tags": (
"Die Hashtags ohne '#' eintragen, und mit Komma kann man mehrere Tags anfügen." "Die Hashtags ohne '#' eintragen, und mit Komma kann man mehrere Tags anfügen."
), ),
"image": "Verwendbare Formate: Bildformate", "image": "Verwendbare Formate: Bildformate",
"is_pinned": ( "is_pinned": (
"Dieses Event soll als erstes auf der Startseite angeheftet werden und sich " "Dieses Event soll als erstes auf der Startseite angeheftet werden und sich "
"automatisch einen Monat nach der Veröffentlichung wieder lösen." "automatisch ein Tag nach dem Eventende wieder lösen."
), ),
} }
labels = { labels = {
"title": "Titel", "title": "Titel",
"subtitle": "Untertitel", "subtitle": "Untertitel",
"image": "Hintergrundbild", "image": "Hintergrundbild",
"body": "Text", "body": "Text",
"event_start": "Start des Events", "event_start": "Start des Events",
"event_end": "Ende des Events", "event_end": "Ende des Events",
"event_place": "Ort des Events", "event_place": "Ort des Events",
"slug": "Permalink", "slug": "Permalink",
"author": "Autor", "author": "Autor",
"public_date": "Veröffentlichung", "public_date": "Veröffentlichung",
"is_pinned": "Event anheften", "is_pinned": "Event anheften",
} }
widgets = {"body": CKEditorUploadingWidget(config_name="default")} widgets = {"body": CKEditorUploadingWidget(config_name="default")}
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
author_qs = self.fields["author"].queryset.order_by("username") author_qs = self.fields["author"].queryset.order_by("username")
self.fields["author"].queryset = author_qs self.fields["author"].queryset = author_qs
self.fields["event_start"].required = True self.fields["event_start"].required = True
self.fields["event_end"].required = False self.fields["event_end"].required = False
if "event_place" in self.fields: if "event_place" in self.fields:
self.fields["event_place"].required = True self.fields["event_place"].required = True
class FetMeetingForm(PostForm): class FetMeetingForm(PostForm):
class Meta: class Meta:
model = FetMeeting model = FetMeeting
fields = ["event_start", "event_end", "event_place", "tags"] fields = ["event_start", "event_end", "event_place", "tags"]
labels = { labels = {
"event_start": "Start der Sitzung", "event_start": "Start der Sitzung",
"event_end": "Ende der Sitzung", "event_end": "Ende der Sitzung",
"event_place": "Ort der Sitzung", "event_place": "Ort der Sitzung",
} }
help_texts = { help_texts = {
"event_end": "Bei einer leeren Eingabe werden 2 Stunden zur Startzeit dazugezählt.", "event_end": "Bei einer leeren Eingabe werden 2 Stunden zur Startzeit dazugezählt.",
"tags": ( "tags": (
"Die Hashtags ohne '#' eintragen, und mit Komma kann man mehrere Tags anfügen." "Die Hashtags ohne '#' eintragen, und mit Komma kann man mehrere Tags anfügen."
), ),
} }
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
self.fields["event_start"].required = True self.fields["event_start"].required = True
self.fields["event_end"].required = False self.fields["event_end"].required = False
self.fields["event_place"].initial = "FET" self.fields["event_place"].initial = "FET"
tags = [] tags = []
tags.append(Tag()) tags.append(Tag())
tags[0].name = "fachschaft" tags[0].name = "fachschaft"
self.fields["tags"].initial = tags self.fields["tags"].initial = tags
class PostSearchForm(forms.Form): class PostSearchForm(forms.Form):
year_choices = [("", "Alle")] year_choices = [("", "Alle")]
month_choices = [("", "Alle")] + list(MONTHS.items()) month_choices = [("", "Alle")] + list(MONTHS.items())
year = forms.ChoiceField(label="Jahr", choices=year_choices, required=False) year = forms.ChoiceField(label="Jahr", choices=year_choices, required=False)
month = forms.ChoiceField(label="Monat", choices=month_choices, required=False) month = forms.ChoiceField(label="Monat", choices=month_choices, required=False)
compact_view = forms.BooleanField( compact_view = forms.BooleanField(
label="Kompakte Ansicht", label="Kompakte Ansicht",
required=False, required=False,
widget=CheckboxInput, widget=CheckboxInput,
) )
fet_meeting_only = forms.BooleanField( fet_meeting_only = forms.BooleanField(
label="nur FET Sitzungen", label="nur FET Sitzungen",
required=False, required=False,
widget=CheckboxInput, widget=CheckboxInput,
) )
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
try: try:
first_post = Post.objects.get_queryset().last() first_post = Post.objects.get_queryset().last()
last_post = Post.objects.get_queryset().first() last_post = Post.objects.get_queryset().first()
if first_post and last_post: if first_post and last_post:
years = range(last_post.date.year, first_post.date.year - 1, -1) years = range(last_post.date.year, first_post.date.year - 1, -1)
year_choices = [("", "Alle")] + [(i, i) for i in years] year_choices = [("", "Alle")] + [(i, i) for i in years]
self.fields["year"].choices = year_choices self.fields["year"].choices = year_choices
except Exception: except Exception:
pass pass
class NewsUpdateForm(forms.ModelForm): class NewsUpdateForm(forms.ModelForm):
class Meta: class Meta:
model = News model = News
fields = [ fields = [
"title", "title",
"status", "status",
"body", "body",
] ]
labels = { labels = {
"title": "Titel", "title": "Titel",
"image": "Hintergrundbild", "image": "Hintergrundbild",
"body": "Text", "body": "Text",
} }
widgets = {"body": CKEditorUploadingWidget(config_name="default")} widgets = {"body": CKEditorUploadingWidget(config_name="default")}
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
self.fields["title"].autofocus = True self.fields["title"].autofocus = True
class EventUpdateForm(forms.ModelForm): class EventUpdateForm(forms.ModelForm):
class Meta: class Meta:
model = Event model = Event
fields = [ fields = [
"title", "title",
"status", "status",
"event_start", "event_start",
"event_end", "event_end",
"event_place", "event_place",
] ]
labels = { labels = {
"title": "Titel", "title": "Titel",
"event_start": "Start des Events", "event_start": "Start des Events",
"event_end": "Ende des Events", "event_end": "Ende des Events",
"event_place": "Ort des Events", "event_place": "Ort des Events",
} }
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
self.fields["event_start"].required = True self.fields["event_start"].required = True
self.fields["event_start"].autofocus = True self.fields["event_start"].autofocus = True
self.fields["event_end"].required = False self.fields["event_end"].required = False
if "event_place" in self.fields: if "event_place" in self.fields:
self.fields["event_place"].required = True self.fields["event_place"].required = True
class FetMeetingCreateForm(forms.ModelForm): class FetMeetingCreateForm(forms.ModelForm):
class Meta: class Meta:
model = FetMeeting model = FetMeeting
fields = ["event_start", "event_end", "event_place"] fields = ["event_start", "event_end", "event_place"]
help_texts = { help_texts = {
"event_end": "Bei einer leeren Eingabe werden 2 Stunden zur Startzeit dazugezählt.", "event_end": "Bei einer leeren Eingabe werden 2 Stunden zur Startzeit dazugezählt.",
} }
labels = { labels = {
"event_start": "Start der Sitzung", "event_start": "Start der Sitzung",
"event_end": "Ende der Sitzung", "event_end": "Ende der Sitzung",
"event_place": "Ort der Sitzung", "event_place": "Ort der Sitzung",
} }
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
self.fields["event_start"].required = True self.fields["event_start"].required = True
self.fields["event_start"].autofocus = True self.fields["event_start"].autofocus = True
self.fields["event_end"].required = False self.fields["event_end"].required = False
self.fields["event_place"].initial = "FET" self.fields["event_place"].initial = "FET"
class FetMeetingUpdateForm(forms.ModelForm): class FetMeetingUpdateForm(forms.ModelForm):
class Meta: class Meta:
model = FetMeeting model = FetMeeting
fields = ["event_start", "event_end", "event_place"] fields = ["event_start", "event_end", "event_place"]
labels = { labels = {
"event_start": "Start der Sitzung", "event_start": "Start der Sitzung",
"event_end": "Ende der Sitzung", "event_end": "Ende der Sitzung",
"event_place": "Ort der Sitzung", "event_place": "Ort der Sitzung",
} }
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
self.fields["event_start"].required = True self.fields["event_start"].required = True
self.fields["event_start"].autofocus = True self.fields["event_start"].autofocus = True
self.fields["event_end"].required = False self.fields["event_end"].required = False
self.fields["event_place"].initial = "FET" self.fields["event_place"].initial = "FET"

View File

@@ -1,201 +1,208 @@
import datetime import calendar
import datetime
from django.db import models
from django.db.models import Case, Q, When from django.db import models
from django.db.models import Case, Q, When
from .choices import PostType, Status from django.utils import timezone
from .choices import PostType, Status
class PublishedManager(models.Manager):
def published(self, public=True):
""" class PublishedManager(models.Manager):
publish all posts with status 'PUBLIC' def published(self, public=True):
""" """
return self.get_queryset().filter(status=Status.PUBLIC) if public else self.get_queryset() publish all posts with status 'PUBLIC'
"""
def published_all(self, public=True): return self.get_queryset().filter(status=Status.PUBLIC) if public else self.get_queryset()
"""
publish all posts with status 'PUBLIC' and 'ONLY_INTERN' def published_all(self, public=True):
""" """
return ( publish all posts with status 'PUBLIC' and 'ONLY_INTERN'
self.get_queryset().filter(~Q(status=Status.DRAFT)) if public else self.get_queryset() """
) return (
self.get_queryset().filter(~Q(status=Status.DRAFT)) if public else self.get_queryset()
)
class PostManager(PublishedManager, models.Manager):
def get_queryset(self):
qs = ( class PostManager(PublishedManager, models.Manager):
super() def get_queryset(self):
.get_queryset() qs = (
.annotate( super()
date=Case( .get_queryset()
When(post_type=PostType.NEWS, then="public_date"), .annotate(
When(post_type=PostType.EVENT, then="event_start__date"), date=Case(
When(post_type=PostType.FETMEETING, then="event_start__date"), When(post_type=PostType.NEWS, then="public_date"),
), When(post_type=PostType.EVENT, then="event_start__date"),
) When(post_type=PostType.FETMEETING, then="event_start__date"),
) ),
return qs.order_by("-date", "-id") )
)
def date_sorted(self, public=True): return qs.order_by("-date", "-id")
return self.published(public)
def date_sorted(self, public=True):
def date_filter( return self.published(public)
self,
public=True, def date_filter(
year=None, self,
month=None, public=True,
fet_meeting_only=None, year=None,
): month=None,
qs_filter = Q() fet_meeting_only=None,
):
if fet_meeting_only: qs_filter = Q()
qs_filter &= Q(post_type=PostType.FETMEETING)
if fet_meeting_only:
if year: qs_filter &= Q(post_type=PostType.FETMEETING)
qs_filter &= Q(date__year=year)
if month: if year:
qs_filter &= Q(date__month=month) qs_filter &= Q(date__year=year)
if month:
return self.published(public).filter(qs_filter) qs_filter &= Q(date__month=month)
return self.published(public).filter(qs_filter)
class ArticleManager(PublishedManager, models.Manager):
"""
Provide a query set only for "Article" class ArticleManager(PublishedManager, models.Manager):
regular fet meetings should not be contained in the news stream """
""" Provide a query set only for "Article"
regular fet meetings should not be contained in the news stream
def get_queryset(self): """
qs = super().get_queryset().filter(Q(post_type=PostType.NEWS) | Q(post_type=PostType.EVENT))
qs = qs.annotate( def get_queryset(self):
date=Case( qs = super().get_queryset().filter(Q(post_type=PostType.NEWS) | Q(post_type=PostType.EVENT))
When(post_type=PostType.NEWS, then="public_date"), qs = qs.annotate(
When(post_type=PostType.EVENT, then="event_start__date"), date=Case(
), When(post_type=PostType.NEWS, then="public_date"),
) When(post_type=PostType.EVENT, then="event_start__date"),
return qs.order_by("-date", "-id") ),
)
def date_sorted(self, public=True): return qs.order_by("-date", "-id")
return self.published(public)
def date_sorted(self, public=True):
def pinned(self, public=True): return self.published(public)
# Get date for pinned news that is max 1 month old.
post_date = datetime.datetime.now(tz=datetime.UTC).date() def pinned(self, public=True):
__month = post_date.month # Get date for pinned news that is max 1 month old.
__year = post_date.year post_date = timezone.now().date()
_day = post_date.day
if __month != 1: _month = post_date.month
__month -= 1 _year = post_date.year
else:
# If the current month is January, you get the date from December of previous year. if _month != 1:
__month = 12 _month -= 1
__year -= 1 else:
# If the current month is January, you get the date from December of previous year.
post_date = post_date.replace(year=__year, month=__month) _month = 12
_year -= 1
# Get date for event posts that is max 1 day old.
event_date = datetime.datetime.now(tz=datetime.UTC).date() - datetime.timedelta(1) # Clamp day to last day of target month (handles 30/31 and Feb)
last_day = calendar.monthrange(_year, _month)[1]
return ( safe_day = min(_day, last_day)
self.published(public)
.filter( post_date = post_date.replace(year=_year, month=_month, day=safe_day)
Q(is_pinned=True)
& ( # Get date for event posts that is max 1 day old.
(Q(post_type=PostType.NEWS) & Q(public_date__gt=post_date)) event_date = timezone.now().date() - datetime.timedelta(1)
| (Q(post_type=PostType.EVENT) & Q(event_end__date__gt=event_date))
) return (
) self.published(public)
.first() .filter(
) Q(is_pinned=True)
& (
(Q(post_type=PostType.NEWS) & Q(public_date__gt=post_date))
class NewsManager(PublishedManager, models.Manager): | (Q(post_type=PostType.EVENT) & Q(event_end__date__gt=event_date))
""" )
Provide a query set only for "News" )
""" .first()
)
def get_queryset(self):
qs = super().get_queryset().filter(post_type=PostType.NEWS)
qs = qs.annotate( class NewsManager(PublishedManager, models.Manager):
date=Case( """
When(post_type=PostType.NEWS, then="public_date"), Provide a query set only for "News"
), """
)
return qs.order_by("-date") def get_queryset(self):
qs = super().get_queryset().filter(post_type=PostType.NEWS)
qs = qs.annotate(
class AllEventManager(PublishedManager, models.Manager): date=Case(
""" When(post_type=PostType.NEWS, then="public_date"),
Provide a query set for all events ("Event" and "Fet Meeting") ),
""" )
return qs.order_by("-date")
def get_queryset(self):
qs = (
super() class AllEventManager(PublishedManager, models.Manager):
.get_queryset() """
.filter(Q(post_type=PostType.EVENT) | Q(post_type=PostType.FETMEETING)) Provide a query set for all events ("Event" and "Fet Meeting")
) """
qs = qs.annotate(
date=Case( def get_queryset(self):
When(post_type=PostType.EVENT, then="event_start__date"), qs = (
When(post_type=PostType.FETMEETING, then="event_start__date"), super()
), .get_queryset()
) .filter(Q(post_type=PostType.EVENT) | Q(post_type=PostType.FETMEETING))
return qs.order_by("-date") )
qs = qs.annotate(
def future_events(self, public=True): date=Case(
date_today = datetime.datetime.now(tz=datetime.UTC).date() When(post_type=PostType.EVENT, then="event_start__date"),
qs = self.published(public).filter(event_start__gt=date_today) When(post_type=PostType.FETMEETING, then="event_start__date"),
return qs.reverse() ),
)
return qs.order_by("-date")
class EventManager(PublishedManager, models.Manager):
""" def future_events(self, public=True):
Provide a query set only for "Events" date_today = timezone.now().date()
regular fet meetings should not be contained in the news stream qs = self.published(public).filter(event_start__gt=date_today)
""" return qs.reverse()
def get_queryset(self):
qs = super().get_queryset().filter(post_type=PostType.EVENT) class EventManager(PublishedManager, models.Manager):
qs = qs.annotate( """
date=Case( Provide a query set only for "Events"
When(post_type=PostType.EVENT, then="event_start__date"), regular fet meetings should not be contained in the news stream
), """
)
return qs.order_by("-date") def get_queryset(self):
qs = super().get_queryset().filter(post_type=PostType.EVENT)
def future_events(self, public=True): qs = qs.annotate(
date_today = datetime.datetime.now(tz=datetime.UTC).date() date=Case(
qs = self.published(public).filter(event_start__gt=date_today) When(post_type=PostType.EVENT, then="event_start__date"),
return qs.reverse() ),
)
def past_events(self, public=True): return qs.order_by("-date")
date_today = datetime.datetime.now(tz=datetime.UTC).date()
qs = self.published(public).filter(event_start__lt=date_today) def future_events(self, public=True):
return qs date_today = timezone.now().date()
qs = self.published(public).filter(event_start__gt=date_today)
return qs.reverse()
class FetMeetingManager(PublishedManager, models.Manager):
""" def past_events(self, public=True):
Provide a query set only for "Fet Meeting" date_today = timezone.now().date()
""" qs = self.published(public).filter(event_start__lt=date_today)
return qs
def get_queryset(self):
qs = super().get_queryset().filter(post_type=PostType.FETMEETING)
qs = qs.annotate( class FetMeetingManager(PublishedManager, models.Manager):
date=Case( """
When(post_type=PostType.FETMEETING, then="event_start__date"), Provide a query set only for "Fet Meeting"
), """
)
return qs.order_by("-date") def get_queryset(self):
qs = super().get_queryset().filter(post_type=PostType.FETMEETING)
def future_events(self): qs = qs.annotate(
date_today = datetime.datetime.now(tz=datetime.UTC).date() date=Case(
qs = self.published().filter(event_start__gt=date_today) When(post_type=PostType.FETMEETING, then="event_start__date"),
return qs.reverse() ),
)
def past_events(self): return qs.order_by("-date")
date_today = datetime.datetime.now(tz=datetime.UTC).date()
qs = self.published().filter(event_start__lt=date_today) def future_events(self):
return qs date_today = timezone.now().date()
qs = self.published().filter(event_start__gt=date_today)
return qs.reverse()
def past_events(self):
date_today = timezone.now().date()
qs = self.published().filter(event_start__lt=date_today)
return qs

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.2.7 on 2025-10-30 14:40
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('rental', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='rental',
name='intern',
field=models.BooleanField(default=False, verbose_name='Interner Verleih'),
),
]

View File

@@ -6,6 +6,7 @@ from django.db.models import Q
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
from django.shortcuts import render from django.shortcuts import render
from django.urls import reverse from django.urls import reverse
from django.utils import timezone
from django.views.generic import ListView, TemplateView from django.views.generic import ListView, TemplateView
from django.views.generic.detail import DetailView from django.views.generic.detail import DetailView
from django.views.generic.edit import CreateView from django.views.generic.edit import CreateView
@@ -64,7 +65,7 @@ def _get_display_period(view_type: str, period: str) -> date:
) )
except Exception: except Exception:
# Get first day of the current week # Get first day of the current week
today = datetime.datetime.now(tz=datetime.UTC).date() today = timezone.now().date()
display_date = today - datetime.timedelta(days=today.weekday()) display_date = today - datetime.timedelta(days=today.weekday())
# Handle month view # Handle month view
@@ -76,7 +77,7 @@ def _get_display_period(view_type: str, period: str) -> date:
) )
except Exception: except Exception:
# Get the first day of the current month # Get the first day of the current month
display_date = datetime.datetime.now(tz=datetime.UTC).date().replace(day=1) display_date = timezone.now().date().replace(day=1)
return display_date return display_date
@@ -170,7 +171,7 @@ class RentalListView(ListView):
context["week_num"] = week_num context["week_num"] = week_num
# Get the current date for the calendar # Get the current date for the calendar
context["today"] = datetime.datetime.now(tz=datetime.UTC).date() context["today"] = timezone.now().date()
# Add rental items to the context for the filter # Add rental items to the context for the filter
context["rentalitems"] = RentalItem.objects.all() context["rentalitems"] = RentalItem.objects.all()
@@ -234,7 +235,7 @@ class RentalCreateView(CreateView):
selected_items = sorted(items, key=lambda x: x.name.lower()) selected_items = sorted(items, key=lambda x: x.name.lower())
for i in range(0, len(selected_items), RENTAL_ITEMS_MAX): for i in range(0, len(selected_items), RENTAL_ITEMS_MAX):
batch = selected_items[i:i + RENTAL_ITEMS_MAX] batch = selected_items[i : i + RENTAL_ITEMS_MAX]
# Clone base_rental — copying all its field values # Clone base_rental — copying all its field values
new_rental = Rental.objects.create( new_rental = Rental.objects.create(
@@ -266,6 +267,7 @@ class RentalCreateView(CreateView):
def get_success_url(self): def get_success_url(self):
return reverse("rental:rental_create_done") return reverse("rental:rental_create_done")
class RentalCreateDoneView(TemplateView): class RentalCreateDoneView(TemplateView):
template_name = "rental/create_done.html" template_name = "rental/create_done.html"