From 8e365e0e8691db3dcae3e8a0ce9c991df2519a66 Mon Sep 17 00:00:00 2001
From: crosspolar <18083323+crosspolar@users.noreply.github.com>
Date: Fri, 8 Aug 2025 18:28:10 +0200
Subject: [PATCH 01/33] feat: show simple statistics per shift
---
.../shifts/templates/shifts/shift_detail.html | 5 +++++
tapir/shifts/views/views.py | 22 +++++++++++++++++++
2 files changed, 27 insertions(+)
diff --git a/tapir/shifts/templates/shifts/shift_detail.html b/tapir/shifts/templates/shifts/shift_detail.html
index e57369279..85a99d402 100644
--- a/tapir/shifts/templates/shifts/shift_detail.html
+++ b/tapir/shifts/templates/shifts/shift_detail.html
@@ -64,6 +64,11 @@
+ {% blocktranslate %}{{ total_valid_attendances }} people worked {{ total_hours }} hours in {{ no_of_past_shifts }} shifts so far.{% endblocktranslate %}
+
+ {% endif %}
diff --git a/tapir/shifts/views/views.py b/tapir/shifts/views/views.py
index 94fbd3de1..510c66808 100644
--- a/tapir/shifts/views/views.py
+++ b/tapir/shifts/views/views.py
@@ -234,6 +234,27 @@ def get_context_data(self, **kwargs):
.prefetch_related("slot_template__attendance_template__user")
)
+ # Get all past shifts related to the same ShiftTemplate
+ if shift.shift_template:
+ past_shifts = Shift.objects.filter(
+ shift_template=shift.shift_template, end_time__lt=timezone.now()
+ ).exclude(id=shift.id)
+ context["no_of_past_shifts"] = len(past_shifts)
+
+ # Sum valid attendances for related shifts
+ total_valid_attendances = sum(
+ s.get_valid_attendances().count() for s in past_shifts
+ )
+ context["total_valid_attendances"] = total_valid_attendances
+ # Calculate total working hours
+ total_hours = sum(
+ (s.end_time - s.start_time).total_seconds()
+ * s.get_valid_attendances().count()
+ / 3600
+ for s in past_shifts
+ )
+ context["total_hours"] = total_hours
+
for slot in slots:
slot.can_self_register = slot.user_can_attend(self.request.user)
slot.can_self_unregister = slot.user_can_self_unregister(self.request.user)
@@ -254,6 +275,7 @@ def get_context_data(self, **kwargs):
Shift.NB_DAYS_FOR_SELF_LOOK_FOR_STAND_IN
)
context["SHIFT_ATTENDANCE_STATES"] = SHIFT_ATTENDANCE_STATES
+
return context
From f06da8412dc0917e19ea2408e0f749cc8ee01e73 Mon Sep 17 00:00:00 2001
From: crosspolar <18083323+crosspolar@users.noreply.github.com>
Date: Fri, 8 Aug 2025 18:43:40 +0200
Subject: [PATCH 02/33] feat: show simple statistics for whole coop
---
.../templates/statistics/main_statistics.html | 5 +++++
tapir/statistics/views/main_view.py | 17 +++++++++++++++++
2 files changed, 22 insertions(+)
diff --git a/tapir/statistics/templates/statistics/main_statistics.html b/tapir/statistics/templates/statistics/main_statistics.html
index 8fe8097d4..995622731 100644
--- a/tapir/statistics/templates/statistics/main_statistics.html
+++ b/tapir/statistics/templates/statistics/main_statistics.html
@@ -109,6 +109,11 @@
+ {% if total_valid_attendances and total_hours and no_of_past_shifts %}
+
+ {% blocktranslate %}{{ total_valid_attendances }} people worked {{ total_hours }} hours in {{ no_of_past_shifts }} shifts so far.{% endblocktranslate %}
+
+ {% endif %}
diff --git a/tapir/statistics/views/main_view.py b/tapir/statistics/views/main_view.py
index 798049b1f..760a93b77 100644
--- a/tapir/statistics/views/main_view.py
+++ b/tapir/statistics/views/main_view.py
@@ -76,6 +76,23 @@ def get_context_data(self, **kwargs):
context_data["working_members"] = self.get_working_members_context()
context_data["target_average_monthly_basket"] = 225
+ past_shifts = Shift.objects.filter(end_time__lt=timezone.now())
+ context_data["no_of_past_shifts"] = len(past_shifts)
+
+ # Sum valid attendances for related shifts
+ total_valid_attendances = sum(
+ s.get_valid_attendances().count() for s in past_shifts
+ )
+ context_data["total_valid_attendances"] = total_valid_attendances
+ # Calculate total working hours
+ total_hours = sum(
+ (s.end_time - s.start_time).total_seconds()
+ * s.get_valid_attendances().count()
+ / 3600
+ for s in past_shifts
+ )
+ context_data["total_hours"] = total_hours
+
return context_data
def get_purchasing_members_context(self):
From d6a1bd25f707de57365da5b1019b3476b6457125 Mon Sep 17 00:00:00 2001
From: crosspolar <18083323+crosspolar@users.noreply.github.com>
Date: Tue, 12 Aug 2025 17:47:22 +0200
Subject: [PATCH 03/33] forgot Shift-import
---
tapir/statistics/views/main_view.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/tapir/statistics/views/main_view.py b/tapir/statistics/views/main_view.py
index 760a93b77..87631152e 100644
--- a/tapir/statistics/views/main_view.py
+++ b/tapir/statistics/views/main_view.py
@@ -32,6 +32,7 @@
ShiftUserData,
ShiftSlotTemplate,
ShiftAttendanceMode,
+ Shift,
)
from tapir.shifts.services.frozen_status_history_service import (
FrozenStatusHistoryService,
From 0db1c0fbd6e6cc9e77edad40cd57a6dae2cd80d1 Mon Sep 17 00:00:00 2001
From: crosspolar <18083323+crosspolar@users.noreply.github.com>
Date: Tue, 12 Aug 2025 18:17:53 +0200
Subject: [PATCH 04/33] new text
---
tapir/shifts/templates/shifts/shift_detail.html | 10 +++++-----
.../templates/statistics/main_statistics.html | 2 +-
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/tapir/shifts/templates/shifts/shift_detail.html b/tapir/shifts/templates/shifts/shift_detail.html
index 85a99d402..a26344000 100644
--- a/tapir/shifts/templates/shifts/shift_detail.html
+++ b/tapir/shifts/templates/shifts/shift_detail.html
@@ -28,6 +28,11 @@
{% translate 'Shift' %}: {{ shift.get_display_name }}
+ {% if total_valid_attendances and total_hours and no_of_past_shifts %}
+
+
+ {% endif %}
{% if perms.shifts.manage %}
{{ shift.shift_template.get_display_name }}
{% endif %}
- {% if total_valid_attendances and total_hours and no_of_past_shifts %}
-
- {% blocktranslate %}{{ total_valid_attendances }} people worked {{ total_hours }} hours in {{ no_of_past_shifts }} shifts so far.{% endblocktranslate %}
-
- {% endif %}
diff --git a/tapir/statistics/templates/statistics/main_statistics.html b/tapir/statistics/templates/statistics/main_statistics.html
index 995622731..f64e1b709 100644
--- a/tapir/statistics/templates/statistics/main_statistics.html
+++ b/tapir/statistics/templates/statistics/main_statistics.html
@@ -111,7 +111,7 @@
{% if total_valid_attendances and total_hours and no_of_past_shifts %}
- {% blocktranslate %}{{ total_valid_attendances }} people worked {{ total_hours }} hours in {{ no_of_past_shifts }} shifts so far.{% endblocktranslate %}
+ {% blocktranslate %}Statistics to date: {{ total_valid_attendances }} people worked {{ total_hours }} hours in {{ no_of_past_shifts }} shifts so far.{% endblocktranslate %}
{% endif %}
From 152ae4d181b1735842c6f069b3f1adc0d815341a Mon Sep 17 00:00:00 2001
From: crosspolar <18083323+crosspolar@users.noreply.github.com>
Date: Tue, 12 Aug 2025 18:18:42 +0200
Subject: [PATCH 05/33] only show when more than one person worked in it so you
don't have weird "one people..."
---
tapir/shifts/views/views.py | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/tapir/shifts/views/views.py b/tapir/shifts/views/views.py
index 510c66808..3d81a0d5c 100644
--- a/tapir/shifts/views/views.py
+++ b/tapir/shifts/views/views.py
@@ -245,7 +245,9 @@ def get_context_data(self, **kwargs):
total_valid_attendances = sum(
s.get_valid_attendances().count() for s in past_shifts
)
- context["total_valid_attendances"] = total_valid_attendances
+ if total_valid_attendances > 1:
+ context["total_valid_attendances"] = total_valid_attendances
+
# Calculate total working hours
total_hours = sum(
(s.end_time - s.start_time).total_seconds()
From 81562bdb02736fb27c8a30a78aaad2f1a8eb6328 Mon Sep 17 00:00:00 2001
From: crosspolar <18083323+crosspolar@users.noreply.github.com>
Date: Sat, 6 Sep 2025 15:05:03 +0200
Subject: [PATCH 06/33] Revert "feat: show simple statistics for whole coop"
This reverts commit 81022b91
---
.../templates/statistics/main_statistics.html | 5 -----
tapir/statistics/views/main_view.py | 17 -----------------
2 files changed, 22 deletions(-)
diff --git a/tapir/statistics/templates/statistics/main_statistics.html b/tapir/statistics/templates/statistics/main_statistics.html
index f64e1b709..8fe8097d4 100644
--- a/tapir/statistics/templates/statistics/main_statistics.html
+++ b/tapir/statistics/templates/statistics/main_statistics.html
@@ -109,11 +109,6 @@
- {% if total_valid_attendances and total_hours and no_of_past_shifts %}
-
- {% blocktranslate %}Statistics to date: {{ total_valid_attendances }} people worked {{ total_hours }} hours in {{ no_of_past_shifts }} shifts so far.{% endblocktranslate %}
-
- {% endif %}
diff --git a/tapir/statistics/views/main_view.py b/tapir/statistics/views/main_view.py
index 87631152e..e1eedc9ea 100644
--- a/tapir/statistics/views/main_view.py
+++ b/tapir/statistics/views/main_view.py
@@ -77,23 +77,6 @@ def get_context_data(self, **kwargs):
context_data["working_members"] = self.get_working_members_context()
context_data["target_average_monthly_basket"] = 225
- past_shifts = Shift.objects.filter(end_time__lt=timezone.now())
- context_data["no_of_past_shifts"] = len(past_shifts)
-
- # Sum valid attendances for related shifts
- total_valid_attendances = sum(
- s.get_valid_attendances().count() for s in past_shifts
- )
- context_data["total_valid_attendances"] = total_valid_attendances
- # Calculate total working hours
- total_hours = sum(
- (s.end_time - s.start_time).total_seconds()
- * s.get_valid_attendances().count()
- / 3600
- for s in past_shifts
- )
- context_data["total_hours"] = total_hours
-
return context_data
def get_purchasing_members_context(self):
From 8aa4292bf256502ff499e9e70a5ceadb3bfaf1c4 Mon Sep 17 00:00:00 2001
From: crosspolar <18083323+crosspolar@users.noreply.github.com>
Date: Sat, 6 Sep 2025 15:32:04 +0200
Subject: [PATCH 07/33] Revert "forgot Shift-import"
This reverts commit 69e5f13539e0ebf4919c6dd08ab1a8c689529d7b.
---
tapir/statistics/views/main_view.py | 1 -
1 file changed, 1 deletion(-)
diff --git a/tapir/statistics/views/main_view.py b/tapir/statistics/views/main_view.py
index e1eedc9ea..798049b1f 100644
--- a/tapir/statistics/views/main_view.py
+++ b/tapir/statistics/views/main_view.py
@@ -32,7 +32,6 @@
ShiftUserData,
ShiftSlotTemplate,
ShiftAttendanceMode,
- Shift,
)
from tapir.shifts.services.frozen_status_history_service import (
FrozenStatusHistoryService,
From 087dcfc4c061f9d0206143f51bef5d374d78fc3b Mon Sep 17 00:00:00 2001
From: crosspolar <18083323+crosspolar@users.noreply.github.com>
Date: Sat, 6 Sep 2025 16:57:06 +0200
Subject: [PATCH 08/33] use annotations to reduce number of queries
---
tapir/shifts/views/views.py | 19 +++++++++++++------
1 file changed, 13 insertions(+), 6 deletions(-)
diff --git a/tapir/shifts/views/views.py b/tapir/shifts/views/views.py
index 3d81a0d5c..ef4d51fae 100644
--- a/tapir/shifts/views/views.py
+++ b/tapir/shifts/views/views.py
@@ -238,20 +238,27 @@ def get_context_data(self, **kwargs):
if shift.shift_template:
past_shifts = Shift.objects.filter(
shift_template=shift.shift_template, end_time__lt=timezone.now()
- ).exclude(id=shift.id)
- context["no_of_past_shifts"] = len(past_shifts)
+ ).annotate(
+ valid_attendance_count=Count(
+ "slots__attendances",
+ filter=Q(
+ slots__attendances__state__in=[
+ ShiftAttendance.State.DONE,
+ ]
+ ),
+ )
+ )
+ context["no_of_past_shifts"] = past_shifts.count()
# Sum valid attendances for related shifts
- total_valid_attendances = sum(
- s.get_valid_attendances().count() for s in past_shifts
- )
+ total_valid_attendances = sum(s.valid_attendance_count for s in past_shifts)
if total_valid_attendances > 1:
context["total_valid_attendances"] = total_valid_attendances
# Calculate total working hours
total_hours = sum(
(s.end_time - s.start_time).total_seconds()
- * s.get_valid_attendances().count()
+ * s.valid_attendance_count
/ 3600
for s in past_shifts
)
From cfc29395d294c4306158f7ad3c0b51dccd079db6 Mon Sep 17 00:00:00 2001
From: crosspolar <18083323+crosspolar@users.noreply.github.com>
Date: Sat, 6 Sep 2025 17:04:11 +0200
Subject: [PATCH 09/33] move to function
---
tapir/shifts/views/views.py | 57 +++++++++++++++++++------------------
1 file changed, 29 insertions(+), 28 deletions(-)
diff --git a/tapir/shifts/views/views.py b/tapir/shifts/views/views.py
index ef4d51fae..33132e778 100644
--- a/tapir/shifts/views/views.py
+++ b/tapir/shifts/views/views.py
@@ -210,6 +210,34 @@ def get_success_url(self):
return self.get_target_user().get_absolute_url()
+def get_past_shifts_data(shift: Shift, context: dict):
+ past_shifts = Shift.objects.filter(
+ shift_template=shift.shift_template, end_time__lt=timezone.now()
+ ).annotate(
+ valid_attendance_count=Count(
+ "slots__attendances",
+ filter=Q(
+ slots__attendances__state__in=[
+ ShiftAttendance.State.DONE,
+ ]
+ ),
+ )
+ )
+ context["no_of_past_shifts"] = past_shifts.count()
+
+ # Sum valid attendances for related shifts
+ total_valid_attendances = sum(s.valid_attendance_count for s in past_shifts)
+ if total_valid_attendances > 1:
+ context["total_valid_attendances"] = total_valid_attendances
+
+ # Calculate total working hours
+ total_hours = sum(
+ (s.end_time - s.start_time).total_seconds() * s.valid_attendance_count / 3600
+ for s in past_shifts
+ )
+ context["total_hours"] = total_hours
+
+
class ShiftDetailView(LoginRequiredMixin, DetailView):
model = Shift
template_name = "shifts/shift_detail.html"
@@ -234,35 +262,8 @@ def get_context_data(self, **kwargs):
.prefetch_related("slot_template__attendance_template__user")
)
- # Get all past shifts related to the same ShiftTemplate
if shift.shift_template:
- past_shifts = Shift.objects.filter(
- shift_template=shift.shift_template, end_time__lt=timezone.now()
- ).annotate(
- valid_attendance_count=Count(
- "slots__attendances",
- filter=Q(
- slots__attendances__state__in=[
- ShiftAttendance.State.DONE,
- ]
- ),
- )
- )
- context["no_of_past_shifts"] = past_shifts.count()
-
- # Sum valid attendances for related shifts
- total_valid_attendances = sum(s.valid_attendance_count for s in past_shifts)
- if total_valid_attendances > 1:
- context["total_valid_attendances"] = total_valid_attendances
-
- # Calculate total working hours
- total_hours = sum(
- (s.end_time - s.start_time).total_seconds()
- * s.valid_attendance_count
- / 3600
- for s in past_shifts
- )
- context["total_hours"] = total_hours
+ get_past_shifts_data(shift, context)
for slot in slots:
slot.can_self_register = slot.user_can_attend(self.request.user)
From 70e4daa1fa18768f0576f4b3948d92212e4cb922 Mon Sep 17 00:00:00 2001
From: crosspolar <18083323+crosspolar@users.noreply.github.com>
Date: Sat, 6 Sep 2025 18:27:31 +0200
Subject: [PATCH 10/33] test
---
.../shifts/tests/test_get_past_shifts_data.py | 92 +++++++++++++++++++
tapir/shifts/views/views.py | 60 ++++++------
2 files changed, 123 insertions(+), 29 deletions(-)
create mode 100644 tapir/shifts/tests/test_get_past_shifts_data.py
diff --git a/tapir/shifts/tests/test_get_past_shifts_data.py b/tapir/shifts/tests/test_get_past_shifts_data.py
new file mode 100644
index 000000000..9279675c8
--- /dev/null
+++ b/tapir/shifts/tests/test_get_past_shifts_data.py
@@ -0,0 +1,92 @@
+import datetime
+
+from celery.states import state
+from django.utils import timezone
+
+from tapir.accounts.tests.factories.factories import TapirUserFactory
+from tapir.shifts.models import ShiftTemplate, ShiftAttendance, Shift
+from tapir.shifts.tests.factories import ShiftFactory, ShiftTemplateFactory
+from tapir.shifts.views import ShiftDetailView
+from tapir.utils.tests_utils import TapirFactoryTestBase
+
+
+class ShiftGetPastShiftsStatisticsTests(TapirFactoryTestBase):
+
+ def test_getPastShiftsData_DifferentStatesOfAttendandance_onlyCountDoneState(self):
+ user_done = TapirUserFactory.create()
+ user_excused = TapirUserFactory.create()
+ shift_template: ShiftTemplate = ShiftTemplateFactory.create(nb_slots=2)
+ shift = shift_template.create_shift(
+ timezone.now().date() - datetime.timedelta(days=2)
+ )
+
+ ShiftAttendance.objects.create(
+ state=ShiftAttendance.State.DONE, user=user_done, slot=shift.slots.all()[0]
+ )
+ ShiftAttendance.objects.create(
+ state=ShiftAttendance.State.MISSED_EXCUSED,
+ user=user_excused,
+ slot=shift.slots.all()[1],
+ )
+ print(ShiftAttendance.objects.all().values())
+
+ context = {}
+ ShiftDetailView.get_past_shifts_data(shift, context=context)
+ self.assertEqual(context["no_of_past_shifts"], 1)
+ self.assertEqual(context["total_valid_attendances"], 1)
+ self.assertEqual(
+ context["total_hours"],
+ (shift.end_time - shift.start_time).total_seconds() / 3600,
+ )
+
+ #
+ def test_getPastShiftsData_MultipleAttendandances_correctValues(self):
+ users = [TapirUserFactory.create(is_in_member_office=False) for _ in range(5)]
+ shift_template = ShiftTemplateFactory.create(nb_slots=len(users))
+ shifts = [
+ shift_template.create_shift(
+ timezone.now().date() - datetime.timedelta(days=i)
+ )
+ for i in range(5)
+ ]
+
+ for shift in shifts:
+ slots = list(shift.slots.all())
+ for i, user in enumerate(users):
+ ShiftAttendance.objects.create(
+ state=ShiftAttendance.State.DONE, user=user, slot=slots[i]
+ )
+
+ context = {}
+ ShiftDetailView.get_past_shifts_data(shifts[-1], context=context)
+
+ self.assertEqual(context["no_of_past_shifts"], len(shifts))
+ self.assertEqual(context["total_valid_attendances"], len(users) * len(shifts))
+ self.assertEqual(
+ context["total_hours"],
+ len(users)
+ * sum(
+ (shift.end_time - shift.start_time).total_seconds() / 3600
+ for shift in shifts
+ ),
+ )
+
+ # def test_no_past_shifts(self):
+ # # Create a future shift
+ # future_shift = ShiftFactory.create(
+ # start_time=self.REFERENCE_TIME + datetime.timedelta(days=1),
+ # end_time=self.REFERENCE_TIME + datetime.timedelta(days=2),
+ # shift_template=self.shift_template
+ # )
+ #
+ # # Create an instance of the view
+ # view = ShiftDetailView()
+ # context = {}
+ #
+ # # Call the method to test
+ # view.get_past_shifts_data(future_shift, context)
+ #
+ # # Assertions
+ # self.assertEqual(context["no_of_past_shifts"], 0)
+ # self.assertNotIn("total_valid_attendances", context)
+ # self.assertNotIn("total_hours", context)
diff --git a/tapir/shifts/views/views.py b/tapir/shifts/views/views.py
index 33132e778..5e7839d5a 100644
--- a/tapir/shifts/views/views.py
+++ b/tapir/shifts/views/views.py
@@ -210,34 +210,6 @@ def get_success_url(self):
return self.get_target_user().get_absolute_url()
-def get_past_shifts_data(shift: Shift, context: dict):
- past_shifts = Shift.objects.filter(
- shift_template=shift.shift_template, end_time__lt=timezone.now()
- ).annotate(
- valid_attendance_count=Count(
- "slots__attendances",
- filter=Q(
- slots__attendances__state__in=[
- ShiftAttendance.State.DONE,
- ]
- ),
- )
- )
- context["no_of_past_shifts"] = past_shifts.count()
-
- # Sum valid attendances for related shifts
- total_valid_attendances = sum(s.valid_attendance_count for s in past_shifts)
- if total_valid_attendances > 1:
- context["total_valid_attendances"] = total_valid_attendances
-
- # Calculate total working hours
- total_hours = sum(
- (s.end_time - s.start_time).total_seconds() * s.valid_attendance_count / 3600
- for s in past_shifts
- )
- context["total_hours"] = total_hours
-
-
class ShiftDetailView(LoginRequiredMixin, DetailView):
model = Shift
template_name = "shifts/shift_detail.html"
@@ -263,7 +235,7 @@ def get_context_data(self, **kwargs):
)
if shift.shift_template:
- get_past_shifts_data(shift, context)
+ self.get_past_shifts_data(shift, context)
for slot in slots:
slot.can_self_register = slot.user_can_attend(self.request.user)
@@ -288,6 +260,36 @@ def get_context_data(self, **kwargs):
return context
+ @staticmethod
+ def get_past_shifts_data(shift: Shift, context: dict):
+ past_shifts = Shift.objects.filter(
+ shift_template=shift.shift_template, end_time__lt=timezone.now()
+ ).annotate(
+ valid_attendance_count=Count(
+ "slots__attendances",
+ filter=Q(
+ slots__attendances__state__in=[
+ ShiftAttendance.State.DONE,
+ ]
+ ),
+ )
+ )
+ context["no_of_past_shifts"] = past_shifts.count()
+
+ # Sum valid attendances for related shifts
+ total_valid_attendances = sum(s.valid_attendance_count for s in past_shifts)
+
+ context["total_valid_attendances"] = total_valid_attendances
+
+ # Calculate total working hours
+ total_hours = sum(
+ (s.end_time - s.start_time).total_seconds()
+ * s.valid_attendance_count
+ / 3600
+ for s in past_shifts
+ )
+ context["total_hours"] = total_hours
+
class ShiftDayPrintableView(LoginRequiredMixin, PermissionRequiredMixin, TemplateView):
template_name = "shifts/shift_day_printable.html"
From a0d341db5755c4fe2bf13bd5047a3fc885d81ef2 Mon Sep 17 00:00:00 2001
From: crosspolar <18083323+crosspolar@users.noreply.github.com>
Date: Sat, 6 Sep 2025 19:05:21 +0200
Subject: [PATCH 11/33] fix tests
---
.../shifts/tests/test_get_past_shifts_data.py | 26 ++++++++++---------
tapir/shifts/views/views.py | 3 ++-
2 files changed, 16 insertions(+), 13 deletions(-)
diff --git a/tapir/shifts/tests/test_get_past_shifts_data.py b/tapir/shifts/tests/test_get_past_shifts_data.py
index 9279675c8..53ae5bcf9 100644
--- a/tapir/shifts/tests/test_get_past_shifts_data.py
+++ b/tapir/shifts/tests/test_get_past_shifts_data.py
@@ -1,6 +1,8 @@
import datetime
+import pytest
from celery.states import state
+from django.core.management import call_command
from django.utils import timezone
from tapir.accounts.tests.factories.factories import TapirUserFactory
@@ -15,9 +17,12 @@ class ShiftGetPastShiftsStatisticsTests(TapirFactoryTestBase):
def test_getPastShiftsData_DifferentStatesOfAttendandance_onlyCountDoneState(self):
user_done = TapirUserFactory.create()
user_excused = TapirUserFactory.create()
+
shift_template: ShiftTemplate = ShiftTemplateFactory.create(nb_slots=2)
- shift = shift_template.create_shift(
- timezone.now().date() - datetime.timedelta(days=2)
+ shift = ShiftFactory(
+ start_time=timezone.now() - datetime.timedelta(days=1),
+ nb_slots=2,
+ shift_template=shift_template,
)
ShiftAttendance.objects.create(
@@ -28,10 +33,8 @@ def test_getPastShiftsData_DifferentStatesOfAttendandance_onlyCountDoneState(sel
user=user_excused,
slot=shift.slots.all()[1],
)
- print(ShiftAttendance.objects.all().values())
- context = {}
- ShiftDetailView.get_past_shifts_data(shift, context=context)
+ context = ShiftDetailView.get_past_shifts_data(shift)
self.assertEqual(context["no_of_past_shifts"], 1)
self.assertEqual(context["total_valid_attendances"], 1)
self.assertEqual(
@@ -39,13 +42,14 @@ def test_getPastShiftsData_DifferentStatesOfAttendandance_onlyCountDoneState(sel
(shift.end_time - shift.start_time).total_seconds() / 3600,
)
- #
def test_getPastShiftsData_MultipleAttendandances_correctValues(self):
- users = [TapirUserFactory.create(is_in_member_office=False) for _ in range(5)]
+ users = [TapirUserFactory.create() for _ in range(5)]
shift_template = ShiftTemplateFactory.create(nb_slots=len(users))
shifts = [
- shift_template.create_shift(
- timezone.now().date() - datetime.timedelta(days=i)
+ ShiftFactory(
+ start_time=timezone.now() - datetime.timedelta(days=i + 1),
+ nb_slots=len(users),
+ shift_template=shift_template,
)
for i in range(5)
]
@@ -56,9 +60,7 @@ def test_getPastShiftsData_MultipleAttendandances_correctValues(self):
ShiftAttendance.objects.create(
state=ShiftAttendance.State.DONE, user=user, slot=slots[i]
)
-
- context = {}
- ShiftDetailView.get_past_shifts_data(shifts[-1], context=context)
+ context = ShiftDetailView.get_past_shifts_data(shifts[-1])
self.assertEqual(context["no_of_past_shifts"], len(shifts))
self.assertEqual(context["total_valid_attendances"], len(users) * len(shifts))
diff --git a/tapir/shifts/views/views.py b/tapir/shifts/views/views.py
index 5e7839d5a..471a4c4ab 100644
--- a/tapir/shifts/views/views.py
+++ b/tapir/shifts/views/views.py
@@ -261,7 +261,7 @@ def get_context_data(self, **kwargs):
return context
@staticmethod
- def get_past_shifts_data(shift: Shift, context: dict):
+ def get_past_shifts_data(shift: Shift, context: dict = {}):
past_shifts = Shift.objects.filter(
shift_template=shift.shift_template, end_time__lt=timezone.now()
).annotate(
@@ -289,6 +289,7 @@ def get_past_shifts_data(shift: Shift, context: dict):
for s in past_shifts
)
context["total_hours"] = total_hours
+ return context
class ShiftDayPrintableView(LoginRequiredMixin, PermissionRequiredMixin, TemplateView):
From ef7ac3d0b5f861695844b8e94dc205178c9d856a Mon Sep 17 00:00:00 2001
From: crosspolar <18083323+crosspolar@users.noreply.github.com>
Date: Tue, 9 Sep 2025 16:36:06 +0200
Subject: [PATCH 12/33] refactor tests
---
.../shifts/tests/test_get_past_shifts_data.py | 26 +++----------------
1 file changed, 3 insertions(+), 23 deletions(-)
diff --git a/tapir/shifts/tests/test_get_past_shifts_data.py b/tapir/shifts/tests/test_get_past_shifts_data.py
index 53ae5bcf9..e5d13264b 100644
--- a/tapir/shifts/tests/test_get_past_shifts_data.py
+++ b/tapir/shifts/tests/test_get_past_shifts_data.py
@@ -14,7 +14,7 @@
class ShiftGetPastShiftsStatisticsTests(TapirFactoryTestBase):
- def test_getPastShiftsData_DifferentStatesOfAttendandance_onlyCountDoneState(self):
+ def test_getPastShiftsData_differentStatesOfAttendandance_onlyCountDoneState(self):
user_done = TapirUserFactory.create()
user_excused = TapirUserFactory.create()
@@ -42,8 +42,8 @@ def test_getPastShiftsData_DifferentStatesOfAttendandance_onlyCountDoneState(sel
(shift.end_time - shift.start_time).total_seconds() / 3600,
)
- def test_getPastShiftsData_MultipleAttendandances_correctValues(self):
- users = [TapirUserFactory.create() for _ in range(5)]
+ def test_getPastShiftsData_multipleAttendandances_correctValues(self):
+ users = TapirUserFactory.create_batch(5)
shift_template = ShiftTemplateFactory.create(nb_slots=len(users))
shifts = [
ShiftFactory(
@@ -72,23 +72,3 @@ def test_getPastShiftsData_MultipleAttendandances_correctValues(self):
for shift in shifts
),
)
-
- # def test_no_past_shifts(self):
- # # Create a future shift
- # future_shift = ShiftFactory.create(
- # start_time=self.REFERENCE_TIME + datetime.timedelta(days=1),
- # end_time=self.REFERENCE_TIME + datetime.timedelta(days=2),
- # shift_template=self.shift_template
- # )
- #
- # # Create an instance of the view
- # view = ShiftDetailView()
- # context = {}
- #
- # # Call the method to test
- # view.get_past_shifts_data(future_shift, context)
- #
- # # Assertions
- # self.assertEqual(context["no_of_past_shifts"], 0)
- # self.assertNotIn("total_valid_attendances", context)
- # self.assertNotIn("total_hours", context)
From df4a34527da9bb748754b66cfcdcbb56389ab39e Mon Sep 17 00:00:00 2001
From: crosspolar <18083323+crosspolar@users.noreply.github.com>
Date: Tue, 9 Sep 2025 16:40:02 +0200
Subject: [PATCH 13/33] translation
---
.../locale/de/LC_MESSAGES/django.po | 85 ++++++++++---------
1 file changed, 45 insertions(+), 40 deletions(-)
diff --git a/tapir/translations/locale/de/LC_MESSAGES/django.po b/tapir/translations/locale/de/LC_MESSAGES/django.po
index b5981d36a..96b9d3518 100644
--- a/tapir/translations/locale/de/LC_MESSAGES/django.po
+++ b/tapir/translations/locale/de/LC_MESSAGES/django.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2025-09-09 07:54+0200\n"
+"POT-Creation-Date: 2025-09-09 16:38+0200\n"
"PO-Revision-Date: 2025-07-07 13:06+0000\n"
"Last-Translator: Weblate Admin \n"
"Language-Team: German \n"
@@ -327,7 +327,7 @@ msgstr "Benutzername bearbeiten"
#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:90
#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:113
#: core/templates/core/featureflag_list.html:30
-#: shifts/templates/shifts/shift_detail.html:44
+#: shifts/templates/shifts/shift_detail.html:49
#: shifts/templates/shifts/shift_template_detail.html:25
#: shifts/templates/shifts/shift_template_detail.html:113
#: shifts/templates/shifts/user_shifts_overview_tag.html:14
@@ -1086,7 +1086,7 @@ msgstr "Bewerber*in erzeugen"
#: coop/templates/coop/draftuser_register_form.html:29
#: shifts/templates/shifts/register_user_to_shift_slot.html:27
#: shifts/templates/shifts/register_user_to_shift_slot_template.html:26
-#: shifts/templates/shifts/shift_detail.html:226
+#: shifts/templates/shifts/shift_detail.html:231
#: shifts/templates/shifts/shift_template_detail.html:84
msgid "Register"
msgstr "Anmelden"
@@ -2371,7 +2371,7 @@ msgstr ""
#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:56
#: shifts/models.py:955 shifts/templates/shifts/shift_day_printable.html:56
-#: shifts/templates/shifts/shift_detail.html:281
+#: shifts/templates/shifts/shift_detail.html:286
#: shifts/templates/shifts/shift_detail_printable.html:51
msgid "Attended"
msgstr "Teilgenommen"
@@ -2781,7 +2781,7 @@ msgstr "Ziel"
#: financingcampaign/templates/financingcampaign/general.html:29
#: financingcampaign/templates/financingcampaign/general.html:72
#: financingcampaign/templates/financingcampaign/general.html:114
-#: shifts/templates/shifts/shift_detail.html:92
+#: shifts/templates/shifts/shift_detail.html:97
msgid "Actions"
msgstr "Aktionen"
@@ -3147,7 +3147,7 @@ msgid "If enabled, members who register for that shift can choose themselves the
msgstr ""
#: shifts/models.py:423 shifts/models.py:846
-#: shifts/templates/shifts/shift_detail.html:95
+#: shifts/templates/shifts/shift_detail.html:100
#: shifts/templates/shifts/shift_template_detail.html:42
msgid "Chosen time"
msgstr ""
@@ -3173,23 +3173,23 @@ msgid "This shift lets you choose at what time you come during the day of the sh
msgstr ""
#: shifts/models.py:956 shifts/templates/shifts/shift_day_printable.html:57
-#: shifts/templates/shifts/shift_detail.html:291
+#: shifts/templates/shifts/shift_detail.html:296
#: shifts/templates/shifts/shift_detail_printable.html:52
msgid "Missed"
msgstr "Nicht erschienen"
#: shifts/models.py:957 shifts/templates/shifts/shift_day_printable.html:58
-#: shifts/templates/shifts/shift_detail.html:321
+#: shifts/templates/shifts/shift_detail.html:326
#: shifts/templates/shifts/shift_detail_printable.html:53
msgid "Excused"
msgstr "Entschuldigt"
-#: shifts/models.py:958 shifts/templates/shifts/shift_detail.html:329
+#: shifts/models.py:958 shifts/templates/shifts/shift_detail.html:334
msgid "Cancelled"
msgstr "Abgesagt"
#: shifts/models.py:959 shifts/templates/shifts/shift_day_printable.html:97
-#: shifts/templates/shifts/shift_detail.html:313
+#: shifts/templates/shifts/shift_detail.html:318
#: shifts/templates/shifts/shift_detail_printable.html:94
#: shifts/templates/shifts/shift_filters.html:83
msgid "Looking for a stand-in"
@@ -3819,7 +3819,7 @@ msgid ""
msgstr ""
#: shifts/templates/shifts/shift_day_printable.html:54
-#: shifts/templates/shifts/shift_detail.html:88
+#: shifts/templates/shifts/shift_detail.html:93
#: shifts/templates/shifts/shift_detail_printable.html:49
msgid "Slot"
msgstr "Platz"
@@ -3840,7 +3840,7 @@ msgid "Flexible time not specified"
msgstr ""
#: shifts/templates/shifts/shift_day_printable.html:94
-#: shifts/templates/shifts/shift_detail.html:122
+#: shifts/templates/shifts/shift_detail.html:127
#: shifts/templates/shifts/shift_detail_printable.html:90
#: shifts/templates/shifts/shift_template_detail.html:64
msgid "Shift partner: "
@@ -3856,90 +3856,95 @@ msgstr "Hat Qualifikationen : "
msgid "Shift"
msgstr "Schicht"
-#: shifts/templates/shifts/shift_detail.html:35
+#: shifts/templates/shifts/shift_detail.html:33
+#, python-format
+msgid "Statistics to date: %(total_valid_attendances)s people worked %(total_hours)s hours in %(no_of_past_shifts)s shifts so far."
+msgstr "Bis heute": %(total_valid_attendances)s Leute haben %(total_hours)s Stunden in %(no_of_past_shifts)s Schichten gearbeitet."
+
+#: shifts/templates/shifts/shift_detail.html:40
msgid "Get printable version"
msgstr "Druckversion erhalten"
-#: shifts/templates/shifts/shift_detail.html:39
+#: shifts/templates/shifts/shift_detail.html:44
#: shifts/templates/shifts/shift_template_detail.html:20
msgid "Add a slot"
msgstr "Slot hinzufügen"
-#: shifts/templates/shifts/shift_detail.html:49
+#: shifts/templates/shifts/shift_detail.html:54
msgid "Cancel the entire shift"
msgstr "Ganze Schicht absagen"
-#: shifts/templates/shifts/shift_detail.html:55
+#: shifts/templates/shifts/shift_detail.html:60
#, fuzzy
#| msgid "Cancel the entire shift"
msgid "Delete the entire shift"
msgstr "Ganze Schicht absagen"
-#: shifts/templates/shifts/shift_detail.html:63
+#: shifts/templates/shifts/shift_detail.html:68
msgid "Generated from"
msgstr "Erzeugt von"
-#: shifts/templates/shifts/shift_detail.html:73
+#: shifts/templates/shifts/shift_detail.html:78
msgid "This shift has been cancelled: "
msgstr ""
-#: shifts/templates/shifts/shift_detail.html:78
+#: shifts/templates/shifts/shift_detail.html:83
msgid "This shift has been deleted."
msgstr ""
-#: shifts/templates/shifts/shift_detail.html:85
+#: shifts/templates/shifts/shift_detail.html:90
msgid "List of slots for this shift"
msgstr "Liste der Slots für diese Schicht"
-#: shifts/templates/shifts/shift_detail.html:88
+#: shifts/templates/shifts/shift_detail.html:93
msgid "Number"
msgstr "Nummer"
-#: shifts/templates/shifts/shift_detail.html:89
+#: shifts/templates/shifts/shift_detail.html:94
#: shifts/templates/shifts/shift_template_detail.html:38
msgid "Details"
msgstr "Details"
-#: shifts/templates/shifts/shift_detail.html:90
+#: shifts/templates/shifts/shift_detail.html:95
#: shifts/templates/shifts/shift_template_detail.html:40
msgid "Registered user"
msgstr "Angemeldete*r Nutzer*in"
-#: shifts/templates/shifts/shift_detail.html:91
+#: shifts/templates/shifts/shift_detail.html:96
msgid "Attendance"
msgstr "Anwesenheit"
-#: shifts/templates/shifts/shift_detail.html:93
+#: shifts/templates/shifts/shift_detail.html:98
msgid "Do you meet the requirements?"
msgstr "Erfüllst du die Voraussetzungen?"
-#: shifts/templates/shifts/shift_detail.html:98
+#: shifts/templates/shifts/shift_detail.html:103
#: shifts/templates/shifts/shift_template_detail.html:45
msgid "Member-Office actions"
msgstr "Mitgliederbüro Aktionen"
-#: shifts/templates/shifts/shift_detail.html:99
+#: shifts/templates/shifts/shift_detail.html:104
msgid "Previous attendances"
msgstr "Vorherige Anwesenheiten"
-#: shifts/templates/shifts/shift_detail.html:130
+#: shifts/templates/shifts/shift_detail.html:135
msgid "since"
msgstr "seit"
-#: shifts/templates/shifts/shift_detail.html:143
+#: shifts/templates/shifts/shift_detail.html:148
msgid "Vacant"
msgstr "Frei"
-#: shifts/templates/shifts/shift_detail.html:160
+#: shifts/templates/shifts/shift_detail.html:165
msgid "Cancels the search for a stand-in. Use this if you want to attend the shift."
msgstr "Beendet die Suche nach Vertretung. Benutze dies, wenn du die Schicht wahrnehmen möchtest."
-#: shifts/templates/shifts/shift_detail.html:162
-#: shifts/templates/shifts/shift_detail.html:302
+#: shifts/templates/shifts/shift_detail.html:167
+#: shifts/templates/shifts/shift_detail.html:307
msgid "Cancel looking for a stand-in"
msgstr "Beende die Suche nach Vertretung"
-#: shifts/templates/shifts/shift_detail.html:170
+#: shifts/templates/shifts/shift_detail.html:175
#, fuzzy, python-format
#| msgid ""
#| "You can only look for a\n"
@@ -3959,11 +3964,11 @@ msgid ""
" possible."
msgstr "Du kannst nur bis %(NB_DAYS_FOR_SELF_LOOK_FOR_STAND_IN)s Tage vor der Schicht nach einer Vertretung suchen. Wenn du die Schicht nicht wahrnehmen kannst, wende dich bitte schnellstmöglich an die Teamleitung."
-#: shifts/templates/shifts/shift_detail.html:183
+#: shifts/templates/shifts/shift_detail.html:188
msgid "Look for a stand-in"
msgstr "Suche nach Vertretung"
-#: shifts/templates/shifts/shift_detail.html:192
+#: shifts/templates/shifts/shift_detail.html:197
#, fuzzy, python-format
#| msgid ""
#| "You can only unregister\n"
@@ -3985,11 +3990,11 @@ msgid ""
" possible."
msgstr "Du kannst dich nur bis %(NB_DAYS_FOR_SELF_UNREGISTER)s Tage vor deiner Schicht abmelden. Von ABCD-Schichten kannst du dich überhaupt nicht abmelden. Wenn du die Schicht nicht wahrnehmen kannst, suche bitte eine Vertretung oder melde dich schnellstmöglich bei deiner Teamleitung."
-#: shifts/templates/shifts/shift_detail.html:207
+#: shifts/templates/shifts/shift_detail.html:212
msgid "Unregister myself"
msgstr "Melde mich ab"
-#: shifts/templates/shifts/shift_detail.html:212
+#: shifts/templates/shifts/shift_detail.html:217
#, fuzzy
#| msgid ""
#| "You can only register\n"
@@ -4018,12 +4023,12 @@ msgstr ""
"- die Schicht in der Zukunft liegt\n"
" "
-#: shifts/templates/shifts/shift_detail.html:254
+#: shifts/templates/shifts/shift_detail.html:259
#: shifts/templates/shifts/shift_template_detail.html:95
msgid "Not specified"
msgstr ""
-#: shifts/templates/shifts/shift_detail.html:337
+#: shifts/templates/shifts/shift_detail.html:342
msgid "Edit slot"
msgstr "Slot bearbeiten"
@@ -4594,7 +4599,7 @@ msgstr "Schichtkonto-Protokoll für: %(name)s"
msgid "Create manual shift account entry for: %(link)s"
msgstr "Erzeuge Schicht-Kontoeintrag für: %(link)s"
-#: shifts/views/views.py:354
+#: shifts/views/views.py:389
msgid "Frozen statuses updated."
msgstr ""
From 372e17b5931b8c13d79f0c5f77f8ccc9e4f1b052 Mon Sep 17 00:00:00 2001
From: crosspolar <18083323+crosspolar@users.noreply.github.com>
Date: Tue, 9 Sep 2025 16:48:24 +0200
Subject: [PATCH 14/33] translation
---
tapir/translations/locale/de/LC_MESSAGES/django.po | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tapir/translations/locale/de/LC_MESSAGES/django.po b/tapir/translations/locale/de/LC_MESSAGES/django.po
index 96b9d3518..044b3654f 100644
--- a/tapir/translations/locale/de/LC_MESSAGES/django.po
+++ b/tapir/translations/locale/de/LC_MESSAGES/django.po
@@ -3859,7 +3859,7 @@ msgstr "Schicht"
#: shifts/templates/shifts/shift_detail.html:33
#, python-format
msgid "Statistics to date: %(total_valid_attendances)s people worked %(total_hours)s hours in %(no_of_past_shifts)s shifts so far."
-msgstr "Bis heute": %(total_valid_attendances)s Leute haben %(total_hours)s Stunden in %(no_of_past_shifts)s Schichten gearbeitet."
+msgstr "Bis heute: %(total_valid_attendances)s Leute haben %(total_hours)s Stunden in %(no_of_past_shifts)s Schichten gearbeitet."
#: shifts/templates/shifts/shift_detail.html:40
msgid "Get printable version"
From 20ce421d02815d37390f991a68928c006e0eeb5a Mon Sep 17 00:00:00 2001
From: crosspolar <18083323+crosspolar@users.noreply.github.com>
Date: Sun, 21 Sep 2025 21:36:26 +0200
Subject: [PATCH 15/33] translation
---
.../locale/de/LC_MESSAGES/django.po | 89 +++++++++----------
1 file changed, 44 insertions(+), 45 deletions(-)
diff --git a/tapir/translations/locale/de/LC_MESSAGES/django.po b/tapir/translations/locale/de/LC_MESSAGES/django.po
index d0c1c4319..0c086c9fe 100644
--- a/tapir/translations/locale/de/LC_MESSAGES/django.po
+++ b/tapir/translations/locale/de/LC_MESSAGES/django.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2025-09-09 16:38+0200\n"
+"POT-Creation-Date: 2025-09-21 21:35+0200\n"
"PO-Revision-Date: 2025-07-07 13:06+0000\n"
"Last-Translator: Weblate Admin \n"
"Language-Team: German \n"
@@ -328,7 +328,7 @@ msgstr "Benutzername bearbeiten"
#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:90
#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:113
#: core/templates/core/featureflag_list.html:30
-#: shifts/templates/shifts/shift_detail.html:49
+#: shifts/templates/shifts/shift_detail.html:65
#: shifts/templates/shifts/shift_template_detail.html:25
#: shifts/templates/shifts/shift_template_detail.html:113
#: shifts/templates/shifts/user_shifts_overview_tag.html:14
@@ -1081,7 +1081,7 @@ msgstr "Bewerber*in erzeugen"
#: coop/templates/coop/draftuser_register_form.html:29
#: shifts/templates/shifts/register_user_to_shift_slot.html:27
#: shifts/templates/shifts/register_user_to_shift_slot_template.html:26
-#: shifts/templates/shifts/shift_detail.html:231
+#: shifts/templates/shifts/shift_detail.html:247
#: shifts/templates/shifts/shift_template_detail.html:84
msgid "Register"
msgstr "Anmelden"
@@ -2366,7 +2366,7 @@ msgstr ""
#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:56
#: shifts/models.py:955 shifts/templates/shifts/shift_day_printable.html:56
-#: shifts/templates/shifts/shift_detail.html:286
+#: shifts/templates/shifts/shift_detail.html:302
#: shifts/templates/shifts/shift_detail_printable.html:51
msgid "Attended"
msgstr "Teilgenommen"
@@ -2776,7 +2776,7 @@ msgstr "Ziel"
#: financingcampaign/templates/financingcampaign/general.html:29
#: financingcampaign/templates/financingcampaign/general.html:72
#: financingcampaign/templates/financingcampaign/general.html:114
-#: shifts/templates/shifts/shift_detail.html:97
+#: shifts/templates/shifts/shift_detail.html:113
msgid "Actions"
msgstr "Aktionen"
@@ -3072,7 +3072,7 @@ msgstr ""
msgid "I understand that this will delete the shift exemption and create a membership pause"
msgstr ""
-#: shifts/forms.py:680 shifts/views/views.py:375 shifts/views/views.py:376
+#: shifts/forms.py:680 shifts/views/views.py:410 shifts/views/views.py:411
msgid "Shift changes you would like to be informed about"
msgstr "Schicht-Änderungen, bei denen du informiert werden möchtest"
@@ -3154,7 +3154,7 @@ msgid "If enabled, members who register for that shift can choose themselves the
msgstr ""
#: shifts/models.py:423 shifts/models.py:846
-#: shifts/templates/shifts/shift_detail.html:100
+#: shifts/templates/shifts/shift_detail.html:116
#: shifts/templates/shifts/shift_template_detail.html:42
msgid "Chosen time"
msgstr ""
@@ -3180,23 +3180,23 @@ msgid "This shift lets you choose at what time you come during the day of the sh
msgstr ""
#: shifts/models.py:956 shifts/templates/shifts/shift_day_printable.html:57
-#: shifts/templates/shifts/shift_detail.html:296
+#: shifts/templates/shifts/shift_detail.html:312
#: shifts/templates/shifts/shift_detail_printable.html:52
msgid "Missed"
msgstr "Nicht erschienen"
#: shifts/models.py:957 shifts/templates/shifts/shift_day_printable.html:58
-#: shifts/templates/shifts/shift_detail.html:326
+#: shifts/templates/shifts/shift_detail.html:342
#: shifts/templates/shifts/shift_detail_printable.html:53
msgid "Excused"
msgstr "Entschuldigt"
-#: shifts/models.py:958 shifts/templates/shifts/shift_detail.html:334
+#: shifts/models.py:958 shifts/templates/shifts/shift_detail.html:350
msgid "Cancelled"
msgstr "Abgesagt"
#: shifts/models.py:959 shifts/templates/shifts/shift_day_printable.html:97
-#: shifts/templates/shifts/shift_detail.html:318
+#: shifts/templates/shifts/shift_detail.html:334
#: shifts/templates/shifts/shift_detail_printable.html:94
#: shifts/templates/shifts/shift_filters.html:83
msgid "Looking for a stand-in"
@@ -3922,7 +3922,7 @@ msgid ""
msgstr ""
#: shifts/templates/shifts/shift_day_printable.html:54
-#: shifts/templates/shifts/shift_detail.html:93
+#: shifts/templates/shifts/shift_detail.html:109
#: shifts/templates/shifts/shift_detail_printable.html:49
msgid "Slot"
msgstr "Platz"
@@ -3943,7 +3943,7 @@ msgid "Flexible time not specified"
msgstr ""
#: shifts/templates/shifts/shift_day_printable.html:94
-#: shifts/templates/shifts/shift_detail.html:127
+#: shifts/templates/shifts/shift_detail.html:143
#: shifts/templates/shifts/shift_detail_printable.html:90
#: shifts/templates/shifts/shift_template_detail.html:64
msgid "Shift partner: "
@@ -3959,104 +3959,103 @@ msgstr "Hat Qualifikationen : "
msgid "Shift"
msgstr "Schicht"
-#: shifts/templates/shifts/shift_detail.html:33
+#: shifts/templates/shifts/shift_detail.html:34
#, python-format
msgid "Statistics to date: %(total_valid_attendances)s people worked %(total_hours)s hours in %(no_of_past_shifts)s shifts so far."
msgstr "Bis heute: %(total_valid_attendances)s Leute haben %(total_hours)s Stunden in %(no_of_past_shifts)s Schichten gearbeitet."
-#: shifts/templates/shifts/shift_detail.html:40
-#: shifts/templates/shifts/shift_detail.html:39
+#: shifts/templates/shifts/shift_detail.html:44
msgid "Unwatch"
msgstr ""
-#: shifts/templates/shifts/shift_detail.html:45
+#: shifts/templates/shifts/shift_detail.html:50
msgid "Watch"
msgstr ""
-#: shifts/templates/shifts/shift_detail.html:51
+#: shifts/templates/shifts/shift_detail.html:56
msgid "Get printable version"
msgstr "Druckversion erhalten"
-#: shifts/templates/shifts/shift_detail.html:44
+#: shifts/templates/shifts/shift_detail.html:60
#: shifts/templates/shifts/shift_template_detail.html:20
msgid "Add a slot"
msgstr "Slot hinzufügen"
-#: shifts/templates/shifts/shift_detail.html:54
+#: shifts/templates/shifts/shift_detail.html:70
msgid "Cancel the entire shift"
msgstr "Ganze Schicht absagen"
-#: shifts/templates/shifts/shift_detail.html:60
+#: shifts/templates/shifts/shift_detail.html:76
#, fuzzy
#| msgid "Cancel the entire shift"
msgid "Delete the entire shift"
msgstr "Ganze Schicht absagen"
-#: shifts/templates/shifts/shift_detail.html:68
+#: shifts/templates/shifts/shift_detail.html:84
msgid "Generated from"
msgstr "Erzeugt von"
-#: shifts/templates/shifts/shift_detail.html:89
+#: shifts/templates/shifts/shift_detail.html:94
msgid "This shift has been cancelled: "
msgstr ""
-#: shifts/templates/shifts/shift_detail.html:94
+#: shifts/templates/shifts/shift_detail.html:99
msgid "This shift has been deleted."
msgstr ""
-#: shifts/templates/shifts/shift_detail.html:101
+#: shifts/templates/shifts/shift_detail.html:106
msgid "List of slots for this shift"
msgstr "Liste der Slots für diese Schicht"
-#: shifts/templates/shifts/shift_detail.html:104
+#: shifts/templates/shifts/shift_detail.html:109
msgid "Number"
msgstr "Nummer"
-#: shifts/templates/shifts/shift_detail.html:105
+#: shifts/templates/shifts/shift_detail.html:110
#: shifts/templates/shifts/shift_template_detail.html:38
msgid "Details"
msgstr "Details"
-#: shifts/templates/shifts/shift_detail.html:106
+#: shifts/templates/shifts/shift_detail.html:111
#: shifts/templates/shifts/shift_template_detail.html:40
msgid "Registered user"
msgstr "Angemeldete*r Nutzer*in"
-#: shifts/templates/shifts/shift_detail.html:107
+#: shifts/templates/shifts/shift_detail.html:112
msgid "Attendance"
msgstr "Anwesenheit"
-#: shifts/templates/shifts/shift_detail.html:109
+#: shifts/templates/shifts/shift_detail.html:114
msgid "Do you meet the requirements?"
msgstr "Erfüllst du die Voraussetzungen?"
-#: shifts/templates/shifts/shift_detail.html:114
+#: shifts/templates/shifts/shift_detail.html:119
#: shifts/templates/shifts/shift_template_detail.html:45
msgid "Member-Office actions"
msgstr "Mitgliederbüro Aktionen"
-#: shifts/templates/shifts/shift_detail.html:115
+#: shifts/templates/shifts/shift_detail.html:120
msgid "Previous attendances"
msgstr "Vorherige Anwesenheiten"
-#: shifts/templates/shifts/shift_detail.html:135
+#: shifts/templates/shifts/shift_detail.html:151
msgid "since"
msgstr "seit"
-#: shifts/templates/shifts/shift_detail.html:148
+#: shifts/templates/shifts/shift_detail.html:164
msgid "Vacant"
msgstr "Frei"
-#: shifts/templates/shifts/shift_detail.html:165
+#: shifts/templates/shifts/shift_detail.html:181
msgid "Cancels the search for a stand-in. Use this if you want to attend the shift."
msgstr "Beendet die Suche nach Vertretung. Benutze dies, wenn du die Schicht wahrnehmen möchtest."
-#: shifts/templates/shifts/shift_detail.html:167
-#: shifts/templates/shifts/shift_detail.html:307
+#: shifts/templates/shifts/shift_detail.html:183
+#: shifts/templates/shifts/shift_detail.html:323
msgid "Cancel looking for a stand-in"
msgstr "Beende die Suche nach Vertretung"
-#: shifts/templates/shifts/shift_detail.html:175
+#: shifts/templates/shifts/shift_detail.html:191
#, fuzzy, python-format
#| msgid ""
#| "You can only look for a\n"
@@ -4076,11 +4075,11 @@ msgid ""
" possible."
msgstr "Du kannst nur bis %(NB_DAYS_FOR_SELF_LOOK_FOR_STAND_IN)s Tage vor der Schicht nach einer Vertretung suchen. Wenn du die Schicht nicht wahrnehmen kannst, wende dich bitte schnellstmöglich an die Teamleitung."
-#: shifts/templates/shifts/shift_detail.html:188
+#: shifts/templates/shifts/shift_detail.html:204
msgid "Look for a stand-in"
msgstr "Suche nach Vertretung"
-#: shifts/templates/shifts/shift_detail.html:197
+#: shifts/templates/shifts/shift_detail.html:213
#, fuzzy, python-format
#| msgid ""
#| "You can only unregister\n"
@@ -4102,11 +4101,11 @@ msgid ""
" possible."
msgstr "Du kannst dich nur bis %(NB_DAYS_FOR_SELF_UNREGISTER)s Tage vor deiner Schicht abmelden. Von ABCD-Schichten kannst du dich überhaupt nicht abmelden. Wenn du die Schicht nicht wahrnehmen kannst, suche bitte eine Vertretung oder melde dich schnellstmöglich bei deiner Teamleitung."
-#: shifts/templates/shifts/shift_detail.html:212
+#: shifts/templates/shifts/shift_detail.html:228
msgid "Unregister myself"
msgstr "Melde mich ab"
-#: shifts/templates/shifts/shift_detail.html:217
+#: shifts/templates/shifts/shift_detail.html:233
#, fuzzy
#| msgid ""
#| "You can only register\n"
@@ -4135,12 +4134,12 @@ msgstr ""
"- die Schicht in der Zukunft liegt\n"
" "
-#: shifts/templates/shifts/shift_detail.html:259
+#: shifts/templates/shifts/shift_detail.html:275
#: shifts/templates/shifts/shift_template_detail.html:95
msgid "Not specified"
msgstr ""
-#: shifts/templates/shifts/shift_detail.html:342
+#: shifts/templates/shifts/shift_detail.html:358
msgid "Edit slot"
msgstr "Slot bearbeiten"
@@ -4717,7 +4716,7 @@ msgstr "Schichtkonto-Protokoll für: %(name)s"
msgid "Create manual shift account entry for: %(link)s"
msgstr "Erzeuge Schicht-Kontoeintrag für: %(link)s"
-#: shifts/views/views.py:389
+#: shifts/views/views.py:393
msgid "Frozen statuses updated."
msgstr ""
From 0eb3b08e0a80fb8734eff679d33dd9b9ac3f1c77 Mon Sep 17 00:00:00 2001
From: crosspolar <18083323+crosspolar@users.noreply.github.com>
Date: Sun, 8 Feb 2026 08:59:24 +0100
Subject: [PATCH 16/33] get_past_shifts_data should accept ShiftTemplate
---
tapir/shifts/tests/test_get_past_shifts_data.py | 4 ++--
tapir/shifts/views/views.py | 6 +++---
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/tapir/shifts/tests/test_get_past_shifts_data.py b/tapir/shifts/tests/test_get_past_shifts_data.py
index e5d13264b..1617bcb8e 100644
--- a/tapir/shifts/tests/test_get_past_shifts_data.py
+++ b/tapir/shifts/tests/test_get_past_shifts_data.py
@@ -34,7 +34,7 @@ def test_getPastShiftsData_differentStatesOfAttendandance_onlyCountDoneState(sel
slot=shift.slots.all()[1],
)
- context = ShiftDetailView.get_past_shifts_data(shift)
+ context = ShiftDetailView.get_past_shifts_data(shift.shift_template)
self.assertEqual(context["no_of_past_shifts"], 1)
self.assertEqual(context["total_valid_attendances"], 1)
self.assertEqual(
@@ -60,7 +60,7 @@ def test_getPastShiftsData_multipleAttendandances_correctValues(self):
ShiftAttendance.objects.create(
state=ShiftAttendance.State.DONE, user=user, slot=slots[i]
)
- context = ShiftDetailView.get_past_shifts_data(shifts[-1])
+ context = ShiftDetailView.get_past_shifts_data(shift_template)
self.assertEqual(context["no_of_past_shifts"], len(shifts))
self.assertEqual(context["total_valid_attendances"], len(users) * len(shifts))
diff --git a/tapir/shifts/views/views.py b/tapir/shifts/views/views.py
index f36d83c63..87e82e72e 100644
--- a/tapir/shifts/views/views.py
+++ b/tapir/shifts/views/views.py
@@ -243,7 +243,7 @@ def get_context_data(self, **kwargs):
)
if shift.shift_template:
- self.get_past_shifts_data(shift, context)
+ self.get_past_shifts_data(shift.shift_template, context)
for slot in slots:
slot.can_self_register = slot.user_can_attend(self.request.user)
@@ -269,9 +269,9 @@ def get_context_data(self, **kwargs):
return context
@staticmethod
- def get_past_shifts_data(shift: Shift, context: dict = {}):
+ def get_past_shifts_data(shift_template: ShiftTemplate, context: dict = {}):
past_shifts = Shift.objects.filter(
- shift_template=shift.shift_template, end_time__lt=timezone.now()
+ shift_template=shift_template, end_time__lt=timezone.now()
).annotate(
valid_attendance_count=Count(
"slots__attendances",
From 7f824d030b8492a4b2d0f96ba1043e6dcaf111f8 Mon Sep 17 00:00:00 2001
From: crosspolar <18083323+crosspolar@users.noreply.github.com>
Date: Sun, 8 Feb 2026 08:59:41 +0100
Subject: [PATCH 17/33]
test_get_past_shifts_data_changedShiftTemplateDuration_correctSum
---
.../shifts/tests/test_get_past_shifts_data.py | 35 ++++++++++++++++++-
1 file changed, 34 insertions(+), 1 deletion(-)
diff --git a/tapir/shifts/tests/test_get_past_shifts_data.py b/tapir/shifts/tests/test_get_past_shifts_data.py
index 1617bcb8e..b696e42cd 100644
--- a/tapir/shifts/tests/test_get_past_shifts_data.py
+++ b/tapir/shifts/tests/test_get_past_shifts_data.py
@@ -6,7 +6,11 @@
from django.utils import timezone
from tapir.accounts.tests.factories.factories import TapirUserFactory
-from tapir.shifts.models import ShiftTemplate, ShiftAttendance, Shift
+from tapir.shifts.models import (
+ ShiftTemplate,
+ ShiftAttendance,
+ Shift,
+)
from tapir.shifts.tests.factories import ShiftFactory, ShiftTemplateFactory
from tapir.shifts.views import ShiftDetailView
from tapir.utils.tests_utils import TapirFactoryTestBase
@@ -72,3 +76,32 @@ def test_getPastShiftsData_multipleAttendandances_correctValues(self):
for shift in shifts
),
)
+
+ def test_get_past_shifts_data_changedShiftTemplateDuration_correctSum(self):
+ shift_template: ShiftTemplate = ShiftTemplateFactory.create(
+ start_time=datetime.time(hour=10, tzinfo=datetime.timezone.utc),
+ end_time=datetime.time(hour=12, tzinfo=datetime.timezone.utc),
+ )
+ shift_2_hours = shift_template.create_shift_if_necessary(
+ timezone.now() - datetime.timedelta(days=7)
+ )
+ ShiftAttendance.objects.create(
+ user=TapirUserFactory.create(),
+ slot=shift_2_hours.slots.first(),
+ state=ShiftAttendance.State.DONE,
+ )
+
+ shift_template.end_time = datetime.time(hour=15, tzinfo=datetime.timezone.utc)
+ shift_template.save()
+
+ shift_5_hours = shift_template.create_shift_if_necessary(
+ timezone.now() - datetime.timedelta(days=14)
+ )
+ ShiftAttendance.objects.create(
+ user=TapirUserFactory.create(),
+ slot=shift_5_hours.slots.first(),
+ state=ShiftAttendance.State.DONE,
+ )
+
+ context = ShiftDetailView.get_past_shifts_data(shift_template)
+ self.assertAlmostEqual(context["total_hours"], 7.0)
From c435d6cf062148973927e3f46a05e9749188e177 Mon Sep 17 00:00:00 2001
From: crosspolar <18083323+crosspolar@users.noreply.github.com>
Date: Sun, 8 Feb 2026 09:00:03 +0100
Subject: [PATCH 18/33] typing
---
tapir/shifts/tests/test_get_past_shifts_data.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/tapir/shifts/tests/test_get_past_shifts_data.py b/tapir/shifts/tests/test_get_past_shifts_data.py
index b696e42cd..525950ad1 100644
--- a/tapir/shifts/tests/test_get_past_shifts_data.py
+++ b/tapir/shifts/tests/test_get_past_shifts_data.py
@@ -23,7 +23,7 @@ def test_getPastShiftsData_differentStatesOfAttendandance_onlyCountDoneState(sel
user_excused = TapirUserFactory.create()
shift_template: ShiftTemplate = ShiftTemplateFactory.create(nb_slots=2)
- shift = ShiftFactory(
+ shift: Shift = ShiftFactory.create(
start_time=timezone.now() - datetime.timedelta(days=1),
nb_slots=2,
shift_template=shift_template,
@@ -49,8 +49,8 @@ def test_getPastShiftsData_differentStatesOfAttendandance_onlyCountDoneState(sel
def test_getPastShiftsData_multipleAttendandances_correctValues(self):
users = TapirUserFactory.create_batch(5)
shift_template = ShiftTemplateFactory.create(nb_slots=len(users))
- shifts = [
- ShiftFactory(
+ shifts: list[Shift] = [
+ ShiftFactory.create(
start_time=timezone.now() - datetime.timedelta(days=i + 1),
nb_slots=len(users),
shift_template=shift_template,
From f962f3fd2612fe072120604269ce7ae7cdb14c67 Mon Sep 17 00:00:00 2001
From: crosspolar <18083323+crosspolar@users.noreply.github.com>
Date: Sun, 8 Feb 2026 09:30:37 +0100
Subject: [PATCH 19/33] remove non-needed imports
---
tapir/shifts/tests/test_get_past_shifts_data.py | 3 ---
1 file changed, 3 deletions(-)
diff --git a/tapir/shifts/tests/test_get_past_shifts_data.py b/tapir/shifts/tests/test_get_past_shifts_data.py
index 525950ad1..b2329c529 100644
--- a/tapir/shifts/tests/test_get_past_shifts_data.py
+++ b/tapir/shifts/tests/test_get_past_shifts_data.py
@@ -1,8 +1,5 @@
import datetime
-import pytest
-from celery.states import state
-from django.core.management import call_command
from django.utils import timezone
from tapir.accounts.tests.factories.factories import TapirUserFactory
From dc4f0d954812b50d92a459ae7746afb2d5069295 Mon Sep 17 00:00:00 2001
From: crosspolar <18083323+crosspolar@users.noreply.github.com>
Date: Sun, 8 Feb 2026 09:32:16 +0100
Subject: [PATCH 20/33] ensure consistent slot assignment
---
tapir/shifts/tests/test_get_past_shifts_data.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/tapir/shifts/tests/test_get_past_shifts_data.py b/tapir/shifts/tests/test_get_past_shifts_data.py
index b2329c529..f038c1fac 100644
--- a/tapir/shifts/tests/test_get_past_shifts_data.py
+++ b/tapir/shifts/tests/test_get_past_shifts_data.py
@@ -25,14 +25,14 @@ def test_getPastShiftsData_differentStatesOfAttendandance_onlyCountDoneState(sel
nb_slots=2,
shift_template=shift_template,
)
-
+ slots = list(shift.slots.all())
ShiftAttendance.objects.create(
- state=ShiftAttendance.State.DONE, user=user_done, slot=shift.slots.all()[0]
+ state=ShiftAttendance.State.DONE, user=user_done, slot=slots[0]
)
ShiftAttendance.objects.create(
state=ShiftAttendance.State.MISSED_EXCUSED,
user=user_excused,
- slot=shift.slots.all()[1],
+ slot=slots[1],
)
context = ShiftDetailView.get_past_shifts_data(shift.shift_template)
From fd44868b28d8de9bbf75f7c2e18a41d9d3fc4481 Mon Sep 17 00:00:00 2001
From: crosspolar <18083323+crosspolar@users.noreply.github.com>
Date: Sun, 8 Feb 2026 09:48:22 +0100
Subject: [PATCH 21/33] transl
---
.../locale/de/LC_MESSAGES/django.po | 99 ++++++++++---------
1 file changed, 52 insertions(+), 47 deletions(-)
diff --git a/tapir/translations/locale/de/LC_MESSAGES/django.po b/tapir/translations/locale/de/LC_MESSAGES/django.po
index b4497e421..0c00f74e5 100644
--- a/tapir/translations/locale/de/LC_MESSAGES/django.po
+++ b/tapir/translations/locale/de/LC_MESSAGES/django.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2026-02-07 14:00+0100\n"
+"POT-Creation-Date: 2026-02-08 09:36+0100\n"
"PO-Revision-Date: 2025-07-07 13:06+0000\n"
"Last-Translator: Weblate Admin \n"
"Language-Team: German \n"
@@ -341,7 +341,7 @@ msgstr "Benutzername bearbeiten"
#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:90
#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:113
#: core/templates/core/featureflag_list.html:30
-#: shifts/templates/shifts/shift_detail.html:61
+#: shifts/templates/shifts/shift_detail.html:66
#: shifts/templates/shifts/shift_template_detail.html:29
#: shifts/templates/shifts/shift_template_detail.html:117
#: shifts/templates/shifts/user_shifts_overview_tag.html:14
@@ -1082,7 +1082,7 @@ msgstr "Bewerber*in erzeugen"
#: coop/templates/coop/draftuser_register_form.html:29
#: shifts/templates/shifts/register_user_to_shift_slot.html:27
#: shifts/templates/shifts/register_user_to_shift_slot_template.html:26
-#: shifts/templates/shifts/shift_detail.html:243
+#: shifts/templates/shifts/shift_detail.html:248
#: shifts/templates/shifts/shift_template_detail.html:88
msgid "Register"
msgstr "Anmelden"
@@ -2383,7 +2383,7 @@ msgstr ""
#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:56
#: shifts/models.py:943 shifts/templates/shifts/shift_day_printable.html:207
#: shifts/templates/shifts/shift_day_printable.html:269
-#: shifts/templates/shifts/shift_detail.html:298
+#: shifts/templates/shifts/shift_detail.html:303
#: shifts/templates/shifts/shift_detail_printable.html:51
msgid "Attended"
msgstr "Teilgenommen"
@@ -2772,7 +2772,7 @@ msgstr "Ziel"
#: financingcampaign/templates/financingcampaign/general.html:29
#: financingcampaign/templates/financingcampaign/general.html:72
#: financingcampaign/templates/financingcampaign/general.html:114
-#: shifts/templates/shifts/shift_detail.html:109
+#: shifts/templates/shifts/shift_detail.html:114
msgid "Actions"
msgstr "Aktionen"
@@ -3095,8 +3095,8 @@ msgstr ""
msgid "I understand that this will delete the shift exemption and create a membership pause"
msgstr ""
-#: shifts/forms.py:726 shifts/forms.py:782 shifts/views/views.py:379
-#: shifts/views/views.py:380
+#: shifts/forms.py:726 shifts/forms.py:782 shifts/views/views.py:414
+#: shifts/views/views.py:415
msgid "Shift changes you would like to be informed about"
msgstr "Schicht-Änderungen, bei denen du informiert werden möchtest"
@@ -3195,7 +3195,7 @@ msgid "If enabled, members who register for that shift can choose themselves the
msgstr ""
#: shifts/models.py:411 shifts/models.py:834
-#: shifts/templates/shifts/shift_detail.html:112
+#: shifts/templates/shifts/shift_detail.html:117
#: shifts/templates/shifts/shift_template_detail.html:46
msgid "Chosen time"
msgstr "Ausgewählte Uhrzeit"
@@ -3220,7 +3220,7 @@ msgstr ""
msgid "This shift lets you choose at what time you come during the day of the shift. In order to help organising the attendance, please specify when you expect to come."
msgstr "Diese Schicht ermöglicht dir auszusuchen, wann du kommen magst. Um die Planung zu erleichtern, gib bitte deine erwartete Ankunftszeit an."
-#: shifts/models.py:944 shifts/templates/shifts/shift_detail.html:308
+#: shifts/models.py:944 shifts/templates/shifts/shift_detail.html:313
#: shifts/templates/shifts/shift_detail_printable.html:52
msgid "Missed"
msgstr "Nicht erschienen"
@@ -3228,17 +3228,17 @@ msgstr "Nicht erschienen"
#: shifts/models.py:945 shifts/templates/shifts/shift_day_printable.html:209
#: shifts/templates/shifts/shift_day_printable.html:274
#: shifts/templates/shifts/shift_day_printable.html:276
-#: shifts/templates/shifts/shift_detail.html:338
+#: shifts/templates/shifts/shift_detail.html:343
#: shifts/templates/shifts/shift_detail_printable.html:53
msgid "Excused"
msgstr "Entschuldigt"
-#: shifts/models.py:946 shifts/templates/shifts/shift_detail.html:346
+#: shifts/models.py:946 shifts/templates/shifts/shift_detail.html:351
msgid "Cancelled"
msgstr "Abgesagt"
#: shifts/models.py:947 shifts/templates/shifts/shift_day_printable.html:257
-#: shifts/templates/shifts/shift_detail.html:330
+#: shifts/templates/shifts/shift_detail.html:335
#: shifts/templates/shifts/shift_detail_printable.html:94
#: shifts/templates/shifts/shift_filters.html:83
msgid "Looking for a stand-in"
@@ -4065,7 +4065,7 @@ msgid "Flexible time not specified"
msgstr "Flexible Arbeitszeit nicht angegeben"
#: shifts/templates/shifts/shift_day_printable.html:252
-#: shifts/templates/shifts/shift_detail.html:139
+#: shifts/templates/shifts/shift_detail.html:144
#: shifts/templates/shifts/shift_detail_printable.html:90
#: shifts/templates/shifts/shift_template_detail.html:68
msgid "Shift partner: "
@@ -4075,106 +4075,111 @@ msgstr "Schicht-Partner: "
msgid "Only to be filled by teamleader, member office or employees."
msgstr "Nur durch Mitgliedsbüro, Teamleitung oder Angestellte auszufüllen."
-#: shifts/templates/shifts/shift_detail.html:39
+#: shifts/templates/shifts/shift_detail.html:34
+#, python-format
+msgid "Statistics to date: %(total_valid_attendances)s people worked %(total_hours)s hours in %(no_of_past_shifts)s shifts so far."
+msgstr ""
+
+#: shifts/templates/shifts/shift_detail.html:44
msgid "Unwatch"
msgstr "Nicht mehr beobachten"
-#: shifts/templates/shifts/shift_detail.html:45
+#: shifts/templates/shifts/shift_detail.html:50
#: shifts/templates/shifts/shiftwatch_overview.html:123
msgid "You will get mail-notifications when shifts you follow change — tailored to the types of updates you choose (e.g., needs help, is full, cancellations)."
msgstr "Du erhältst E-Mail-Benachrichtigungen, wenn sich Schichten, denen du folgst, ändern – angepasst an die von dir ausgewählten Arten von Aktualisierungen (z. B. Hilfe benötigt, voll, Stornierungen)."
-#: shifts/templates/shifts/shift_detail.html:46
+#: shifts/templates/shifts/shift_detail.html:51
msgid "Watch"
msgstr "Beobachten"
-#: shifts/templates/shifts/shift_detail.html:52
+#: shifts/templates/shifts/shift_detail.html:57
msgid "Get printable version"
msgstr "Druckversion erhalten"
-#: shifts/templates/shifts/shift_detail.html:56
+#: shifts/templates/shifts/shift_detail.html:61
#: shifts/templates/shifts/shift_template_detail.html:24
msgid "Add a slot"
msgstr "Slot hinzufügen"
-#: shifts/templates/shifts/shift_detail.html:66
+#: shifts/templates/shifts/shift_detail.html:71
msgid "Cancel the entire shift"
msgstr "Ganze Schicht absagen"
-#: shifts/templates/shifts/shift_detail.html:72
+#: shifts/templates/shifts/shift_detail.html:77
msgid "Delete the entire shift"
msgstr "Ganze Schicht löschen"
-#: shifts/templates/shifts/shift_detail.html:80
+#: shifts/templates/shifts/shift_detail.html:85
msgid "Generated from"
msgstr "Erzeugt von"
-#: shifts/templates/shifts/shift_detail.html:90
+#: shifts/templates/shifts/shift_detail.html:95
msgid "This shift has been cancelled: "
msgstr "Diese Schicht wurde abgesagt: "
-#: shifts/templates/shifts/shift_detail.html:95
+#: shifts/templates/shifts/shift_detail.html:100
msgid "This shift has been deleted."
msgstr "Diese Schicht wurde gelöscht."
-#: shifts/templates/shifts/shift_detail.html:102
+#: shifts/templates/shifts/shift_detail.html:107
msgid "List of slots for this shift"
msgstr "Liste der Slots für diese Schicht"
-#: shifts/templates/shifts/shift_detail.html:105
+#: shifts/templates/shifts/shift_detail.html:110
#: shifts/templates/shifts/shift_detail_printable.html:49
msgid "Slot"
msgstr "Platz"
-#: shifts/templates/shifts/shift_detail.html:105
+#: shifts/templates/shifts/shift_detail.html:110
msgid "Number"
msgstr "Nummer"
-#: shifts/templates/shifts/shift_detail.html:106
+#: shifts/templates/shifts/shift_detail.html:111
#: shifts/templates/shifts/shift_template_detail.html:42
msgid "Details"
msgstr "Details"
-#: shifts/templates/shifts/shift_detail.html:107
+#: shifts/templates/shifts/shift_detail.html:112
#: shifts/templates/shifts/shift_template_detail.html:44
msgid "Registered user"
msgstr "Angemeldete*r Nutzer*in"
-#: shifts/templates/shifts/shift_detail.html:108
+#: shifts/templates/shifts/shift_detail.html:113
msgid "Attendance"
msgstr "Anwesenheit"
-#: shifts/templates/shifts/shift_detail.html:110
+#: shifts/templates/shifts/shift_detail.html:115
msgid "Do you meet the requirements?"
msgstr "Erfüllst du die Voraussetzungen?"
-#: shifts/templates/shifts/shift_detail.html:115
+#: shifts/templates/shifts/shift_detail.html:120
#: shifts/templates/shifts/shift_template_detail.html:49
msgid "Member-Office actions"
msgstr "Mitgliederbüro Aktionen"
-#: shifts/templates/shifts/shift_detail.html:116
+#: shifts/templates/shifts/shift_detail.html:121
msgid "Previous attendances"
msgstr "Vorherige Anwesenheiten"
-#: shifts/templates/shifts/shift_detail.html:147
+#: shifts/templates/shifts/shift_detail.html:152
msgid "since"
msgstr "seit"
-#: shifts/templates/shifts/shift_detail.html:160
+#: shifts/templates/shifts/shift_detail.html:165
msgid "Vacant"
msgstr "Frei"
-#: shifts/templates/shifts/shift_detail.html:177
+#: shifts/templates/shifts/shift_detail.html:182
msgid "Cancels the search for a stand-in. Use this if you want to attend the shift."
msgstr "Beendet die Suche nach Vertretung. Benutze dies, wenn du die Schicht wahrnehmen möchtest."
-#: shifts/templates/shifts/shift_detail.html:179
-#: shifts/templates/shifts/shift_detail.html:319
+#: shifts/templates/shifts/shift_detail.html:184
+#: shifts/templates/shifts/shift_detail.html:324
msgid "Cancel looking for a stand-in"
msgstr "Beende die Suche nach Vertretung"
-#: shifts/templates/shifts/shift_detail.html:187
+#: shifts/templates/shifts/shift_detail.html:192
#, python-format
msgid ""
"You can only look for\n"
@@ -4195,11 +4200,11 @@ msgstr ""
" teilnehmen kannst, wende dich bitte schnellstmöglich\n"
" an deine Teamleitung."
-#: shifts/templates/shifts/shift_detail.html:200
+#: shifts/templates/shifts/shift_detail.html:205
msgid "Look for a stand-in"
msgstr "Suche nach Vertretung"
-#: shifts/templates/shifts/shift_detail.html:209
+#: shifts/templates/shifts/shift_detail.html:214
#, fuzzy, python-format
#| msgid ""
#| "You can only unregister\n"
@@ -4221,11 +4226,11 @@ msgid ""
" possible."
msgstr "Du kannst dich nur bis %(NB_DAYS_FOR_SELF_UNREGISTER)s Tage vor deiner Schicht abmelden. Von ABCD-Schichten kannst du dich überhaupt nicht abmelden. Wenn du die Schicht nicht wahrnehmen kannst, suche bitte eine Vertretung oder melde dich schnellstmöglich bei deiner Teamleitung."
-#: shifts/templates/shifts/shift_detail.html:224
+#: shifts/templates/shifts/shift_detail.html:229
msgid "Unregister myself"
msgstr "Melde mich ab"
-#: shifts/templates/shifts/shift_detail.html:229
+#: shifts/templates/shifts/shift_detail.html:234
#, fuzzy
#| msgid ""
#| "You can only register\n"
@@ -4254,12 +4259,12 @@ msgstr ""
"- die Schicht in der Zukunft liegt\n"
" "
-#: shifts/templates/shifts/shift_detail.html:271
+#: shifts/templates/shifts/shift_detail.html:276
#: shifts/templates/shifts/shift_template_detail.html:99
msgid "Not specified"
msgstr ""
-#: shifts/templates/shifts/shift_detail.html:354
+#: shifts/templates/shifts/shift_detail.html:359
msgid "Edit slot"
msgstr "Slot bearbeiten"
@@ -4470,7 +4475,7 @@ msgid "Recurring Shift Watches"
msgstr "Wiederholende Schichtbeobachtungen"
#: shifts/templates/shifts/shiftwatch_overview.html:25
-#: shifts/views/views.py:424
+#: shifts/views/views.py:459
msgid "Create a rule for recurring Shift Watches"
msgstr "Erstelle eine Regel für sich wiederholende Schichtbeobachtungen"
@@ -4923,11 +4928,11 @@ msgstr "Schichtkonto-Protokoll für: %(name)s"
msgid "Create manual shift account entry for: %(link)s"
msgstr "Erzeuge Schicht-Kontoeintrag für: %(link)s"
-#: shifts/views/views.py:362
+#: shifts/views/views.py:397
msgid "Frozen statuses updated."
msgstr ""
-#: shifts/views/views.py:427
+#: shifts/views/views.py:462
#, python-format
msgid "Please select either %(shift_template_group)s and/or weekdays, or alternatively %(shift_templates)s."
msgstr ""
From db15129ac8be2b705901f6d60abda67941fc39f4 Mon Sep 17 00:00:00 2001
From: crosspolar <18083323+crosspolar@users.noreply.github.com>
Date: Sun, 8 Feb 2026 11:18:34 +0100
Subject: [PATCH 22/33] also show date of first_shift
---
tapir/shifts/views/views.py | 27 ++++++++++++++++-----------
1 file changed, 16 insertions(+), 11 deletions(-)
diff --git a/tapir/shifts/views/views.py b/tapir/shifts/views/views.py
index 87e82e72e..cd06a193e 100644
--- a/tapir/shifts/views/views.py
+++ b/tapir/shifts/views/views.py
@@ -270,17 +270,21 @@ def get_context_data(self, **kwargs):
@staticmethod
def get_past_shifts_data(shift_template: ShiftTemplate, context: dict = {}):
- past_shifts = Shift.objects.filter(
- shift_template=shift_template, end_time__lt=timezone.now()
- ).annotate(
- valid_attendance_count=Count(
- "slots__attendances",
- filter=Q(
- slots__attendances__state__in=[
- ShiftAttendance.State.DONE,
- ]
- ),
+ past_shifts = (
+ Shift.objects.filter(
+ shift_template=shift_template, end_time__lt=timezone.now()
+ )
+ .annotate(
+ valid_attendance_count=Count(
+ "slots__attendances",
+ filter=Q(
+ slots__attendances__state__in=[
+ ShiftAttendance.State.DONE,
+ ]
+ ),
+ )
)
+ .order_by("start_time")
)
context["no_of_past_shifts"] = past_shifts.count()
@@ -296,7 +300,8 @@ def get_past_shifts_data(shift_template: ShiftTemplate, context: dict = {}):
/ 3600
for s in past_shifts
)
- context["total_hours"] = total_hours
+ context["total_hours"] = round(total_hours)
+ context["first_shift_date"] = past_shifts.first().start_time.date()
return context
From 36e36745bed053976a755d20425ced9f35b88ca4 Mon Sep 17 00:00:00 2001
From: crosspolar <18083323+crosspolar@users.noreply.github.com>
Date: Sun, 8 Feb 2026 11:22:27 +0100
Subject: [PATCH 23/33] show random text
---
.../shifts/templates/shifts/shift_detail.html | 3 +-
tapir/shifts/templatetags/shifts.py | 23 ++++++++++
.../locale/de/LC_MESSAGES/django.po | 44 ++++++++++++++-----
3 files changed, 56 insertions(+), 14 deletions(-)
diff --git a/tapir/shifts/templates/shifts/shift_detail.html b/tapir/shifts/templates/shifts/shift_detail.html
index c1aa0a8a1..f649239be 100644
--- a/tapir/shifts/templates/shifts/shift_detail.html
+++ b/tapir/shifts/templates/shifts/shift_detail.html
@@ -31,8 +31,7 @@
{% if total_valid_attendances and total_hours and no_of_past_shifts %}
-
+ title="{% random_shift_text total_hours no_of_past_shifts first_shift_date %}">
{% endif %}
{% if request.user|user_watching_shift:shift %}
diff --git a/tapir/shifts/templatetags/shifts.py b/tapir/shifts/templatetags/shifts.py
index d4698387e..6c5806c4e 100644
--- a/tapir/shifts/templatetags/shifts.py
+++ b/tapir/shifts/templatetags/shifts.py
@@ -1,4 +1,5 @@
import datetime
+import random
from django import template
from django.db.models import QuerySet
@@ -306,3 +307,25 @@ def shiftwatch_display_without_user(shiftwatch: ShiftWatch):
staffing_status,
recurring_part,
)
+
+
+@register.simple_tag
+def random_shift_text(total_hours, no_of_past_shifts, first_shift_date):
+ texts = [
+ _(
+ "{hours} hours across {shifts} total shifts have been contributed in this ABCD-shift since {date}."
+ ).format(hours=total_hours, shifts=no_of_past_shifts, date=first_shift_date),
+ _(
+ "Since {date}, this team has worked {hours} hours across {shifts} shifts."
+ ).format(date=first_shift_date, hours=total_hours, shifts=no_of_past_shifts),
+ _(
+ "Collaborative effort: {hours} hours spread over {shifts} shifts starting {date}."
+ ).format(hours=total_hours, shifts=no_of_past_shifts, date=first_shift_date),
+ _("{hours} hours of shared work in {shifts} shifts, since {date}.").format(
+ hours=total_hours, shifts=no_of_past_shifts, date=first_shift_date
+ ),
+ _(
+ "{hours} hours completed by this team in {shifts} shifts since {date}."
+ ).format(hours=total_hours, shifts=no_of_past_shifts, date=first_shift_date),
+ ]
+ return random.choice(texts)
diff --git a/tapir/translations/locale/de/LC_MESSAGES/django.po b/tapir/translations/locale/de/LC_MESSAGES/django.po
index 0c00f74e5..69f85d14a 100644
--- a/tapir/translations/locale/de/LC_MESSAGES/django.po
+++ b/tapir/translations/locale/de/LC_MESSAGES/django.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2026-02-08 09:36+0100\n"
+"POT-Creation-Date: 2026-02-08 11:19+0100\n"
"PO-Revision-Date: 2025-07-07 13:06+0000\n"
"Last-Translator: Weblate Admin \n"
"Language-Team: German \n"
@@ -3095,8 +3095,8 @@ msgstr ""
msgid "I understand that this will delete the shift exemption and create a membership pause"
msgstr ""
-#: shifts/forms.py:726 shifts/forms.py:782 shifts/views/views.py:414
-#: shifts/views/views.py:415
+#: shifts/forms.py:726 shifts/forms.py:782 shifts/views/views.py:419
+#: shifts/views/views.py:420
msgid "Shift changes you would like to be informed about"
msgstr "Schicht-Änderungen, bei denen du informiert werden möchtest"
@@ -4075,11 +4075,6 @@ msgstr "Schicht-Partner: "
msgid "Only to be filled by teamleader, member office or employees."
msgstr "Nur durch Mitgliedsbüro, Teamleitung oder Angestellte auszufüllen."
-#: shifts/templates/shifts/shift_detail.html:34
-#, python-format
-msgid "Statistics to date: %(total_valid_attendances)s people worked %(total_hours)s hours in %(no_of_past_shifts)s shifts so far."
-msgstr ""
-
#: shifts/templates/shifts/shift_detail.html:44
msgid "Unwatch"
msgstr "Nicht mehr beobachten"
@@ -4475,7 +4470,7 @@ msgid "Recurring Shift Watches"
msgstr "Wiederholende Schichtbeobachtungen"
#: shifts/templates/shifts/shiftwatch_overview.html:25
-#: shifts/views/views.py:459
+#: shifts/views/views.py:464
msgid "Create a rule for recurring Shift Watches"
msgstr "Erstelle eine Regel für sich wiederholende Schichtbeobachtungen"
@@ -4812,11 +4807,36 @@ msgstr "Nächste beobachtete Schicht"
msgid "No watched shifts"
msgstr "Keine beobachteten Schichten"
-#: shifts/templatetags/shifts.py:71 shifts/templatetags/shifts.py:176
+#: shifts/templatetags/shifts.py:72 shifts/templatetags/shifts.py:177
#: shifts/views/views.py:152
msgid "General"
msgstr "Allgemein"
+#: shifts/templatetags/shifts.py:316
+#, python-brace-format
+msgid "{hours} hours across {shifts} total shifts have been contributed in this ABCD-shift since {date}."
+msgstr "{hours} Stunden über {shifts} Schichten wurden seit dem {date} beigetragen."
+
+#: shifts/templatetags/shifts.py:319
+#, python-brace-format
+msgid "Since {date}, this team has worked {hours} hours across {shifts} shifts."
+msgstr "Seit dem {date} hat dieses Team {hours} Stunden in {shifts} Schichten gearbeitet."
+
+#: shifts/templatetags/shifts.py:322
+#, python-brace-format
+msgid "Collaborative effort: {hours} hours spread over {shifts} shifts starting {date}."
+msgstr "{hours} Stunden gemeinsamer Arbeit in {shifts} Schichten seit dem {date}."
+
+#: shifts/templatetags/shifts.py:324
+#, python-brace-format
+msgid "{hours} hours of shared work in {shifts} shifts, since {date}."
+msgstr "{hours} Stunden gemeinsamer Arbeit in {shifts} Schichten seit dem {date}."
+
+#: shifts/templatetags/shifts.py:328
+#, python-brace-format
+msgid "{hours} hours completed by this team in {shifts} shifts since {date}."
+msgstr "{hours} Stunden in {shifts} Schichten wurden seit dem {date} beigetragen."
+
#: shifts/views/attendance.py:202 shifts/views/attendance.py:346
#, python-format
msgid "Shift attendance: %(name)s"
@@ -4928,11 +4948,11 @@ msgstr "Schichtkonto-Protokoll für: %(name)s"
msgid "Create manual shift account entry for: %(link)s"
msgstr "Erzeuge Schicht-Kontoeintrag für: %(link)s"
-#: shifts/views/views.py:397
+#: shifts/views/views.py:402
msgid "Frozen statuses updated."
msgstr ""
-#: shifts/views/views.py:462
+#: shifts/views/views.py:467
#, python-format
msgid "Please select either %(shift_template_group)s and/or weekdays, or alternatively %(shift_templates)s."
msgstr ""
From fe14c971f30ef96a5606ef1ac18bafdd605cba05 Mon Sep 17 00:00:00 2001
From: crosspolar <18083323+crosspolar@users.noreply.github.com>
Date: Sun, 8 Feb 2026 11:26:01 +0100
Subject: [PATCH 24/33] transl
---
.../locale/de/LC_MESSAGES/django.po | 84 +++++++++----------
1 file changed, 42 insertions(+), 42 deletions(-)
diff --git a/tapir/translations/locale/de/LC_MESSAGES/django.po b/tapir/translations/locale/de/LC_MESSAGES/django.po
index 69f85d14a..f22ed35ed 100644
--- a/tapir/translations/locale/de/LC_MESSAGES/django.po
+++ b/tapir/translations/locale/de/LC_MESSAGES/django.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2026-02-08 11:19+0100\n"
+"POT-Creation-Date: 2026-02-08 11:25+0100\n"
"PO-Revision-Date: 2025-07-07 13:06+0000\n"
"Last-Translator: Weblate Admin \n"
"Language-Team: German \n"
@@ -341,7 +341,7 @@ msgstr "Benutzername bearbeiten"
#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:90
#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:113
#: core/templates/core/featureflag_list.html:30
-#: shifts/templates/shifts/shift_detail.html:66
+#: shifts/templates/shifts/shift_detail.html:65
#: shifts/templates/shifts/shift_template_detail.html:29
#: shifts/templates/shifts/shift_template_detail.html:117
#: shifts/templates/shifts/user_shifts_overview_tag.html:14
@@ -1082,7 +1082,7 @@ msgstr "Bewerber*in erzeugen"
#: coop/templates/coop/draftuser_register_form.html:29
#: shifts/templates/shifts/register_user_to_shift_slot.html:27
#: shifts/templates/shifts/register_user_to_shift_slot_template.html:26
-#: shifts/templates/shifts/shift_detail.html:248
+#: shifts/templates/shifts/shift_detail.html:247
#: shifts/templates/shifts/shift_template_detail.html:88
msgid "Register"
msgstr "Anmelden"
@@ -2383,7 +2383,7 @@ msgstr ""
#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:56
#: shifts/models.py:943 shifts/templates/shifts/shift_day_printable.html:207
#: shifts/templates/shifts/shift_day_printable.html:269
-#: shifts/templates/shifts/shift_detail.html:303
+#: shifts/templates/shifts/shift_detail.html:302
#: shifts/templates/shifts/shift_detail_printable.html:51
msgid "Attended"
msgstr "Teilgenommen"
@@ -2772,7 +2772,7 @@ msgstr "Ziel"
#: financingcampaign/templates/financingcampaign/general.html:29
#: financingcampaign/templates/financingcampaign/general.html:72
#: financingcampaign/templates/financingcampaign/general.html:114
-#: shifts/templates/shifts/shift_detail.html:114
+#: shifts/templates/shifts/shift_detail.html:113
msgid "Actions"
msgstr "Aktionen"
@@ -3195,7 +3195,7 @@ msgid "If enabled, members who register for that shift can choose themselves the
msgstr ""
#: shifts/models.py:411 shifts/models.py:834
-#: shifts/templates/shifts/shift_detail.html:117
+#: shifts/templates/shifts/shift_detail.html:116
#: shifts/templates/shifts/shift_template_detail.html:46
msgid "Chosen time"
msgstr "Ausgewählte Uhrzeit"
@@ -3220,7 +3220,7 @@ msgstr ""
msgid "This shift lets you choose at what time you come during the day of the shift. In order to help organising the attendance, please specify when you expect to come."
msgstr "Diese Schicht ermöglicht dir auszusuchen, wann du kommen magst. Um die Planung zu erleichtern, gib bitte deine erwartete Ankunftszeit an."
-#: shifts/models.py:944 shifts/templates/shifts/shift_detail.html:313
+#: shifts/models.py:944 shifts/templates/shifts/shift_detail.html:312
#: shifts/templates/shifts/shift_detail_printable.html:52
msgid "Missed"
msgstr "Nicht erschienen"
@@ -3228,17 +3228,17 @@ msgstr "Nicht erschienen"
#: shifts/models.py:945 shifts/templates/shifts/shift_day_printable.html:209
#: shifts/templates/shifts/shift_day_printable.html:274
#: shifts/templates/shifts/shift_day_printable.html:276
-#: shifts/templates/shifts/shift_detail.html:343
+#: shifts/templates/shifts/shift_detail.html:342
#: shifts/templates/shifts/shift_detail_printable.html:53
msgid "Excused"
msgstr "Entschuldigt"
-#: shifts/models.py:946 shifts/templates/shifts/shift_detail.html:351
+#: shifts/models.py:946 shifts/templates/shifts/shift_detail.html:350
msgid "Cancelled"
msgstr "Abgesagt"
#: shifts/models.py:947 shifts/templates/shifts/shift_day_printable.html:257
-#: shifts/templates/shifts/shift_detail.html:335
+#: shifts/templates/shifts/shift_detail.html:334
#: shifts/templates/shifts/shift_detail_printable.html:94
#: shifts/templates/shifts/shift_filters.html:83
msgid "Looking for a stand-in"
@@ -4065,7 +4065,7 @@ msgid "Flexible time not specified"
msgstr "Flexible Arbeitszeit nicht angegeben"
#: shifts/templates/shifts/shift_day_printable.html:252
-#: shifts/templates/shifts/shift_detail.html:144
+#: shifts/templates/shifts/shift_detail.html:143
#: shifts/templates/shifts/shift_detail_printable.html:90
#: shifts/templates/shifts/shift_template_detail.html:68
msgid "Shift partner: "
@@ -4075,106 +4075,106 @@ msgstr "Schicht-Partner: "
msgid "Only to be filled by teamleader, member office or employees."
msgstr "Nur durch Mitgliedsbüro, Teamleitung oder Angestellte auszufüllen."
-#: shifts/templates/shifts/shift_detail.html:44
+#: shifts/templates/shifts/shift_detail.html:43
msgid "Unwatch"
msgstr "Nicht mehr beobachten"
-#: shifts/templates/shifts/shift_detail.html:50
+#: shifts/templates/shifts/shift_detail.html:49
#: shifts/templates/shifts/shiftwatch_overview.html:123
msgid "You will get mail-notifications when shifts you follow change — tailored to the types of updates you choose (e.g., needs help, is full, cancellations)."
msgstr "Du erhältst E-Mail-Benachrichtigungen, wenn sich Schichten, denen du folgst, ändern – angepasst an die von dir ausgewählten Arten von Aktualisierungen (z. B. Hilfe benötigt, voll, Stornierungen)."
-#: shifts/templates/shifts/shift_detail.html:51
+#: shifts/templates/shifts/shift_detail.html:50
msgid "Watch"
msgstr "Beobachten"
-#: shifts/templates/shifts/shift_detail.html:57
+#: shifts/templates/shifts/shift_detail.html:56
msgid "Get printable version"
msgstr "Druckversion erhalten"
-#: shifts/templates/shifts/shift_detail.html:61
+#: shifts/templates/shifts/shift_detail.html:60
#: shifts/templates/shifts/shift_template_detail.html:24
msgid "Add a slot"
msgstr "Slot hinzufügen"
-#: shifts/templates/shifts/shift_detail.html:71
+#: shifts/templates/shifts/shift_detail.html:70
msgid "Cancel the entire shift"
msgstr "Ganze Schicht absagen"
-#: shifts/templates/shifts/shift_detail.html:77
+#: shifts/templates/shifts/shift_detail.html:76
msgid "Delete the entire shift"
msgstr "Ganze Schicht löschen"
-#: shifts/templates/shifts/shift_detail.html:85
+#: shifts/templates/shifts/shift_detail.html:84
msgid "Generated from"
msgstr "Erzeugt von"
-#: shifts/templates/shifts/shift_detail.html:95
+#: shifts/templates/shifts/shift_detail.html:94
msgid "This shift has been cancelled: "
msgstr "Diese Schicht wurde abgesagt: "
-#: shifts/templates/shifts/shift_detail.html:100
+#: shifts/templates/shifts/shift_detail.html:99
msgid "This shift has been deleted."
msgstr "Diese Schicht wurde gelöscht."
-#: shifts/templates/shifts/shift_detail.html:107
+#: shifts/templates/shifts/shift_detail.html:106
msgid "List of slots for this shift"
msgstr "Liste der Slots für diese Schicht"
-#: shifts/templates/shifts/shift_detail.html:110
+#: shifts/templates/shifts/shift_detail.html:109
#: shifts/templates/shifts/shift_detail_printable.html:49
msgid "Slot"
msgstr "Platz"
-#: shifts/templates/shifts/shift_detail.html:110
+#: shifts/templates/shifts/shift_detail.html:109
msgid "Number"
msgstr "Nummer"
-#: shifts/templates/shifts/shift_detail.html:111
+#: shifts/templates/shifts/shift_detail.html:110
#: shifts/templates/shifts/shift_template_detail.html:42
msgid "Details"
msgstr "Details"
-#: shifts/templates/shifts/shift_detail.html:112
+#: shifts/templates/shifts/shift_detail.html:111
#: shifts/templates/shifts/shift_template_detail.html:44
msgid "Registered user"
msgstr "Angemeldete*r Nutzer*in"
-#: shifts/templates/shifts/shift_detail.html:113
+#: shifts/templates/shifts/shift_detail.html:112
msgid "Attendance"
msgstr "Anwesenheit"
-#: shifts/templates/shifts/shift_detail.html:115
+#: shifts/templates/shifts/shift_detail.html:114
msgid "Do you meet the requirements?"
msgstr "Erfüllst du die Voraussetzungen?"
-#: shifts/templates/shifts/shift_detail.html:120
+#: shifts/templates/shifts/shift_detail.html:119
#: shifts/templates/shifts/shift_template_detail.html:49
msgid "Member-Office actions"
msgstr "Mitgliederbüro Aktionen"
-#: shifts/templates/shifts/shift_detail.html:121
+#: shifts/templates/shifts/shift_detail.html:120
msgid "Previous attendances"
msgstr "Vorherige Anwesenheiten"
-#: shifts/templates/shifts/shift_detail.html:152
+#: shifts/templates/shifts/shift_detail.html:151
msgid "since"
msgstr "seit"
-#: shifts/templates/shifts/shift_detail.html:165
+#: shifts/templates/shifts/shift_detail.html:164
msgid "Vacant"
msgstr "Frei"
-#: shifts/templates/shifts/shift_detail.html:182
+#: shifts/templates/shifts/shift_detail.html:181
msgid "Cancels the search for a stand-in. Use this if you want to attend the shift."
msgstr "Beendet die Suche nach Vertretung. Benutze dies, wenn du die Schicht wahrnehmen möchtest."
-#: shifts/templates/shifts/shift_detail.html:184
-#: shifts/templates/shifts/shift_detail.html:324
+#: shifts/templates/shifts/shift_detail.html:183
+#: shifts/templates/shifts/shift_detail.html:323
msgid "Cancel looking for a stand-in"
msgstr "Beende die Suche nach Vertretung"
-#: shifts/templates/shifts/shift_detail.html:192
+#: shifts/templates/shifts/shift_detail.html:191
#, python-format
msgid ""
"You can only look for\n"
@@ -4195,11 +4195,11 @@ msgstr ""
" teilnehmen kannst, wende dich bitte schnellstmöglich\n"
" an deine Teamleitung."
-#: shifts/templates/shifts/shift_detail.html:205
+#: shifts/templates/shifts/shift_detail.html:204
msgid "Look for a stand-in"
msgstr "Suche nach Vertretung"
-#: shifts/templates/shifts/shift_detail.html:214
+#: shifts/templates/shifts/shift_detail.html:213
#, fuzzy, python-format
#| msgid ""
#| "You can only unregister\n"
@@ -4221,11 +4221,11 @@ msgid ""
" possible."
msgstr "Du kannst dich nur bis %(NB_DAYS_FOR_SELF_UNREGISTER)s Tage vor deiner Schicht abmelden. Von ABCD-Schichten kannst du dich überhaupt nicht abmelden. Wenn du die Schicht nicht wahrnehmen kannst, suche bitte eine Vertretung oder melde dich schnellstmöglich bei deiner Teamleitung."
-#: shifts/templates/shifts/shift_detail.html:229
+#: shifts/templates/shifts/shift_detail.html:228
msgid "Unregister myself"
msgstr "Melde mich ab"
-#: shifts/templates/shifts/shift_detail.html:234
+#: shifts/templates/shifts/shift_detail.html:233
#, fuzzy
#| msgid ""
#| "You can only register\n"
@@ -4254,12 +4254,12 @@ msgstr ""
"- die Schicht in der Zukunft liegt\n"
" "
-#: shifts/templates/shifts/shift_detail.html:276
+#: shifts/templates/shifts/shift_detail.html:275
#: shifts/templates/shifts/shift_template_detail.html:99
msgid "Not specified"
msgstr ""
-#: shifts/templates/shifts/shift_detail.html:359
+#: shifts/templates/shifts/shift_detail.html:358
msgid "Edit slot"
msgstr "Slot bearbeiten"
From 44dd717b8cf334214c8acc2c36fe67d9a371c8a4 Mon Sep 17 00:00:00 2001
From: Frederik <18083323+crosspolar@users.noreply.github.com>
Date: Tue, 24 Feb 2026 20:11:05 +0100
Subject: [PATCH 25/33] Update tapir/shifts/tests/test_get_past_shifts_data.py
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Théophile MADET
---
tapir/shifts/tests/test_get_past_shifts_data.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tapir/shifts/tests/test_get_past_shifts_data.py b/tapir/shifts/tests/test_get_past_shifts_data.py
index f038c1fac..2dbefa141 100644
--- a/tapir/shifts/tests/test_get_past_shifts_data.py
+++ b/tapir/shifts/tests/test_get_past_shifts_data.py
@@ -74,7 +74,7 @@ def test_getPastShiftsData_multipleAttendandances_correctValues(self):
),
)
- def test_get_past_shifts_data_changedShiftTemplateDuration_correctSum(self):
+ def test_getPastShiftsData_changedShiftTemplateDuration_correctSum(self):
shift_template: ShiftTemplate = ShiftTemplateFactory.create(
start_time=datetime.time(hour=10, tzinfo=datetime.timezone.utc),
end_time=datetime.time(hour=12, tzinfo=datetime.timezone.utc),
From dcb891d7c1cb5e65aeb518c771e9df59a7f42361 Mon Sep 17 00:00:00 2001
From: Frederik <18083323+crosspolar@users.noreply.github.com>
Date: Tue, 24 Feb 2026 20:12:47 +0100
Subject: [PATCH 26/33] Update tapir/shifts/tests/test_get_past_shifts_data.py
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Théophile MADET
---
tapir/shifts/tests/test_get_past_shifts_data.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tapir/shifts/tests/test_get_past_shifts_data.py b/tapir/shifts/tests/test_get_past_shifts_data.py
index 2dbefa141..326a06b05 100644
--- a/tapir/shifts/tests/test_get_past_shifts_data.py
+++ b/tapir/shifts/tests/test_get_past_shifts_data.py
@@ -15,7 +15,7 @@
class ShiftGetPastShiftsStatisticsTests(TapirFactoryTestBase):
- def test_getPastShiftsData_differentStatesOfAttendandance_onlyCountDoneState(self):
+ def test_getPastShiftsData_differentStatesOfAttendance_onlyCountDoneState(self):
user_done = TapirUserFactory.create()
user_excused = TapirUserFactory.create()
From c8fa2748c2600267c9cb385a7281131be3f1c2bb Mon Sep 17 00:00:00 2001
From: crosspolar <18083323+crosspolar@users.noreply.github.com>
Date: Tue, 24 Feb 2026 20:15:09 +0100
Subject: [PATCH 27/33] fix mutable context
---
tapir/shifts/views/views.py | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/tapir/shifts/views/views.py b/tapir/shifts/views/views.py
index cd06a193e..c23069f92 100644
--- a/tapir/shifts/views/views.py
+++ b/tapir/shifts/views/views.py
@@ -269,7 +269,9 @@ def get_context_data(self, **kwargs):
return context
@staticmethod
- def get_past_shifts_data(shift_template: ShiftTemplate, context: dict = {}):
+ def get_past_shifts_data(shift_template: ShiftTemplate, context: dict = None):
+ if context is None:
+ context = {}
past_shifts = (
Shift.objects.filter(
shift_template=shift_template, end_time__lt=timezone.now()
From 2d8050c843b2569150d4a1c60fa4a2f5cc4ef462 Mon Sep 17 00:00:00 2001
From: crosspolar <18083323+crosspolar@users.noreply.github.com>
Date: Tue, 24 Feb 2026 20:28:13 +0100
Subject: [PATCH 28/33] test: use constant not expensive expression
---
tapir/shifts/tests/test_get_past_shifts_data.py | 8 +++-----
1 file changed, 3 insertions(+), 5 deletions(-)
diff --git a/tapir/shifts/tests/test_get_past_shifts_data.py b/tapir/shifts/tests/test_get_past_shifts_data.py
index 326a06b05..5d7a1cf41 100644
--- a/tapir/shifts/tests/test_get_past_shifts_data.py
+++ b/tapir/shifts/tests/test_get_past_shifts_data.py
@@ -67,11 +67,9 @@ def test_getPastShiftsData_multipleAttendandances_correctValues(self):
self.assertEqual(context["total_valid_attendances"], len(users) * len(shifts))
self.assertEqual(
context["total_hours"],
- len(users)
- * sum(
- (shift.end_time - shift.start_time).total_seconds() / 3600
- for shift in shifts
- ),
+ 70.0,
+ "The value should have been calculated by "
+ "len(users)* sum((shift.end_time - shift.start_time).total_seconds() / 3600 for shift in shifts)",
)
def test_getPastShiftsData_changedShiftTemplateDuration_correctSum(self):
From 9b51401d905f0e77015aa03161d1d45dc17c3760 Mon Sep 17 00:00:00 2001
From: crosspolar <18083323+crosspolar@users.noreply.github.com>
Date: Tue, 24 Feb 2026 20:36:49 +0100
Subject: [PATCH 29/33] transl
---
.../locale/de/LC_MESSAGES/django.po | 121 +++++++++++-------
1 file changed, 73 insertions(+), 48 deletions(-)
diff --git a/tapir/translations/locale/de/LC_MESSAGES/django.po b/tapir/translations/locale/de/LC_MESSAGES/django.po
index ed594021d..b8e099e6d 100644
--- a/tapir/translations/locale/de/LC_MESSAGES/django.po
+++ b/tapir/translations/locale/de/LC_MESSAGES/django.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2026-02-20 08:35+0100\n"
+"POT-Creation-Date: 2026-02-24 20:35+0100\n"
"PO-Revision-Date: 2025-07-07 13:06+0000\n"
"Last-Translator: Weblate Admin \n"
"Language-Team: German \n"
@@ -341,7 +341,7 @@ msgstr "Benutzername bearbeiten"
#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:90
#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:113
#: core/templates/core/featureflag_list.html:30
-#: shifts/templates/shifts/shift_detail.html:61
+#: shifts/templates/shifts/shift_detail.html:65
#: shifts/templates/shifts/shift_template_detail.html:29
#: shifts/templates/shifts/shift_template_detail.html:117
#: shifts/templates/shifts/user_shifts_overview_tag.html:14
@@ -1082,7 +1082,7 @@ msgstr "Bewerber*in erzeugen"
#: coop/templates/coop/draftuser_register_form.html:29
#: shifts/templates/shifts/register_user_to_shift_slot.html:27
#: shifts/templates/shifts/register_user_to_shift_slot_template.html:26
-#: shifts/templates/shifts/shift_detail.html:243
+#: shifts/templates/shifts/shift_detail.html:247
#: shifts/templates/shifts/shift_template_detail.html:88
msgid "Register"
msgstr "Anmelden"
@@ -2383,7 +2383,7 @@ msgstr ""
#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:56
#: shifts/models.py:943 shifts/templates/shifts/shift_day_printable.html:207
#: shifts/templates/shifts/shift_day_printable.html:269
-#: shifts/templates/shifts/shift_detail.html:298
+#: shifts/templates/shifts/shift_detail.html:302
#: shifts/templates/shifts/shift_detail_printable.html:51
msgid "Attended"
msgstr "Teilgenommen"
@@ -2772,7 +2772,7 @@ msgstr "Ziel"
#: financingcampaign/templates/financingcampaign/general.html:29
#: financingcampaign/templates/financingcampaign/general.html:72
#: financingcampaign/templates/financingcampaign/general.html:114
-#: shifts/templates/shifts/shift_detail.html:109
+#: shifts/templates/shifts/shift_detail.html:113
msgid "Actions"
msgstr "Aktionen"
@@ -3095,8 +3095,8 @@ msgstr ""
msgid "I understand that this will delete the shift exemption and create a membership pause"
msgstr ""
-#: shifts/forms.py:726 shifts/forms.py:782 shifts/views/views.py:379
-#: shifts/views/views.py:380
+#: shifts/forms.py:726 shifts/forms.py:782 shifts/views/views.py:421
+#: shifts/views/views.py:422
msgid "Shift changes you would like to be informed about"
msgstr "Schicht-Änderungen, bei denen du informiert werden möchtest"
@@ -3195,7 +3195,7 @@ msgid "If enabled, members who register for that shift can choose themselves the
msgstr ""
#: shifts/models.py:411 shifts/models.py:834
-#: shifts/templates/shifts/shift_detail.html:112
+#: shifts/templates/shifts/shift_detail.html:116
#: shifts/templates/shifts/shift_template_detail.html:46
msgid "Chosen time"
msgstr "Ausgewählte Uhrzeit"
@@ -3220,7 +3220,7 @@ msgstr ""
msgid "This shift lets you choose at what time you come during the day of the shift. In order to help organising the attendance, please specify when you expect to come."
msgstr "Diese Schicht ermöglicht dir auszusuchen, wann du kommen magst. Um die Planung zu erleichtern, gib bitte deine erwartete Ankunftszeit an."
-#: shifts/models.py:944 shifts/templates/shifts/shift_detail.html:308
+#: shifts/models.py:944 shifts/templates/shifts/shift_detail.html:312
#: shifts/templates/shifts/shift_detail_printable.html:52
msgid "Missed"
msgstr "Nicht erschienen"
@@ -3228,17 +3228,17 @@ msgstr "Nicht erschienen"
#: shifts/models.py:945 shifts/templates/shifts/shift_day_printable.html:209
#: shifts/templates/shifts/shift_day_printable.html:274
#: shifts/templates/shifts/shift_day_printable.html:276
-#: shifts/templates/shifts/shift_detail.html:338
+#: shifts/templates/shifts/shift_detail.html:342
#: shifts/templates/shifts/shift_detail_printable.html:53
msgid "Excused"
msgstr "Entschuldigt"
-#: shifts/models.py:946 shifts/templates/shifts/shift_detail.html:346
+#: shifts/models.py:946 shifts/templates/shifts/shift_detail.html:350
msgid "Cancelled"
msgstr "Abgesagt"
#: shifts/models.py:947 shifts/templates/shifts/shift_day_printable.html:257
-#: shifts/templates/shifts/shift_detail.html:330
+#: shifts/templates/shifts/shift_detail.html:334
#: shifts/templates/shifts/shift_detail_printable.html:94
#: shifts/templates/shifts/shift_filters.html:83
msgid "Looking for a stand-in"
@@ -4065,7 +4065,7 @@ msgid "Flexible time not specified"
msgstr "Flexible Arbeitszeit nicht angegeben"
#: shifts/templates/shifts/shift_day_printable.html:252
-#: shifts/templates/shifts/shift_detail.html:139
+#: shifts/templates/shifts/shift_detail.html:143
#: shifts/templates/shifts/shift_detail_printable.html:90
#: shifts/templates/shifts/shift_template_detail.html:68
msgid "Shift partner: "
@@ -4075,106 +4075,106 @@ msgstr "Schicht-Partner: "
msgid "Only to be filled by teamleader, member office or employees."
msgstr "Nur durch Mitgliedsbüro, Teamleitung oder Angestellte auszufüllen."
-#: shifts/templates/shifts/shift_detail.html:39
+#: shifts/templates/shifts/shift_detail.html:43
msgid "Unwatch"
msgstr "Nicht mehr beobachten"
-#: shifts/templates/shifts/shift_detail.html:45
+#: shifts/templates/shifts/shift_detail.html:49
#: shifts/templates/shifts/shiftwatch_overview.html:123
msgid "You will get mail-notifications when shifts you follow change — tailored to the types of updates you choose (e.g., needs help, is full, cancellations)."
msgstr "Du erhältst E-Mail-Benachrichtigungen, wenn sich Schichten, denen du folgst, ändern – angepasst an die von dir ausgewählten Arten von Aktualisierungen (z. B. Hilfe benötigt, voll, Stornierungen)."
-#: shifts/templates/shifts/shift_detail.html:46
+#: shifts/templates/shifts/shift_detail.html:50
msgid "Watch"
msgstr "Beobachten"
-#: shifts/templates/shifts/shift_detail.html:52
+#: shifts/templates/shifts/shift_detail.html:56
msgid "Get printable version"
msgstr "Druckversion erhalten"
-#: shifts/templates/shifts/shift_detail.html:56
+#: shifts/templates/shifts/shift_detail.html:60
#: shifts/templates/shifts/shift_template_detail.html:24
msgid "Add a slot"
msgstr "Slot hinzufügen"
-#: shifts/templates/shifts/shift_detail.html:66
+#: shifts/templates/shifts/shift_detail.html:70
msgid "Cancel the entire shift"
msgstr "Ganze Schicht absagen"
-#: shifts/templates/shifts/shift_detail.html:72
+#: shifts/templates/shifts/shift_detail.html:76
msgid "Delete the entire shift"
msgstr "Ganze Schicht löschen"
-#: shifts/templates/shifts/shift_detail.html:80
+#: shifts/templates/shifts/shift_detail.html:84
msgid "Generated from"
msgstr "Erzeugt von"
-#: shifts/templates/shifts/shift_detail.html:90
+#: shifts/templates/shifts/shift_detail.html:94
msgid "This shift has been cancelled: "
msgstr "Diese Schicht wurde abgesagt: "
-#: shifts/templates/shifts/shift_detail.html:95
+#: shifts/templates/shifts/shift_detail.html:99
msgid "This shift has been deleted."
msgstr "Diese Schicht wurde gelöscht."
-#: shifts/templates/shifts/shift_detail.html:102
+#: shifts/templates/shifts/shift_detail.html:106
msgid "List of slots for this shift"
msgstr "Liste der Slots für diese Schicht"
-#: shifts/templates/shifts/shift_detail.html:105
+#: shifts/templates/shifts/shift_detail.html:109
#: shifts/templates/shifts/shift_detail_printable.html:49
msgid "Slot"
msgstr "Platz"
-#: shifts/templates/shifts/shift_detail.html:105
+#: shifts/templates/shifts/shift_detail.html:109
msgid "Number"
msgstr "Nummer"
-#: shifts/templates/shifts/shift_detail.html:106
+#: shifts/templates/shifts/shift_detail.html:110
#: shifts/templates/shifts/shift_template_detail.html:42
msgid "Details"
msgstr "Details"
-#: shifts/templates/shifts/shift_detail.html:107
+#: shifts/templates/shifts/shift_detail.html:111
#: shifts/templates/shifts/shift_template_detail.html:44
msgid "Registered user"
msgstr "Angemeldete*r Nutzer*in"
-#: shifts/templates/shifts/shift_detail.html:108
+#: shifts/templates/shifts/shift_detail.html:112
msgid "Attendance"
msgstr "Anwesenheit"
-#: shifts/templates/shifts/shift_detail.html:110
+#: shifts/templates/shifts/shift_detail.html:114
msgid "Do you meet the requirements?"
msgstr "Erfüllst du die Voraussetzungen?"
-#: shifts/templates/shifts/shift_detail.html:115
+#: shifts/templates/shifts/shift_detail.html:119
#: shifts/templates/shifts/shift_template_detail.html:49
msgid "Member-Office actions"
msgstr "Mitgliederbüro Aktionen"
-#: shifts/templates/shifts/shift_detail.html:116
+#: shifts/templates/shifts/shift_detail.html:120
msgid "Previous attendances"
msgstr "Vorherige Anwesenheiten"
-#: shifts/templates/shifts/shift_detail.html:147
+#: shifts/templates/shifts/shift_detail.html:151
msgid "since"
msgstr "seit"
-#: shifts/templates/shifts/shift_detail.html:160
+#: shifts/templates/shifts/shift_detail.html:164
msgid "Vacant"
msgstr "Frei"
-#: shifts/templates/shifts/shift_detail.html:177
+#: shifts/templates/shifts/shift_detail.html:181
msgid "Cancels the search for a stand-in. Use this if you want to attend the shift."
msgstr "Beendet die Suche nach Vertretung. Benutze dies, wenn du die Schicht wahrnehmen möchtest."
-#: shifts/templates/shifts/shift_detail.html:179
-#: shifts/templates/shifts/shift_detail.html:319
+#: shifts/templates/shifts/shift_detail.html:183
+#: shifts/templates/shifts/shift_detail.html:323
msgid "Cancel looking for a stand-in"
msgstr "Beende die Suche nach Vertretung"
-#: shifts/templates/shifts/shift_detail.html:187
+#: shifts/templates/shifts/shift_detail.html:191
#, python-format
msgid ""
"You can only look for\n"
@@ -4195,11 +4195,11 @@ msgstr ""
" teilnehmen kannst, wende dich bitte schnellstmöglich\n"
" an deine Teamleitung."
-#: shifts/templates/shifts/shift_detail.html:200
+#: shifts/templates/shifts/shift_detail.html:204
msgid "Look for a stand-in"
msgstr "Suche nach Vertretung"
-#: shifts/templates/shifts/shift_detail.html:209
+#: shifts/templates/shifts/shift_detail.html:213
#, fuzzy, python-format
#| msgid ""
#| "You can only unregister\n"
@@ -4221,11 +4221,11 @@ msgid ""
" possible."
msgstr "Du kannst dich nur bis %(NB_DAYS_FOR_SELF_UNREGISTER)s Tage vor deiner Schicht abmelden. Von ABCD-Schichten kannst du dich überhaupt nicht abmelden. Wenn du die Schicht nicht wahrnehmen kannst, suche bitte eine Vertretung oder melde dich schnellstmöglich bei deiner Teamleitung."
-#: shifts/templates/shifts/shift_detail.html:224
+#: shifts/templates/shifts/shift_detail.html:228
msgid "Unregister myself"
msgstr "Melde mich ab"
-#: shifts/templates/shifts/shift_detail.html:229
+#: shifts/templates/shifts/shift_detail.html:233
#, fuzzy
#| msgid ""
#| "You can only register\n"
@@ -4254,12 +4254,12 @@ msgstr ""
"- die Schicht in der Zukunft liegt\n"
" "
-#: shifts/templates/shifts/shift_detail.html:271
+#: shifts/templates/shifts/shift_detail.html:275
#: shifts/templates/shifts/shift_template_detail.html:99
msgid "Not specified"
msgstr ""
-#: shifts/templates/shifts/shift_detail.html:354
+#: shifts/templates/shifts/shift_detail.html:358
msgid "Edit slot"
msgstr "Slot bearbeiten"
@@ -4470,7 +4470,7 @@ msgid "Recurring Shift Watches"
msgstr "Wiederholende Schichtbeobachtungen"
#: shifts/templates/shifts/shiftwatch_overview.html:25
-#: shifts/views/views.py:424
+#: shifts/views/views.py:466
msgid "Create a rule for recurring Shift Watches"
msgstr "Erstelle eine Regel für sich wiederholende Schichtbeobachtungen"
@@ -4807,11 +4807,36 @@ msgstr "Nächste beobachtete Schicht"
msgid "No watched shifts"
msgstr "Keine beobachteten Schichten"
-#: shifts/templatetags/shifts.py:71 shifts/templatetags/shifts.py:176
+#: shifts/templatetags/shifts.py:72 shifts/templatetags/shifts.py:177
#: shifts/views/views.py:152
msgid "General"
msgstr "Allgemein"
+#: shifts/templatetags/shifts.py:316
+#, python-brace-format
+msgid "{hours} hours across {shifts} total shifts have been contributed in this ABCD-shift since {date}."
+msgstr ""
+
+#: shifts/templatetags/shifts.py:319
+#, python-brace-format
+msgid "Since {date}, this team has worked {hours} hours across {shifts} shifts."
+msgstr ""
+
+#: shifts/templatetags/shifts.py:322
+#, python-brace-format
+msgid "Collaborative effort: {hours} hours spread over {shifts} shifts starting {date}."
+msgstr ""
+
+#: shifts/templatetags/shifts.py:324
+#, python-brace-format
+msgid "{hours} hours of shared work in {shifts} shifts, since {date}."
+msgstr ""
+
+#: shifts/templatetags/shifts.py:328
+#, python-brace-format
+msgid "{hours} hours completed by this team in {shifts} shifts since {date}."
+msgstr ""
+
#: shifts/views/attendance.py:202 shifts/views/attendance.py:346
#, python-format
msgid "Shift attendance: %(name)s"
@@ -4923,11 +4948,11 @@ msgstr "Schichtkonto-Protokoll für: %(name)s"
msgid "Create manual shift account entry for: %(link)s"
msgstr "Erzeuge Schicht-Kontoeintrag für: %(link)s"
-#: shifts/views/views.py:362
+#: shifts/views/views.py:404
msgid "Frozen statuses updated."
msgstr ""
-#: shifts/views/views.py:427
+#: shifts/views/views.py:469
#, python-format
msgid "Please select either %(shift_template_group)s and/or weekdays, or alternatively %(shift_templates)s."
msgstr ""
From 28ea082d274ba5470af06406de35e69fca26d250 Mon Sep 17 00:00:00 2001
From: crosspolar <18083323+crosspolar@users.noreply.github.com>
Date: Fri, 13 Mar 2026 15:29:56 +0100
Subject: [PATCH 30/33] remove random_shift_text
---
.../shifts/templates/shifts/shift_detail.html | 2 +-
tapir/shifts/templatetags/shifts.py | 22 -------------------
2 files changed, 1 insertion(+), 23 deletions(-)
diff --git a/tapir/shifts/templates/shifts/shift_detail.html b/tapir/shifts/templates/shifts/shift_detail.html
index f649239be..40a77433d 100644
--- a/tapir/shifts/templates/shifts/shift_detail.html
+++ b/tapir/shifts/templates/shifts/shift_detail.html
@@ -31,7 +31,7 @@
{% if total_valid_attendances and total_hours and no_of_past_shifts %}
+ title="Total Hours: {{ total_hours }}, Number of Past Shifts: {{ no_of_past_shifts }}, First Shift Date: {{ first_shift_date|date:'d.m.y' }}">
{% endif %}
{% if request.user|user_watching_shift:shift %}
diff --git a/tapir/shifts/templatetags/shifts.py b/tapir/shifts/templatetags/shifts.py
index 6c5806c4e..930d442b4 100644
--- a/tapir/shifts/templatetags/shifts.py
+++ b/tapir/shifts/templatetags/shifts.py
@@ -307,25 +307,3 @@ def shiftwatch_display_without_user(shiftwatch: ShiftWatch):
staffing_status,
recurring_part,
)
-
-
-@register.simple_tag
-def random_shift_text(total_hours, no_of_past_shifts, first_shift_date):
- texts = [
- _(
- "{hours} hours across {shifts} total shifts have been contributed in this ABCD-shift since {date}."
- ).format(hours=total_hours, shifts=no_of_past_shifts, date=first_shift_date),
- _(
- "Since {date}, this team has worked {hours} hours across {shifts} shifts."
- ).format(date=first_shift_date, hours=total_hours, shifts=no_of_past_shifts),
- _(
- "Collaborative effort: {hours} hours spread over {shifts} shifts starting {date}."
- ).format(hours=total_hours, shifts=no_of_past_shifts, date=first_shift_date),
- _("{hours} hours of shared work in {shifts} shifts, since {date}.").format(
- hours=total_hours, shifts=no_of_past_shifts, date=first_shift_date
- ),
- _(
- "{hours} hours completed by this team in {shifts} shifts since {date}."
- ).format(hours=total_hours, shifts=no_of_past_shifts, date=first_shift_date),
- ]
- return random.choice(texts)
From 5d670bee62835885502db79b26184b582612009d Mon Sep 17 00:00:00 2001
From: crosspolar <18083323+crosspolar@users.noreply.github.com>
Date: Fri, 13 Mar 2026 15:32:42 +0100
Subject: [PATCH 31/33] remove random_shift_text
---
.../locale/de/LC_MESSAGES/django.po | 96 +++++++++----------
1 file changed, 48 insertions(+), 48 deletions(-)
diff --git a/tapir/translations/locale/de/LC_MESSAGES/django.po b/tapir/translations/locale/de/LC_MESSAGES/django.po
index 896115661..1cf7bd05d 100644
--- a/tapir/translations/locale/de/LC_MESSAGES/django.po
+++ b/tapir/translations/locale/de/LC_MESSAGES/django.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2026-03-06 14:56+0100\n"
+"POT-Creation-Date: 2026-03-13 15:30+0100\n"
"PO-Revision-Date: 2025-07-07 13:06+0000\n"
"Last-Translator: Weblate Admin \n"
"Language-Team: German \n"
@@ -341,7 +341,7 @@ msgstr "Benutzername bearbeiten"
#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:90
#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:113
#: core/templates/core/featureflag_list.html:30
-#: shifts/templates/shifts/shift_detail.html:61
+#: shifts/templates/shifts/shift_detail.html:65
#: shifts/templates/shifts/shift_template_detail.html:29
#: shifts/templates/shifts/shift_template_detail.html:117
#: shifts/templates/shifts/user_shifts_overview_tag.html:14
@@ -1082,7 +1082,7 @@ msgstr "Bewerber*in erzeugen"
#: coop/templates/coop/draftuser_register_form.html:29
#: shifts/templates/shifts/register_user_to_shift_slot.html:27
#: shifts/templates/shifts/register_user_to_shift_slot_template.html:26
-#: shifts/templates/shifts/shift_detail.html:243
+#: shifts/templates/shifts/shift_detail.html:247
#: shifts/templates/shifts/shift_template_detail.html:88
msgid "Register"
msgstr "Anmelden"
@@ -2383,7 +2383,7 @@ msgstr ""
#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:56
#: shifts/models.py:943 shifts/templates/shifts/shift_day_printable.html:214
#: shifts/templates/shifts/shift_day_printable.html:276
-#: shifts/templates/shifts/shift_detail.html:298
+#: shifts/templates/shifts/shift_detail.html:302
#: shifts/templates/shifts/shift_detail_printable.html:51
msgid "Attended"
msgstr "Teilgenommen"
@@ -2772,7 +2772,7 @@ msgstr "Ziel"
#: financingcampaign/templates/financingcampaign/general.html:29
#: financingcampaign/templates/financingcampaign/general.html:72
#: financingcampaign/templates/financingcampaign/general.html:114
-#: shifts/templates/shifts/shift_detail.html:109
+#: shifts/templates/shifts/shift_detail.html:113
msgid "Actions"
msgstr "Aktionen"
@@ -3095,8 +3095,8 @@ msgstr ""
msgid "I understand that this will delete the shift exemption and create a membership pause"
msgstr ""
-#: shifts/forms.py:726 shifts/forms.py:782 shifts/views/views.py:379
-#: shifts/views/views.py:380
+#: shifts/forms.py:726 shifts/forms.py:782 shifts/views/views.py:421
+#: shifts/views/views.py:422
msgid "Shift changes you would like to be informed about"
msgstr "Schicht-Änderungen, bei denen du informiert werden möchtest"
@@ -3195,7 +3195,7 @@ msgid "If enabled, members who register for that shift can choose themselves the
msgstr ""
#: shifts/models.py:411 shifts/models.py:834
-#: shifts/templates/shifts/shift_detail.html:112
+#: shifts/templates/shifts/shift_detail.html:116
#: shifts/templates/shifts/shift_template_detail.html:46
msgid "Chosen time"
msgstr "Ausgewählte Uhrzeit"
@@ -3220,7 +3220,7 @@ msgstr ""
msgid "This shift lets you choose at what time you come during the day of the shift. In order to help organising the attendance, please specify when you expect to come."
msgstr "Diese Schicht ermöglicht dir auszusuchen, wann du kommen magst. Um die Planung zu erleichtern, gib bitte deine erwartete Ankunftszeit an."
-#: shifts/models.py:944 shifts/templates/shifts/shift_detail.html:308
+#: shifts/models.py:944 shifts/templates/shifts/shift_detail.html:312
#: shifts/templates/shifts/shift_detail_printable.html:52
msgid "Missed"
msgstr "Nicht erschienen"
@@ -3228,17 +3228,17 @@ msgstr "Nicht erschienen"
#: shifts/models.py:945 shifts/templates/shifts/shift_day_printable.html:216
#: shifts/templates/shifts/shift_day_printable.html:281
#: shifts/templates/shifts/shift_day_printable.html:283
-#: shifts/templates/shifts/shift_detail.html:338
+#: shifts/templates/shifts/shift_detail.html:342
#: shifts/templates/shifts/shift_detail_printable.html:53
msgid "Excused"
msgstr "Entschuldigt"
-#: shifts/models.py:946 shifts/templates/shifts/shift_detail.html:346
+#: shifts/models.py:946 shifts/templates/shifts/shift_detail.html:350
msgid "Cancelled"
msgstr "Abgesagt"
#: shifts/models.py:947 shifts/templates/shifts/shift_day_printable.html:264
-#: shifts/templates/shifts/shift_detail.html:330
+#: shifts/templates/shifts/shift_detail.html:334
#: shifts/templates/shifts/shift_detail_printable.html:94
#: shifts/templates/shifts/shift_filters.html:83
msgid "Looking for a stand-in"
@@ -4065,7 +4065,7 @@ msgid "Flexible time not specified"
msgstr "Flexible Arbeitszeit nicht angegeben"
#: shifts/templates/shifts/shift_day_printable.html:259
-#: shifts/templates/shifts/shift_detail.html:139
+#: shifts/templates/shifts/shift_detail.html:143
#: shifts/templates/shifts/shift_detail_printable.html:90
#: shifts/templates/shifts/shift_template_detail.html:68
msgid "Shift partner: "
@@ -4075,106 +4075,106 @@ msgstr "Schicht-Partner: "
msgid "Only to be filled by teamleader, member office or employees."
msgstr "Nur durch Mitgliedsbüro, Teamleitung oder Angestellte auszufüllen."
-#: shifts/templates/shifts/shift_detail.html:39
+#: shifts/templates/shifts/shift_detail.html:43
msgid "Unwatch"
msgstr "Nicht mehr beobachten"
-#: shifts/templates/shifts/shift_detail.html:45
+#: shifts/templates/shifts/shift_detail.html:49
#: shifts/templates/shifts/shiftwatch_overview.html:123
msgid "You will get mail-notifications when shifts you follow change — tailored to the types of updates you choose (e.g., needs help, is full, cancellations)."
msgstr "Du erhältst E-Mail-Benachrichtigungen, wenn sich Schichten, denen du folgst, ändern – angepasst an die von dir ausgewählten Arten von Aktualisierungen (z. B. Hilfe benötigt, voll, Stornierungen)."
-#: shifts/templates/shifts/shift_detail.html:46
+#: shifts/templates/shifts/shift_detail.html:50
msgid "Watch"
msgstr "Beobachten"
-#: shifts/templates/shifts/shift_detail.html:52
+#: shifts/templates/shifts/shift_detail.html:56
msgid "Get printable version"
msgstr "Druckversion erhalten"
-#: shifts/templates/shifts/shift_detail.html:56
+#: shifts/templates/shifts/shift_detail.html:60
#: shifts/templates/shifts/shift_template_detail.html:24
msgid "Add a slot"
msgstr "Slot hinzufügen"
-#: shifts/templates/shifts/shift_detail.html:66
+#: shifts/templates/shifts/shift_detail.html:70
msgid "Cancel the entire shift"
msgstr "Ganze Schicht absagen"
-#: shifts/templates/shifts/shift_detail.html:72
+#: shifts/templates/shifts/shift_detail.html:76
msgid "Delete the entire shift"
msgstr "Ganze Schicht löschen"
-#: shifts/templates/shifts/shift_detail.html:80
+#: shifts/templates/shifts/shift_detail.html:84
msgid "Generated from"
msgstr "Erzeugt von"
-#: shifts/templates/shifts/shift_detail.html:90
+#: shifts/templates/shifts/shift_detail.html:94
msgid "This shift has been cancelled: "
msgstr "Diese Schicht wurde abgesagt: "
-#: shifts/templates/shifts/shift_detail.html:95
+#: shifts/templates/shifts/shift_detail.html:99
msgid "This shift has been deleted."
msgstr "Diese Schicht wurde gelöscht."
-#: shifts/templates/shifts/shift_detail.html:102
+#: shifts/templates/shifts/shift_detail.html:106
msgid "List of slots for this shift"
msgstr "Liste der Slots für diese Schicht"
-#: shifts/templates/shifts/shift_detail.html:105
+#: shifts/templates/shifts/shift_detail.html:109
#: shifts/templates/shifts/shift_detail_printable.html:49
msgid "Slot"
msgstr "Platz"
-#: shifts/templates/shifts/shift_detail.html:105
+#: shifts/templates/shifts/shift_detail.html:109
msgid "Number"
msgstr "Nummer"
-#: shifts/templates/shifts/shift_detail.html:106
+#: shifts/templates/shifts/shift_detail.html:110
#: shifts/templates/shifts/shift_template_detail.html:42
msgid "Details"
msgstr "Details"
-#: shifts/templates/shifts/shift_detail.html:107
+#: shifts/templates/shifts/shift_detail.html:111
#: shifts/templates/shifts/shift_template_detail.html:44
msgid "Registered user"
msgstr "Angemeldete*r Nutzer*in"
-#: shifts/templates/shifts/shift_detail.html:108
+#: shifts/templates/shifts/shift_detail.html:112
msgid "Attendance"
msgstr "Anwesenheit"
-#: shifts/templates/shifts/shift_detail.html:110
+#: shifts/templates/shifts/shift_detail.html:114
msgid "Do you meet the requirements?"
msgstr "Erfüllst du die Voraussetzungen?"
-#: shifts/templates/shifts/shift_detail.html:115
+#: shifts/templates/shifts/shift_detail.html:119
#: shifts/templates/shifts/shift_template_detail.html:49
msgid "Member-Office actions"
msgstr "Mitgliederbüro Aktionen"
-#: shifts/templates/shifts/shift_detail.html:116
+#: shifts/templates/shifts/shift_detail.html:120
msgid "Previous attendances"
msgstr "Vorherige Anwesenheiten"
-#: shifts/templates/shifts/shift_detail.html:147
+#: shifts/templates/shifts/shift_detail.html:151
msgid "since"
msgstr "seit"
-#: shifts/templates/shifts/shift_detail.html:160
+#: shifts/templates/shifts/shift_detail.html:164
msgid "Vacant"
msgstr "Frei"
-#: shifts/templates/shifts/shift_detail.html:177
+#: shifts/templates/shifts/shift_detail.html:181
msgid "Cancels the search for a stand-in. Use this if you want to attend the shift."
msgstr "Beendet die Suche nach Vertretung. Benutze dies, wenn du die Schicht wahrnehmen möchtest."
-#: shifts/templates/shifts/shift_detail.html:179
-#: shifts/templates/shifts/shift_detail.html:319
+#: shifts/templates/shifts/shift_detail.html:183
+#: shifts/templates/shifts/shift_detail.html:323
msgid "Cancel looking for a stand-in"
msgstr "Beende die Suche nach Vertretung"
-#: shifts/templates/shifts/shift_detail.html:187
+#: shifts/templates/shifts/shift_detail.html:191
#, python-format
msgid ""
"You can only look for\n"
@@ -4195,11 +4195,11 @@ msgstr ""
" teilnehmen kannst, wende dich bitte schnellstmöglich\n"
" an deine Teamleitung."
-#: shifts/templates/shifts/shift_detail.html:200
+#: shifts/templates/shifts/shift_detail.html:204
msgid "Look for a stand-in"
msgstr "Suche nach Vertretung"
-#: shifts/templates/shifts/shift_detail.html:209
+#: shifts/templates/shifts/shift_detail.html:213
#, fuzzy, python-format
#| msgid ""
#| "You can only unregister\n"
@@ -4221,11 +4221,11 @@ msgid ""
" possible."
msgstr "Du kannst dich nur bis %(NB_DAYS_FOR_SELF_UNREGISTER)s Tage vor deiner Schicht abmelden. Von ABCD-Schichten kannst du dich überhaupt nicht abmelden. Wenn du die Schicht nicht wahrnehmen kannst, suche bitte eine Vertretung oder melde dich schnellstmöglich bei deiner Teamleitung."
-#: shifts/templates/shifts/shift_detail.html:224
+#: shifts/templates/shifts/shift_detail.html:228
msgid "Unregister myself"
msgstr "Melde mich ab"
-#: shifts/templates/shifts/shift_detail.html:229
+#: shifts/templates/shifts/shift_detail.html:233
#, fuzzy
#| msgid ""
#| "You can only register\n"
@@ -4254,12 +4254,12 @@ msgstr ""
"- die Schicht in der Zukunft liegt\n"
" "
-#: shifts/templates/shifts/shift_detail.html:271
+#: shifts/templates/shifts/shift_detail.html:275
#: shifts/templates/shifts/shift_template_detail.html:99
msgid "Not specified"
msgstr ""
-#: shifts/templates/shifts/shift_detail.html:354
+#: shifts/templates/shifts/shift_detail.html:358
msgid "Edit slot"
msgstr "Slot bearbeiten"
@@ -4470,7 +4470,7 @@ msgid "Recurring Shift Watches"
msgstr "Wiederholende Schichtbeobachtungen"
#: shifts/templates/shifts/shiftwatch_overview.html:25
-#: shifts/views/views.py:424
+#: shifts/views/views.py:466
msgid "Create a rule for recurring Shift Watches"
msgstr "Erstelle eine Regel für sich wiederholende Schichtbeobachtungen"
@@ -4807,7 +4807,7 @@ msgstr "Nächste beobachtete Schicht"
msgid "No watched shifts"
msgstr "Keine beobachteten Schichten"
-#: shifts/templatetags/shifts.py:71 shifts/templatetags/shifts.py:176
+#: shifts/templatetags/shifts.py:72 shifts/templatetags/shifts.py:177
#: shifts/views/views.py:152
msgid "General"
msgstr "Allgemein"
@@ -4923,11 +4923,11 @@ msgstr "Schichtkonto-Protokoll für: %(name)s"
msgid "Create manual shift account entry for: %(link)s"
msgstr "Erzeuge Schicht-Kontoeintrag für: %(link)s"
-#: shifts/views/views.py:362
+#: shifts/views/views.py:404
msgid "Frozen statuses updated."
msgstr ""
-#: shifts/views/views.py:427
+#: shifts/views/views.py:469
#, python-format
msgid "Please select either %(shift_template_group)s and/or weekdays, or alternatively %(shift_templates)s."
msgstr ""
From c2cfaa112a693c184ab5925a678d2c7450a9a593 Mon Sep 17 00:00:00 2001
From: crosspolar <18083323+crosspolar@users.noreply.github.com>
Date: Fri, 13 Mar 2026 15:34:47 +0100
Subject: [PATCH 32/33] transl
---
.../locale/de/LC_MESSAGES/django.po | 96 +++++++++----------
1 file changed, 48 insertions(+), 48 deletions(-)
diff --git a/tapir/translations/locale/de/LC_MESSAGES/django.po b/tapir/translations/locale/de/LC_MESSAGES/django.po
index d7f5e3750..f39223edf 100644
--- a/tapir/translations/locale/de/LC_MESSAGES/django.po
+++ b/tapir/translations/locale/de/LC_MESSAGES/django.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2026-03-11 07:46+0100\n"
+"POT-Creation-Date: 2026-03-13 15:34+0100\n"
"PO-Revision-Date: 2025-07-07 13:06+0000\n"
"Last-Translator: Weblate Admin \n"
"Language-Team: German \n"
@@ -341,7 +341,7 @@ msgstr "Benutzername bearbeiten"
#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:90
#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:113
#: core/templates/core/featureflag_list.html:30
-#: shifts/templates/shifts/shift_detail.html:61
+#: shifts/templates/shifts/shift_detail.html:65
#: shifts/templates/shifts/shift_template_detail.html:29
#: shifts/templates/shifts/shift_template_detail.html:117
#: shifts/templates/shifts/user_shifts_overview_tag.html:14
@@ -1083,7 +1083,7 @@ msgstr "Bewerber*in erzeugen"
#: coop/templates/coop/draftuser_register_form.html:29
#: shifts/templates/shifts/register_user_to_shift_slot.html:27
#: shifts/templates/shifts/register_user_to_shift_slot_template.html:26
-#: shifts/templates/shifts/shift_detail.html:243
+#: shifts/templates/shifts/shift_detail.html:247
#: shifts/templates/shifts/shift_template_detail.html:88
msgid "Register"
msgstr "Anmelden"
@@ -2384,7 +2384,7 @@ msgstr ""
#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:56
#: shifts/models.py:943 shifts/templates/shifts/shift_day_printable.html:214
#: shifts/templates/shifts/shift_day_printable.html:276
-#: shifts/templates/shifts/shift_detail.html:298
+#: shifts/templates/shifts/shift_detail.html:302
#: shifts/templates/shifts/shift_detail_printable.html:51
msgid "Attended"
msgstr "Teilgenommen"
@@ -2769,7 +2769,7 @@ msgstr "Ziel"
#: financingcampaign/templates/financingcampaign/general.html:29
#: financingcampaign/templates/financingcampaign/general.html:72
#: financingcampaign/templates/financingcampaign/general.html:114
-#: shifts/templates/shifts/shift_detail.html:109
+#: shifts/templates/shifts/shift_detail.html:113
msgid "Actions"
msgstr "Aktionen"
@@ -3092,8 +3092,8 @@ msgstr ""
msgid "I understand that this will delete the shift exemption and create a membership pause"
msgstr ""
-#: shifts/forms.py:726 shifts/forms.py:782 shifts/views/views.py:379
-#: shifts/views/views.py:380
+#: shifts/forms.py:726 shifts/forms.py:782 shifts/views/views.py:421
+#: shifts/views/views.py:422
msgid "Shift changes you would like to be informed about"
msgstr "Schicht-Änderungen, bei denen du informiert werden möchtest"
@@ -3192,7 +3192,7 @@ msgid "If enabled, members who register for that shift can choose themselves the
msgstr ""
#: shifts/models.py:411 shifts/models.py:834
-#: shifts/templates/shifts/shift_detail.html:112
+#: shifts/templates/shifts/shift_detail.html:116
#: shifts/templates/shifts/shift_template_detail.html:46
msgid "Chosen time"
msgstr "Ausgewählte Uhrzeit"
@@ -3217,7 +3217,7 @@ msgstr ""
msgid "This shift lets you choose at what time you come during the day of the shift. In order to help organising the attendance, please specify when you expect to come."
msgstr "Diese Schicht ermöglicht dir auszusuchen, wann du kommen magst. Um die Planung zu erleichtern, gib bitte deine erwartete Ankunftszeit an."
-#: shifts/models.py:944 shifts/templates/shifts/shift_detail.html:308
+#: shifts/models.py:944 shifts/templates/shifts/shift_detail.html:312
#: shifts/templates/shifts/shift_detail_printable.html:52
msgid "Missed"
msgstr "Nicht erschienen"
@@ -3225,17 +3225,17 @@ msgstr "Nicht erschienen"
#: shifts/models.py:945 shifts/templates/shifts/shift_day_printable.html:216
#: shifts/templates/shifts/shift_day_printable.html:281
#: shifts/templates/shifts/shift_day_printable.html:283
-#: shifts/templates/shifts/shift_detail.html:338
+#: shifts/templates/shifts/shift_detail.html:342
#: shifts/templates/shifts/shift_detail_printable.html:53
msgid "Excused"
msgstr "Entschuldigt"
-#: shifts/models.py:946 shifts/templates/shifts/shift_detail.html:346
+#: shifts/models.py:946 shifts/templates/shifts/shift_detail.html:350
msgid "Cancelled"
msgstr "Abgesagt"
#: shifts/models.py:947 shifts/templates/shifts/shift_day_printable.html:264
-#: shifts/templates/shifts/shift_detail.html:330
+#: shifts/templates/shifts/shift_detail.html:334
#: shifts/templates/shifts/shift_detail_printable.html:94
#: shifts/templates/shifts/shift_filters.html:83
msgid "Looking for a stand-in"
@@ -4062,7 +4062,7 @@ msgid "Flexible time not specified"
msgstr "Flexible Arbeitszeit nicht angegeben"
#: shifts/templates/shifts/shift_day_printable.html:259
-#: shifts/templates/shifts/shift_detail.html:139
+#: shifts/templates/shifts/shift_detail.html:143
#: shifts/templates/shifts/shift_detail_printable.html:90
#: shifts/templates/shifts/shift_template_detail.html:68
msgid "Shift partner: "
@@ -4072,106 +4072,106 @@ msgstr "Schicht-Partner: "
msgid "Only to be filled by teamleader, member office or employees."
msgstr "Nur durch Mitgliedsbüro, Teamleitung oder Angestellte auszufüllen."
-#: shifts/templates/shifts/shift_detail.html:39
+#: shifts/templates/shifts/shift_detail.html:43
msgid "Unwatch"
msgstr "Nicht mehr beobachten"
-#: shifts/templates/shifts/shift_detail.html:45
+#: shifts/templates/shifts/shift_detail.html:49
#: shifts/templates/shifts/shiftwatch_overview.html:123
msgid "You will get mail-notifications when shifts you follow change — tailored to the types of updates you choose (e.g., needs help, is full, cancellations)."
msgstr "Du erhältst E-Mail-Benachrichtigungen, wenn sich Schichten, denen du folgst, ändern – angepasst an die von dir ausgewählten Arten von Aktualisierungen (z. B. Hilfe benötigt, voll, Stornierungen)."
-#: shifts/templates/shifts/shift_detail.html:46
+#: shifts/templates/shifts/shift_detail.html:50
msgid "Watch"
msgstr "Beobachten"
-#: shifts/templates/shifts/shift_detail.html:52
+#: shifts/templates/shifts/shift_detail.html:56
msgid "Get printable version"
msgstr "Druckversion erhalten"
-#: shifts/templates/shifts/shift_detail.html:56
+#: shifts/templates/shifts/shift_detail.html:60
#: shifts/templates/shifts/shift_template_detail.html:24
msgid "Add a slot"
msgstr "Slot hinzufügen"
-#: shifts/templates/shifts/shift_detail.html:66
+#: shifts/templates/shifts/shift_detail.html:70
msgid "Cancel the entire shift"
msgstr "Ganze Schicht absagen"
-#: shifts/templates/shifts/shift_detail.html:72
+#: shifts/templates/shifts/shift_detail.html:76
msgid "Delete the entire shift"
msgstr "Ganze Schicht löschen"
-#: shifts/templates/shifts/shift_detail.html:80
+#: shifts/templates/shifts/shift_detail.html:84
msgid "Generated from"
msgstr "Erzeugt von"
-#: shifts/templates/shifts/shift_detail.html:90
+#: shifts/templates/shifts/shift_detail.html:94
msgid "This shift has been cancelled: "
msgstr "Diese Schicht wurde abgesagt: "
-#: shifts/templates/shifts/shift_detail.html:95
+#: shifts/templates/shifts/shift_detail.html:99
msgid "This shift has been deleted."
msgstr "Diese Schicht wurde gelöscht."
-#: shifts/templates/shifts/shift_detail.html:102
+#: shifts/templates/shifts/shift_detail.html:106
msgid "List of slots for this shift"
msgstr "Liste der Slots für diese Schicht"
-#: shifts/templates/shifts/shift_detail.html:105
+#: shifts/templates/shifts/shift_detail.html:109
#: shifts/templates/shifts/shift_detail_printable.html:49
msgid "Slot"
msgstr "Platz"
-#: shifts/templates/shifts/shift_detail.html:105
+#: shifts/templates/shifts/shift_detail.html:109
msgid "Number"
msgstr "Nummer"
-#: shifts/templates/shifts/shift_detail.html:106
+#: shifts/templates/shifts/shift_detail.html:110
#: shifts/templates/shifts/shift_template_detail.html:42
msgid "Details"
msgstr "Details"
-#: shifts/templates/shifts/shift_detail.html:107
+#: shifts/templates/shifts/shift_detail.html:111
#: shifts/templates/shifts/shift_template_detail.html:44
msgid "Registered user"
msgstr "Angemeldete*r Nutzer*in"
-#: shifts/templates/shifts/shift_detail.html:108
+#: shifts/templates/shifts/shift_detail.html:112
msgid "Attendance"
msgstr "Anwesenheit"
-#: shifts/templates/shifts/shift_detail.html:110
+#: shifts/templates/shifts/shift_detail.html:114
msgid "Do you meet the requirements?"
msgstr "Erfüllst du die Voraussetzungen?"
-#: shifts/templates/shifts/shift_detail.html:115
+#: shifts/templates/shifts/shift_detail.html:119
#: shifts/templates/shifts/shift_template_detail.html:49
msgid "Member-Office actions"
msgstr "Mitgliederbüro Aktionen"
-#: shifts/templates/shifts/shift_detail.html:116
+#: shifts/templates/shifts/shift_detail.html:120
msgid "Previous attendances"
msgstr "Vorherige Anwesenheiten"
-#: shifts/templates/shifts/shift_detail.html:147
+#: shifts/templates/shifts/shift_detail.html:151
msgid "since"
msgstr "seit"
-#: shifts/templates/shifts/shift_detail.html:160
+#: shifts/templates/shifts/shift_detail.html:164
msgid "Vacant"
msgstr "Frei"
-#: shifts/templates/shifts/shift_detail.html:177
+#: shifts/templates/shifts/shift_detail.html:181
msgid "Cancels the search for a stand-in. Use this if you want to attend the shift."
msgstr "Beendet die Suche nach Vertretung. Benutze dies, wenn du die Schicht wahrnehmen möchtest."
-#: shifts/templates/shifts/shift_detail.html:179
-#: shifts/templates/shifts/shift_detail.html:319
+#: shifts/templates/shifts/shift_detail.html:183
+#: shifts/templates/shifts/shift_detail.html:323
msgid "Cancel looking for a stand-in"
msgstr "Beende die Suche nach Vertretung"
-#: shifts/templates/shifts/shift_detail.html:187
+#: shifts/templates/shifts/shift_detail.html:191
#, python-format
msgid ""
"You can only look for\n"
@@ -4192,11 +4192,11 @@ msgstr ""
" teilnehmen kannst, wende dich bitte schnellstmöglich\n"
" an deine Teamleitung."
-#: shifts/templates/shifts/shift_detail.html:200
+#: shifts/templates/shifts/shift_detail.html:204
msgid "Look for a stand-in"
msgstr "Suche nach Vertretung"
-#: shifts/templates/shifts/shift_detail.html:209
+#: shifts/templates/shifts/shift_detail.html:213
#, fuzzy, python-format
#| msgid ""
#| "You can only unregister\n"
@@ -4218,11 +4218,11 @@ msgid ""
" possible."
msgstr "Du kannst dich nur bis %(NB_DAYS_FOR_SELF_UNREGISTER)s Tage vor deiner Schicht abmelden. Von ABCD-Schichten kannst du dich überhaupt nicht abmelden. Wenn du die Schicht nicht wahrnehmen kannst, suche bitte eine Vertretung oder melde dich schnellstmöglich bei deiner Teamleitung."
-#: shifts/templates/shifts/shift_detail.html:224
+#: shifts/templates/shifts/shift_detail.html:228
msgid "Unregister myself"
msgstr "Melde mich ab"
-#: shifts/templates/shifts/shift_detail.html:229
+#: shifts/templates/shifts/shift_detail.html:233
#, fuzzy
#| msgid ""
#| "You can only register\n"
@@ -4251,12 +4251,12 @@ msgstr ""
"- die Schicht in der Zukunft liegt\n"
" "
-#: shifts/templates/shifts/shift_detail.html:271
+#: shifts/templates/shifts/shift_detail.html:275
#: shifts/templates/shifts/shift_template_detail.html:99
msgid "Not specified"
msgstr ""
-#: shifts/templates/shifts/shift_detail.html:354
+#: shifts/templates/shifts/shift_detail.html:358
msgid "Edit slot"
msgstr "Slot bearbeiten"
@@ -4467,7 +4467,7 @@ msgid "Recurring Shift Watches"
msgstr "Wiederholende Schichtbeobachtungen"
#: shifts/templates/shifts/shiftwatch_overview.html:25
-#: shifts/views/views.py:424
+#: shifts/views/views.py:466
msgid "Create a rule for recurring Shift Watches"
msgstr "Erstelle eine Regel für sich wiederholende Schichtbeobachtungen"
@@ -4804,7 +4804,7 @@ msgstr "Nächste beobachtete Schicht"
msgid "No watched shifts"
msgstr "Keine beobachteten Schichten"
-#: shifts/templatetags/shifts.py:71 shifts/templatetags/shifts.py:176
+#: shifts/templatetags/shifts.py:72 shifts/templatetags/shifts.py:177
#: shifts/views/views.py:152
msgid "General"
msgstr "Allgemein"
@@ -4920,11 +4920,11 @@ msgstr "Schichtkonto-Protokoll für: %(name)s"
msgid "Create manual shift account entry for: %(link)s"
msgstr "Erzeuge Schicht-Kontoeintrag für: %(link)s"
-#: shifts/views/views.py:362
+#: shifts/views/views.py:404
msgid "Frozen statuses updated."
msgstr ""
-#: shifts/views/views.py:427
+#: shifts/views/views.py:469
#, python-format
msgid "Please select either %(shift_template_group)s and/or weekdays, or alternatively %(shift_templates)s."
msgstr ""
From c19bab27493a5619fa367bb11e60581cb5ab9d96 Mon Sep 17 00:00:00 2001
From: crosspolar <18083323+crosspolar@users.noreply.github.com>
Date: Sat, 28 Mar 2026 12:29:17 +0100
Subject: [PATCH 33/33] translation
---
.../locale/de/LC_MESSAGES/django.po | 96 +++++++++----------
1 file changed, 48 insertions(+), 48 deletions(-)
diff --git a/tapir/translations/locale/de/LC_MESSAGES/django.po b/tapir/translations/locale/de/LC_MESSAGES/django.po
index cd6b9f553..c15e963af 100644
--- a/tapir/translations/locale/de/LC_MESSAGES/django.po
+++ b/tapir/translations/locale/de/LC_MESSAGES/django.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2026-03-15 12:03+0100\n"
+"POT-Creation-Date: 2026-03-28 12:28+0100\n"
"PO-Revision-Date: 2025-07-07 13:06+0000\n"
"Last-Translator: Weblate Admin \n"
"Language-Team: German \n"
@@ -341,7 +341,7 @@ msgstr "Benutzername bearbeiten"
#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:90
#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:113
#: core/templates/core/featureflag_list.html:30
-#: shifts/templates/shifts/shift_detail.html:61
+#: shifts/templates/shifts/shift_detail.html:65
#: shifts/templates/shifts/shift_template_detail.html:29
#: shifts/templates/shifts/shift_template_detail.html:117
#: shifts/templates/shifts/user_shifts_overview_tag.html:14
@@ -1083,7 +1083,7 @@ msgstr "Bewerber*in erzeugen"
#: coop/templates/coop/draftuser_register_form.html:29
#: shifts/templates/shifts/register_user_to_shift_slot.html:27
#: shifts/templates/shifts/register_user_to_shift_slot_template.html:26
-#: shifts/templates/shifts/shift_detail.html:243
+#: shifts/templates/shifts/shift_detail.html:247
#: shifts/templates/shifts/shift_template_detail.html:88
msgid "Register"
msgstr "Anmelden"
@@ -2384,7 +2384,7 @@ msgstr ""
#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:56
#: shifts/models.py:934 shifts/templates/shifts/shift_day_printable.html:214
#: shifts/templates/shifts/shift_day_printable.html:276
-#: shifts/templates/shifts/shift_detail.html:298
+#: shifts/templates/shifts/shift_detail.html:302
#: shifts/templates/shifts/shift_detail_printable.html:51
msgid "Attended"
msgstr "Teilgenommen"
@@ -2769,7 +2769,7 @@ msgstr "Ziel"
#: financingcampaign/templates/financingcampaign/general.html:29
#: financingcampaign/templates/financingcampaign/general.html:72
#: financingcampaign/templates/financingcampaign/general.html:114
-#: shifts/templates/shifts/shift_detail.html:109
+#: shifts/templates/shifts/shift_detail.html:113
msgid "Actions"
msgstr "Aktionen"
@@ -3092,8 +3092,8 @@ msgstr ""
msgid "I understand that this will delete the shift exemption and create a membership pause"
msgstr ""
-#: shifts/forms.py:726 shifts/forms.py:782 shifts/views/views.py:379
-#: shifts/views/views.py:380
+#: shifts/forms.py:726 shifts/forms.py:782 shifts/views/views.py:421
+#: shifts/views/views.py:422
msgid "Shift changes you would like to be informed about"
msgstr "Schicht-Änderungen, bei denen du informiert werden möchtest"
@@ -3192,7 +3192,7 @@ msgid "If enabled, members who register for that shift can choose themselves the
msgstr ""
#: shifts/models.py:409 shifts/models.py:825
-#: shifts/templates/shifts/shift_detail.html:112
+#: shifts/templates/shifts/shift_detail.html:116
#: shifts/templates/shifts/shift_template_detail.html:46
msgid "Chosen time"
msgstr "Ausgewählte Uhrzeit"
@@ -3217,7 +3217,7 @@ msgstr ""
msgid "This shift lets you choose at what time you come during the day of the shift. In order to help organising the attendance, please specify when you expect to come."
msgstr "Diese Schicht ermöglicht dir auszusuchen, wann du kommen magst. Um die Planung zu erleichtern, gib bitte deine erwartete Ankunftszeit an."
-#: shifts/models.py:935 shifts/templates/shifts/shift_detail.html:308
+#: shifts/models.py:935 shifts/templates/shifts/shift_detail.html:312
#: shifts/templates/shifts/shift_detail_printable.html:52
msgid "Missed"
msgstr "Nicht erschienen"
@@ -3225,17 +3225,17 @@ msgstr "Nicht erschienen"
#: shifts/models.py:936 shifts/templates/shifts/shift_day_printable.html:216
#: shifts/templates/shifts/shift_day_printable.html:281
#: shifts/templates/shifts/shift_day_printable.html:283
-#: shifts/templates/shifts/shift_detail.html:338
+#: shifts/templates/shifts/shift_detail.html:342
#: shifts/templates/shifts/shift_detail_printable.html:53
msgid "Excused"
msgstr "Entschuldigt"
-#: shifts/models.py:937 shifts/templates/shifts/shift_detail.html:346
+#: shifts/models.py:937 shifts/templates/shifts/shift_detail.html:350
msgid "Cancelled"
msgstr "Abgesagt"
#: shifts/models.py:938 shifts/templates/shifts/shift_day_printable.html:264
-#: shifts/templates/shifts/shift_detail.html:330
+#: shifts/templates/shifts/shift_detail.html:334
#: shifts/templates/shifts/shift_detail_printable.html:94
#: shifts/templates/shifts/shift_filters.html:83
msgid "Looking for a stand-in"
@@ -4110,7 +4110,7 @@ msgid "Flexible time not specified"
msgstr "Flexible Arbeitszeit nicht angegeben"
#: shifts/templates/shifts/shift_day_printable.html:259
-#: shifts/templates/shifts/shift_detail.html:139
+#: shifts/templates/shifts/shift_detail.html:143
#: shifts/templates/shifts/shift_detail_printable.html:90
#: shifts/templates/shifts/shift_template_detail.html:68
msgid "Shift partner: "
@@ -4120,106 +4120,106 @@ msgstr "Schicht-Partner: "
msgid "Only to be filled by teamleader, member office or employees."
msgstr "Nur durch Mitgliedsbüro, Teamleitung oder Angestellte auszufüllen."
-#: shifts/templates/shifts/shift_detail.html:39
+#: shifts/templates/shifts/shift_detail.html:43
msgid "Unwatch"
msgstr "Nicht mehr beobachten"
-#: shifts/templates/shifts/shift_detail.html:45
+#: shifts/templates/shifts/shift_detail.html:49
#: shifts/templates/shifts/shiftwatch_overview.html:123
msgid "You will get mail-notifications when shifts you follow change — tailored to the types of updates you choose (e.g., needs help, is full, cancellations)."
msgstr "Du erhältst E-Mail-Benachrichtigungen, wenn sich Schichten, denen du folgst, ändern – angepasst an die von dir ausgewählten Arten von Aktualisierungen (z. B. Hilfe benötigt, voll, Stornierungen)."
-#: shifts/templates/shifts/shift_detail.html:46
+#: shifts/templates/shifts/shift_detail.html:50
msgid "Watch"
msgstr "Beobachten"
-#: shifts/templates/shifts/shift_detail.html:52
+#: shifts/templates/shifts/shift_detail.html:56
msgid "Get printable version"
msgstr "Druckversion erhalten"
-#: shifts/templates/shifts/shift_detail.html:56
+#: shifts/templates/shifts/shift_detail.html:60
#: shifts/templates/shifts/shift_template_detail.html:24
msgid "Add a slot"
msgstr "Slot hinzufügen"
-#: shifts/templates/shifts/shift_detail.html:66
+#: shifts/templates/shifts/shift_detail.html:70
msgid "Cancel the entire shift"
msgstr "Ganze Schicht absagen"
-#: shifts/templates/shifts/shift_detail.html:72
+#: shifts/templates/shifts/shift_detail.html:76
msgid "Delete the entire shift"
msgstr "Ganze Schicht löschen"
-#: shifts/templates/shifts/shift_detail.html:80
+#: shifts/templates/shifts/shift_detail.html:84
msgid "Generated from"
msgstr "Erzeugt von"
-#: shifts/templates/shifts/shift_detail.html:90
+#: shifts/templates/shifts/shift_detail.html:94
msgid "This shift has been cancelled: "
msgstr "Diese Schicht wurde abgesagt: "
-#: shifts/templates/shifts/shift_detail.html:95
+#: shifts/templates/shifts/shift_detail.html:99
msgid "This shift has been deleted."
msgstr "Diese Schicht wurde gelöscht."
-#: shifts/templates/shifts/shift_detail.html:102
+#: shifts/templates/shifts/shift_detail.html:106
msgid "List of slots for this shift"
msgstr "Liste der Slots für diese Schicht"
-#: shifts/templates/shifts/shift_detail.html:105
+#: shifts/templates/shifts/shift_detail.html:109
#: shifts/templates/shifts/shift_detail_printable.html:49
msgid "Slot"
msgstr "Platz"
-#: shifts/templates/shifts/shift_detail.html:105
+#: shifts/templates/shifts/shift_detail.html:109
msgid "Number"
msgstr "Nummer"
-#: shifts/templates/shifts/shift_detail.html:106
+#: shifts/templates/shifts/shift_detail.html:110
#: shifts/templates/shifts/shift_template_detail.html:42
msgid "Details"
msgstr "Details"
-#: shifts/templates/shifts/shift_detail.html:107
+#: shifts/templates/shifts/shift_detail.html:111
#: shifts/templates/shifts/shift_template_detail.html:44
msgid "Registered user"
msgstr "Angemeldete*r Nutzer*in"
-#: shifts/templates/shifts/shift_detail.html:108
+#: shifts/templates/shifts/shift_detail.html:112
msgid "Attendance"
msgstr "Anwesenheit"
-#: shifts/templates/shifts/shift_detail.html:110
+#: shifts/templates/shifts/shift_detail.html:114
msgid "Do you meet the requirements?"
msgstr "Erfüllst du die Voraussetzungen?"
-#: shifts/templates/shifts/shift_detail.html:115
+#: shifts/templates/shifts/shift_detail.html:119
#: shifts/templates/shifts/shift_template_detail.html:49
msgid "Member-Office actions"
msgstr "Mitgliederbüro Aktionen"
-#: shifts/templates/shifts/shift_detail.html:116
+#: shifts/templates/shifts/shift_detail.html:120
msgid "Previous attendances"
msgstr "Vorherige Anwesenheiten"
-#: shifts/templates/shifts/shift_detail.html:147
+#: shifts/templates/shifts/shift_detail.html:151
msgid "since"
msgstr "seit"
-#: shifts/templates/shifts/shift_detail.html:160
+#: shifts/templates/shifts/shift_detail.html:164
msgid "Vacant"
msgstr "Frei"
-#: shifts/templates/shifts/shift_detail.html:177
+#: shifts/templates/shifts/shift_detail.html:181
msgid "Cancels the search for a stand-in. Use this if you want to attend the shift."
msgstr "Beendet die Suche nach Vertretung. Benutze dies, wenn du die Schicht wahrnehmen möchtest."
-#: shifts/templates/shifts/shift_detail.html:179
-#: shifts/templates/shifts/shift_detail.html:319
+#: shifts/templates/shifts/shift_detail.html:183
+#: shifts/templates/shifts/shift_detail.html:323
msgid "Cancel looking for a stand-in"
msgstr "Beende die Suche nach Vertretung"
-#: shifts/templates/shifts/shift_detail.html:187
+#: shifts/templates/shifts/shift_detail.html:191
#, python-format
msgid ""
"You can only look for\n"
@@ -4240,11 +4240,11 @@ msgstr ""
" teilnehmen kannst, wende dich bitte schnellstmöglich\n"
" an deine Teamleitung."
-#: shifts/templates/shifts/shift_detail.html:200
+#: shifts/templates/shifts/shift_detail.html:204
msgid "Look for a stand-in"
msgstr "Suche nach Vertretung"
-#: shifts/templates/shifts/shift_detail.html:209
+#: shifts/templates/shifts/shift_detail.html:213
#, fuzzy, python-format
#| msgid ""
#| "You can only unregister\n"
@@ -4266,11 +4266,11 @@ msgid ""
" possible."
msgstr "Du kannst dich nur bis %(NB_DAYS_FOR_SELF_UNREGISTER)s Tage vor deiner Schicht abmelden. Von ABCD-Schichten kannst du dich überhaupt nicht abmelden. Wenn du die Schicht nicht wahrnehmen kannst, suche bitte eine Vertretung oder melde dich schnellstmöglich bei deiner Teamleitung."
-#: shifts/templates/shifts/shift_detail.html:224
+#: shifts/templates/shifts/shift_detail.html:228
msgid "Unregister myself"
msgstr "Melde mich ab"
-#: shifts/templates/shifts/shift_detail.html:229
+#: shifts/templates/shifts/shift_detail.html:233
#, fuzzy
#| msgid ""
#| "You can only register\n"
@@ -4299,12 +4299,12 @@ msgstr ""
"- die Schicht in der Zukunft liegt\n"
" "
-#: shifts/templates/shifts/shift_detail.html:271
+#: shifts/templates/shifts/shift_detail.html:275
#: shifts/templates/shifts/shift_template_detail.html:99
msgid "Not specified"
msgstr ""
-#: shifts/templates/shifts/shift_detail.html:354
+#: shifts/templates/shifts/shift_detail.html:358
msgid "Edit slot"
msgstr "Slot bearbeiten"
@@ -4515,7 +4515,7 @@ msgid "Recurring Shift Watches"
msgstr "Wiederholende Schichtbeobachtungen"
#: shifts/templates/shifts/shiftwatch_overview.html:25
-#: shifts/views/views.py:424
+#: shifts/views/views.py:466
msgid "Create a rule for recurring Shift Watches"
msgstr "Erstelle eine Regel für sich wiederholende Schichtbeobachtungen"
@@ -4852,7 +4852,7 @@ msgstr "Nächste beobachtete Schicht"
msgid "No watched shifts"
msgstr "Keine beobachteten Schichten"
-#: shifts/templatetags/shifts.py:70 shifts/templatetags/shifts.py:175
+#: shifts/templatetags/shifts.py:71 shifts/templatetags/shifts.py:176
#: shifts/views/views.py:152
msgid "General"
msgstr "Allgemein"
@@ -4968,11 +4968,11 @@ msgstr "Schichtkonto-Protokoll für: %(name)s"
msgid "Create manual shift account entry for: %(link)s"
msgstr "Erzeuge Schicht-Kontoeintrag für: %(link)s"
-#: shifts/views/views.py:362
+#: shifts/views/views.py:404
msgid "Frozen statuses updated."
msgstr ""
-#: shifts/views/views.py:427
+#: shifts/views/views.py:469
#, python-format
msgid "Please select either %(shift_template_group)s and/or weekdays, or alternatively %(shift_templates)s."
msgstr ""