From b4aa592ef848c92a8436c51206fdf17c9e137089 Mon Sep 17 00:00:00 2001 From: yazanzarka1 Date: Wed, 2 Oct 2024 01:09:06 +0200 Subject: [PATCH 01/14] filtering by year and study --- app/content/filters/registration.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/app/content/filters/registration.py b/app/content/filters/registration.py index 94f35b037..a6dc8b639 100644 --- a/app/content/filters/registration.py +++ b/app/content/filters/registration.py @@ -1,9 +1,28 @@ from django_filters.rest_framework import FilterSet +from django_filters import rest_framework as filters +from app.common.enums import NativeGroupType as GroupType from app.content.models.registration import Registration class RegistrationFilter(FilterSet): + + study = filters.CharFilter( + field_name="user__groups__name", + lookup_expr="icontains", + method='filter_study' + ) + year = filters.CharFilter( + field_name="user__groups__name", + lookup_expr="icontains", + method='filter_year' + ) class Meta: model = Registration - fields = ["has_attended", "is_on_wait"] + fields = ["has_attended", "is_on_wait", "study", "year"] + + def filter_study(self, queryset, name, value): + return queryset.filter(user__memberships__group__name__icontains=value, user__memberships__group__type=GroupType.STUDY) + + def filter_year(self, queryset, name, value): + return queryset.filter(user__memberships__group__name__icontains=value, user__memberships__group__type=GroupType.STUDYYEAR) From cf503162cb30b64d263a65a01877b3dbb0355f09 Mon Sep 17 00:00:00 2001 From: yazanzarka1 Date: Thu, 3 Oct 2024 22:29:55 +0200 Subject: [PATCH 02/14] linting fix --- app/content/filters/registration.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/app/content/filters/registration.py b/app/content/filters/registration.py index a6dc8b639..071ec977a 100644 --- a/app/content/filters/registration.py +++ b/app/content/filters/registration.py @@ -8,21 +8,24 @@ class RegistrationFilter(FilterSet): study = filters.CharFilter( - field_name="user__groups__name", - lookup_expr="icontains", - method='filter_study' + field_name="user__groups__name", lookup_expr="icontains", method="filter_study" ) year = filters.CharFilter( - field_name="user__groups__name", - lookup_expr="icontains", - method='filter_year' + field_name="user__groups__name", lookup_expr="icontains", method="filter_year" ) + class Meta: model = Registration fields = ["has_attended", "is_on_wait", "study", "year"] - + def filter_study(self, queryset, name, value): - return queryset.filter(user__memberships__group__name__icontains=value, user__memberships__group__type=GroupType.STUDY) + return queryset.filter( + user__memberships__group__name__icontains=value, + user__memberships__group__type=GroupType.STUDY, + ) def filter_year(self, queryset, name, value): - return queryset.filter(user__memberships__group__name__icontains=value, user__memberships__group__type=GroupType.STUDYYEAR) + return queryset.filter( + user__memberships__group__name__icontains=value, + user__memberships__group__type=GroupType.STUDYYEAR, + ) From 7bb2df6900c5c97b1f59be34ade1b219c40a0595 Mon Sep 17 00:00:00 2001 From: yazanzarka1 Date: Mon, 7 Oct 2024 17:52:53 +0200 Subject: [PATCH 03/14] added allergy filter --- app/content/filters/registration.py | 14 ++++- app/content/serializers/event.py | 92 +++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+), 1 deletion(-) diff --git a/app/content/filters/registration.py b/app/content/filters/registration.py index a6dc8b639..c1abe2217 100644 --- a/app/content/filters/registration.py +++ b/app/content/filters/registration.py @@ -17,12 +17,24 @@ class RegistrationFilter(FilterSet): lookup_expr="icontains", method='filter_year' ) + + has_allergy = filters.BooleanFilter( + field_name="user__allergy", + method='filter_has_allergy' + ) + + class Meta: model = Registration - fields = ["has_attended", "is_on_wait", "study", "year"] + fields = ["has_attended", "is_on_wait", "study", "year", "has_allergy"] def filter_study(self, queryset, name, value): return queryset.filter(user__memberships__group__name__icontains=value, user__memberships__group__type=GroupType.STUDY) def filter_year(self, queryset, name, value): return queryset.filter(user__memberships__group__name__icontains=value, user__memberships__group__type=GroupType.STUDYYEAR) + + def filter_has_allergy(self, queryset, name, value): + if value: + return queryset.exclude(user__allergy__isnull=True).exclude(user__allergy__exact='') + return queryset diff --git a/app/content/serializers/event.py b/app/content/serializers/event.py index 73be7ad54..7c7203eae 100644 --- a/app/content/serializers/event.py +++ b/app/content/serializers/event.py @@ -302,3 +302,95 @@ def get_studies(self, obj, *args, **kwargs): Group.objects.filter(type=GroupType.STUDY), ), ) + + +class EventCategoriesSerializer(BaseModelSerializer): + has_attended_count = serializers.SerializerMethodField() + studyyears = serializers.SerializerMethodField() + studies = serializers.SerializerMethodField() + + class Meta: + model = Event + fields = ( + "has_attended_count", + "list_count", + "waiting_list_count", + "studyyears", + "studies", + ) + + def get_has_attended_count(self, obj, *args, **kwargs): + return obj.registrations.filter(is_on_wait=False, has_attended=True).count() + + def get_studyyears(self, obj, *args, **kwargs): + return filter( + lambda studyyear: studyyear["amount"] > 0, + map( + lambda group: { + "studyyear": group.name, + "amount": obj.registrations.filter( + user__memberships__group=group, is_on_wait=False + ).count(), + }, + Group.objects.filter(type=GroupType.STUDYYEAR), + ), + ) + + def get_studies(self, obj, *args, **kwargs): + return filter( + lambda study: study["amount"] > 0, + map( + lambda group: { + "study": group.name, + "amount": obj.registrations.filter( + user__memberships__group=group, is_on_wait=False + ).count(), + }, + Group.objects.filter(type=GroupType.STUDY), + ), + ) + +class EventRegistrationSerializer(BaseModelSerializer): + has_attended_count = serializers.SerializerMethodField() + studyyears = serializers.SerializerMethodField() + studies = serializers.SerializerMethodField() + + class Meta: + model = Event + fields = ( + "list_count", + "waiting_list_count", + "studyyears", + "studies", + ) + + def get_has_attended_count(self, obj, *args, **kwargs): + return obj.registrations.filter(is_on_wait=False, has_attended=True).all() + + def get_studyyears(self, obj, *args, **kwargs): + return filter( + lambda studyyear: studyyear["amount"] > 0, + map( + lambda group: { + "studyyear": group.name, + "amount": obj.registrations.filter( + user__memberships__group=group, is_on_wait=False + ).count(), + }, + Group.objects.filter(type=GroupType.STUDYYEAR), + ), + ) + + def get_studies(self, obj, *args, **kwargs): + return filter( + lambda study: study["amount"] > 0, + map( + lambda group: { + "study": group.name, + "amount": obj.registrations.filter( + user__memberships__group=group, is_on_wait=False + ).count(), + }, + Group.objects.filter(type=GroupType.STUDY), + ), + ) From 149e29d67a5be48d2f11f846301c3633b35025ad Mon Sep 17 00:00:00 2001 From: yazanzarka1 Date: Mon, 7 Oct 2024 19:37:51 +0200 Subject: [PATCH 04/14] added filter by allergies and participants with allergy count to event. --- app/content/filters/registration.py | 2 +- app/content/serializers/event.py | 94 ++--------------------------- 2 files changed, 5 insertions(+), 91 deletions(-) diff --git a/app/content/filters/registration.py b/app/content/filters/registration.py index 3b58f83f9..5e2819b4c 100644 --- a/app/content/filters/registration.py +++ b/app/content/filters/registration.py @@ -36,4 +36,4 @@ def filter_year(self, queryset, name, value): def filter_has_allergy(self, queryset, name, value): if value: return queryset.exclude(user__allergy__isnull=True).exclude(user__allergy__exact='') - return queryset + return queryset.filter(user__allergy__isnull=True) | queryset.filter(user__allergy__exact='') diff --git a/app/content/serializers/event.py b/app/content/serializers/event.py index 8b9f26c2e..f4ac7af92 100644 --- a/app/content/serializers/event.py +++ b/app/content/serializers/event.py @@ -263,6 +263,7 @@ class EventStatisticsSerializer(BaseModelSerializer): has_attended_count = serializers.SerializerMethodField() studyyears = serializers.SerializerMethodField() studies = serializers.SerializerMethodField() + has_allergy_count = serializers.SerializerMethodField() class Meta: model = Event @@ -272,57 +273,14 @@ class Meta: "waiting_list_count", "studyyears", "studies", + "has_allergy_count", ) def get_has_attended_count(self, obj, *args, **kwargs): return obj.registrations.filter(is_on_wait=False, has_attended=True).count() - - def get_studyyears(self, obj, *args, **kwargs): - return filter( - lambda studyyear: studyyear["amount"] > 0, - map( - lambda group: { - "studyyear": group.name, - "amount": obj.registrations.filter( - user__memberships__group=group, is_on_wait=False - ).count(), - }, - Group.objects.filter(type=GroupType.STUDYYEAR), - ), - ) - - def get_studies(self, obj, *args, **kwargs): - return filter( - lambda study: study["amount"] > 0, - map( - lambda group: { - "study": group.name, - "amount": obj.registrations.filter( - user__memberships__group=group, is_on_wait=False - ).count(), - }, - Group.objects.filter(type=GroupType.STUDY), - ), - ) - -class EventCategoriesSerializer(BaseModelSerializer): - has_attended_count = serializers.SerializerMethodField() - studyyears = serializers.SerializerMethodField() - studies = serializers.SerializerMethodField() - - class Meta: - model = Event - fields = ( - "has_attended_count", - "list_count", - "waiting_list_count", - "studyyears", - "studies", - ) - - def get_has_attended_count(self, obj, *args, **kwargs): - return obj.registrations.filter(is_on_wait=False, has_attended=True).count() + def get_has_allergy_count(self, obj, *args, **kwargs): + return obj.registrations.exclude(user__allergy__isnull=True).exclude(user__allergy__exact='').count() def get_studyyears(self, obj, *args, **kwargs): return filter( @@ -352,47 +310,3 @@ def get_studies(self, obj, *args, **kwargs): ), ) -class EventRegistrationSerializer(BaseModelSerializer): - has_attended_count = serializers.SerializerMethodField() - studyyears = serializers.SerializerMethodField() - studies = serializers.SerializerMethodField() - - class Meta: - model = Event - fields = ( - "list_count", - "waiting_list_count", - "studyyears", - "studies", - ) - - def get_has_attended_count(self, obj, *args, **kwargs): - return obj.registrations.filter(is_on_wait=False, has_attended=True).all() - - def get_studyyears(self, obj, *args, **kwargs): - return filter( - lambda studyyear: studyyear["amount"] > 0, - map( - lambda group: { - "studyyear": group.name, - "amount": obj.registrations.filter( - user__memberships__group=group, is_on_wait=False - ).count(), - }, - Group.objects.filter(type=GroupType.STUDYYEAR), - ), - ) - - def get_studies(self, obj, *args, **kwargs): - return filter( - lambda study: study["amount"] > 0, - map( - lambda group: { - "study": group.name, - "amount": obj.registrations.filter( - user__memberships__group=group, is_on_wait=False - ).count(), - }, - Group.objects.filter(type=GroupType.STUDY), - ), - ) From 858da7c8e112f1d0e2018d850dedafbfcf0610d4 Mon Sep 17 00:00:00 2001 From: yazanzarka1 Date: Mon, 7 Oct 2024 19:55:10 +0200 Subject: [PATCH 05/14] Lint fix --- app/content/filters/registration.py | 19 ++++++++++++------- app/content/serializers/event.py | 9 ++++++--- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/app/content/filters/registration.py b/app/content/filters/registration.py index 5e2819b4c..6bab53b2c 100644 --- a/app/content/filters/registration.py +++ b/app/content/filters/registration.py @@ -15,15 +15,13 @@ class RegistrationFilter(FilterSet): ) has_allergy = filters.BooleanFilter( - field_name="user__allergy", - method='filter_has_allergy' + field_name="user__allergy", method="filter_has_allergy" ) - class Meta: model = Registration fields = ["has_attended", "is_on_wait", "study", "year", "has_allergy"] - + def filter_study(self, queryset, name, value): return queryset.filter( user__memberships__group__name__icontains=value, @@ -31,9 +29,16 @@ def filter_study(self, queryset, name, value): ) def filter_year(self, queryset, name, value): - return queryset.filter(user__memberships__group__name__icontains=value, user__memberships__group__type=GroupType.STUDYYEAR) + return queryset.filter( + user__memberships__group__name__icontains=value, + user__memberships__group__type=GroupType.STUDYYEAR, + ) def filter_has_allergy(self, queryset, name, value): if value: - return queryset.exclude(user__allergy__isnull=True).exclude(user__allergy__exact='') - return queryset.filter(user__allergy__isnull=True) | queryset.filter(user__allergy__exact='') + return queryset.exclude(user__allergy__isnull=True).exclude( + user__allergy__exact="" + ) + return queryset.filter(user__allergy__isnull=True) | queryset.filter( + user__allergy__exact="" + ) diff --git a/app/content/serializers/event.py b/app/content/serializers/event.py index f4ac7af92..7467c53dd 100644 --- a/app/content/serializers/event.py +++ b/app/content/serializers/event.py @@ -278,9 +278,13 @@ class Meta: def get_has_attended_count(self, obj, *args, **kwargs): return obj.registrations.filter(is_on_wait=False, has_attended=True).count() - + def get_has_allergy_count(self, obj, *args, **kwargs): - return obj.registrations.exclude(user__allergy__isnull=True).exclude(user__allergy__exact='').count() + return ( + obj.registrations.exclude(user__allergy__isnull=True) + .exclude(user__allergy__exact="") + .count() + ) def get_studyyears(self, obj, *args, **kwargs): return filter( @@ -309,4 +313,3 @@ def get_studies(self, obj, *args, **kwargs): Group.objects.filter(type=GroupType.STUDY), ), ) - From 29633ff844d5b945ceeb0091e32a962bb92b85db Mon Sep 17 00:00:00 2001 From: Harry Linrui XU Date: Tue, 15 Oct 2024 10:22:51 +0200 Subject: [PATCH 06/14] Add new fixture for admin user --- app/tests/conftest.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/tests/conftest.py b/app/tests/conftest.py index e2efd0300..db2e7f4eb 100644 --- a/app/tests/conftest.py +++ b/app/tests/conftest.py @@ -311,3 +311,9 @@ def codex_event(): @pytest.fixture() def codex_event_registration(): return CodexEventRegistrationFactory() + +@pytest.fixture() +def new_admin_user(): + admin = UserFactory() + add_user_to_group_with_name(admin, AdminGroup.HS) + return admin From d6eb455a15d8b8927cd9294343e04d176d69164b Mon Sep 17 00:00:00 2001 From: Harry Linrui XU Date: Tue, 15 Oct 2024 10:23:10 +0200 Subject: [PATCH 07/14] Start testing filtering + finished allergy filter test --- .../content/test_registration_integration.py | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/app/tests/content/test_registration_integration.py b/app/tests/content/test_registration_integration.py index 5b9f20f84..abbeee7f4 100644 --- a/app/tests/content/test_registration_integration.py +++ b/app/tests/content/test_registration_integration.py @@ -6,6 +6,8 @@ from app.common.enums import AdminGroup from app.common.enums import NativeGroupType as GroupType +from app.common.enums import NativeUserStudy as StudyType +from app.common.enums import NativeUserClass as StudyYear from app.common.enums import NativeMembershipType as MembershipType from app.content.factories import EventFactory, RegistrationFactory, UserFactory from app.content.factories.priority_pool_factory import PriorityPoolFactory @@ -1071,3 +1073,64 @@ def test_delete_registration_with_paid_order_as_self( response = client.delete(url) assert response.status_code == status_code + +@pytest.mark.django_db +def test_member_cannot_filter_participants(member, event): + RegistrationFactory(user=member, event=event) + client = get_api_client(user=member) + + # TODO: Blir det forbidden når member prøver å filterere eller blir det ok, men ingen filtrering blir gjort? + url = _get_registration_url(event) + "?allergy=True" + print(url) + response = client.get(url) + print(response.data['count']) + + assert response.status_code == "" + +@pytest.mark.django_db +@pytest.mark.parametrize( + ("filter_parameter", "filter_value", "participant_count", "status_code"), + [ + ("has_allergy", True, 1, status.HTTP_200_OK), + # ("year", 1, status.HTTP_200_OK), + # ("study", 1, status.HTTP_200_OK), + # # not sure if this is correct + # ("study", 0, status.HTTP_401_UNAUTHORIZED), + ], +) +def test_filter_participants( + new_admin_user, member, event, filter_parameter, filter_value, participant_count, status_code +): + """ + An admin should be able to filter the participants of an event + """ + + add_user_to_group_with_name( + member, StudyType.DATAING, MembershipType.MEMBER) + add_user_to_group_with_name( + member, StudyYear.FIRST, MembershipType.MEMBER) + member.allergy = "Pizza" + member.save() + print(member.memberships) + + RegistrationFactory(user=member, event=event) + RegistrationFactory(user=new_admin_user, event=event) + client = get_api_client(user=new_admin_user) + + # TODO: + # --- + # Test filter by each parameter + # Test member cannot filter + # Test search for member (maybe separate test) + # --- + # *** Url needs to have query param for filtering - assert list size + + # registration = RegistrationFactory(user=member, event=event) + url = _get_registration_url(event) + "?" + \ + filter_parameter + "=" + str(filter_value) + print(url) + response = client.get(url) + print(response.data['count']) + + assert participant_count == response.data['count'] + assert response.status_code == status_code From 29e705fb5482f7781cd8c462d8c4a822d5556920 Mon Sep 17 00:00:00 2001 From: yazanzarka1 Date: Wed, 16 Oct 2024 16:20:27 +0200 Subject: [PATCH 08/14] Added integration test for participants filtering --- app/tests/conftest.py | 4 +- .../content/test_registration_integration.py | 73 +++++++++---------- 2 files changed, 36 insertions(+), 41 deletions(-) diff --git a/app/tests/conftest.py b/app/tests/conftest.py index 6fa479e21..fbdccddf2 100644 --- a/app/tests/conftest.py +++ b/app/tests/conftest.py @@ -313,14 +313,14 @@ def codex_event(): def codex_event_registration(): return CodexEventRegistrationFactory() - + @pytest.fixture() def new_admin_user(): admin = UserFactory() add_user_to_group_with_name(admin, AdminGroup.HS) return admin - + @pytest.fixture() def feedback_bug(): return BugFactory() diff --git a/app/tests/content/test_registration_integration.py b/app/tests/content/test_registration_integration.py index abbeee7f4..3710a337b 100644 --- a/app/tests/content/test_registration_integration.py +++ b/app/tests/content/test_registration_integration.py @@ -1074,63 +1074,58 @@ def test_delete_registration_with_paid_order_as_self( assert response.status_code == status_code -@pytest.mark.django_db -def test_member_cannot_filter_participants(member, event): - RegistrationFactory(user=member, event=event) - client = get_api_client(user=member) - - # TODO: Blir det forbidden når member prøver å filterere eller blir det ok, men ingen filtrering blir gjort? - url = _get_registration_url(event) + "?allergy=True" - print(url) - response = client.get(url) - print(response.data['count']) - - assert response.status_code == "" @pytest.mark.django_db @pytest.mark.parametrize( - ("filter_parameter", "filter_value", "participant_count", "status_code"), + ("filter_params", "participant_count", "status_code"), [ - ("has_allergy", True, 1, status.HTTP_200_OK), - # ("year", 1, status.HTTP_200_OK), - # ("study", 1, status.HTTP_200_OK), - # # not sure if this is correct - # ("study", 0, status.HTTP_401_UNAUTHORIZED), + ({"has_allergy": True}, 2, status.HTTP_200_OK), + ({"year": "2050"}, 1, status.HTTP_200_OK), + ({"year": "2051"}, 1, status.HTTP_200_OK), + ({"study": StudyType.DATAING}, 2, status.HTTP_200_OK), + ({"year": "2050", "study": StudyType.DATAING}, 1, status.HTTP_200_OK), + ( + {"has_allergy": True, "year": "2051", "study": StudyType.DATAING}, + 1, + status.HTTP_200_OK, + ), + ( + {"has_allergy": True, "year": "2050", "study": StudyType.DATAING}, + 1, + status.HTTP_200_OK, + ), ], ) def test_filter_participants( - new_admin_user, member, event, filter_parameter, filter_value, participant_count, status_code + new_admin_user, member, event, filter_params, participant_count, status_code ): """ - An admin should be able to filter the participants of an event + An admin should be able to filter the participants of an event using multiple parameters """ - add_user_to_group_with_name( - member, StudyType.DATAING, MembershipType.MEMBER) - add_user_to_group_with_name( - member, StudyYear.FIRST, MembershipType.MEMBER) member.allergy = "Pizza" member.save() - print(member.memberships) + + new_admin_user.allergy = "Fisk" + new_admin_user.save() + + add_user_to_group_with_name(member, StudyType.DATAING, GroupType.STUDY) + add_user_to_group_with_name(member, "2050", GroupType.STUDYYEAR) + + add_user_to_group_with_name(new_admin_user, "2051", GroupType.STUDYYEAR) + add_user_to_group_with_name(new_admin_user, StudyType.DATAING, GroupType.STUDY) RegistrationFactory(user=member, event=event) RegistrationFactory(user=new_admin_user, event=event) client = get_api_client(user=new_admin_user) - # TODO: - # --- - # Test filter by each parameter - # Test member cannot filter - # Test search for member (maybe separate test) - # --- - # *** Url needs to have query param for filtering - assert list size - - # registration = RegistrationFactory(user=member, event=event) - url = _get_registration_url(event) + "?" + \ - filter_parameter + "=" + str(filter_value) - print(url) + # Build the query string with multiple filter parameters + url = ( + _get_registration_url(event) + + "?" + + "&".join([f"{key}={value}" for key, value in filter_params.items()]) + ) response = client.get(url) - print(response.data['count']) - assert participant_count == response.data['count'] + assert participant_count == response.data["count"] assert response.status_code == status_code From d7c3bc3a93e3f8f1f8173721ec48d82af76e7eb3 Mon Sep 17 00:00:00 2001 From: yazanzarka1 Date: Wed, 16 Oct 2024 20:30:09 +0200 Subject: [PATCH 09/14] lint fix --- app/content/filters/registration.py | 4 ++-- app/tests/content/test_registration_integration.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/content/filters/registration.py b/app/content/filters/registration.py index 6bab53b2c..3daefbab5 100644 --- a/app/content/filters/registration.py +++ b/app/content/filters/registration.py @@ -1,7 +1,7 @@ -from django_filters.rest_framework import FilterSet from django_filters import rest_framework as filters -from app.common.enums import NativeGroupType as GroupType +from django_filters.rest_framework import FilterSet +from app.common.enums import NativeGroupType as GroupType from app.content.models.registration import Registration diff --git a/app/tests/content/test_registration_integration.py b/app/tests/content/test_registration_integration.py index 3710a337b..baed07eda 100644 --- a/app/tests/content/test_registration_integration.py +++ b/app/tests/content/test_registration_integration.py @@ -6,9 +6,9 @@ from app.common.enums import AdminGroup from app.common.enums import NativeGroupType as GroupType -from app.common.enums import NativeUserStudy as StudyType -from app.common.enums import NativeUserClass as StudyYear from app.common.enums import NativeMembershipType as MembershipType +from app.common.enums import NativeUserClass as StudyYear +from app.common.enums import NativeUserStudy as StudyType from app.content.factories import EventFactory, RegistrationFactory, UserFactory from app.content.factories.priority_pool_factory import PriorityPoolFactory from app.forms.enums import NativeEventFormType as EventFormType From af6a1d23c9c0c7bd0e5950f4c0d42abb243542fa Mon Sep 17 00:00:00 2001 From: yazanzarka1 Date: Wed, 16 Oct 2024 20:33:06 +0200 Subject: [PATCH 10/14] removed unused import --- app/tests/content/test_registration_integration.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/tests/content/test_registration_integration.py b/app/tests/content/test_registration_integration.py index baed07eda..a92e27bb1 100644 --- a/app/tests/content/test_registration_integration.py +++ b/app/tests/content/test_registration_integration.py @@ -6,9 +6,8 @@ from app.common.enums import AdminGroup from app.common.enums import NativeGroupType as GroupType -from app.common.enums import NativeMembershipType as MembershipType -from app.common.enums import NativeUserClass as StudyYear from app.common.enums import NativeUserStudy as StudyType +from app.common.enums import NativeMembershipType as MembershipType from app.content.factories import EventFactory, RegistrationFactory, UserFactory from app.content.factories.priority_pool_factory import PriorityPoolFactory from app.forms.enums import NativeEventFormType as EventFormType From ca23b2de019e24abba30aab85505345eac8b526a Mon Sep 17 00:00:00 2001 From: Harry Linrui XU Date: Mon, 21 Oct 2024 17:04:10 +0200 Subject: [PATCH 11/14] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ae1eaec33..c4c74f5f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ --- ## Neste versjon +- ✨ **Filtrering**. Admin kan nå filtere deltakere av et arrangement på studie, studieår, om deltakere har allergier, (om deltakere godtar å bli tatt bilde av, om deltakere har ankommet), i tillegg til søk på fornavn og etternavn. ## Versjon 2024.10.11 - ✨ **Tilbakemelding-funksjon**. Man kan nå opprette tilbakemeldinger for bugs og idé. From 7f5aa58c0ca75d7b6a1e8fe064fa228d87258cdc Mon Sep 17 00:00:00 2001 From: yazanzarka1 Date: Fri, 25 Oct 2024 17:35:22 +0200 Subject: [PATCH 12/14] merge with dev and more filters --- app/content/filters/registration.py | 15 ++++- app/content/serializers/event.py | 14 +++++ .../content/test_registration_integration.py | 57 +++++++++++++++++++ 3 files changed, 85 insertions(+), 1 deletion(-) diff --git a/app/content/filters/registration.py b/app/content/filters/registration.py index 3daefbab5..18be2bb86 100644 --- a/app/content/filters/registration.py +++ b/app/content/filters/registration.py @@ -3,6 +3,7 @@ from app.common.enums import NativeGroupType as GroupType from app.content.models.registration import Registration +from app.payment.enums import OrderStatus class RegistrationFilter(FilterSet): @@ -18,15 +19,27 @@ class RegistrationFilter(FilterSet): field_name="user__allergy", method="filter_has_allergy" ) + has_paid = filters.BooleanFilter(field_name="event__orders__status", method="filter_has_paid") + class Meta: model = Registration - fields = ["has_attended", "is_on_wait", "study", "year", "has_allergy"] + fields = ["has_attended", "is_on_wait", "study", "year", "has_allergy", "allow_photo", "has_paid"] def filter_study(self, queryset, name, value): return queryset.filter( user__memberships__group__name__icontains=value, user__memberships__group__type=GroupType.STUDY, ) + + def filter_has_paid(self, queryset, name, value): + if value: + return queryset.filter( + event__orders__status=OrderStatus.SALE + ) + return queryset.exclude( + event__orders__status=OrderStatus.SALE + ) + def filter_year(self, queryset, name, value): return queryset.filter( diff --git a/app/content/serializers/event.py b/app/content/serializers/event.py index 7467c53dd..744100207 100644 --- a/app/content/serializers/event.py +++ b/app/content/serializers/event.py @@ -4,6 +4,7 @@ from sentry_sdk import capture_exception from app.common.enums import NativeGroupType as GroupType +from app.payment.enums import OrderStatus from app.common.serializers import BaseModelSerializer from app.content.models import Event, PriorityPool from app.content.serializers.category import SimpleCategorySerializer @@ -264,6 +265,8 @@ class EventStatisticsSerializer(BaseModelSerializer): studyyears = serializers.SerializerMethodField() studies = serializers.SerializerMethodField() has_allergy_count = serializers.SerializerMethodField() + has_paid_count = serializers.SerializerMethodField() + allow_photo_count = serializers.SerializerMethodField() class Meta: model = Event @@ -274,6 +277,8 @@ class Meta: "studyyears", "studies", "has_allergy_count", + "has_paid_count", + "allow_photo_count" ) def get_has_attended_count(self, obj, *args, **kwargs): @@ -313,3 +318,12 @@ def get_studies(self, obj, *args, **kwargs): Group.objects.filter(type=GroupType.STUDY), ), ) + + def get_allow_photo_count(self, obj, *args, **kwargs): + return obj.registrations.filter(allow_photo=False).count() + + def get_has_paid_count(self, obj, *args, **kwargs): + if obj.is_paid_event: + orders = obj.orders.filter(status=OrderStatus.SALE, event=obj).count() + return orders + return 0 diff --git a/app/tests/content/test_registration_integration.py b/app/tests/content/test_registration_integration.py index a92e27bb1..801acd398 100644 --- a/app/tests/content/test_registration_integration.py +++ b/app/tests/content/test_registration_integration.py @@ -13,6 +13,7 @@ from app.forms.enums import NativeEventFormType as EventFormType from app.forms.tests.form_factories import EventFormFactory, SubmissionFactory from app.group.factories import GroupFactory +from app.payment.factories import OrderFactory from app.payment.enums import OrderStatus from app.util.test_utils import add_user_to_group_with_name, get_api_client from app.util.utils import now @@ -1128,3 +1129,59 @@ def test_filter_participants( assert participant_count == response.data["count"] assert response.status_code == status_code + +@pytest.mark.django_db +@pytest.mark.parametrize( + ("filter_params", "participant_count", "status_code"), + [ + # ({"study": StudyType.DATAING}, 2, status.HTTP_200_OK), + ({"has_paid": True}, 2, status.HTTP_200_OK), + # ({"has_paid": False}, 1, status.HTTP_200_OK), + ], +) +def test_filter_participants_paid_event( + new_admin_user, member, event, paid_event, filter_params, participant_count, status_code +): + """ + An admin should be able to filter the participants of an event using multiple parameters + """ + + paid_event.event = event + + paid_event.save() + + member.allergy = "Pizza" + member.save() + + new_admin_user.allergy = "Fisk" + new_admin_user.save() + + new_user = UserFactory() + + add_user_to_group_with_name(member, StudyType.DATAING, GroupType.STUDY) + add_user_to_group_with_name(member, "2050", GroupType.STUDYYEAR) + + add_user_to_group_with_name(new_admin_user, "2051", GroupType.STUDYYEAR) + add_user_to_group_with_name(new_admin_user, StudyType.DATAING, GroupType.STUDY) + + RegistrationFactory(user=member, event=event) + RegistrationFactory(user=new_admin_user, event=event) + RegistrationFactory(user=new_user, event=event) + + OrderFactory(event=event, user=member, status=OrderStatus.SALE) + OrderFactory(event=event, user=new_admin_user, status=OrderStatus.SALE) + OrderFactory(event=event, user=new_user, status=OrderStatus.SALE) + + client = get_api_client(user=new_admin_user) + + # Build the query string with multiple filter parameters + url = ( + _get_registration_url(paid_event) + + "?" + + "&".join([f"{key}={value}" for key, value in filter_params.items()]) + ) + response = client.get(url) + assert participant_count == response.data["count"] + assert response.status_code == status_code + + From c2af1a260c652b1aa17315bf5327e5a2493ea345 Mon Sep 17 00:00:00 2001 From: yazanzarka1 Date: Sun, 27 Oct 2024 11:25:52 +0100 Subject: [PATCH 13/14] Fixed has_paid filter and added filter combination test --- app/content/filters/registration.py | 17 ++++++++----- .../content/test_registration_integration.py | 25 +++++++++++++++---- 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/app/content/filters/registration.py b/app/content/filters/registration.py index 18be2bb86..da5b11e30 100644 --- a/app/content/filters/registration.py +++ b/app/content/filters/registration.py @@ -4,6 +4,8 @@ from app.common.enums import NativeGroupType as GroupType from app.content.models.registration import Registration from app.payment.enums import OrderStatus +from app.payment.models import Order +from django.db.models import Exists, OuterRef class RegistrationFilter(FilterSet): @@ -32,13 +34,16 @@ def filter_study(self, queryset, name, value): ) def filter_has_paid(self, queryset, name, value): - if value: - return queryset.filter( - event__orders__status=OrderStatus.SALE - ) - return queryset.exclude( - event__orders__status=OrderStatus.SALE + sale_order_exists = Order.objects.filter( + event=OuterRef('event_id'), + user=OuterRef('user_id'), + status=OrderStatus.SALE ) + + if value: + return queryset.filter(Exists(sale_order_exists)) + else: + return queryset.exclude(Exists(sale_order_exists)) def filter_year(self, queryset, name, value): diff --git a/app/tests/content/test_registration_integration.py b/app/tests/content/test_registration_integration.py index 801acd398..7fdb264bd 100644 --- a/app/tests/content/test_registration_integration.py +++ b/app/tests/content/test_registration_integration.py @@ -1134,9 +1134,13 @@ def test_filter_participants( @pytest.mark.parametrize( ("filter_params", "participant_count", "status_code"), [ - # ({"study": StudyType.DATAING}, 2, status.HTTP_200_OK), - ({"has_paid": True}, 2, status.HTTP_200_OK), - # ({"has_paid": False}, 1, status.HTTP_200_OK), + ({"study": StudyType.DATAING, "has_paid": True}, 1, status.HTTP_200_OK), + ({"study": StudyType.DIGFOR, "has_paid": True}, 2, status.HTTP_200_OK), + ({"study": StudyType.DIGFOR, "has_paid": False}, 1, status.HTTP_200_OK), + ({"has_paid": True, "year": "2050"}, 1, status.HTTP_200_OK), + ({"has_paid": True, "year": "2051"}, 1, status.HTTP_200_OK), + ({"has_paid": True}, 4, status.HTTP_200_OK), + ({"has_paid": False}, 2, status.HTTP_200_OK), ], ) def test_filter_participants_paid_event( @@ -1157,20 +1161,31 @@ def test_filter_participants_paid_event( new_admin_user.save() new_user = UserFactory() + new_user2 = UserFactory() + new_user3 = UserFactory() + new_user4 = UserFactory() add_user_to_group_with_name(member, StudyType.DATAING, GroupType.STUDY) add_user_to_group_with_name(member, "2050", GroupType.STUDYYEAR) add_user_to_group_with_name(new_admin_user, "2051", GroupType.STUDYYEAR) - add_user_to_group_with_name(new_admin_user, StudyType.DATAING, GroupType.STUDY) + add_user_to_group_with_name(new_admin_user, StudyType.DIGFOR, GroupType.STUDY) + add_user_to_group_with_name(new_user2, StudyType.DIGFOR, GroupType.STUDY) + add_user_to_group_with_name(new_user3, StudyType.DIGFOR, GroupType.STUDY) RegistrationFactory(user=member, event=event) RegistrationFactory(user=new_admin_user, event=event) RegistrationFactory(user=new_user, event=event) + RegistrationFactory(user=new_user2, event=event) + RegistrationFactory(user=new_user3, event=event) + RegistrationFactory(user=new_user4, event=event) OrderFactory(event=event, user=member, status=OrderStatus.SALE) OrderFactory(event=event, user=new_admin_user, status=OrderStatus.SALE) - OrderFactory(event=event, user=new_user, status=OrderStatus.SALE) + OrderFactory(event=event, user=new_user4, status=OrderStatus.SALE) + OrderFactory(event=event, user=new_user2, status=OrderStatus.SALE) + OrderFactory(event=event, user=new_user, status=OrderStatus.CANCEL) + OrderFactory(event=event, user=new_user3, status=OrderStatus.CANCEL) client = get_api_client(user=new_admin_user) From f4d5b6094f1a2ef8f69d29f83e19f40e23d562cc Mon Sep 17 00:00:00 2001 From: yazanzarka1 Date: Sun, 27 Oct 2024 11:38:22 +0100 Subject: [PATCH 14/14] ran linting script --- app/content/filters/registration.py | 27 ++++++++++++------- app/content/serializers/event.py | 8 +++--- .../content/test_registration_integration.py | 17 +++++++----- 3 files changed, 33 insertions(+), 19 deletions(-) diff --git a/app/content/filters/registration.py b/app/content/filters/registration.py index da5b11e30..92b7eee6a 100644 --- a/app/content/filters/registration.py +++ b/app/content/filters/registration.py @@ -1,3 +1,4 @@ +from django.db.models import Exists, OuterRef from django_filters import rest_framework as filters from django_filters.rest_framework import FilterSet @@ -5,7 +6,6 @@ from app.content.models.registration import Registration from app.payment.enums import OrderStatus from app.payment.models import Order -from django.db.models import Exists, OuterRef class RegistrationFilter(FilterSet): @@ -21,31 +21,40 @@ class RegistrationFilter(FilterSet): field_name="user__allergy", method="filter_has_allergy" ) - has_paid = filters.BooleanFilter(field_name="event__orders__status", method="filter_has_paid") + has_paid = filters.BooleanFilter( + field_name="event__orders__status", method="filter_has_paid" + ) class Meta: model = Registration - fields = ["has_attended", "is_on_wait", "study", "year", "has_allergy", "allow_photo", "has_paid"] + fields = [ + "has_attended", + "is_on_wait", + "study", + "year", + "has_allergy", + "allow_photo", + "has_paid", + ] def filter_study(self, queryset, name, value): return queryset.filter( user__memberships__group__name__icontains=value, user__memberships__group__type=GroupType.STUDY, ) - + def filter_has_paid(self, queryset, name, value): sale_order_exists = Order.objects.filter( - event=OuterRef('event_id'), - user=OuterRef('user_id'), - status=OrderStatus.SALE + event=OuterRef("event_id"), + user=OuterRef("user_id"), + status=OrderStatus.SALE, ) - + if value: return queryset.filter(Exists(sale_order_exists)) else: return queryset.exclude(Exists(sale_order_exists)) - def filter_year(self, queryset, name, value): return queryset.filter( user__memberships__group__name__icontains=value, diff --git a/app/content/serializers/event.py b/app/content/serializers/event.py index 744100207..b9e9fd4ce 100644 --- a/app/content/serializers/event.py +++ b/app/content/serializers/event.py @@ -4,7 +4,6 @@ from sentry_sdk import capture_exception from app.common.enums import NativeGroupType as GroupType -from app.payment.enums import OrderStatus from app.common.serializers import BaseModelSerializer from app.content.models import Event, PriorityPool from app.content.serializers.category import SimpleCategorySerializer @@ -16,6 +15,7 @@ from app.emoji.serializers.reaction import ReactionSerializer from app.group.models.group import Group from app.group.serializers.group import SimpleGroupSerializer +from app.payment.enums import OrderStatus from app.payment.models.paid_event import PaidEvent from app.payment.serializers.paid_event import PaidEventCreateSerializer @@ -278,7 +278,7 @@ class Meta: "studies", "has_allergy_count", "has_paid_count", - "allow_photo_count" + "allow_photo_count", ) def get_has_attended_count(self, obj, *args, **kwargs): @@ -318,10 +318,10 @@ def get_studies(self, obj, *args, **kwargs): Group.objects.filter(type=GroupType.STUDY), ), ) - + def get_allow_photo_count(self, obj, *args, **kwargs): return obj.registrations.filter(allow_photo=False).count() - + def get_has_paid_count(self, obj, *args, **kwargs): if obj.is_paid_event: orders = obj.orders.filter(status=OrderStatus.SALE, event=obj).count() diff --git a/app/tests/content/test_registration_integration.py b/app/tests/content/test_registration_integration.py index 7fdb264bd..86d111158 100644 --- a/app/tests/content/test_registration_integration.py +++ b/app/tests/content/test_registration_integration.py @@ -6,15 +6,15 @@ from app.common.enums import AdminGroup from app.common.enums import NativeGroupType as GroupType -from app.common.enums import NativeUserStudy as StudyType from app.common.enums import NativeMembershipType as MembershipType +from app.common.enums import NativeUserStudy as StudyType from app.content.factories import EventFactory, RegistrationFactory, UserFactory from app.content.factories.priority_pool_factory import PriorityPoolFactory from app.forms.enums import NativeEventFormType as EventFormType from app.forms.tests.form_factories import EventFormFactory, SubmissionFactory from app.group.factories import GroupFactory -from app.payment.factories import OrderFactory from app.payment.enums import OrderStatus +from app.payment.factories import OrderFactory from app.util.test_utils import add_user_to_group_with_name, get_api_client from app.util.utils import now @@ -1130,6 +1130,7 @@ def test_filter_participants( assert participant_count == response.data["count"] assert response.status_code == status_code + @pytest.mark.django_db @pytest.mark.parametrize( ("filter_params", "participant_count", "status_code"), @@ -1144,7 +1145,13 @@ def test_filter_participants( ], ) def test_filter_participants_paid_event( - new_admin_user, member, event, paid_event, filter_params, participant_count, status_code + new_admin_user, + member, + event, + paid_event, + filter_params, + participant_count, + status_code, ): """ An admin should be able to filter the participants of an event using multiple parameters @@ -1186,7 +1193,7 @@ def test_filter_participants_paid_event( OrderFactory(event=event, user=new_user2, status=OrderStatus.SALE) OrderFactory(event=event, user=new_user, status=OrderStatus.CANCEL) OrderFactory(event=event, user=new_user3, status=OrderStatus.CANCEL) - + client = get_api_client(user=new_admin_user) # Build the query string with multiple filter parameters @@ -1198,5 +1205,3 @@ def test_filter_participants_paid_event( response = client.get(url) assert participant_count == response.data["count"] assert response.status_code == status_code - -