Skip to content

Commit

Permalink
Merge pull request #2559 from carpentries/feature/2398-Add-a-filter-t…
Browse files Browse the repository at this point in the history
…o-the-workshop-request-view-to-filter-for-requests-with-invalid-member-codes

Add a filter to the workshop request view for requests where an active member did not use their code
  • Loading branch information
elichad authored Oct 30, 2023
2 parents c45fc44 + 5096b94 commit 12090b9
Show file tree
Hide file tree
Showing 2 changed files with 294 additions and 3 deletions.
31 changes: 30 additions & 1 deletion amy/extrequests/filters.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from datetime import date
import re

from django.db.models import Q
from django.db.models import Case, F, Q, QuerySet, When
from django.forms import widgets
from django.http import QueryDict
import django_filters
Expand Down Expand Up @@ -171,6 +172,11 @@ class WorkshopRequestFilter(AMYFilterSet, StateFilterSet):
queryset=Curriculum.objects.all(),
widget=widgets.CheckboxSelectMultiple(),
)
unused_member_code = django_filters.BooleanFilter(
label="Institution has an active member code but did not provide it",
method="filter_unused_member_code",
widget=widgets.CheckboxInput(),
)

order_by = django_filters.OrderingFilter(
fields=("created_at",),
Expand All @@ -185,6 +191,29 @@ class Meta:
"country",
]

def filter_unused_member_code(
self, queryset: QuerySet, name: str, apply_filter: bool
) -> QuerySet:
if apply_filter:
# find requests where no member code was provided
requests_without_code = queryset.filter(member_code="")

# find requests where institution has an active membership
# ideally compare to workshop dates, but fall back on today
return requests_without_code.annotate(
date_to_check=Case(
When(
preferred_dates__isnull=False,
then=F("preferred_dates"),
),
default=date.today(),
)
).filter(
institution__memberships__agreement_end__gte=F("date_to_check"),
institution__memberships__agreement_start__lte=F("date_to_check"),
)
return queryset


# ------------------------------------------------------------
# WorkshopInquiryRequest related filter and filter methods
Expand Down
266 changes: 264 additions & 2 deletions amy/extrequests/tests/test_filters.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,20 @@
from datetime import datetime, timedelta

from extrequests.filters import TrainingRequestFilter
from workshops.models import Event, Membership, Role, Tag, Task, TrainingRequest
from dashboard.models import Continent
from extrequests.filters import TrainingRequestFilter, WorkshopRequestFilter
from workshops.models import (
Curriculum,
Event,
Language,
Member,
MemberRole,
Membership,
Role,
Tag,
Task,
TrainingRequest,
WorkshopRequest,
)
from workshops.tests.base import TestBase


Expand Down Expand Up @@ -338,3 +351,252 @@ def test_filter_order_by(self):
self.assertQuerysetEqual(
results[field], expected_results[field], ordered=True
)


class TestWorkshopRequestFilter(TestBase):
"""
A test should exist for each filter listed in test_fields().
"""

def setUp(self) -> None:
super().setUp() # create some persons
self._setUpTags()
self._setUpRoles()

self.model = WorkshopRequest

self.membership = Membership.objects.create(
name="Alpha Organization",
registration_code="valid123",
agreement_start=datetime.today(),
agreement_end=datetime.today() + timedelta(weeks=52),
)
Member.objects.create(
membership=self.membership,
organization=self.org_alpha,
role=MemberRole.objects.first(),
)

kwargs = dict(
state="p",
personal="Harry",
family="Potter",
email="harry@hogwarts.edu",
institution_other_name="Hogwarts",
location="Scotland",
country="GB",
preferred_dates=None,
other_preferred_dates="soon",
language=Language.objects.get(name="English"),
audience_description="Students of Hogwarts",
administrative_fee="nonprofit",
scholarship_circumstances="",
travel_expences_management="booked",
travel_expences_management_other="",
institution_restrictions="no_restrictions",
institution_restrictions_other="",
carpentries_info_source_other="",
user_notes="",
)

