From a3b252c9bee11cde036f18a0db40f3c94e45a487 Mon Sep 17 00:00:00 2001 From: Patrick Mayr Date: Fri, 31 Oct 2025 13:30:43 +0100 Subject: [PATCH] Fix: Find the current last day; Use the correct get now time function --- fet2020/finance/forms.py | 3 +- fet2020/finance/utils.py | 4 +- fet2020/posts/managers.py | 409 +++++++++++++++++++------------------- fet2020/rental/views.py | 10 +- 4 files changed, 218 insertions(+), 208 deletions(-) diff --git a/fet2020/finance/forms.py b/fet2020/finance/forms.py index 4968750b..77003fec 100644 --- a/fet2020/finance/forms.py +++ b/fet2020/finance/forms.py @@ -6,6 +6,7 @@ from django import forms from django.core.validators import ValidationError from django.db.models import Count, Q from django.forms import DateInput +from django.utils import timezone from members.models import Member @@ -412,7 +413,7 @@ class BillAdminForm(forms.ModelForm): self.fields["resolution"].queryset = self.fields["resolution"].queryset.filter( ( 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) ) diff --git a/fet2020/finance/utils.py b/fet2020/finance/utils.py index 270f5765..32121e62 100644 --- a/fet2020/finance/utils.py +++ b/fet2020/finance/utils.py @@ -1,8 +1,8 @@ -import datetime import io from pathlib import Path from django.core.files import File +from django.utils import timezone from pypdf import PdfReader, PdfWriter from pypdf.constants import FieldDictionaryAttributes as FA # noqa: N814 @@ -34,7 +34,7 @@ def generate_pdf(wiref): ) # Get budget year - today = datetime.datetime.now(tz=datetime.UTC).date() + today = timezone.now().date() if today.month < 7: budget_year = f"{today.year - 1}-{today.year}" else: diff --git a/fet2020/posts/managers.py b/fet2020/posts/managers.py index db874880..ff4d40b1 100644 --- a/fet2020/posts/managers.py +++ b/fet2020/posts/managers.py @@ -1,201 +1,208 @@ -import datetime - -from django.db import models -from django.db.models import Case, Q, When - -from .choices import PostType, Status - - -class PublishedManager(models.Manager): - def published(self, public=True): - """ - publish all posts with status 'PUBLIC' - """ - return self.get_queryset().filter(status=Status.PUBLIC) if public else self.get_queryset() - - def published_all(self, public=True): - """ - publish all posts with status 'PUBLIC' and 'ONLY_INTERN' - """ - 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 = ( - super() - .get_queryset() - .annotate( - date=Case( - 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 self.published(public) - - def date_filter( - self, - public=True, - year=None, - month=None, - fet_meeting_only=None, - ): - qs_filter = Q() - - if fet_meeting_only: - qs_filter &= Q(post_type=PostType.FETMEETING) - - if year: - qs_filter &= Q(date__year=year) - if month: - 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" - 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( - 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 self.published(public) - - def pinned(self, public=True): - # Get date for pinned news that is max 1 month old. - post_date = datetime.datetime.now(tz=datetime.UTC).date() - __month = post_date.month - __year = post_date.year - - if __month != 1: - __month -= 1 - else: - # If the current month is January, you get the date from December of previous year. - __month = 12 - __year -= 1 - - post_date = post_date.replace(year=__year, month=__month) - - # Get date for event posts that is max 1 day old. - event_date = datetime.datetime.now(tz=datetime.UTC).date() - datetime.timedelta(1) - - return ( - self.published(public) - .filter( - Q(is_pinned=True) - & ( - (Q(post_type=PostType.NEWS) & Q(public_date__gt=post_date)) - | (Q(post_type=PostType.EVENT) & Q(event_end__date__gt=event_date)) - ) - ) - .first() - ) - - -class NewsManager(PublishedManager, models.Manager): - """ - Provide a query set only for "News" - """ - - def get_queryset(self): - qs = super().get_queryset().filter(post_type=PostType.NEWS) - qs = qs.annotate( - date=Case( - When(post_type=PostType.NEWS, then="public_date"), - ), - ) - return qs.order_by("-date") - - -class AllEventManager(PublishedManager, models.Manager): - """ - Provide a query set for all events ("Event" and "Fet Meeting") - """ - - def get_queryset(self): - qs = ( - super() - .get_queryset() - .filter(Q(post_type=PostType.EVENT) | Q(post_type=PostType.FETMEETING)) - ) - qs = qs.annotate( - date=Case( - When(post_type=PostType.EVENT, then="event_start__date"), - When(post_type=PostType.FETMEETING, then="event_start__date"), - ), - ) - return qs.order_by("-date") - - def future_events(self, public=True): - date_today = datetime.datetime.now(tz=datetime.UTC).date() - qs = self.published(public).filter(event_start__gt=date_today) - return qs.reverse() - - -class EventManager(PublishedManager, models.Manager): - """ - Provide a query set only for "Events" - regular fet meetings should not be contained in the news stream - """ - - def get_queryset(self): - qs = super().get_queryset().filter(post_type=PostType.EVENT) - qs = qs.annotate( - date=Case( - When(post_type=PostType.EVENT, then="event_start__date"), - ), - ) - return qs.order_by("-date") - - def future_events(self, public=True): - date_today = datetime.datetime.now(tz=datetime.UTC).date() - qs = self.published(public).filter(event_start__gt=date_today) - return qs.reverse() - - def past_events(self, public=True): - date_today = datetime.datetime.now(tz=datetime.UTC).date() - qs = self.published(public).filter(event_start__lt=date_today) - return qs - - -class FetMeetingManager(PublishedManager, models.Manager): - """ - Provide a query set only for "Fet Meeting" - """ - - def get_queryset(self): - qs = super().get_queryset().filter(post_type=PostType.FETMEETING) - qs = qs.annotate( - date=Case( - When(post_type=PostType.FETMEETING, then="event_start__date"), - ), - ) - return qs.order_by("-date") - - def future_events(self): - date_today = datetime.datetime.now(tz=datetime.UTC).date() - qs = self.published().filter(event_start__gt=date_today) - return qs.reverse() - - def past_events(self): - date_today = datetime.datetime.now(tz=datetime.UTC).date() - qs = self.published().filter(event_start__lt=date_today) - return qs +import calendar +import datetime + +from django.db import models +from django.db.models import Case, Q, When +from django.utils import timezone + +from .choices import PostType, Status + + +class PublishedManager(models.Manager): + def published(self, public=True): + """ + publish all posts with status 'PUBLIC' + """ + return self.get_queryset().filter(status=Status.PUBLIC) if public else self.get_queryset() + + def published_all(self, public=True): + """ + publish all posts with status 'PUBLIC' and 'ONLY_INTERN' + """ + 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 = ( + super() + .get_queryset() + .annotate( + date=Case( + 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 self.published(public) + + def date_filter( + self, + public=True, + year=None, + month=None, + fet_meeting_only=None, + ): + qs_filter = Q() + + if fet_meeting_only: + qs_filter &= Q(post_type=PostType.FETMEETING) + + if year: + qs_filter &= Q(date__year=year) + if month: + 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" + 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( + 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 self.published(public) + + def pinned(self, public=True): + # Get date for pinned news that is max 1 month old. + post_date = timezone.now().date() + _day = post_date.day + _month = post_date.month + _year = post_date.year + + if _month != 1: + _month -= 1 + else: + # If the current month is January, you get the date from December of previous year. + _month = 12 + _year -= 1 + + # Clamp day to last day of target month (handles 30/31 and Feb) + last_day = calendar.monthrange(_year, _month)[1] + safe_day = min(_day, last_day) + + post_date = post_date.replace(year=_year, month=_month, day=safe_day) + + # Get date for event posts that is max 1 day old. + event_date = timezone.now().date() - datetime.timedelta(1) + + return ( + self.published(public) + .filter( + Q(is_pinned=True) + & ( + (Q(post_type=PostType.NEWS) & Q(public_date__gt=post_date)) + | (Q(post_type=PostType.EVENT) & Q(event_end__date__gt=event_date)) + ) + ) + .first() + ) + + +class NewsManager(PublishedManager, models.Manager): + """ + Provide a query set only for "News" + """ + + def get_queryset(self): + qs = super().get_queryset().filter(post_type=PostType.NEWS) + qs = qs.annotate( + date=Case( + When(post_type=PostType.NEWS, then="public_date"), + ), + ) + return qs.order_by("-date") + + +class AllEventManager(PublishedManager, models.Manager): + """ + Provide a query set for all events ("Event" and "Fet Meeting") + """ + + def get_queryset(self): + qs = ( + super() + .get_queryset() + .filter(Q(post_type=PostType.EVENT) | Q(post_type=PostType.FETMEETING)) + ) + qs = qs.annotate( + date=Case( + When(post_type=PostType.EVENT, then="event_start__date"), + When(post_type=PostType.FETMEETING, then="event_start__date"), + ), + ) + return qs.order_by("-date") + + def future_events(self, public=True): + date_today = timezone.now().date() + qs = self.published(public).filter(event_start__gt=date_today) + return qs.reverse() + + +class EventManager(PublishedManager, models.Manager): + """ + Provide a query set only for "Events" + regular fet meetings should not be contained in the news stream + """ + + def get_queryset(self): + qs = super().get_queryset().filter(post_type=PostType.EVENT) + qs = qs.annotate( + date=Case( + When(post_type=PostType.EVENT, then="event_start__date"), + ), + ) + return qs.order_by("-date") + + def future_events(self, public=True): + date_today = timezone.now().date() + qs = self.published(public).filter(event_start__gt=date_today) + return qs.reverse() + + def past_events(self, public=True): + date_today = timezone.now().date() + qs = self.published(public).filter(event_start__lt=date_today) + return qs + + +class FetMeetingManager(PublishedManager, models.Manager): + """ + Provide a query set only for "Fet Meeting" + """ + + def get_queryset(self): + qs = super().get_queryset().filter(post_type=PostType.FETMEETING) + qs = qs.annotate( + date=Case( + When(post_type=PostType.FETMEETING, then="event_start__date"), + ), + ) + return qs.order_by("-date") + + def future_events(self): + 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 diff --git a/fet2020/rental/views.py b/fet2020/rental/views.py index c4808b7c..4815d588 100644 --- a/fet2020/rental/views.py +++ b/fet2020/rental/views.py @@ -6,6 +6,7 @@ from django.db.models import Q from django.http import HttpResponseRedirect from django.shortcuts import render from django.urls import reverse +from django.utils import timezone from django.views.generic import ListView, TemplateView from django.views.generic.detail import DetailView from django.views.generic.edit import CreateView @@ -64,7 +65,7 @@ def _get_display_period(view_type: str, period: str) -> date: ) except Exception: # 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()) # Handle month view @@ -76,7 +77,7 @@ def _get_display_period(view_type: str, period: str) -> date: ) except Exception: # 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 @@ -170,7 +171,7 @@ class RentalListView(ListView): context["week_num"] = week_num # 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 context["rentalitems"] = RentalItem.objects.all() @@ -234,7 +235,7 @@ class RentalCreateView(CreateView): selected_items = sorted(items, key=lambda x: x.name.lower()) 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 new_rental = Rental.objects.create( @@ -266,6 +267,7 @@ class RentalCreateView(CreateView): def get_success_url(self): return reverse("rental:rental_create_done") + class RentalCreateDoneView(TemplateView): template_name = "rental/create_done.html"