From e10fa77c3ac69008b1998b897c6453a2abbc2e1e Mon Sep 17 00:00:00 2001 From: Patrick Mayr Date: Wed, 29 Oct 2025 22:29:10 +0100 Subject: [PATCH] add week view --- fet2020/rental/views.py | 241 +++++++++---- fet2020/templates/rental/calendar.html | 473 +++++++++++++++---------- 2 files changed, 453 insertions(+), 261 deletions(-) diff --git a/fet2020/rental/views.py b/fet2020/rental/views.py index f6116094..022cb7f1 100644 --- a/fet2020/rental/views.py +++ b/fet2020/rental/views.py @@ -1,5 +1,6 @@ import calendar import datetime +from datetime import date from django.db.models import Q from django.shortcuts import render @@ -12,87 +13,176 @@ from .forms import RentalCreateForm from .models import Rental, RentalItem +def _calc_days_from_current_month(month: date) -> list: + last_day_of_month = month.replace(day=calendar.monthrange(month.year, month.month)[1]) + + return [month + datetime.timedelta(days=i) for i in range((last_day_of_month - month).days + 1)] + + +def _calc_days_from_prev_month(month: date) -> list: + days_of_prev_period = [] + + if month.weekday() != calendar.MONDAY: + for i in range(1, 7): + day = month + datetime.timedelta(days=-i) + days_of_prev_period.append(day) + if day.weekday() == calendar.MONDAY: + break + + return sorted(days_of_prev_period) + + +def _calc_days_from_next_month(month: date) -> list: + last_day_of_month = month.replace(day=calendar.monthrange(month.year, month.month)[1]) + days_of_next_period = [] + + if last_day_of_month.weekday() != calendar.SUNDAY: + for i in range(1, 7): + day = last_day_of_month + datetime.timedelta(days=i) + days_of_next_period.append(day) + if day.weekday() == calendar.SUNDAY: + break + + return days_of_next_period + + +def _get_display_period(view_type: str, period: str) -> tuple[date, bool]: + display_date: date = None + changed: bool = False # Indicate if the view has changed (used to reset filters) + + # Handle week view + if view_type == "week": + try: + # Parse the requested calendar week + display_date = ( + datetime.datetime.strptime(f"{period}-1", "%G-KW%V-%u") + .replace(tzinfo=datetime.UTC) + .date() + ) + except Exception: + # Get first day of the current week + today = datetime.datetime.now(tz=datetime.UTC).date() + display_date = today - datetime.timedelta(days=today.weekday()) + + changed = True + + # Handle month view + else: + try: + # Parse the requested month + display_date = ( + datetime.datetime.strptime(period, "%Y-%m").replace(tzinfo=datetime.UTC).date() + ) + except Exception: + # Get the first day of the current month + display_date = datetime.datetime.now(tz=datetime.UTC).date().replace(day=1) + + changed = True + + return display_date, changed + + +def _get_rental_filters(view_type: str, filters: list) -> list: + if not filters: + items = RentalItem.objects.all() + # if view_type == "month": + # items = items[:4] + filters = [item.name for item in items] + # else: + # if view_type == "month": + # filters = filters[:4] + + return filters + + class RentalListView(ListView): model = Rental template_name = "rental/calendar.html" - # Month is the month displayed in the calendar (and should be the first day of the month) - month = None - # Rental items to filter - rentalitem_filters = [] + def __init__(self): + super().__init__() + # Current display period and view settings + self.display_period = None + self.view_type = "month" # Default view + self.rentalitem_filters = [] def get(self, request, *args, **kwargs): - # Get the rental items from the filter (max. 4) - self.rentalitem_filters = request.GET.getlist("rentalitems", [])[:4] - if not self.rentalitem_filters: - for rentalitem in RentalItem.objects.all()[:4]: - self.rentalitem_filters.append(rentalitem.name) + # Get view parameters from request + _view_type = request.GET.get("view_type", "week") + _period = request.GET.get("period_value", "") + _prev_period = request.GET.get("prev_period", "") + _next_period = request.GET.get("next_period", "") - # Get the displayed month from the request - _date_str = request.GET.get("month", "") - if _date_str: - self.month = ( - datetime.datetime.strptime(_date_str, "%Y-%m").replace(tzinfo=datetime.UTC).date() - ) - else: - self.month = datetime.datetime.now(tz=datetime.UTC).date().replace(day=1) + if _prev_period: + _period = _prev_period + elif _next_period: + _period = _next_period + + self.view_type = _view_type + self.display_period, changed = _get_display_period(_view_type, _period) + + _filters = request.GET.getlist("rentalitems", []) + # Reset filter if switched to week view + # if changed: + # _filters = [] + self.rentalitem_filters = _get_rental_filters(_view_type, _filters) + + # Update request.GET + _request_get_list = request.GET.copy() + _request_get_list.pop("prev_period", None) + _request_get_list.pop("next_period", None) + _request_get_list["period_value"] = _period + request.GET = _request_get_list return super().get(request, *args, **kwargs) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - # Calculate the day of the previous month from Monday - days_of_prev_month = [] + if self.view_type != "week": + # Add the displayed, previous and next month + context["view_period"] = self.display_period + context["prev_period"] = self.display_period + datetime.timedelta(days=-1) + context["next_period"] = self.display_period + datetime.timedelta( + days=calendar.monthrange(self.display_period.year, self.display_period.month)[1] + 1 + ) - if self.month.weekday() != calendar.MONDAY: - for i in range(1, 7): - day = self.month + datetime.timedelta(days=-i) - days_of_prev_month.append(day) - if day.weekday() == calendar.MONDAY: - break + # Add the days of the displayed, previous and next month + days_of_view_period = _calc_days_from_current_month(self.display_period) + context["days_of_view_period"] = days_of_view_period + context["days_of_prev_period"] = _calc_days_from_prev_month(self.display_period) + context["days_of_next_period"] = _calc_days_from_next_month(self.display_period) - # Calculate the days of the next month until Sunday - last_day_of_month = self.month.replace( - day=calendar.monthrange(self.month.year, self.month.month)[1] - ) - days_of_next_month = [] + context["view_type"] = "month" - if last_day_of_month.weekday() != calendar.SUNDAY: - for i in range(1, 7): - day = last_day_of_month + datetime.timedelta(days=i) - days_of_next_month.append(day) - if day.weekday() == calendar.SUNDAY: - break + context["week_num"] = None - # Calculate the days of the displayed month - days_of_month = [ - self.month + datetime.timedelta(days=i) - for i in range((last_day_of_month - self.month).days + 1) - ] + else: + # Current week + year, week_num, _ = self.display_period.isocalendar() + context["view_period"] = f"{year}-KW{week_num:02d}" # formats as "2025-KW02" - # Create a dictionary with the rental items for each day - rental_dict = {} - for rental in self.get_queryset(): - for day in days_of_month: - if rental["date_start"] <= day and rental["date_end"] >= day: - if day not in rental_dict: - rental_dict[day] = [] + # Calculate previous week + prev_week = self.display_period - datetime.timedelta(days=7) + prev_year, prev_week_num, _ = prev_week.isocalendar() + context["prev_period"] = f"{prev_year}-KW{prev_week_num:02d}" - if rental["rentalitems__name"] not in rental_dict[day]: - rental_dict[day].append(rental["rentalitems__name"]) + # Calculate next week + next_week = self.display_period + datetime.timedelta(days=7) + next_year, next_week_num, _ = next_week.isocalendar() + context["next_period"] = f"{next_year}-KW{next_week_num:02d}" - # Add the displayed, previous and next month - context["month"] = self.month - context["prev_month"] = self.month + datetime.timedelta(days=-1) - context["next_month"] = self.month + datetime.timedelta( - days=calendar.monthrange(self.month.year, self.month.month)[1] + 1 - ) + # Add days of week (7 days starting from first_day_of_week) + days_of_view_period = [ + self.display_period + datetime.timedelta(days=i) for i in range(7) + ] + context["days_of_view_period"] = days_of_view_period + context["days_of_prev_period"] = [] + context["days_of_next_period"] = [] - # Add the days of the displayed, previous and next month - context["days_of_month"] = days_of_month - context["days_of_prev_month"] = sorted(days_of_prev_month) - context["days_of_next_month"] = days_of_next_month + context["view_type"] = "week" + + context["week_num"] = week_num # Get the current date for the calendar context["today"] = datetime.datetime.now(tz=datetime.UTC).date() @@ -103,6 +193,17 @@ class RentalListView(ListView): # Add the selected rental items to the context for the filter context["rentalitem_filters"] = {"rentalitems": self.rentalitem_filters} + # Create a dictionary with the rental items for each day + rental_dict = {} + for rental in self.get_queryset(): + for day in days_of_view_period: + if rental["date_start"] <= day and rental["date_end"] >= day: + if day not in rental_dict: + rental_dict[day] = [] + + if rental["rentalitems__name"] not in rental_dict[day]: + rental_dict[day].append(rental["rentalitems__name"]) + context["rental_dict"] = rental_dict return context @@ -118,20 +219,22 @@ class RentalListView(ListView): ) ) - last_day_of_month = self.month.replace( - day=calendar.monthrange(self.month.year, self.month.month)[1] - ) + if self.view_type == "week": + qs_date_end = self.display_period + datetime.timedelta(days=6) + else: + qs_date_end = self.display_period.replace( + day=calendar.monthrange(self.display_period.year, self.display_period.month)[1] + ) # Filter by date - qs_new = qs.filter(date_start__gte=self.month, date_start__lte=last_day_of_month) - qs_new |= qs.filter(date_end__gte=self.month, date_end__lte=last_day_of_month) + if self.display_period and qs_date_end: + qs_new = qs.filter(date_start__gte=self.display_period, date_start__lte=qs_date_end) + qs_new |= qs.filter(date_end__gte=self.display_period, date_end__lte=qs_date_end) # Filter by rental items qs = qs.filter(rentalitems__name__in=self.rentalitem_filters).distinct() - qs = qs.values("id", "date_start", "date_end", "rentalitems__name").distinct() - - return qs + return qs.values("id", "date_start", "date_end", "rentalitems__name").distinct() class RentalCreateView(CreateView): @@ -175,7 +278,7 @@ class RentalItemDetailView(DetailView): def rental_calendar(request): """ - ICS-calendar for outlook, google calender, ... + ICS-calendar for Outlook, Google Calendar, etc. """ rentals = Rental.objects.all() diff --git a/fet2020/templates/rental/calendar.html b/fet2020/templates/rental/calendar.html index 8cca61f5..722da159 100644 --- a/fet2020/templates/rental/calendar.html +++ b/fet2020/templates/rental/calendar.html @@ -1,192 +1,281 @@ -{% extends 'base.html' %} - -{% load static %} - -{% block title %}Verleih{% endblock %} - -{% block content %} -
-