# add some workshop requests
# together these cover all test cases
# 1. Request for a workshop in GB,
# pending
self.req_pending_country_gb = WorkshopRequest.objects.create(**kwargs)
# 2. Request for a workshop in US,
# from a member institution but without the member code,
# assigned to an admin,
# discarded
kwargs["institution_other_name"] = ""
kwargs["institution"] = self.org_alpha
kwargs["assigned_to"] = self.spiderman
kwargs["country"] = "US"
kwargs["state"] = "d"
self.req_assigned_discarded_unused_code = WorkshopRequest.objects.create(
**kwargs
)
# 3. Request for a workshop in US,
# from a member institution but without the member code,
# with a preferred date selected which falls during the membership,
# pending
kwargs.pop("assigned_to")
kwargs["preferred_dates"] = datetime.today() + timedelta(weeks=5)
kwargs["state"] = "p"
self.req_pending_dates_unused_code = WorkshopRequest.objects.create(**kwargs)
# 4. Request for a workshop in the US,
# from a member institution with their code,
# with a preferred curriculum selected,
# accepted
kwargs["member_code"] = "valid123"
kwargs["state"] = "a"
self.req_accepted_valid_code_curriculum_chosen = WorkshopRequest.objects.create(
**kwargs
)
self.req_accepted_valid_code_curriculum_chosen.requested_workshop_types.set(
Curriculum.objects.filter(slug="dc-ecology-r")
)

# get filterset
self.filterset = WorkshopRequestFilter({})

# get queryset
self.qs = WorkshopRequest.objects.all()

def test_fields(self):
# Arrange & Act stages happen in setUp()
# Assert
self.assertEqual(
set(self.filterset.filters.keys()),
{
"state",
"assigned_to",
"country",
"continent",
"requested_workshop_types",
"unused_member_code",
"order_by",
},
)

def test_filter_state__any(self):
# Arrange
filter_name = "state"
value = ""

# Act
result = self.filterset.filters[filter_name].filter(self.qs, value)

# Assert
self.assertQuerysetEqual(result, self.qs, ordered=False)

def test_filter_state__pending(self):
# Arrange
filter_name = "state"
value = "p"

# Act
result = self.filterset.filters[filter_name].filter(self.qs, value)

# Assert
self.assertQuerysetEqual(
result, [self.req_pending_country_gb, self.req_pending_dates_unused_code]
)

def test_filter_state__accepted(self):
# Arrange
filter_name = "state"
value = "a"

# Act
result = self.filterset.filters[filter_name].filter(self.qs, value)

# Assert
self.assertQuerysetEqual(
result, [self.req_accepted_valid_code_curriculum_chosen]
)

def test_filter_state_discarded(self):
# Arrange
filter_name = "state"
value = "d"

# Act
result = self.filterset.filters[filter_name].filter(self.qs, value)

# Assert
self.assertQuerysetEqual(result, [self.req_assigned_discarded_unused_code])

def test_filter_assigned_to(self):
# Arrange
filter_name = "assigned_to"
value = self.spiderman

# Act
result = self.filterset.filters[filter_name].filter(self.qs, value)

# Assert
self.assertQuerysetEqual(result, [self.req_assigned_discarded_unused_code])

def test_filter_country(self):
# Arrange
filter_name = "country"
value = "GB"

# Act
result = self.filterset.filters[filter_name].filter(self.qs, value)

# Assert
self.assertQuerysetEqual(result, [self.req_pending_country_gb])

def test_filter_continent(self):
# Arrange
filter_name = "continent"
value = Continent.objects.get(name="Europe").pk

# Act
result = self.filterset.filters[filter_name].filter(self.qs, value)

# Assert
self.assertQuerysetEqual(result, [self.req_pending_country_gb])

def test_filter_requested_workshop_types(self):
# Arrange
filter_name = "requested_workshop_types"
value = Curriculum.objects.filter(slug="dc-ecology-r")

# Act
result = self.filterset.filters[filter_name].filter(self.qs, value)

# Assert
self.assertQuerysetEqual(
result, [self.req_accepted_valid_code_curriculum_chosen]
)

def test_filter_unused_member_code(self):
# Arrange
filter_name = "unused_member_code"
value = True

# Act
result = self.filterset.filters[filter_name].filter(self.qs, value)

# Assert
self.assertQuerysetEqual(
result,
[
self.req_assigned_discarded_unused_code,
self.req_pending_dates_unused_code,
],
)

def test_filter_order_by(self):
# Arrange
filter_name = "order_by"
fields = self.filterset.filters[filter_name].param_map
results = {}

# default ordering is ascending
expected_results = {
"created_at": [
self.req_pending_country_gb,
self.req_assigned_discarded_unused_code,
self.req_pending_dates_unused_code,
self.req_accepted_valid_code_curriculum_chosen,
],
}

# Act
for field in fields.keys():
results[field] = self.filterset.filters[filter_name].filter(
self.qs, [field]
)

# Assert
# we don't have any unexpected fields
self.assertEqual(fields.keys(), expected_results.keys())
# each field was filtered correctly
for field in fields.keys():
self.assertQuerysetEqual(
results[field], expected_results[field], ordered=True
)

0 comments on commit 12090b9

Please sign in to comment.