From 5966569ad95a3ec517199a90a53875106370cb1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Madet?= Date: Mon, 16 Dec 2024 16:31:38 +0100 Subject: [PATCH 01/13] WIP attendance changes. --- tapir/shifts/models.py | 10 -- .../reasons_cant_self_unregister_service.py | 46 ++++++++ .../shifts/templates/shifts/cant_attend.html | 101 ++++++++++++++++++ .../shifts/templates/shifts/shift_detail.html | 49 +-------- tapir/shifts/urls.py | 5 + tapir/shifts/views/attendance.py | 59 +++++++++- 6 files changed, 214 insertions(+), 56 deletions(-) create mode 100644 tapir/shifts/services/reasons_cant_self_unregister_service.py create mode 100644 tapir/shifts/templates/shifts/cant_attend.html diff --git a/tapir/shifts/models.py b/tapir/shifts/models.py index 03d140e8d..7f6cd7b44 100644 --- a/tapir/shifts/models.py +++ b/tapir/shifts/models.py @@ -731,16 +731,6 @@ def user_can_self_unregister(self, user: TapirUser) -> bool: and early_enough ) - def user_can_look_for_standin(self, user: TapirUser) -> bool: - user_is_registered_to_slot = ( - self.get_valid_attendance() is not None - and self.get_valid_attendance().user == user - ) - early_enough = ( - self.shift.start_time - timezone.now() - ).days >= Shift.NB_DAYS_FOR_SELF_LOOK_FOR_STAND_IN - return user_is_registered_to_slot and early_enough - def update_attendance_from_template(self): """Updates the attendance of this slot. diff --git a/tapir/shifts/services/reasons_cant_self_unregister_service.py b/tapir/shifts/services/reasons_cant_self_unregister_service.py new file mode 100644 index 000000000..e6116f889 --- /dev/null +++ b/tapir/shifts/services/reasons_cant_self_unregister_service.py @@ -0,0 +1,46 @@ +from django.utils import timezone +from django.utils.translation import gettext_lazy as _ + +from tapir.accounts.models import TapirUser +from tapir.shifts.models import ShiftAttendance, ShiftAttendanceTemplate, Shift + + +class ReasonsCantSelfUnregisterService: + @staticmethod + def should_show_is_abcd_attendance_reason( + user: TapirUser, attendance: ShiftAttendance + ): + if not attendance.slot.slot_template: + return False + + return ShiftAttendanceTemplate.objects.filter( + user=user, slot_template=attendance.slot.slot_template + ).exists() + + @staticmethod + def should_show_not_enough_days_before_shift_reason(attendance: ShiftAttendance): + return ( + attendance.slot.shift.start_time.date() - timezone.now().date() + ).days <= Shift.NB_DAYS_FOR_SELF_UNREGISTER + + @classmethod + def build_reasons_why_cant_self_unregister( + cls, user: TapirUser, attendance: ShiftAttendance + ): + reasons_why_cant_self_unregister = [] + + if cls.should_show_not_enough_days_before_shift_reason(attendance): + reasons_why_cant_self_unregister.append( + _( + "It is only possible to unregister from a shift at least 7 days before the shift." + ) + ) + + if cls.should_show_is_abcd_attendance_reason(user, attendance): + reasons_why_cant_self_unregister.append( + _( + "It is not possible to unregister from a shift that comes from your ABCD shift." + ) + ) + + return reasons_why_cant_self_unregister diff --git a/tapir/shifts/templates/shifts/cant_attend.html b/tapir/shifts/templates/shifts/cant_attend.html new file mode 100644 index 000000000..c823816ab --- /dev/null +++ b/tapir/shifts/templates/shifts/cant_attend.html @@ -0,0 +1,101 @@ +{% extends "core/base.html" %} +{% load django_bootstrap5 %} +{% load i18n %} +{% load static %} +{% load core %} +{% block title %} + {% translate "Can't attend:" %} {{ attendance }} +{% endblock title %} +{% block content %} +
+
+

+ If you can't attend +
+ {{ attendance.slot }} +

+
+
+
+
+
{% translate "Option 1: unregister" %}
+
+
+ {% if can_unregister %} + {% blocktranslate %} + You can unregister from this shift. + You will be removed from the attendance list and won't get any negative + point. + {% endblocktranslate %} + {% else %} + {% blocktranslate %} + You cannot unregister from this shift.
+ The reasons are: + {% endblocktranslate %} +
    + {% for reason in reasons_why_cant_self_unregister %}
  • {{ reason }}
  • {% endfor %} +
+ {% endif %} +
+
+ {% csrf_token %} +
+ +
+
+
+
+
+
+
+
{% translate "Option 2: enable looking for a stand-in" %}
+
+ {% blocktranslate %} +

+ If you cannot unregister from the shift, your other option is to look for a replacement ("stand-in"). + This will notify the team that you can't come, and will allow other members to take over your slot. + Please enable this as soon as you know you won't be able to come to your shift. +

+

+ This is always possible up to the time of the shift. +

+

+ After enabling this, it is still absolutely necessary that you contact the member office + ({{ email_address_member_office }}) + as soon as possible. +

+

+ If you have a valid reason for not coming (see the + Member Manual), + the member office will unregister you from the shift and you won't get any negative points. +

+

+ If you don't have a valid reason for not coming, you may still get lucky: + someone may take over your shift. The shift calendar shows where someone is looking for a stand-in.
+ If that happens, you will be notified by email and will not get any negative points.
+ There is no guarantee that this happens. If your slot is not taken over, you will get negative points. +

+ {% endblocktranslate %} +
+ {% csrf_token %} +
+ +
+
+
+
+
+
+
+{% endblock content %} diff --git a/tapir/shifts/templates/shifts/shift_detail.html b/tapir/shifts/templates/shifts/shift_detail.html index 3fad01b6a..2979d76d4 100644 --- a/tapir/shifts/templates/shifts/shift_detail.html +++ b/tapir/shifts/templates/shifts/shift_detail.html @@ -154,51 +154,12 @@
#{{ forloop.counter }}
{% else %} -
- {% csrf_token %} - {% blocktranslate asvar stand_in_tooltip %}You can only look for - a - stand-in - {{ NB_DAYS_FOR_SELF_LOOK_FOR_STAND_IN }} days before the - shift. If - you can't - attend, contact your shift leader as soon as - possible.{% endblocktranslate %} - - - -
+ + event_busy + {% translate "I can't attend this shift" %} + {% endif %} -
- {% csrf_token %} - {% blocktranslate asvar self_unregister_tooltip %}You can only - unregister - yourself - {{ NB_DAYS_FOR_SELF_UNREGISTER }} days before the shift. Also, - ABCD-Shifts - can't be unregistered from. If you can't - attend, look for a stand-in or contact your shift leader as soon - as - possible.{% endblocktranslate %} - - - -
{% elif not slot.is_occupied %} {% blocktranslate asvar self_register_tooltip %}You can only register yourself diff --git a/tapir/shifts/urls.py b/tapir/shifts/urls.py index 0f46a3336..14a10cb67 100644 --- a/tapir/shifts/urls.py +++ b/tapir/shifts/urls.py @@ -207,4 +207,9 @@ views.UpdateShiftAttendanceTemplateCustomTimeView.as_view(), name="attendance_template_custom_time", ), + path( + "attendance//cant_attend", + views.CantAttendView.as_view(), + name="attendance_cant_attend", + ), ] diff --git a/tapir/shifts/views/attendance.py b/tapir/shifts/views/attendance.py index cd7a5f843..6c3b0f8e3 100644 --- a/tapir/shifts/views/attendance.py +++ b/tapir/shifts/views/attendance.py @@ -11,8 +11,10 @@ CreateView, UpdateView, FormView, + TemplateView, ) +from tapir import settings from tapir.accounts.models import TapirUser from tapir.core.views import TapirFormMixin from tapir.settings import PERMISSION_SHIFTS_MANAGE @@ -37,6 +39,9 @@ ShiftAttendanceTakenOverLogEntry, SolidarityShift, ) +from tapir.shifts.services.reasons_cant_self_unregister_service import ( + ReasonsCantSelfUnregisterService, +) from tapir.shifts.views.views import SelectedUserViewMixin from tapir.utils.shortcuts import safe_redirect, get_html_link from tapir.utils.user_utils import UserUtils @@ -118,8 +123,15 @@ class UpdateShiftAttendanceStateBase( model = ShiftAttendance get_state_from_kwargs = True + def __init__(self): + super().__init__() + self.attendance = None + def get_attendance(self): - return ShiftAttendance.objects.get(pk=self.kwargs["pk"]) + if not self.attendance: + self.attendance = ShiftAttendance.objects.get(pk=self.kwargs["pk"]) + + return self.attendance def get_permission_required(self): state = self.kwargs["state"] @@ -129,7 +141,7 @@ def get_permission_required(self): ) look_for_standing = ( state == ShiftAttendance.State.LOOKING_FOR_STAND_IN - and self.get_attendance().slot.user_can_look_for_standin(self.request.user) + and self.get_attendance().slot.shift.start_time > timezone.now() ) cancel_look_for_standing = ( state == ShiftAttendance.State.PENDING @@ -391,3 +403,46 @@ def get_context_data(self, **kwargs): } ) return context + + +class CantAttendView(LoginRequiredMixin, PermissionRequiredMixin, TemplateView): + template_name = "shifts/cant_attend.html" + + def __init__(self): + super().__init__() + self.attendance = None + + def get_attendance(self): + if not self.attendance: + self.attendance = get_object_or_404(ShiftAttendance, pk=self.kwargs["pk"]) + + return self.attendance + + def get_permission_required(self): + attendance = self.get_attendance() + if attendance.user == self.request.user: + return [] + return [PERMISSION_SHIFTS_MANAGE] + + def get_context_data(self, **kwargs): + context_data = super().get_context_data() + context_data["attendance"] = self.get_attendance() + + context_data["reasons_why_cant_self_unregister"] = ( + ReasonsCantSelfUnregisterService.build_reasons_why_cant_self_unregister( + self.request.user, self.get_attendance() + ) + ) + context_data["can_unregister"] = ( + len(context_data["reasons_why_cant_self_unregister"]) == 0 + ) + + context_data["email_address_member_office"] = ( + settings.EMAIL_ADDRESS_MEMBER_OFFICE + ) + + context_data["can_look_for_standin"] = ( + self.get_attendance().slot.shift.start_time > timezone.now() + ) + + return context_data From a57aada6b82326c5198e5fd535b70a076afd54db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Madet?= Date: Mon, 16 Dec 2024 16:37:21 +0100 Subject: [PATCH 02/13] Fixed test about accounting being able to see resignations. --- tapir/coop/tests/membership_resignation/test_list_view.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tapir/coop/tests/membership_resignation/test_list_view.py b/tapir/coop/tests/membership_resignation/test_list_view.py index f864fe9f0..a5a668b7c 100644 --- a/tapir/coop/tests/membership_resignation/test_list_view.py +++ b/tapir/coop/tests/membership_resignation/test_list_view.py @@ -20,6 +20,7 @@ def get_allowed_groups(self): settings.GROUP_VORSTAND, settings.GROUP_EMPLOYEES, settings.GROUP_MEMBER_OFFICE, + settings.GROUP_ACCOUNTING, ] def do_request(self): From f526b074e7f2aaf084d01c19b240b2504cd66c6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Madet?= Date: Thu, 2 Jan 2025 20:54:27 +0100 Subject: [PATCH 03/13] Services update --- tapir/shifts/models.py | 19 ------ .../services/can_look_for_standin_service.py | 9 +++ .../reasons_cant_self_unregister_service.py | 46 ------------- .../services/self_unregister_service.py | 64 +++++++++++++++++++ tapir/shifts/views/attendance.py | 17 +++-- tapir/shifts/views/views.py | 4 -- 6 files changed, 83 insertions(+), 76 deletions(-) create mode 100644 tapir/shifts/services/can_look_for_standin_service.py delete mode 100644 tapir/shifts/services/reasons_cant_self_unregister_service.py create mode 100644 tapir/shifts/services/self_unregister_service.py diff --git a/tapir/shifts/models.py b/tapir/shifts/models.py index 7f6cd7b44..669463e90 100644 --- a/tapir/shifts/models.py +++ b/tapir/shifts/models.py @@ -712,25 +712,6 @@ def user_can_attend(self, user): and not self.shift.cancelled ) - def user_can_self_unregister(self, user: TapirUser) -> bool: - user_is_registered_to_slot = ( - self.get_valid_attendance() is not None - and self.get_valid_attendance().user == user - ) - user_is_not_registered_to_slot_template = ( - self.slot_template is None - or not hasattr(self.slot_template, "attendance_template") - or not self.slot_template.attendance_template.user == user - ) - early_enough = ( - self.shift.start_time.date() - timezone.now().date() - ).days >= Shift.NB_DAYS_FOR_SELF_UNREGISTER - return ( - user_is_registered_to_slot - and user_is_not_registered_to_slot_template - and early_enough - ) - def update_attendance_from_template(self): """Updates the attendance of this slot. diff --git a/tapir/shifts/services/can_look_for_standin_service.py b/tapir/shifts/services/can_look_for_standin_service.py new file mode 100644 index 000000000..db318798c --- /dev/null +++ b/tapir/shifts/services/can_look_for_standin_service.py @@ -0,0 +1,9 @@ +from django.utils import timezone + +from tapir.shifts.models import ShiftSlot + + +class CanLookForStandinService: + @staticmethod + def can_look_for_a_standin(slot: ShiftSlot): + return slot.shift.start_time > timezone.now() diff --git a/tapir/shifts/services/reasons_cant_self_unregister_service.py b/tapir/shifts/services/reasons_cant_self_unregister_service.py deleted file mode 100644 index e6116f889..000000000 --- a/tapir/shifts/services/reasons_cant_self_unregister_service.py +++ /dev/null @@ -1,46 +0,0 @@ -from django.utils import timezone -from django.utils.translation import gettext_lazy as _ - -from tapir.accounts.models import TapirUser -from tapir.shifts.models import ShiftAttendance, ShiftAttendanceTemplate, Shift - - -class ReasonsCantSelfUnregisterService: - @staticmethod - def should_show_is_abcd_attendance_reason( - user: TapirUser, attendance: ShiftAttendance - ): - if not attendance.slot.slot_template: - return False - - return ShiftAttendanceTemplate.objects.filter( - user=user, slot_template=attendance.slot.slot_template - ).exists() - - @staticmethod - def should_show_not_enough_days_before_shift_reason(attendance: ShiftAttendance): - return ( - attendance.slot.shift.start_time.date() - timezone.now().date() - ).days <= Shift.NB_DAYS_FOR_SELF_UNREGISTER - - @classmethod - def build_reasons_why_cant_self_unregister( - cls, user: TapirUser, attendance: ShiftAttendance - ): - reasons_why_cant_self_unregister = [] - - if cls.should_show_not_enough_days_before_shift_reason(attendance): - reasons_why_cant_self_unregister.append( - _( - "It is only possible to unregister from a shift at least 7 days before the shift." - ) - ) - - if cls.should_show_is_abcd_attendance_reason(user, attendance): - reasons_why_cant_self_unregister.append( - _( - "It is not possible to unregister from a shift that comes from your ABCD shift." - ) - ) - - return reasons_why_cant_self_unregister diff --git a/tapir/shifts/services/self_unregister_service.py b/tapir/shifts/services/self_unregister_service.py new file mode 100644 index 000000000..f418a776c --- /dev/null +++ b/tapir/shifts/services/self_unregister_service.py @@ -0,0 +1,64 @@ +from django.utils import timezone +from django.utils.translation import gettext_lazy as _ + +from tapir.accounts.models import TapirUser +from tapir.shifts.models import ( + ShiftAttendance, + ShiftAttendanceTemplate, + Shift, +) + + +class SelfUnregisterService: + @classmethod + def should_show_is_abcd_attendance_reason( + cls, user: TapirUser, attendance: ShiftAttendance + ): + if not attendance.slot.slot_template: + return False + + return ShiftAttendanceTemplate.objects.filter( + user=user, slot_template=attendance.slot.slot_template + ).exists() + + @classmethod + def should_show_not_enough_days_before_shift_reason( + cls, attendance: ShiftAttendance, **_ + ): + return ( + attendance.slot.shift.start_time.date() - timezone.now().date() + ).days <= Shift.NB_DAYS_FOR_SELF_UNREGISTER + + @classmethod + def build_reasons_why_cant_self_unregister( + cls, user: TapirUser, attendance: ShiftAttendance + ): + return [ + message + for check, message in cls.get_check_to_message_map().items() + if check( + user=user, + attendance=attendance, + ) + ] + + @classmethod + def user_can_self_unregister( + cls, user: TapirUser, attendance: ShiftAttendance + ) -> bool: + for check in cls.get_check_to_message_map().keys(): + if check(user=user, attendance=attendance): + return False + + return True + + @classmethod + def get_check_to_message_map(cls): + return { + cls.should_show_is_abcd_attendance_reason: _( + "It is not possible to unregister from a shift that comes from your ABCD shift." + ), + cls.should_show_not_enough_days_before_shift_reason: _( + "It is only possible to unregister from a shift at least 7 days before the shift." + ), + } diff --git a/tapir/shifts/views/attendance.py b/tapir/shifts/views/attendance.py index 6c3b0f8e3..420340a23 100644 --- a/tapir/shifts/views/attendance.py +++ b/tapir/shifts/views/attendance.py @@ -39,9 +39,8 @@ ShiftAttendanceTakenOverLogEntry, SolidarityShift, ) -from tapir.shifts.services.reasons_cant_self_unregister_service import ( - ReasonsCantSelfUnregisterService, -) +from tapir.shifts.services.can_look_for_standin_service import CanLookForStandinService +from tapir.shifts.services.self_unregister_service import SelfUnregisterService from tapir.shifts.views.views import SelectedUserViewMixin from tapir.utils.shortcuts import safe_redirect, get_html_link from tapir.utils.user_utils import UserUtils @@ -137,11 +136,15 @@ def get_permission_required(self): state = self.kwargs["state"] self_unregister = ( state == ShiftAttendance.State.CANCELLED - and self.get_attendance().slot.user_can_self_unregister(self.request.user) + and SelfUnregisterService.user_can_self_unregister( + self.request.user, self.get_attendance() + ) ) look_for_standing = ( state == ShiftAttendance.State.LOOKING_FOR_STAND_IN - and self.get_attendance().slot.shift.start_time > timezone.now() + and CanLookForStandinService.can_look_for_a_standin( + self.get_attendance().slot + ) ) cancel_look_for_standing = ( state == ShiftAttendance.State.PENDING @@ -429,7 +432,7 @@ def get_context_data(self, **kwargs): context_data["attendance"] = self.get_attendance() context_data["reasons_why_cant_self_unregister"] = ( - ReasonsCantSelfUnregisterService.build_reasons_why_cant_self_unregister( + SelfUnregisterService.build_reasons_why_cant_self_unregister( self.request.user, self.get_attendance() ) ) @@ -442,7 +445,7 @@ def get_context_data(self, **kwargs): ) context_data["can_look_for_standin"] = ( - self.get_attendance().slot.shift.start_time > timezone.now() + CanLookForStandinService.can_look_for_a_standin(self.get_attendance().slot) ) return context_data diff --git a/tapir/shifts/views/views.py b/tapir/shifts/views/views.py index 94fbd3de1..1ac901785 100644 --- a/tapir/shifts/views/views.py +++ b/tapir/shifts/views/views.py @@ -236,10 +236,6 @@ def get_context_data(self, **kwargs): 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) - slot.can_look_for_stand_in = slot.user_can_look_for_standin( - self.request.user - ) slot.previous_attendances = ShiftAttendance.objects.filter(slot=slot) if slot.get_valid_attendance() is not None: From c40e689d67ccb83f70b277a3514bc43d5e1d643d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Madet?= Date: Thu, 2 Jan 2025 21:10:55 +0100 Subject: [PATCH 04/13] Added tests for CanLookForStandinService --- .../test_can_look_for_standin_service.py | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 tapir/shifts/tests/test_can_look_for_standin_service.py diff --git a/tapir/shifts/tests/test_can_look_for_standin_service.py b/tapir/shifts/tests/test_can_look_for_standin_service.py new file mode 100644 index 000000000..9da1d3039 --- /dev/null +++ b/tapir/shifts/tests/test_can_look_for_standin_service.py @@ -0,0 +1,26 @@ +import datetime +from unittest.mock import Mock + +from django.test import SimpleTestCase + +from tapir.shifts.services.can_look_for_standin_service import CanLookForStandinService +from tapir.utils.tests_utils import mock_timezone_now + + +class TestCanLookForStandinService(SimpleTestCase): + NOW = datetime.datetime(year=2024, month=7, day=8) + + def setUp(self): + self.NOW = mock_timezone_now(self, self.NOW) + + def test_canLookForAStandin_shiftIsInThePast_returnsFalse(self): + slot = Mock() + slot.shift.start_time = self.NOW - datetime.timedelta(hours=1) + + self.assertFalse(CanLookForStandinService.can_look_for_a_standin(slot)) + + def test_canLookForAStandin_shiftIsInTheFuture_returnsTrue(self): + slot = Mock() + slot.shift.start_time = self.NOW + datetime.timedelta(hours=1) + + self.assertTrue(CanLookForStandinService.can_look_for_a_standin(slot)) From e132a5c2e94719aa11ba9e6f492afb6de6de347d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Madet?= Date: Thu, 2 Jan 2025 21:22:51 +0100 Subject: [PATCH 05/13] First tests for SelfUnregisterService --- .../tests/test_self_unregister_service.py | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 tapir/shifts/tests/test_self_unregister_service.py diff --git a/tapir/shifts/tests/test_self_unregister_service.py b/tapir/shifts/tests/test_self_unregister_service.py new file mode 100644 index 000000000..2ace260af --- /dev/null +++ b/tapir/shifts/tests/test_self_unregister_service.py @@ -0,0 +1,59 @@ +import datetime + +from tapir.accounts.tests.factories.factories import TapirUserFactory +from tapir.shifts.models import ( + ShiftAttendance, + ShiftSlot, + ShiftAttendanceTemplate, + ShiftSlotTemplate, +) +from tapir.shifts.services.self_unregister_service import SelfUnregisterService +from tapir.shifts.tests.factories import ShiftFactory, ShiftTemplateFactory +from tapir.utils.tests_utils import TapirFactoryTestBase + + +class TestSelfUnregisterService(TapirFactoryTestBase): + def test_shouldShowIsAbcdAttendanceReason_shiftIsNotAbcd_returnsFalse(self): + tapir_user = TapirUserFactory.create() + ShiftFactory.create() + attendance = ShiftAttendance.objects.create( + user=tapir_user, slot=ShiftSlot.objects.get() + ) + + self.assertFalse( + SelfUnregisterService.should_show_is_abcd_attendance_reason( + user=tapir_user, attendance=attendance + ) + ) + + def test_shouldShowIsAbcdAttendanceReason_shiftIsAbcdButAttendanceIsNotAbcd_returnsFalse( + self, + ): + shift_template = ShiftTemplateFactory.create() + shift_template.create_shift(datetime.date(year=2024, month=10, day=5)) + tapir_user = TapirUserFactory.create() + attendance = ShiftAttendance.objects.create( + user=tapir_user, slot=ShiftSlot.objects.get() + ) + + self.assertFalse( + SelfUnregisterService.should_show_is_abcd_attendance_reason( + user=tapir_user, attendance=attendance + ) + ) + + def test_shouldShowIsAbcdAttendanceReason_shiftIsFromAnAbcdAttendance_returnsTrue( + self, + ): + shift_template = ShiftTemplateFactory.create() + tapir_user = TapirUserFactory.create() + ShiftAttendanceTemplate.objects.create( + user=tapir_user, slot_template=ShiftSlotTemplate.objects.get() + ) + shift_template.create_shift(datetime.date(year=2024, month=10, day=5)) + + self.assertTrue( + SelfUnregisterService.should_show_is_abcd_attendance_reason( + user=tapir_user, attendance=ShiftAttendance.objects.get() + ) + ) From 9a4e39e8d7aca71f2aa9b593bec67e6d2398a7d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Madet?= Date: Fri, 3 Jan 2025 11:36:01 +0100 Subject: [PATCH 06/13] WIP more tests for SelfUnregisterService --- .../tests/test_self_unregister_service.py | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/tapir/shifts/tests/test_self_unregister_service.py b/tapir/shifts/tests/test_self_unregister_service.py index 2ace260af..8555d6e59 100644 --- a/tapir/shifts/tests/test_self_unregister_service.py +++ b/tapir/shifts/tests/test_self_unregister_service.py @@ -1,4 +1,5 @@ import datetime +from unittest.mock import Mock from tapir.accounts.tests.factories.factories import TapirUserFactory from tapir.shifts.models import ( @@ -9,10 +10,15 @@ ) from tapir.shifts.services.self_unregister_service import SelfUnregisterService from tapir.shifts.tests.factories import ShiftFactory, ShiftTemplateFactory -from tapir.utils.tests_utils import TapirFactoryTestBase +from tapir.utils.tests_utils import TapirFactoryTestBase, mock_timezone_now class TestSelfUnregisterService(TapirFactoryTestBase): + NOW = datetime.datetime(year=2024, month=3, day=4) + + def setUp(self) -> None: + self.NOW = mock_timezone_now(self, self.NOW) + def test_shouldShowIsAbcdAttendanceReason_shiftIsNotAbcd_returnsFalse(self): tapir_user = TapirUserFactory.create() ShiftFactory.create() @@ -57,3 +63,27 @@ def test_shouldShowIsAbcdAttendanceReason_shiftIsFromAnAbcdAttendance_returnsTru user=tapir_user, attendance=ShiftAttendance.objects.get() ) ) + + def test_shouldShowNotEnoughDaysBeforeShiftReason_dateIsNotEnoughDaysBeforeShift_returnsTrue( + self, + ): + attendance = Mock() + attendance.slot.shift.start_time = self.NOW + datetime.timedelta(days=6) + + self.assertTrue( + SelfUnregisterService.should_show_not_enough_days_before_shift_reason( + attendance=attendance + ) + ) + + def test_shouldShowNotEnoughDaysBeforeShiftReason_dateIsEnoughDaysBeforeShift_returnsFalse( + self, + ): + attendance = Mock() + attendance.slot.shift.start_time = self.NOW + datetime.timedelta(days=8) + + self.assertFalse( + SelfUnregisterService.should_show_not_enough_days_before_shift_reason( + attendance=attendance + ) + ) From d564493fd422936a1edc6c3f4ea51dd5d9b3a477 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Madet?= Date: Mon, 6 Jan 2025 17:27:20 +0100 Subject: [PATCH 07/13] Update tests --- tapir/shifts/models.py | 1 - .../test_member_self_look_for_stand_in.py | 13 +- .../tests/test_member_self_unregisters.py | 8 +- tapir/shifts/views/views.py | 3 - .../locale/de/LC_MESSAGES/django.po | 260 ++++++++++++------ 5 files changed, 186 insertions(+), 99 deletions(-) diff --git a/tapir/shifts/models.py b/tapir/shifts/models.py index 669463e90..b7f619d39 100644 --- a/tapir/shifts/models.py +++ b/tapir/shifts/models.py @@ -545,7 +545,6 @@ class Shift(models.Model): cancelled_reason = models.CharField(null=True, max_length=1000) NB_DAYS_FOR_SELF_UNREGISTER = 7 - NB_DAYS_FOR_SELF_LOOK_FOR_STAND_IN = 2 def __str__(self): display_name = "%s: %s %s-%s" % ( diff --git a/tapir/shifts/tests/test_member_self_look_for_stand_in.py b/tapir/shifts/tests/test_member_self_look_for_stand_in.py index ceeafc315..0e48986c0 100644 --- a/tapir/shifts/tests/test_member_self_look_for_stand_in.py +++ b/tapir/shifts/tests/test_member_self_look_for_stand_in.py @@ -9,7 +9,6 @@ from tapir.shifts.models import ( ShiftSlot, ShiftAttendance, - Shift, ) from tapir.shifts.tests.factories import ShiftFactory from tapir.shifts.tests.utils import register_user_to_shift @@ -21,9 +20,7 @@ class TestMemberSelfLookForStandIn(TapirFactoryTestBase, TapirEmailTestMixin): def test_member_self_look_for_stand_in(self): user = self.login_as_normal_user() - start_time = timezone.now() + datetime.timedelta( - days=Shift.NB_DAYS_FOR_SELF_LOOK_FOR_STAND_IN, hours=1 - ) + start_time = timezone.now() + datetime.timedelta(hours=1) shift = ShiftFactory.create(start_time=start_time) register_user_to_shift(self.client, user, shift) @@ -38,17 +35,15 @@ def test_member_self_look_for_stand_in(self): self.assertEqual( ShiftAttendance.objects.get(slot__shift=shift, user=user).state, ShiftAttendance.State.LOOKING_FOR_STAND_IN, - "The attendance state should have been set to cancelled.", + "The attendance state should have been set to looking for a stand in.", ) def test_member_self_look_for_stand_in_threshold(self): user = self.login_as_normal_user() - start_time = timezone.now() + datetime.timedelta( - days=Shift.NB_DAYS_FOR_SELF_LOOK_FOR_STAND_IN, hours=-1 - ) + start_time = timezone.now() + datetime.timedelta(hours=-1) shift = ShiftFactory.create(start_time=start_time) + ShiftAttendance.objects.create(user=user, slot=ShiftSlot.objects.get()) - register_user_to_shift(self.client, user, shift) attendance = ShiftAttendance.objects.get(slot__shift=shift, user=user) response = self.client.post( reverse( diff --git a/tapir/shifts/tests/test_member_self_unregisters.py b/tapir/shifts/tests/test_member_self_unregisters.py index 9e1b87975..82d299716 100644 --- a/tapir/shifts/tests/test_member_self_unregisters.py +++ b/tapir/shifts/tests/test_member_self_unregisters.py @@ -18,19 +18,21 @@ class TestMemberSelfUnregisters(TapirFactoryTestBase): def test_member_self_unregisters(self): user = self.login_as_normal_user() start_time = timezone.now() + datetime.timedelta( - days=Shift.NB_DAYS_FOR_SELF_UNREGISTER, hours=1 + days=Shift.NB_DAYS_FOR_SELF_UNREGISTER + 1 ) shift = ShiftFactory.create(start_time=start_time) register_user_to_shift(self.client, user, shift) attendance = ShiftAttendance.objects.get(slot__shift=shift, user=user) - self.client.post( + response = self.client.post( reverse( "shifts:update_shift_attendance_state", args=[attendance.id, ShiftAttendance.State.CANCELLED], - ) + ), + follow=True, ) + self.assertStatusCode(response, 200) self.assertEqual( ShiftAttendance.objects.get(slot__shift=shift, user=user).state, ShiftAttendance.State.CANCELLED, diff --git a/tapir/shifts/views/views.py b/tapir/shifts/views/views.py index 1ac901785..a1c6f3384 100644 --- a/tapir/shifts/views/views.py +++ b/tapir/shifts/views/views.py @@ -246,9 +246,6 @@ def get_context_data(self, **kwargs): context["slots"] = slots context["attendance_states"] = ShiftAttendance.State context["NB_DAYS_FOR_SELF_UNREGISTER"] = Shift.NB_DAYS_FOR_SELF_UNREGISTER - context["NB_DAYS_FOR_SELF_LOOK_FOR_STAND_IN"] = ( - Shift.NB_DAYS_FOR_SELF_LOOK_FOR_STAND_IN - ) context["SHIFT_ATTENDANCE_STATES"] = SHIFT_ATTENDANCE_STATES return context diff --git a/tapir/translations/locale/de/LC_MESSAGES/django.po b/tapir/translations/locale/de/LC_MESSAGES/django.po index 31f4f8c44..9329ad6e3 100644 --- a/tapir/translations/locale/de/LC_MESSAGES/django.po +++ b/tapir/translations/locale/de/LC_MESSAGES/django.po @@ -2,12 +2,12 @@ # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. -# +# msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-12-26 22:12+0100\n" +"POT-Creation-Date: 2025-01-06 17:27+0100\n" "PO-Revision-Date: 2024-12-17 14:15+0100\n" "Last-Translator: \n" "Language-Team: \n" @@ -454,6 +454,7 @@ msgstr "" " Diese E-Mail enthält auch deinen Benutzernamen, falls du diesen vergessen hast.
\n" " Falls du deine E-Mail und Benuternamen vergessen hast, sende eine Nachricht an an Mitgliedsbüro : %(contact_member_office)s, mit der Angabe deines vollen Namens und (falls bekannt) deiner Mitgliedsnummer.\n" " " + #: accounts/templates/registration/password_reset_form.html:24 msgid "Back to login form" msgstr "Zurück zum Login" @@ -1034,7 +1035,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:217 +#: shifts/templates/shifts/shift_detail.html:178 #: shifts/templates/shifts/shift_template_detail.html:84 msgid "Register" msgstr "Anmelden" @@ -1918,6 +1919,7 @@ msgid "" msgstr "" "\n" " Eine neue Mitgliedsschaftspause startet am %(start_date)s: %(description)s.\n" + #: coop/templates/coop/log/create_payment_log_entry.html:2 #, python-format msgid "New Payment: %(amount)s € on %(payment_date)s" @@ -2307,14 +2309,14 @@ msgid "" msgstr "" #: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:56 -#: shifts/models.py:945 shifts/templates/shifts/shift_day_printable.html:56 -#: shifts/templates/shifts/shift_detail.html:272 +#: shifts/models.py:915 shifts/templates/shifts/shift_day_printable.html:56 +#: shifts/templates/shifts/shift_detail.html:233 #: shifts/templates/shifts/shift_detail_printable.html:51 msgid "Attended" msgstr "Teilgenommen" #: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:58 -#: shifts/models.py:944 +#: shifts/models.py:914 msgid "Pending" msgstr "Ausstehend" @@ -2650,7 +2652,7 @@ msgid "List of all emails" msgstr "Liste alle E-Mails" #: core/templates/core/email_list.html:39 shifts/models.py:513 -#: shifts/models.py:1083 shifts/templates/shifts/user_shift_account_log.html:29 +#: shifts/models.py:1053 shifts/templates/shifts/user_shift_account_log.html:29 msgid "Description" msgstr "Beschreibung" @@ -3087,7 +3089,7 @@ msgstr "Zeit flexibel" msgid "If enabled, members who register for that shift can choose themselves the time where they come do their shift." msgstr "" -#: shifts/models.py:417 shifts/models.py:836 +#: shifts/models.py:417 shifts/models.py:806 #: shifts/templates/shifts/shift_detail.html:86 #: shifts/templates/shifts/shift_template_detail.html:42 msgid "Chosen time" @@ -3113,53 +3115,61 @@ msgstr "" msgid "If 'flexible time' is enabled, then the time component is ignored" msgstr "" -#: shifts/models.py:838 +#: shifts/models.py:808 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 "" -#: shifts/models.py:946 shifts/templates/shifts/shift_day_printable.html:57 -#: shifts/templates/shifts/shift_detail.html:282 +#: shifts/models.py:916 shifts/templates/shifts/shift_day_printable.html:57 +#: shifts/templates/shifts/shift_detail.html:243 #: shifts/templates/shifts/shift_detail_printable.html:52 msgid "Missed" msgstr "Nicht erschienen" -#: shifts/models.py:947 shifts/templates/shifts/shift_day_printable.html:58 -#: shifts/templates/shifts/shift_detail.html:312 +#: shifts/models.py:917 shifts/templates/shifts/shift_day_printable.html:58 +#: shifts/templates/shifts/shift_detail.html:273 #: shifts/templates/shifts/shift_detail_printable.html:53 msgid "Excused" msgstr "Entschuldigt" -#: shifts/models.py:948 shifts/templates/shifts/shift_detail.html:320 +#: shifts/models.py:918 shifts/templates/shifts/shift_detail.html:281 msgid "Cancelled" msgstr "Abgesagt" -#: shifts/models.py:949 shifts/templates/shifts/shift_day_printable.html:97 -#: shifts/templates/shifts/shift_detail.html:304 +#: shifts/models.py:919 shifts/templates/shifts/shift_day_printable.html:97 +#: shifts/templates/shifts/shift_detail.html:265 #: shifts/templates/shifts/shift_detail_printable.html:94 #: shifts/templates/shifts/shift_filters.html:83 msgid "Looking for a stand-in" msgstr "Sucht Vertretung" -#: shifts/models.py:982 +#: shifts/models.py:952 msgid "🏠 ABCD" msgstr "🏠 ABCD" -#: shifts/models.py:983 +#: shifts/models.py:953 msgid "✈ Flying" msgstr "✈ Fliegend" -#: shifts/models.py:984 +#: shifts/models.py:954 msgid "❄ Frozen" msgstr "" -#: shifts/models.py:1015 +#: shifts/models.py:985 msgid "Is frozen" msgstr "" -#: shifts/models.py:1189 +#: shifts/models.py:1159 msgid "Cycle start date" msgstr "Anfangsdatum" +#: shifts/services/self_unregister_service.py:59 +msgid "It is not possible to unregister from a shift that comes from your ABCD shift." +msgstr "" + +#: shifts/services/self_unregister_service.py:62 +msgid "It is only possible to unregister from a shift at least 7 days before the shift." +msgstr "" + #: shifts/templates/shifts/cancel_shift.html:7 #: shifts/templates/shifts/cancel_shift.html:13 msgid "Cancel shift:" @@ -3181,6 +3191,97 @@ msgstr "" msgid "Confirm cancellation" msgstr "Absage bestätigen" +#: shifts/templates/shifts/cant_attend.html:7 +msgid "Can't attend:" +msgstr "" + +#: shifts/templates/shifts/cant_attend.html:21 +#, fuzzy +#| msgid "Unregister" +msgid "Option 1: unregister" +msgstr "Abmelden" + +#: shifts/templates/shifts/cant_attend.html:25 +#, fuzzy +#| msgid "" +#| "\n" +#| " The current target food basket value per member and per month to reach the break-even is\n" +#| " %(target_basket)s€.\n" +#| " " +msgid "" +"\n" +" You can unregister from this shift.\n" +" You will be removed from the attendance list and won't get any negative\n" +" point.\n" +" " +msgstr "" +"\n" +" Der derzeitige Zielwert des Warenkorbs pro Mitglied und Monat um den Break-Even zu erreichen ist\n" +" %(target_basket)s€.\n" +" " + +#: shifts/templates/shifts/cant_attend.html:31 +#, fuzzy +#| msgid "" +#| "\n" +#| " Each member can designate one person (whether in their own household or not) to shop\n" +#| " under the same membership number. This can be investing members or non-members.\n" +#| " " +msgid "" +"\n" +" You cannot unregister from this shift.
\n" +" The reasons are:\n" +" " +msgstr "" +"\n" +"Jedes Mitglied kann eine Person (egal, ob im eigenen Haushalt oder nicht) bestimmen, die unter der gleichen Mitgliedsnummer einkaufen kann. Das können investierende Mitglieder oder Nichtmitglieder sein. " + +#: shifts/templates/shifts/cant_attend.html:48 +#: shifts/templates/shifts/shift_template_detail.html:73 +msgid "Unregister" +msgstr "Abmelden" + +#: shifts/templates/shifts/cant_attend.html:57 +#, fuzzy +#| msgid "Cancel looking for a stand-in" +msgid "Option 2: enable looking for a stand-in" +msgstr "Beende die Suche nach Vertretung" + +#: shifts/templates/shifts/cant_attend.html:59 +#, python-format +msgid "" +"\n" +"

\n" +" If you cannot unregister from the shift, your other option is to look for a replacement (\"stand-in\").\n" +" This will notify the team that you can't come, and will allow other members to take over your slot.\n" +" Please enable this as soon as you know you won't be able to come to your shift.\n" +"

\n" +"

\n" +" This is always possible up to the time of the shift.\n" +"

\n" +"

\n" +" After enabling this, it is still absolutely necessary that you contact the member office\n" +" (%(email_address_member_office)s)\n" +" as soon as possible.\n" +"

\n" +"

\n" +" If you have a valid reason for not coming (see the\n" +" Member Manual),\n" +" the member office will unregister you from the shift and you won't get any negative points.\n" +"

\n" +"

\n" +" If you don't have a valid reason for not coming, you may still get lucky:\n" +" someone may take over your shift. The shift calendar shows where someone is looking for a stand-in.
\n" +" If that happens, you will be notified by email and will not get any negative points.
\n" +" There is no guarantee that this happens. If your slot is not taken over, you will get negative points.\n" +"

\n" +" " +msgstr "" + +#: shifts/templates/shifts/cant_attend.html:92 +msgid "Look for a stand-in" +msgstr "Suche nach Vertretung" + #: shifts/templates/shifts/convert_exemption_to_pause_form.html:12 #: shifts/templates/shifts/convert_exemption_to_pause_form.html:18 msgid "Convert exemption to pause" @@ -3834,61 +3935,15 @@ msgid "Cancels the search for a stand-in. Use this if you want to attend the shi msgstr "Beendet die Suche nach Vertretung. Benutze dies, wenn du die Schicht wahrnehmen möchtest." #: shifts/templates/shifts/shift_detail.html:153 -#: shifts/templates/shifts/shift_detail.html:293 +#: shifts/templates/shifts/shift_detail.html:254 msgid "Cancel looking for a stand-in" msgstr "Beende die Suche nach Vertretung" -#: shifts/templates/shifts/shift_detail.html:161 -#, fuzzy, python-format -#| msgid "" -#| "You can only look for a\n" -#| " stand-in\n" -#| " %(NB_DAYS_FOR_SELF_LOOK_FOR_STAND_IN)s days before the shift. If\n" -#| " you can't\n" -#| " attend, contact your shift leader as soon as\n" -#| " possible." -msgid "" -"You can only look for\n" -" a\n" -" stand-in\n" -" %(NB_DAYS_FOR_SELF_LOOK_FOR_STAND_IN)s days before the\n" -" shift. If\n" -" you can't\n" -" attend, contact your shift leader as soon as\n" -" 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:174 -msgid "Look for a stand-in" -msgstr "Suche nach Vertretung" +#: shifts/templates/shifts/shift_detail.html:160 +msgid "I can't attend this shift" +msgstr "" -#: shifts/templates/shifts/shift_detail.html:183 -#, fuzzy, python-format -#| msgid "" -#| "You can only unregister\n" -#| " yourself\n" -#| " %(NB_DAYS_FOR_SELF_UNREGISTER)s days before the shift. Also,\n" -#| " ABCD-Shifts\n" -#| " can't be unregistered from. If you can't\n" -#| " attend, look for a stand-in or contact your shift leader as soon as\n" -#| " possible." -msgid "" -"You can only\n" -" unregister\n" -" yourself\n" -" %(NB_DAYS_FOR_SELF_UNREGISTER)s days before the shift. Also,\n" -" ABCD-Shifts\n" -" can't be unregistered from. If you can't\n" -" attend, look for a stand-in or contact your shift leader as soon\n" -" as\n" -" 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:198 -msgid "Unregister myself" -msgstr "Melde mich ab" - -#: shifts/templates/shifts/shift_detail.html:203 +#: shifts/templates/shifts/shift_detail.html:164 #, fuzzy #| msgid "" #| "You can only register\n" @@ -3917,12 +3972,12 @@ msgstr "" "- die Schicht in der Zukunft liegt\n" " " -#: shifts/templates/shifts/shift_detail.html:245 +#: shifts/templates/shifts/shift_detail.html:206 #: shifts/templates/shifts/shift_template_detail.html:95 msgid "Not specified" msgstr "" -#: shifts/templates/shifts/shift_detail.html:328 +#: shifts/templates/shifts/shift_detail.html:289 msgid "Edit slot" msgstr "Slot bearbeiten" @@ -4046,10 +4101,6 @@ msgstr "Liste des Slots für diese ABCD-Schicht" msgid "Requirements" msgstr "" -#: shifts/templates/shifts/shift_template_detail.html:73 -msgid "Unregister" -msgstr "Abmelden" - #: shifts/templates/shifts/shift_template_detail.html:122 msgid "Future generated Shifts" msgstr "Zukünftige erstellte Schichten" @@ -4395,23 +4446,23 @@ msgstr "Allgemein" msgid "Unknown mode {attendance_mode}" msgstr "" -#: shifts/views/attendance.py:194 shifts/views/attendance.py:336 +#: shifts/views/attendance.py:209 shifts/views/attendance.py:351 #, python-format msgid "Shift attendance: %(name)s" msgstr "Schicht-Anwesenheit: %(name)s" -#: shifts/views/attendance.py:200 shifts/views/attendance.py:342 +#: shifts/views/attendance.py:215 shifts/views/attendance.py:357 #, python-format msgid "Updating shift attendance: %(member_link)s, %(slot_link)s" msgstr "Schichtanwesenheit aktualisieren: %(member_link)s, %(slot_link)s" -#: shifts/views/attendance.py:376 +#: shifts/views/attendance.py:391 #, fuzzy, python-format #| msgid "Shift attendance: %(name)s" msgid "ABCD attendance: %(name)s" msgstr "Schicht-Anwesenheit: %(name)s" -#: shifts/views/attendance.py:383 +#: shifts/views/attendance.py:398 #, fuzzy, python-format #| msgid "Updating shift attendance: %(member_link)s, %(slot_link)s" msgid "Updating ABCD attendance: %(member_link)s, %(slot_link)s" @@ -4498,7 +4549,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:347 msgid "Frozen statuses updated." msgstr "" @@ -5756,6 +5807,49 @@ msgstr "%(name)s ist kein Mitglied der Genossenschaft. Vielleicht haben sie dere msgid "%(name)s has not attended a welcome session yet. Make sure they plan to do it!" msgstr "%(name)s hat an dem Willkommenstreffen noch nicht teilgenommen. Stelle sicher, dass er*sie es entsprechend einplant!" +#, fuzzy, python-format +#~| msgid "" +#~| "You can only look for a\n" +#~| " stand-in\n" +#~| " %(NB_DAYS_FOR_SELF_LOOK_FOR_STAND_IN)s days before the shift. If\n" +#~| " you can't\n" +#~| " attend, contact your shift leader as soon as\n" +#~| " possible." +#~ msgid "" +#~ "You can only look for\n" +#~ " a\n" +#~ " stand-in\n" +#~ " %(NB_DAYS_FOR_SELF_LOOK_FOR_STAND_IN)s days before the\n" +#~ " shift. If\n" +#~ " you can't\n" +#~ " attend, contact your shift leader as soon as\n" +#~ " 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." + +#, fuzzy, python-format +#~| msgid "" +#~| "You can only unregister\n" +#~| " yourself\n" +#~| " %(NB_DAYS_FOR_SELF_UNREGISTER)s days before the shift. Also,\n" +#~| " ABCD-Shifts\n" +#~| " can't be unregistered from. If you can't\n" +#~| " attend, look for a stand-in or contact your shift leader as soon as\n" +#~| " possible." +#~ msgid "" +#~ "You can only\n" +#~ " unregister\n" +#~ " yourself\n" +#~ " %(NB_DAYS_FOR_SELF_UNREGISTER)s days before the shift. Also,\n" +#~ " ABCD-Shifts\n" +#~ " can't be unregistered from. If you can't\n" +#~ " attend, look for a stand-in or contact your shift leader as soon\n" +#~ " as\n" +#~ " 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." + +#~ msgid "Unregister myself" +#~ msgstr "Melde mich ab" + #, fuzzy #~| msgid "Shift attendance: %(name)s" #~ msgid "Shift attendance rates" From 24c127d4fcb3693e68bde5668b9f642655a169da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Madet?= Date: Mon, 6 Jan 2025 17:53:25 +0100 Subject: [PATCH 08/13] Added tests for shifts:attendance_cant_attend --- tapir/accounts/tests/factories/factories.py | 9 +++ .../test_create_view.py | 4 +- .../test_delete_view.py | 4 +- .../test_detail_view.py | 4 +- .../membership_resignation/test_edit_view.py | 4 +- .../membership_resignation/test_list_view.py | 4 +- tapir/shifts/tests/test_cant_attend_view.py | 73 +++++++++++++++++++ tapir/utils/tests_utils.py | 8 +- 8 files changed, 96 insertions(+), 14 deletions(-) create mode 100644 tapir/shifts/tests/test_cant_attend_view.py diff --git a/tapir/accounts/tests/factories/factories.py b/tapir/accounts/tests/factories/factories.py index 6db301707..451c52c35 100644 --- a/tapir/accounts/tests/factories/factories.py +++ b/tapir/accounts/tests/factories/factories.py @@ -65,6 +65,15 @@ def is_employee(self: TapirUser, create, is_employee): set_group_membership([self], settings.GROUP_EMPLOYEES, is_employee) + @factory.post_generation + def is_welcome_desk_account(self: TapirUser, create, is_welcome_desk_account): + if not create: + return + + set_group_membership( + [self], settings.GROUP_WELCOME_DESK, is_welcome_desk_account + ) + @factory.post_generation def shift_capabilities(self: TapirUser, create, shift_capabilities, **kwargs): if not create: diff --git a/tapir/coop/tests/membership_resignation/test_create_view.py b/tapir/coop/tests/membership_resignation/test_create_view.py index da212489e..60478fda1 100644 --- a/tapir/coop/tests/membership_resignation/test_create_view.py +++ b/tapir/coop/tests/membership_resignation/test_create_view.py @@ -44,14 +44,14 @@ def setUp(self) -> None: self.given_feature_flag_value(feature_flag_membership_resignation, True) mock_timezone_now(self, self.NOW) - def get_allowed_groups(self): + def permission_test_get_allowed_groups(self): return [ settings.GROUP_VORSTAND, settings.GROUP_EMPLOYEES, settings.GROUP_MEMBER_OFFICE, ] - def do_request(self): + def permission_test_do_request(self): return self.client.get(reverse("coop:membership_resignation_create")) def test_membershipResignationCreateView_featureFlagDisabled_accessDenied(self): diff --git a/tapir/coop/tests/membership_resignation/test_delete_view.py b/tapir/coop/tests/membership_resignation/test_delete_view.py index d5adf88ce..4e2883144 100644 --- a/tapir/coop/tests/membership_resignation/test_delete_view.py +++ b/tapir/coop/tests/membership_resignation/test_delete_view.py @@ -33,14 +33,14 @@ def setUp(self) -> None: super().setUp() self.given_feature_flag_value(feature_flag_membership_resignation, True) - def get_allowed_groups(self): + def permission_test_get_allowed_groups(self): return [ settings.GROUP_VORSTAND, settings.GROUP_EMPLOYEES, settings.GROUP_MEMBER_OFFICE, ] - def do_request(self): + def permission_test_do_request(self): resignation: MembershipResignation = MembershipResignationFactory.create() return self.client.post( reverse("coop:membership_resignation_delete", args=[resignation.id]), diff --git a/tapir/coop/tests/membership_resignation/test_detail_view.py b/tapir/coop/tests/membership_resignation/test_detail_view.py index be6915449..012608221 100644 --- a/tapir/coop/tests/membership_resignation/test_detail_view.py +++ b/tapir/coop/tests/membership_resignation/test_detail_view.py @@ -17,7 +17,7 @@ class TestMembershipResignationDetailView( PermissionTestMixin, FeatureFlagTestMixin, TapirFactoryTestBase ): - def get_allowed_groups(self): + def permission_test_get_allowed_groups(self): return [ settings.GROUP_VORSTAND, settings.GROUP_EMPLOYEES, @@ -25,7 +25,7 @@ def get_allowed_groups(self): settings.GROUP_ACCOUNTING, ] - def do_request(self): + def permission_test_do_request(self): self.given_feature_flag_value(feature_flag_membership_resignation, True) resignation: MembershipResignation = MembershipResignationFactory.create() return self.client.get( diff --git a/tapir/coop/tests/membership_resignation/test_edit_view.py b/tapir/coop/tests/membership_resignation/test_edit_view.py index c0046488a..dc762966b 100644 --- a/tapir/coop/tests/membership_resignation/test_edit_view.py +++ b/tapir/coop/tests/membership_resignation/test_edit_view.py @@ -26,14 +26,14 @@ def setUp(self) -> None: self.given_feature_flag_value(feature_flag_membership_resignation, True) mock_timezone_now(self, self.NOW) - def get_allowed_groups(self): + def permission_test_get_allowed_groups(self): return [ settings.GROUP_VORSTAND, settings.GROUP_EMPLOYEES, settings.GROUP_MEMBER_OFFICE, ] - def do_request(self): + def permission_test_do_request(self): resignation: MembershipResignation = MembershipResignationFactory.create() return self.client.get( reverse("coop:membership_resignation_edit", args=[resignation.id]) diff --git a/tapir/coop/tests/membership_resignation/test_list_view.py b/tapir/coop/tests/membership_resignation/test_list_view.py index a5a668b7c..ee014c989 100644 --- a/tapir/coop/tests/membership_resignation/test_list_view.py +++ b/tapir/coop/tests/membership_resignation/test_list_view.py @@ -15,7 +15,7 @@ class TestMembershipResignationListView( PermissionTestMixin, FeatureFlagTestMixin, TapirFactoryTestBase ): - def get_allowed_groups(self): + def permission_test_get_allowed_groups(self): return [ settings.GROUP_VORSTAND, settings.GROUP_EMPLOYEES, @@ -23,7 +23,7 @@ def get_allowed_groups(self): settings.GROUP_ACCOUNTING, ] - def do_request(self): + def permission_test_do_request(self): self.given_feature_flag_value(feature_flag_membership_resignation, True) return self.client.get(reverse("coop:membership_resignation_list")) diff --git a/tapir/shifts/tests/test_cant_attend_view.py b/tapir/shifts/tests/test_cant_attend_view.py new file mode 100644 index 000000000..f9a7c2749 --- /dev/null +++ b/tapir/shifts/tests/test_cant_attend_view.py @@ -0,0 +1,73 @@ +from unittest.mock import patch, Mock + +from django.template.response import TemplateResponse +from django.urls import reverse + +from tapir import settings +from tapir.accounts.tests.factories.factories import TapirUserFactory +from tapir.shifts.models import ShiftAttendance, ShiftSlot +from tapir.shifts.services.can_look_for_standin_service import CanLookForStandinService +from tapir.shifts.services.self_unregister_service import SelfUnregisterService +from tapir.shifts.tests.factories import ShiftFactory +from tapir.utils.tests_utils import TapirFactoryTestBase, PermissionTestMixin + + +class TestCantAttendView(PermissionTestMixin, TapirFactoryTestBase): + def permission_test_get_allowed_groups(self): + return [ + settings.GROUP_VORSTAND, + settings.GROUP_EMPLOYEES, + settings.GROUP_MEMBER_OFFICE, + settings.GROUP_SHIFT_MANAGER, + ] + + def permission_test_do_request(self): + ShiftFactory.create() + member_registered_to_the_shift = TapirUserFactory.create() + attendance = ShiftAttendance.objects.create( + user=member_registered_to_the_shift, slot=ShiftSlot.objects.get() + ) + + return self.client.get( + reverse("shifts:attendance_cant_attend", args=[attendance.id]) + ) + + @patch.object(CanLookForStandinService, "can_look_for_a_standin") + @patch.object(SelfUnregisterService, "build_reasons_why_cant_self_unregister") + def test_cantAttendView_default_contextDataIsCorrect( + self, + mock_build_reasons_why_cant_self_unregister: Mock, + mock_can_look_for_a_standin: Mock, + ): + shift = ShiftFactory.create() + member_registered_to_the_shift = self.login_as_normal_user() + attendance = ShiftAttendance.objects.create( + user=member_registered_to_the_shift, slot=shift.slots.get() + ) + mock_build_reasons_why_cant_self_unregister.return_value = ( + "test reasons why cant self unregister" + ) + mock_can_look_for_a_standin.return_value = "test can look for standin" + + response: TemplateResponse = self.client.get( + reverse("shifts:attendance_cant_attend", args=[attendance.id]) + ) + + self.assertStatusCode(response, 200) + self.assertEqual(attendance, response.context_data["attendance"]) + mock_build_reasons_why_cant_self_unregister.assert_called_once_with( + member_registered_to_the_shift, attendance + ) + self.assertEqual( + "test reasons why cant self unregister", + response.context_data["reasons_why_cant_self_unregister"], + ) + self.assertEqual( + False, + response.context_data["can_unregister"], + ) + mock_can_look_for_a_standin.assert_called_once_with(shift.slots.get()) + self.assertEqual( + "test can look for standin", + response.context_data["can_look_for_standin"], + ) diff --git a/tapir/utils/tests_utils.py b/tapir/utils/tests_utils.py index 7783de105..350578b78 100644 --- a/tapir/utils/tests_utils.py +++ b/tapir/utils/tests_utils.py @@ -270,12 +270,12 @@ def given_feature_flag_value(self, flag_name: str, flag_value: bool): class PermissionTestMixin: - def get_allowed_groups(self): + def permission_test_get_allowed_groups(self): raise NotImplementedError( "Children of PermissionTestMixin must implement get_allowed_groups to say which groups should have access to the view." ) - def do_request(self): + def permission_test_do_request(self): raise NotImplementedError( "Children of PermissionTestMixin must implement do_request, a function that visits the target view and returns the response." ) @@ -290,12 +290,12 @@ def test_accessView_loggedInAsMemberOfGroup_accessAsExpected(self, group): self.assertEqual(set([group]), user.get_ldap_user().group_names) self.login_as_user(user) - response = self.do_request() + response = self.permission_test_do_request() self.assertStatusCode( response, ( HTTPStatus.OK - if group in self.get_allowed_groups() + if group in self.permission_test_get_allowed_groups() else HTTPStatus.FORBIDDEN ), ) From d11281f5c32be0b58b0cf034299608a2e8b9edd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Madet?= Date: Fri, 10 Jan 2025 11:32:49 +0100 Subject: [PATCH 09/13] Fixed main stats page not loading because of unoptimized sql queries. --- tapir/statistics/views/main_view.py | 10 ++++++++++ .../locale/de/LC_MESSAGES/django.po | 18 +++++++++--------- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/tapir/statistics/views/main_view.py b/tapir/statistics/views/main_view.py index 4b9a82496..f3e9808c7 100644 --- a/tapir/statistics/views/main_view.py +++ b/tapir/statistics/views/main_view.py @@ -33,6 +33,9 @@ ShiftSlotTemplate, ShiftAttendanceMode, ) +from tapir.shifts.services.frozen_status_history_service import ( + FrozenStatusHistoryService, +) from tapir.shifts.services.shift_attendance_mode_service import ( ShiftAttendanceModeService, ) @@ -96,6 +99,9 @@ def get_purchasing_members_context(self): share_owners = ShiftAttendanceModeService.annotate_share_owner_queryset_with_attendance_mode_at_datetime( share_owners, self.reference_time ) + share_owners = FrozenStatusHistoryService.annotate_share_owner_queryset_with_is_frozen_at_datetime( + share_owners, self.reference_time + ) current_number_of_purchasing_members = len( [ @@ -153,6 +159,10 @@ def get_working_members_context(self): .prefetch_related("user__share_owner__share_ownerships") .prefetch_related("shift_exemptions") ) + + shift_user_datas = FrozenStatusHistoryService.annotate_shift_user_data_queryset_with_is_frozen_at_datetime( + shift_user_datas, self.reference_time + ) share_owners = NumberOfSharesService.annotate_share_owner_queryset_with_nb_of_active_shares( ShareOwner.objects.all(), self.reference_date ) diff --git a/tapir/translations/locale/de/LC_MESSAGES/django.po b/tapir/translations/locale/de/LC_MESSAGES/django.po index 9329ad6e3..6ba8c006d 100644 --- a/tapir/translations/locale/de/LC_MESSAGES/django.po +++ b/tapir/translations/locale/de/LC_MESSAGES/django.po @@ -2,12 +2,12 @@ # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. -# +# msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-01-06 17:27+0100\n" +"POT-Creation-Date: 2025-01-10 11:33+0100\n" "PO-Revision-Date: 2024-12-17 14:15+0100\n" "Last-Translator: \n" "Language-Team: \n" @@ -1064,7 +1064,7 @@ msgid "" msgstr "" #: coop/templates/coop/email/accounting_recap.body.default.html:21 -#: statistics/views/main_view.py:301 +#: statistics/views/main_view.py:311 msgid "New members" msgstr "Neue Mitglieder" @@ -4566,7 +4566,7 @@ msgid "Main statistics" msgstr "Hauptstatistik" #: statistics/templates/statistics/main_statistics.html:19 -#: statistics/views/main_view.py:260 +#: statistics/views/main_view.py:270 msgid "Total number of members" msgstr "Gesamte Mitgliederzahl" @@ -4663,7 +4663,7 @@ msgstr "" #: statistics/templates/statistics/main_statistics.html:118 #: statistics/templates/statistics/main_statistics.html:129 -#: statistics/views/main_view.py:382 +#: statistics/views/main_view.py:392 msgid "Frozen members" msgstr "Eingefrorene Mitglieder" @@ -4769,19 +4769,19 @@ msgstr "" msgid "Evolution of total spends per month" msgstr "Entwicklung der Gesamtausgaben pro Monat" -#: statistics/views/main_view.py:332 +#: statistics/views/main_view.py:342 msgid "Total spends per month" msgstr "Gesamtausgaben pro Monat" -#: statistics/views/main_view.py:382 +#: statistics/views/main_view.py:392 msgid "Purchasing members" msgstr "Einkaufsberechtigten Mitglieder*innen" -#: statistics/views/main_view.py:400 +#: statistics/views/main_view.py:410 msgid "Percentage of members with a co-purchaser relative to the number of active members" msgstr "Prozentualer Anteil der Mitglieder mit einem Miterwerber im Verhältnis zur Zahl der aktiven Mitglieder" -#: statistics/views/main_view.py:552 +#: statistics/views/main_view.py:562 msgid "Purchase data updated" msgstr "Kaufdaten aktualisiert" From 978b63683891c9e66db5b20ffe6a8d828d35c4f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Madet?= Date: Tue, 4 Feb 2025 14:17:36 +0100 Subject: [PATCH 10/13] Text updates from Vicky --- .../shifts/templates/shifts/cant_attend.html | 7 +- .../locale/de/LC_MESSAGES/django.po | 268 ++++++++++++------ .../locale/de/LC_MESSAGES/djangojs.po | 4 +- 3 files changed, 192 insertions(+), 87 deletions(-) diff --git a/tapir/shifts/templates/shifts/cant_attend.html b/tapir/shifts/templates/shifts/cant_attend.html index c823816ab..0142e7a35 100644 --- a/tapir/shifts/templates/shifts/cant_attend.html +++ b/tapir/shifts/templates/shifts/cant_attend.html @@ -10,7 +10,7 @@

- If you can't attend + {% translate "If you can't attend" %}
{{ attendance.slot }}

@@ -54,7 +54,7 @@
{% translate "Option 1: unregister" %}
-
{% translate "Option 2: enable looking for a stand-in" %}
+
{% translate "Option 2: Enable looking for a stand-in" %}
{% blocktranslate %}

@@ -78,7 +78,8 @@

{% translate "Option 2: enable looking for a stand-in" %

If you don't have a valid reason for not coming, you may still get lucky: someone may take over your shift. The shift calendar shows where someone is looking for a stand-in.
- If that happens, you will be notified by email and will not get any negative points.
+ If that happens, you will be notified by email and will not get any negative points. + You are still obliged to sign up for an alternative shift in this shift cycle.
There is no guarantee that this happens. If your slot is not taken over, you will get negative points.

{% endblocktranslate %} diff --git a/tapir/translations/locale/de/LC_MESSAGES/django.po b/tapir/translations/locale/de/LC_MESSAGES/django.po index f0bb823a0..64806c9a7 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-01-31 11:52+0100\n" +"POT-Creation-Date: 2025-02-04 14:08+0100\n" "PO-Revision-Date: 2024-12-17 14:15+0100\n" "Last-Translator: \n" "Language-Team: \n" @@ -1047,7 +1047,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:217 +#: shifts/templates/shifts/shift_detail.html:178 #: shifts/templates/shifts/shift_template_detail.html:84 msgid "Register" msgstr "Anmelden" @@ -2321,14 +2321,14 @@ msgid "" msgstr "" #: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:56 -#: shifts/models.py:947 shifts/templates/shifts/shift_day_printable.html:56 -#: shifts/templates/shifts/shift_detail.html:272 +#: shifts/models.py:917 shifts/templates/shifts/shift_day_printable.html:56 +#: shifts/templates/shifts/shift_detail.html:233 #: shifts/templates/shifts/shift_detail_printable.html:51 msgid "Attended" msgstr "Teilgenommen" #: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:58 -#: shifts/models.py:946 +#: shifts/models.py:916 msgid "Pending" msgstr "Ausstehend" @@ -2664,7 +2664,7 @@ msgid "List of all emails" msgstr "Liste alle E-Mails" #: core/templates/core/email_list.html:39 shifts/models.py:513 -#: shifts/models.py:1085 shifts/templates/shifts/user_shift_account_log.html:29 +#: shifts/models.py:1055 shifts/templates/shifts/user_shift_account_log.html:29 msgid "Description" msgstr "Beschreibung" @@ -3101,7 +3101,7 @@ msgstr "Zeit flexibel" msgid "If enabled, members who register for that shift can choose themselves the time where they come do their shift." msgstr "" -#: shifts/models.py:417 shifts/models.py:838 +#: shifts/models.py:417 shifts/models.py:808 #: shifts/templates/shifts/shift_detail.html:86 #: shifts/templates/shifts/shift_template_detail.html:42 msgid "Chosen time" @@ -3127,53 +3127,61 @@ msgstr "" msgid "If 'flexible time' is enabled, then the time component is ignored" msgstr "" -#: shifts/models.py:840 +#: shifts/models.py:810 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 "" -#: shifts/models.py:948 shifts/templates/shifts/shift_day_printable.html:57 -#: shifts/templates/shifts/shift_detail.html:282 +#: shifts/models.py:918 shifts/templates/shifts/shift_day_printable.html:57 +#: shifts/templates/shifts/shift_detail.html:243 #: shifts/templates/shifts/shift_detail_printable.html:52 msgid "Missed" msgstr "Nicht erschienen" -#: shifts/models.py:949 shifts/templates/shifts/shift_day_printable.html:58 -#: shifts/templates/shifts/shift_detail.html:312 +#: shifts/models.py:919 shifts/templates/shifts/shift_day_printable.html:58 +#: shifts/templates/shifts/shift_detail.html:273 #: shifts/templates/shifts/shift_detail_printable.html:53 msgid "Excused" msgstr "Entschuldigt" -#: shifts/models.py:950 shifts/templates/shifts/shift_detail.html:320 +#: shifts/models.py:920 shifts/templates/shifts/shift_detail.html:281 msgid "Cancelled" msgstr "Abgesagt" -#: shifts/models.py:951 shifts/templates/shifts/shift_day_printable.html:97 -#: shifts/templates/shifts/shift_detail.html:304 +#: shifts/models.py:921 shifts/templates/shifts/shift_day_printable.html:97 +#: shifts/templates/shifts/shift_detail.html:265 #: shifts/templates/shifts/shift_detail_printable.html:94 #: shifts/templates/shifts/shift_filters.html:83 msgid "Looking for a stand-in" msgstr "Sucht Vertretung" -#: shifts/models.py:984 +#: shifts/models.py:954 msgid "🏠 ABCD" msgstr "🏠 ABCD" -#: shifts/models.py:985 +#: shifts/models.py:955 msgid "✈ Flying" msgstr "✈ Fliegend" -#: shifts/models.py:986 +#: shifts/models.py:956 msgid "❄ Frozen" msgstr "" -#: shifts/models.py:1017 +#: shifts/models.py:987 msgid "Is frozen" msgstr "" -#: shifts/models.py:1191 +#: shifts/models.py:1161 msgid "Cycle start date" msgstr "Anfangsdatum" +#: shifts/services/self_unregister_service.py:59 +msgid "It is not possible to unregister from a shift that comes from your ABCD shift." +msgstr "Es ist nicht möglich, sich von einer Schicht abzumelden, die aus einer ABCD-Schicht stammt." + +#: shifts/services/self_unregister_service.py:62 +msgid "It is only possible to unregister from a shift at least 7 days before the shift." +msgstr "Es ist nur möglich, sich von einer Schicht abzumelden, wenn dies mindestens 7 Tage vor der Schicht geschieht." + #: shifts/templates/shifts/cancel_shift.html:7 #: shifts/templates/shifts/cancel_shift.html:13 msgid "Cancel shift:" @@ -3195,6 +3203,109 @@ msgstr "" msgid "Confirm cancellation" msgstr "Absage bestätigen" +#: shifts/templates/shifts/cant_attend.html:7 +msgid "Can't attend:" +msgstr "" + +#: shifts/templates/shifts/cant_attend.html:13 +msgid "If you can't attend" +msgstr "Wenn du an diese Schicht nicht teilnehmen kannst" + +#: shifts/templates/shifts/cant_attend.html:21 +msgid "Option 1: unregister" +msgstr "Option 1: Abmelden" + +#: shifts/templates/shifts/cant_attend.html:25 +msgid "" +"\n" +" You can unregister from this shift.\n" +" You will be removed from the attendance list and won't get any negative\n" +" point.\n" +" " +msgstr "" +"\n" +" Du kannst dich von dieser Schicht abmelden.\n" +" Du wirst aus der Anwesenheitsliste entfernt und bekommst keine negative Punkte.\n" +" " + +#: shifts/templates/shifts/cant_attend.html:31 +msgid "" +"\n" +" You cannot unregister from this shift.
\n" +" The reasons are:\n" +" " +msgstr "" +"\n" +" Du kannst dich nicht von dieser Schicht abmelden.
\n" +" Die Gründe dafür sind:\n" +" " + +#: shifts/templates/shifts/cant_attend.html:48 +#: shifts/templates/shifts/shift_template_detail.html:73 +msgid "Unregister" +msgstr "Abmelden" + +#: shifts/templates/shifts/cant_attend.html:57 +msgid "Option 2: Enable looking for a stand-in" +msgstr "Option 2: Suche nach einem Ersatz aktivieren" + +#: shifts/templates/shifts/cant_attend.html:59 +#, python-format +msgid "" +"\n" +"

\n" +" If you cannot unregister from the shift, your other option is to look for a replacement (\"stand-in\").\n" +" This will notify the team that you can't come, and will allow other members to take over your slot.\n" +" Please enable this as soon as you know you won't be able to come to your shift.\n" +"

\n" +"

\n" +" This is always possible up to the time of the shift.\n" +"

\n" +"

\n" +" After enabling this, it is still absolutely necessary that you contact the member office\n" +" (%(email_address_member_office)s)\n" +" as soon as possible.\n" +"

\n" +"

\n" +" If you have a valid reason for not coming (see the\n" +" Member Manual),\n" +" the member office will unregister you from the shift and you won't get any negative points.\n" +"

\n" +"

\n" +" If you don't have a valid reason for not coming, you may still get lucky:\n" +" someone may take over your shift. The shift calendar shows where someone is looking for a stand-in.
\n" +" If that happens, you will be notified by email and will not get any negative points.\n" +" You are still obliged to sign up for an alternative shift in this shift cycle.
\n" +" There is no guarantee that this happens. If your slot is not taken over, you will get negative points.\n" +"

\n" +" " +msgstr "" +"\n" +"

\n" +" Wenn du dich nicht von der Schicht abmelden kannst, besteht eine andere Möglichkeit darin, einen Ersatz zu suchen („stand-in“). Damit teilst du dem Team mit, dass du nicht kommen kannst und ermöglichst es anderen Mitgliedern, deinen Platz einzunehmen. Bitte aktiviere dies, sobald du weißt, dass du nicht zu deiner Schicht kommen kannst.

\n" +"

\n" +" Dies ist immer bis zum Zeitpunkt der Schicht möglich.\n" +"

\n" +"

\n" +" Auch nach der Aktivierung ist es unbedingt erforderlich, dass du das Mitgliederbüro \n" +" (%(email_address_member_office)s)\n" +" so schnell wie möglich kontaktierst.\n" +"

\n" +"

\n" +" Wenn du einen triftigen Grund für Ihr Nichterscheinen hast (siehe \n" +" Mitgliederhandbuch),\n" +" wird das Mitgliederbüro dich von der Schicht abmelden und du erhältst keine Minuspunkte.\n" +"

\n" +"

\n" +" Wenn du keinen triftigen Grund für dein Nichterscheinen hast, kannst du trotzdem Glück haben: Vielleicht übernimmt jemand deine Schicht. Der Schichtkalender zeigt an, wo jemand eine Vertretung sucht. Wenn das passiert, wirst du per E-Mail benachrichtigt und erhältst keine Minuspunkte. Du bist dann trotzdem verpflichtet, dich für eine alternative Schicht in diesem Schichtzyklus anzumelden.
\n" +" Es gibt keine Garantie, dass ein Mitglied deine Schicht übernimmt. Wenn dies nicht geschieht, bekommst du Minuspunkte.\n" +"

\n" +" " + +#: shifts/templates/shifts/cant_attend.html:93 +msgid "Look for a stand-in" +msgstr "Suche nach Vertretung" + #: shifts/templates/shifts/convert_exemption_to_pause_form.html:12 #: shifts/templates/shifts/convert_exemption_to_pause_form.html:18 msgid "Convert exemption to pause" @@ -3848,61 +3959,15 @@ msgid "Cancels the search for a stand-in. Use this if you want to attend the shi msgstr "Beendet die Suche nach Vertretung. Benutze dies, wenn du die Schicht wahrnehmen möchtest." #: shifts/templates/shifts/shift_detail.html:153 -#: shifts/templates/shifts/shift_detail.html:293 +#: shifts/templates/shifts/shift_detail.html:254 msgid "Cancel looking for a stand-in" msgstr "Beende die Suche nach Vertretung" -#: shifts/templates/shifts/shift_detail.html:161 -#, fuzzy, python-format -#| msgid "" -#| "You can only look for a\n" -#| " stand-in\n" -#| " %(NB_DAYS_FOR_SELF_LOOK_FOR_STAND_IN)s days before the shift. If\n" -#| " you can't\n" -#| " attend, contact your shift leader as soon as\n" -#| " possible." -msgid "" -"You can only look for\n" -" a\n" -" stand-in\n" -" %(NB_DAYS_FOR_SELF_LOOK_FOR_STAND_IN)s days before the\n" -" shift. If\n" -" you can't\n" -" attend, contact your shift leader as soon as\n" -" 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:174 -msgid "Look for a stand-in" -msgstr "Suche nach Vertretung" +#: shifts/templates/shifts/shift_detail.html:160 +msgid "I can't attend this shift" +msgstr "Ich kann nicht teilnehmen" -#: shifts/templates/shifts/shift_detail.html:183 -#, fuzzy, python-format -#| msgid "" -#| "You can only unregister\n" -#| " yourself\n" -#| " %(NB_DAYS_FOR_SELF_UNREGISTER)s days before the shift. Also,\n" -#| " ABCD-Shifts\n" -#| " can't be unregistered from. If you can't\n" -#| " attend, look for a stand-in or contact your shift leader as soon as\n" -#| " possible." -msgid "" -"You can only\n" -" unregister\n" -" yourself\n" -" %(NB_DAYS_FOR_SELF_UNREGISTER)s days before the shift. Also,\n" -" ABCD-Shifts\n" -" can't be unregistered from. If you can't\n" -" attend, look for a stand-in or contact your shift leader as soon\n" -" as\n" -" 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:198 -msgid "Unregister myself" -msgstr "Melde mich ab" - -#: shifts/templates/shifts/shift_detail.html:203 +#: shifts/templates/shifts/shift_detail.html:164 #, fuzzy #| msgid "" #| "You can only register\n" @@ -3931,12 +3996,12 @@ msgstr "" "- die Schicht in der Zukunft liegt\n" " " -#: shifts/templates/shifts/shift_detail.html:245 +#: shifts/templates/shifts/shift_detail.html:206 #: shifts/templates/shifts/shift_template_detail.html:95 msgid "Not specified" msgstr "" -#: shifts/templates/shifts/shift_detail.html:328 +#: shifts/templates/shifts/shift_detail.html:289 msgid "Edit slot" msgstr "Slot bearbeiten" @@ -4060,10 +4125,6 @@ msgstr "Liste des Slots für diese ABCD-Schicht" msgid "Requirements" msgstr "" -#: shifts/templates/shifts/shift_template_detail.html:73 -msgid "Unregister" -msgstr "Abmelden" - #: shifts/templates/shifts/shift_template_detail.html:122 msgid "Future generated Shifts" msgstr "Zukünftige erstellte Schichten" @@ -4409,23 +4470,23 @@ msgstr "Allgemein" msgid "Unknown mode {attendance_mode}" msgstr "" -#: shifts/views/attendance.py:197 shifts/views/attendance.py:341 +#: shifts/views/attendance.py:212 shifts/views/attendance.py:356 #, python-format msgid "Shift attendance: %(name)s" msgstr "Schicht-Anwesenheit: %(name)s" -#: shifts/views/attendance.py:203 shifts/views/attendance.py:347 +#: shifts/views/attendance.py:218 shifts/views/attendance.py:362 #, python-format msgid "Updating shift attendance: %(member_link)s, %(slot_link)s" msgstr "Schichtanwesenheit aktualisieren: %(member_link)s, %(slot_link)s" -#: shifts/views/attendance.py:381 +#: shifts/views/attendance.py:396 #, fuzzy, python-format #| msgid "Shift attendance: %(name)s" msgid "ABCD attendance: %(name)s" msgstr "Schicht-Anwesenheit: %(name)s" -#: shifts/views/attendance.py:388 +#: shifts/views/attendance.py:403 #, fuzzy, python-format #| msgid "Updating shift attendance: %(member_link)s, %(slot_link)s" msgid "Updating ABCD attendance: %(member_link)s, %(slot_link)s" @@ -4512,7 +4573,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:347 msgid "Frozen statuses updated." msgstr "" @@ -5906,6 +5967,49 @@ msgstr "%(name)s ist kein Mitglied der Genossenschaft. Vielleicht haben sie dere msgid "%(name)s has not attended a welcome session yet. Make sure they plan to do it!" msgstr "%(name)s hat an dem Willkommenstreffen noch nicht teilgenommen. Stelle sicher, dass er*sie es entsprechend einplant!" +#, fuzzy, python-format +#~| msgid "" +#~| "You can only look for a\n" +#~| " stand-in\n" +#~| " %(NB_DAYS_FOR_SELF_LOOK_FOR_STAND_IN)s days before the shift. If\n" +#~| " you can't\n" +#~| " attend, contact your shift leader as soon as\n" +#~| " possible." +#~ msgid "" +#~ "You can only look for\n" +#~ " a\n" +#~ " stand-in\n" +#~ " %(NB_DAYS_FOR_SELF_LOOK_FOR_STAND_IN)s days before the\n" +#~ " shift. If\n" +#~ " you can't\n" +#~ " attend, contact your shift leader as soon as\n" +#~ " 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." + +#, fuzzy, python-format +#~| msgid "" +#~| "You can only unregister\n" +#~| " yourself\n" +#~| " %(NB_DAYS_FOR_SELF_UNREGISTER)s days before the shift. Also,\n" +#~| " ABCD-Shifts\n" +#~| " can't be unregistered from. If you can't\n" +#~| " attend, look for a stand-in or contact your shift leader as soon as\n" +#~| " possible." +#~ msgid "" +#~ "You can only\n" +#~ " unregister\n" +#~ " yourself\n" +#~ " %(NB_DAYS_FOR_SELF_UNREGISTER)s days before the shift. Also,\n" +#~ " ABCD-Shifts\n" +#~ " can't be unregistered from. If you can't\n" +#~ " attend, look for a stand-in or contact your shift leader as soon\n" +#~ " as\n" +#~ " 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." + +#~ msgid "Unregister myself" +#~ msgstr "Melde mich ab" + #, fuzzy #~| msgid "Shift attendance: %(name)s" #~ msgid "Shift attendance rates" diff --git a/tapir/translations/locale/de/LC_MESSAGES/djangojs.po b/tapir/translations/locale/de/LC_MESSAGES/djangojs.po index 53eff401a..e933d1348 100644 --- a/tapir/translations/locale/de/LC_MESSAGES/djangojs.po +++ b/tapir/translations/locale/de/LC_MESSAGES/djangojs.po @@ -2,13 +2,13 @@ # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. -# +# #, fuzzy msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-01-31 12:35+0100\n" +"POT-Creation-Date: 2025-02-04 13:57+0100\n" "PO-Revision-Date: 2024-11-05 11:14+0100\n" "Last-Translator: \n" "Language-Team: \n" From c7ef64f013b8b6c0c50b9b207cd606fe30ee0b5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Madet?= Date: Fri, 21 Mar 2025 10:40:01 +0100 Subject: [PATCH 11/13] Restored translations after merging from master. --- .../locale/de/LC_MESSAGES/django.po | 272 ++++++++++++------ .../locale/de/LC_MESSAGES/djangojs.po | 2 +- 2 files changed, 190 insertions(+), 84 deletions(-) diff --git a/tapir/translations/locale/de/LC_MESSAGES/django.po b/tapir/translations/locale/de/LC_MESSAGES/django.po index b60b8759e..ebe939a05 100644 --- a/tapir/translations/locale/de/LC_MESSAGES/django.po +++ b/tapir/translations/locale/de/LC_MESSAGES/django.po @@ -2,12 +2,12 @@ # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. -# +# msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-03-20 19:43+0100\n" +"POT-Creation-Date: 2025-03-21 10:39+0100\n" "PO-Revision-Date: 2024-12-17 14:15+0100\n" "Last-Translator: \n" "Language-Team: \n" @@ -1034,7 +1034,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:187 #: shifts/templates/shifts/shift_template_detail.html:84 msgid "Register" msgstr "Anmelden" @@ -2309,14 +2309,14 @@ msgid "" msgstr "" #: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:56 -#: shifts/models.py:949 shifts/templates/shifts/shift_day_printable.html:56 -#: shifts/templates/shifts/shift_detail.html:281 +#: shifts/models.py:919 shifts/templates/shifts/shift_day_printable.html:56 +#: shifts/templates/shifts/shift_detail.html:242 #: shifts/templates/shifts/shift_detail_printable.html:51 msgid "Attended" msgstr "Teilgenommen" #: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:58 -#: shifts/models.py:948 +#: shifts/models.py:918 msgid "Pending" msgstr "Ausstehend" @@ -2648,7 +2648,7 @@ msgid "List of all emails" msgstr "Liste alle E-Mails" #: core/templates/core/email_list.html:39 shifts/models.py:513 -#: shifts/models.py:1087 shifts/templates/shifts/user_shift_account_log.html:29 +#: shifts/models.py:1057 shifts/templates/shifts/user_shift_account_log.html:29 msgid "Description" msgstr "Beschreibung" @@ -3101,7 +3101,7 @@ msgstr "Zeit flexibel" msgid "If enabled, members who register for that shift can choose themselves the time where they come do their shift." msgstr "" -#: shifts/models.py:417 shifts/models.py:840 +#: shifts/models.py:417 shifts/models.py:810 #: shifts/templates/shifts/shift_detail.html:95 #: shifts/templates/shifts/shift_template_detail.html:42 msgid "Chosen time" @@ -3127,53 +3127,61 @@ msgstr "" msgid "If 'flexible time' is enabled, then the time component is ignored" msgstr "" -#: shifts/models.py:842 +#: shifts/models.py:812 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 "" -#: shifts/models.py:950 shifts/templates/shifts/shift_day_printable.html:57 -#: shifts/templates/shifts/shift_detail.html:291 +#: shifts/models.py:920 shifts/templates/shifts/shift_day_printable.html:57 +#: shifts/templates/shifts/shift_detail.html:252 #: shifts/templates/shifts/shift_detail_printable.html:52 msgid "Missed" msgstr "Nicht erschienen" -#: shifts/models.py:951 shifts/templates/shifts/shift_day_printable.html:58 -#: shifts/templates/shifts/shift_detail.html:321 +#: shifts/models.py:921 shifts/templates/shifts/shift_day_printable.html:58 +#: shifts/templates/shifts/shift_detail.html:282 #: shifts/templates/shifts/shift_detail_printable.html:53 msgid "Excused" msgstr "Entschuldigt" -#: shifts/models.py:952 shifts/templates/shifts/shift_detail.html:329 +#: shifts/models.py:922 shifts/templates/shifts/shift_detail.html:290 msgid "Cancelled" msgstr "Abgesagt" -#: shifts/models.py:953 shifts/templates/shifts/shift_day_printable.html:97 -#: shifts/templates/shifts/shift_detail.html:313 +#: shifts/models.py:923 shifts/templates/shifts/shift_day_printable.html:97 +#: shifts/templates/shifts/shift_detail.html:274 #: shifts/templates/shifts/shift_detail_printable.html:94 #: shifts/templates/shifts/shift_filters.html:83 msgid "Looking for a stand-in" msgstr "Sucht Vertretung" -#: shifts/models.py:986 +#: shifts/models.py:956 msgid "🏠 ABCD" msgstr "🏠 ABCD" -#: shifts/models.py:987 +#: shifts/models.py:957 msgid "✈ Flying" msgstr "✈ Fliegend" -#: shifts/models.py:988 +#: shifts/models.py:958 msgid "❄ Frozen" msgstr "" -#: shifts/models.py:1019 +#: shifts/models.py:989 msgid "Is frozen" msgstr "" -#: shifts/models.py:1193 +#: shifts/models.py:1163 msgid "Cycle start date" msgstr "Anfangsdatum" +#: shifts/services/self_unregister_service.py:59 +msgid "It is not possible to unregister from a shift that comes from your ABCD shift." +msgstr "Es ist nicht möglich, sich von einer Schicht abzumelden, die aus einer ABCD-Schicht stammt." + +#: shifts/services/self_unregister_service.py:62 +msgid "It is only possible to unregister from a shift at least 7 days before the shift." +msgstr "Es ist nur möglich, sich von einer Schicht abzumelden, wenn dies mindestens 7 Tage vor der Schicht geschieht." + #: shifts/templates/shifts/cancel_shift.html:7 msgid "Cancel shift:" msgstr "Schicht absagen:" @@ -3208,6 +3216,111 @@ msgstr "" msgid "Confirm cancellation" msgstr "Absage bestätigen" +#: shifts/templates/shifts/cant_attend.html:7 +msgid "Can't attend:" +msgstr "" + +#: shifts/templates/shifts/cant_attend.html:13 +msgid "If you can't attend" +msgstr "Wenn du an diese Schicht nicht teilnehmen kannst" + +#: shifts/templates/shifts/cant_attend.html:21 +#, fuzzy +#| msgid "Unregister" +msgid "Option 1: unregister" +msgstr "Option 1: Abmelden" + +#: shifts/templates/shifts/cant_attend.html:25 +msgid "" +"\n" +" You can unregister from this shift.\n" +" You will be removed from the attendance list and won't get any negative\n" +" point.\n" +" " +msgstr "" +"\n" +" Du kannst dich von dieser Schicht abmelden.\n" +" Du wirst aus der Anwesenheitsliste entfernt und bekommst keine negative Punkte.\n" +" " + +#: shifts/templates/shifts/cant_attend.html:31 +msgid "" +"\n" +" You cannot unregister from this shift.
\n" +" The reasons are:\n" +" " +msgstr "" +"\n" +" Du kannst dich nicht von dieser Schicht abmelden.
\n" +" Die Gründe dafür sind:\n" +" " + +#: shifts/templates/shifts/cant_attend.html:48 +#: shifts/templates/shifts/shift_template_detail.html:73 +msgid "Unregister" +msgstr "Abmelden" + +#: shifts/templates/shifts/cant_attend.html:57 +msgid "Option 2: Enable looking for a stand-in" +msgstr "Option 2: Suche nach einem Ersatz aktivieren" + +#: shifts/templates/shifts/cant_attend.html:59 +#, python-format +msgid "" +"\n" +"

\n" +" If you cannot unregister from the shift, your other option is to look for a replacement (\"stand-in\").\n" +" This will notify the team that you can't come, and will allow other members to take over your slot.\n" +" Please enable this as soon as you know you won't be able to come to your shift.\n" +"

\n" +"

\n" +" This is always possible up to the time of the shift.\n" +"

\n" +"

\n" +" After enabling this, it is still absolutely necessary that you contact the member office\n" +" (%(email_address_member_office)s)\n" +" as soon as possible.\n" +"

\n" +"

\n" +" If you have a valid reason for not coming (see the\n" +" Member Manual),\n" +" the member office will unregister you from the shift and you won't get any negative points.\n" +"

\n" +"

\n" +" If you don't have a valid reason for not coming, you may still get lucky:\n" +" someone may take over your shift. The shift calendar shows where someone is looking for a stand-in.
\n" +" If that happens, you will be notified by email and will not get any negative points.\n" +" You are still obliged to sign up for an alternative shift in this shift cycle.
\n" +" There is no guarantee that this happens. If your slot is not taken over, you will get negative points.\n" +"

\n" +" " +msgstr "" +"\n" +"

\n" +" Wenn du dich nicht von der Schicht abmelden kannst, besteht eine andere Möglichkeit darin, einen Ersatz zu suchen („stand-in“). Damit teilst du dem Team mit, dass du nicht kommen kannst und ermöglichst es anderen Mitgliedern, deinen Platz einzunehmen. Bitte aktiviere dies, sobald du weißt, dass du nicht zu deiner Schicht kommen kannst.

\n" +"

\n" +" Dies ist immer bis zum Zeitpunkt der Schicht möglich.\n" +"

\n" +"

\n" +" Auch nach der Aktivierung ist es unbedingt erforderlich, dass du das Mitgliederbüro \n" +" (%(email_address_member_office)s)\n" +" so schnell wie möglich kontaktierst.\n" +"

\n" +"

\n" +" Wenn du einen triftigen Grund für Ihr Nichterscheinen hast (siehe \n" +" Mitgliederhandbuch),\n" +" wird das Mitgliederbüro dich von der Schicht abmelden und du erhältst keine Minuspunkte.\n" +"

\n" +"

\n" +" Wenn du keinen triftigen Grund für dein Nichterscheinen hast, kannst du trotzdem Glück haben: Vielleicht übernimmt jemand deine Schicht. Der Schichtkalender zeigt an, wo jemand eine Vertretung sucht. Wenn das passiert, wirst du per E-Mail benachrichtigt und erhältst keine Minuspunkte. Du bist dann trotzdem verpflichtet, dich für eine alternative Schicht in diesem Schichtzyklus anzumelden.
\n" +" Es gibt keine Garantie, dass ein Mitglied deine Schicht übernimmt. Wenn dies nicht geschieht, bekommst du Minuspunkte.\n" +"

\n" +" " + +#: shifts/templates/shifts/cant_attend.html:93 +msgid "Look for a stand-in" +msgstr "Suche nach Vertretung" + #: shifts/templates/shifts/convert_exemption_to_pause_form.html:12 #: shifts/templates/shifts/convert_exemption_to_pause_form.html:18 msgid "Convert exemption to pause" @@ -3894,61 +4007,15 @@ msgid "Cancels the search for a stand-in. Use this if you want to attend the shi 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:263 msgid "Cancel looking for a stand-in" msgstr "Beende die Suche nach Vertretung" -#: shifts/templates/shifts/shift_detail.html:170 -#, fuzzy, python-format -#| msgid "" -#| "You can only look for a\n" -#| " stand-in\n" -#| " %(NB_DAYS_FOR_SELF_LOOK_FOR_STAND_IN)s days before the shift. If\n" -#| " you can't\n" -#| " attend, contact your shift leader as soon as\n" -#| " possible." -msgid "" -"You can only look for\n" -" a\n" -" stand-in\n" -" %(NB_DAYS_FOR_SELF_LOOK_FOR_STAND_IN)s days before the\n" -" shift. If\n" -" you can't\n" -" attend, contact your shift leader as soon as\n" -" 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 -msgid "Look for a stand-in" -msgstr "Suche nach Vertretung" +#: shifts/templates/shifts/shift_detail.html:169 +msgid "I can't attend this shift" +msgstr "Ich kann nicht teilnehmen" -#: shifts/templates/shifts/shift_detail.html:192 -#, fuzzy, python-format -#| msgid "" -#| "You can only unregister\n" -#| " yourself\n" -#| " %(NB_DAYS_FOR_SELF_UNREGISTER)s days before the shift. Also,\n" -#| " ABCD-Shifts\n" -#| " can't be unregistered from. If you can't\n" -#| " attend, look for a stand-in or contact your shift leader as soon as\n" -#| " possible." -msgid "" -"You can only\n" -" unregister\n" -" yourself\n" -" %(NB_DAYS_FOR_SELF_UNREGISTER)s days before the shift. Also,\n" -" ABCD-Shifts\n" -" can't be unregistered from. If you can't\n" -" attend, look for a stand-in or contact your shift leader as soon\n" -" as\n" -" 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 -msgid "Unregister myself" -msgstr "Melde mich ab" - -#: shifts/templates/shifts/shift_detail.html:212 +#: shifts/templates/shifts/shift_detail.html:173 #, fuzzy #| msgid "" #| "You can only register\n" @@ -3977,12 +4044,12 @@ msgstr "" "- die Schicht in der Zukunft liegt\n" " " -#: shifts/templates/shifts/shift_detail.html:254 +#: shifts/templates/shifts/shift_detail.html:215 #: 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:298 msgid "Edit slot" msgstr "Slot bearbeiten" @@ -4106,10 +4173,6 @@ msgstr "Liste des Slots für diese ABCD-Schicht" msgid "Requirements" msgstr "" -#: shifts/templates/shifts/shift_template_detail.html:73 -msgid "Unregister" -msgstr "Abmelden" - #: shifts/templates/shifts/shift_template_detail.html:122 msgid "Future generated Shifts" msgstr "Zukünftige erstellte Schichten" @@ -4455,23 +4518,23 @@ msgstr "Allgemein" msgid "Unknown mode {attendance_mode}" msgstr "" -#: shifts/views/attendance.py:197 shifts/views/attendance.py:341 +#: shifts/views/attendance.py:212 shifts/views/attendance.py:356 #, python-format msgid "Shift attendance: %(name)s" msgstr "Schicht-Anwesenheit: %(name)s" -#: shifts/views/attendance.py:203 shifts/views/attendance.py:347 +#: shifts/views/attendance.py:218 shifts/views/attendance.py:362 #, python-format msgid "Updating shift attendance: %(member_link)s, %(slot_link)s" msgstr "Schichtanwesenheit aktualisieren: %(member_link)s, %(slot_link)s" -#: shifts/views/attendance.py:381 +#: shifts/views/attendance.py:396 #, fuzzy, python-format #| msgid "Shift attendance: %(name)s" msgid "ABCD attendance: %(name)s" msgstr "Schicht-Anwesenheit: %(name)s" -#: shifts/views/attendance.py:388 +#: shifts/views/attendance.py:403 #, fuzzy, python-format #| msgid "Updating shift attendance: %(member_link)s, %(slot_link)s" msgid "Updating ABCD attendance: %(member_link)s, %(slot_link)s" @@ -4558,7 +4621,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:347 msgid "Frozen statuses updated." msgstr "" @@ -5980,6 +6043,49 @@ msgstr "%(name)s ist kein Mitglied der Genossenschaft. Vielleicht haben sie dere msgid "%(name)s has not attended a welcome session yet. Make sure they plan to do it!" msgstr "%(name)s hat an dem Willkommenstreffen noch nicht teilgenommen. Stelle sicher, dass er*sie es entsprechend einplant!" +#, fuzzy, python-format +#~| msgid "" +#~| "You can only look for a\n" +#~| " stand-in\n" +#~| " %(NB_DAYS_FOR_SELF_LOOK_FOR_STAND_IN)s days before the shift. If\n" +#~| " you can't\n" +#~| " attend, contact your shift leader as soon as\n" +#~| " possible." +#~ msgid "" +#~ "You can only look for\n" +#~ " a\n" +#~ " stand-in\n" +#~ " %(NB_DAYS_FOR_SELF_LOOK_FOR_STAND_IN)s days before the\n" +#~ " shift. If\n" +#~ " you can't\n" +#~ " attend, contact your shift leader as soon as\n" +#~ " 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." + +#, fuzzy, python-format +#~| msgid "" +#~| "You can only unregister\n" +#~| " yourself\n" +#~| " %(NB_DAYS_FOR_SELF_UNREGISTER)s days before the shift. Also,\n" +#~| " ABCD-Shifts\n" +#~| " can't be unregistered from. If you can't\n" +#~| " attend, look for a stand-in or contact your shift leader as soon as\n" +#~| " possible." +#~ msgid "" +#~ "You can only\n" +#~ " unregister\n" +#~ " yourself\n" +#~ " %(NB_DAYS_FOR_SELF_UNREGISTER)s days before the shift. Also,\n" +#~ " ABCD-Shifts\n" +#~ " can't be unregistered from. If you can't\n" +#~ " attend, look for a stand-in or contact your shift leader as soon\n" +#~ " as\n" +#~ " 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." + +#~ msgid "Unregister myself" +#~ msgstr "Melde mich ab" + #~ msgid "Paid Shares" #~ msgstr "Anteil(e) bezahlt" diff --git a/tapir/translations/locale/de/LC_MESSAGES/djangojs.po b/tapir/translations/locale/de/LC_MESSAGES/djangojs.po index df5d21902..a97409c2f 100644 --- a/tapir/translations/locale/de/LC_MESSAGES/djangojs.po +++ b/tapir/translations/locale/de/LC_MESSAGES/djangojs.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-10 12:35+0100\n" +"POT-Creation-Date: 2025-03-21 10:35+0100\n" "PO-Revision-Date: 2024-11-05 11:14+0100\n" "Last-Translator: \n" "Language-Team: \n" From 160735076d32fa185704686c6705b8ca6e716983 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Madet?= Date: Fri, 21 Mar 2025 10:46:41 +0100 Subject: [PATCH 12/13] Function name fix --- tapir/shifts/tests/test_shift_delete_view.py | 4 ++-- tapir/utils/tests_utils.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tapir/shifts/tests/test_shift_delete_view.py b/tapir/shifts/tests/test_shift_delete_view.py index af0e671dc..ae4721381 100644 --- a/tapir/shifts/tests/test_shift_delete_view.py +++ b/tapir/shifts/tests/test_shift_delete_view.py @@ -6,7 +6,7 @@ class TestShiftDeleteForm(PermissionTestMixin, TapirFactoryTestBase): - def get_allowed_groups(self): + def permission_test_get_allowed_groups(self): return [ settings.GROUP_VORSTAND, settings.GROUP_EMPLOYEES, @@ -14,7 +14,7 @@ def get_allowed_groups(self): settings.GROUP_SHIFT_MANAGER, ] - def do_request(self): + def permission_test_do_request(self): shift = ShiftFactory.create() return self.client.get(reverse("shifts:shift_delete", args=[shift.id])) diff --git a/tapir/utils/tests_utils.py b/tapir/utils/tests_utils.py index 61a14b4ac..e483184fa 100644 --- a/tapir/utils/tests_utils.py +++ b/tapir/utils/tests_utils.py @@ -272,12 +272,12 @@ def given_feature_flag_value(self, flag_name: str, flag_value: bool): class PermissionTestMixin: def permission_test_get_allowed_groups(self): raise NotImplementedError( - "Children of PermissionTestMixin must implement get_allowed_groups to say which groups should have access to the view." + "Children of PermissionTestMixin must implement permission_test_get_allowed_groups to say which groups should have access to the view." ) def permission_test_do_request(self): raise NotImplementedError( - "Children of PermissionTestMixin must implement do_request, a function that visits the target view and returns the response." + "Children of PermissionTestMixin must implement permission_test_do_request, a function that visits the target view and returns the response." ) @parameterized.expand(settings.LDAP_GROUPS) From 209135998bd2504e855d246b6a4d9be1663c10f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Madet?= Date: Fri, 21 Mar 2025 10:55:35 +0100 Subject: [PATCH 13/13] Translation fix --- tapir/translations/locale/de/LC_MESSAGES/django.po | 2 -- 1 file changed, 2 deletions(-) diff --git a/tapir/translations/locale/de/LC_MESSAGES/django.po b/tapir/translations/locale/de/LC_MESSAGES/django.po index ebe939a05..f14f62831 100644 --- a/tapir/translations/locale/de/LC_MESSAGES/django.po +++ b/tapir/translations/locale/de/LC_MESSAGES/django.po @@ -3225,8 +3225,6 @@ msgid "If you can't attend" msgstr "Wenn du an diese Schicht nicht teilnehmen kannst" #: shifts/templates/shifts/cant_attend.html:21 -#, fuzzy -#| msgid "Unregister" msgid "Option 1: unregister" msgstr "Option 1: Abmelden"