Verleih

- -
-

- Willkommen bei unserem Verleih! -

- - {% if user.is_authenticated %} - Verleih-Kalender abonnieren - {% endif %} - - - Verleih anfragen - -
- -
-
-
- - - - -
-
- -
-
-
- {{ month|date:'F Y' }} - -
- - -
-
- - - - - - - - - - - - - - - - {% for day in days_of_prev_month %} - {% if day.weekday == 0 %} - - {% endif %} - - - {% endfor %} - - {% for day in days_of_month %} - {% if day.weekday == 0 %} - - {% endif %} - - - - {% if day.weekday == 6 %} - - {% endif %} - {% endfor %} - - {% for day in days_of_next_month %} - - - {% if day.weekday == 6 %} - - {% endif %} - {% endfor %} - - -
- - Mon - - - Tue - - - Wed - - - Thu - - - Fri - - - Sat - - - Sun -
-
-
- {{ day.day }} -
-
-
-
-
-
- {% if day == today %} - {{ day.day }} - {% else %} - {{ day.day }} - {% endif %} -
- - {% for key, names in rental_dict.items %} - {% if key == day %} - {% for name in names %} -
-
- - - - - - {{ name|truncatechars:3 }} -
-
- {% empty %} -
- {% endfor %} - {% endif %} - {% endfor %} -
-
-
-
- {{ day.day }} -
-
-
-
-
-
-
-
-{% endblock content %} +{% extends 'base.html' %} + +{% load static %} + +{% block title %}Verleih{% endblock %} + +{% block content %} +
+

