Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat(registration)/filter participants #895

Merged
merged 18 commits into from
Oct 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading