Skip to content

Commit

Permalink
Feat(registration)/filter participants (#895)
Browse files Browse the repository at this point in the history
* filtering by year and study

* linting fix

* added allergy filter

* added filter by allergies and participants with allergy count to event.

* Lint fix

* Add new fixture for admin user

* Start testing filtering + finished allergy filter test

* Added integration test for participants filtering

* lint fix

* removed unused import

* Update changelog

* merge with dev and more filters

* Fixed has_paid filter and added filter combination test

* ran linting script

---------

Co-authored-by: Harry Linrui XU <[email protected]>
Co-authored-by: Mads Nylund <[email protected]>
  • Loading branch information
3 people authored Oct 28, 2024
1 parent 119bbdb commit 0e053d4
Show file tree
Hide file tree
Showing 5 changed files with 228 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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é.
Expand Down
64 changes: 63 additions & 1 deletion app/content/filters/registration.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,71 @@
from django.db.models import Exists, OuterRef
from django_filters import rest_framework as filters
from django_filters.rest_framework import FilterSet

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


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"
)

has_allergy = filters.BooleanFilter(
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"]
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,
)

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,
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=""
)
23 changes: 23 additions & 0 deletions app/content/serializers/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,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

Expand Down Expand Up @@ -263,6 +264,9 @@ class EventStatisticsSerializer(BaseModelSerializer):
has_attended_count = serializers.SerializerMethodField()
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
Expand All @@ -272,11 +276,21 @@ class Meta:
"waiting_list_count",
"studyyears",
"studies",
"has_allergy_count",
"has_paid_count",
"allow_photo_count",
)

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(
lambda studyyear: studyyear["amount"] > 0,
Expand Down Expand Up @@ -304,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
7 changes: 7 additions & 0 deletions app/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,13 @@ 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()
Expand Down
134 changes: 134 additions & 0 deletions app/tests/content/test_registration_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@
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 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.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

Expand Down Expand Up @@ -1071,3 +1073,135 @@ def test_delete_registration_with_paid_order_as_self(
response = client.delete(url)

assert response.status_code == status_code


@pytest.mark.django_db
@pytest.mark.parametrize(
("filter_params", "participant_count", "status_code"),
[
({"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_params, participant_count, status_code
):
"""
An admin should be able to filter the participants of an event using multiple parameters
"""

member.allergy = "Pizza"
member.save()

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)

# 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)

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, "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(
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()
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.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_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)

# 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

0 comments on commit 0e053d4

Please sign in to comment.