From 24810a4f2b70d8fd8381ed690588f4c698e1afa9 Mon Sep 17 00:00:00 2001 From: Christian Merten Date: Tue, 11 Feb 2025 01:12:06 +0100 Subject: [PATCH] fix(members/models): align SJR subsidizable participants with regulations --- jdav_web/members/models.py | 44 +++++++++++++++++++++++++++---------- jdav_web/members/tests.py | 45 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 12 deletions(-) diff --git a/jdav_web/members/models.py b/jdav_web/members/models.py index 1d03a40..05b6999 100644 --- a/jdav_web/members/models.py +++ b/jdav_web/members/models.py @@ -1303,26 +1303,46 @@ class Freizeit(CommonModel): sks.append(dict(name=activity, skill_avg=skill_avg, skill_min=skill_min, skill_max=skill_max)) return (people, sks) + def sjr_application_numbers(self): + members = set(map(lambda x: x.member, self.membersonlist.distinct())) + jls = set(self.jugendleiter.distinct()) + participants = members - jls + b27_local = len([m for m in participants + if m.age() <= 27 and settings.SEKTION in m.town]) + b27_non_local = len([m for m in participants + if m.age() <= 27 and not settings.SEKTION in m.town]) + staff = len(jls) + total = b27_local + b27_non_local + len(jls) + relevant_b27 = min(b27_local + b27_non_local, math.floor(3/2 * b27_local)) + subsidizable = relevant_b27 + min(math.ceil(relevant_b27 / 7), staff) + duration = self.night_count + 1 + return { + 'b27_local': b27_local, + 'b27_non_local': b27_non_local, + 'staff': staff, + 'total': total, + 'relevant_b27': relevant_b27, + 'subsidizable': subsidizable, + 'subsidized_days': duration * subsidizable, + 'duration': duration + } + def sjr_application_fields(self): members = set(map(lambda x: x.member, self.membersonlist.distinct())) - total = len(members) - total_b27_local = len([m for m in members - if m.age() <= 27 and settings.SEKTION in m.town]) - total_b27_non_local = len([m for m in members - if m.age() <= 27 and not settings.SEKTION in m.town]) - jls = self.jugendleiter.distinct() + jls = set(self.jugendleiter.distinct()) + numbers = self.sjr_application_numbers() title = self.ljpproposal.title if hasattr(self, 'ljpproposal') else self.name base = {'Haushaltsjahr': str(datetime.now().year), 'Art / Thema / Titel': title, 'Ort': self.place, 'Datum von': self.date.strftime('%d.%m.%Y'), 'Datum bis': self.end.strftime('%d.%m.%Y'), - 'Dauer': str(self.duration).replace('.', ','), - 'Teilnehmenden gesamt': str(total), - 'bis 27 aus HD': str(total_b27_local), - 'bis 27 nicht aus HD': str(total_b27_non_local), - 'Verpflegungstage': str(self.duration * self.participant_count).replace('.', ','), - 'Betreuer/in': str(len(jls)), + 'Dauer': str(numbers['duration']), + 'Teilnehmenden gesamt': str(numbers['total']), + 'bis 27 aus HD': str(numbers['b27_local']), + 'bis 27 nicht aus HD': str(numbers['b27_non_local']), + 'Verpflegungstage': str(numbers['subsidized_days']).replace('.', ','), + 'Betreuer/in': str(numbers['staff']), 'Auswahl Veranstaltung': 'Auswahl2', 'Ort, Datum': '{p}, {d}'.format(p=settings.SEKTION, d=datetime.now().strftime('%d.%m.%Y'))} print(members) diff --git a/jdav_web/members/tests.py b/jdav_web/members/tests.py index e6d75e7..1fd9033 100644 --- a/jdav_web/members/tests.py +++ b/jdav_web/members/tests.py @@ -581,6 +581,31 @@ class FreizeitTestCase(BasicMemberTestCase): tour_approach=MUSKELKRAFT_ANREISE, difficulty=1) + def _setup_test_sjr_application_numbers(self, n_yl, n_b27_local, n_b27_non_local): + for i in range(n_yl): + m = Member.objects.create(prename='Peter {}'.format(i), + lastname='Wulter', + birth_date=datetime.datetime.today() - relativedelta(years=50), + email=settings.TEST_MAIL, + gender=FEMALE) + self.ex.jugendleiter.add(m) + NewMemberOnList.objects.create(member=m, comments='a', memberlist=self.ex) + for i in range(n_b27_local): + m = Member.objects.create(prename='Lise {}'.format(i), + lastname='Walter', + birth_date=datetime.datetime.today() - relativedelta(years=10), + town=settings.SEKTION, + email=settings.TEST_MAIL, + gender=FEMALE) + NewMemberOnList.objects.create(member=m, comments='a', memberlist=self.ex) + for i in range(n_b27_non_local): + m = Member.objects.create(prename='Lise {}'.format(i), + lastname='Walter', + birth_date=datetime.datetime.today() - relativedelta(years=10), + email=settings.TEST_MAIL, + gender=FEMALE) + NewMemberOnList.objects.create(member=m, comments='a', memberlist=self.ex) + def _setup_test_ljp_participant_count(self, n_yl, n_correct_age, n_too_old): for i in range(n_yl): # a 50 years old @@ -648,6 +673,26 @@ class FreizeitTestCase(BasicMemberTestCase): self._test_ljp_participant_count_proportion(2, 1, 1) self._test_ljp_participant_count_proportion(2, 5, 1) + def _test_sjr_application_numbers(self, n_yl, n_b27_local, n_b27_non_local): + self._setup_test_sjr_application_numbers(n_yl, n_b27_local, n_b27_non_local) + numbers = self.ex.sjr_application_numbers() + + self.assertEqual(numbers['b27_local'], n_b27_local) + self.assertEqual(numbers['b27_non_local'], n_b27_non_local) + self.assertEqual(numbers['staff'], n_yl) + self.assertLessEqual(numbers['relevant_b27'], n_b27_local + n_b27_non_local) + self.assertLessEqual(numbers['relevant_b27'] - n_b27_local, 1/3 * numbers['relevant_b27']) + self.assertLessEqual(numbers['subsidizable'] - numbers['relevant_b27'], n_yl) + self.assertLessEqual(numbers['subsidizable'] - numbers['relevant_b27'], numbers['relevant_b27'] / 7 + 1) + + # cleanup + self._cleanup_excursion() + + def test_sjr_application_numbers(self): + self._test_sjr_application_numbers(0, 10, 0) + for i in range(10): + self._test_sjr_application_numbers(10, 10 - i, i) + class PDFActionMixin: def _test_pdf(self, name, pk, model='freizeit', invalid=False, username='superuser', post_data=None):