Verleih

+ +
+

+ Willkommen bei unserem Verleih! +

+ + {% if user.is_authenticated %} + + Verleih-Kalender abonnieren + + {% endif %} + + + Verleih anfragen + +
+ +
+
+
+ + + +
+ + +
+
+ + +
+ + + + +
+
+ +
+
+
+ + {% if view_type == 'month' %} + {{ view_period|date:'F Y' }} + {% else %} + {% if days_of_view_period.0.month != days_of_view_period.6.month %} + KW{{ week_num|stringformat:"02d" }} - {{ days_of_view_period.0|date:'F' }} / {{ days_of_view_period.6|date:'F Y' }} + {% else %} + KW{{ week_num|stringformat:"02d" }} - {{ days_of_view_period.0|date:'F Y' }} + {% endif %} + {% endif %} + + +
+ + +
+
+ + + + + + + + + + + + + + + + {% for day in days_of_prev_period %} + {% if day.weekday == 0 %} + + {% endif %} + + + {% endfor %} + + {% for day in days_of_view_period %} + {% if day.weekday == 0 %} + + {% endif %} + + + + {% if day.weekday == 6 %} + + {% endif %} + {% endfor %} + + {% for day in days_of_next_period %} + + + {% if day.weekday == 6 %} + + {% endif %} + {% endfor %} + + +
+ + Mo + + + Di + + + Mi + + + Do + + + Fr + + + Sa + + + So +
+
+
+ {{ day.day }} +
+
+
+
+
+
+ {% if day == today %} + {{ day.day }} + {% else %} + {{ day.day }} + {% endif %} +
+ + {% for key, names in rental_dict.items %} + {% if key == day %} + {% if view_type == 'month' %} + {% for name in names|slice:":3" %} +
+
+ + + + + + {{ name|truncatechars:3 }} +
+
+ {% endfor %} + {% if names|length > 3 %} + + {% endif %} + {% else %} + {% for name in names %} +
+
+ + + + + + {{ name|truncatechars:3 }} +
+
+ {% endfor %} + {% endif %} + {% endif %} + {% endfor %} +
+
+
+
+ {{ day.day }} +
+
+
+
+
+
+
+
+{% endblock content %}