Skip to content

Commit

Permalink
Merge pull request #843 from betagouv/826-unassigned-filter
Browse files Browse the repository at this point in the history
Ajout la possibilité de filtrer sur les décla "non assignée"
  • Loading branch information
alemangui authored Aug 13, 2024
2 parents 6e8c49f + e081ca0 commit 0c2f0d3
Show file tree
Hide file tree
Showing 3 changed files with 144 additions and 6 deletions.
96 changes: 96 additions & 0 deletions api/tests/test_declaration.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

from data.choices import AuthorizationModes, CountryChoices, FrAuthorizationReasons
from data.factories import (
AwaitingInstructionDeclarationFactory,
AwaitingVisaDeclarationFactory,
CompanyFactory,
ConditionFactory,
DeclarantRoleFactory,
Expand Down Expand Up @@ -801,6 +803,53 @@ def test_filter_instructor_all_declarations(self):
self.assertEqual(len(results), 1)
self.assertEqual(results[0]["id"], emma_declaration.id)

@authenticate
def test_filter_instructor_not_assigned(self):
"""
Les déclarations peuvent être filtrées par celles qui n'ont pas encore d'instructrice
"""
InstructionRoleFactory(user=authenticate.user)

emma = InstructionRoleFactory()
edouard = InstructionRoleFactory()
stephane = InstructionRoleFactory()

OngoingInstructionDeclarationFactory(instructor=emma)
OngoingInstructionDeclarationFactory(instructor=edouard)
OngoingInstructionDeclarationFactory(instructor=stephane)
declaration = AwaitingInstructionDeclarationFactory()

unassigned_filter_url = f"{reverse('api:list_all_declarations')}?instructor=None"
response = self.client.get(unassigned_filter_url, format="json")
results = response.json()["results"]
self.assertEqual(len(results), 1)
self.assertEqual(results[0]["id"], declaration.id)

@authenticate
def test_filter_instructor_not_assigned_and_assigned(self):
"""
Les déclarations peuvent être filtrées par celles qui n'ont pas encore d'instructrice
et par une instructrice en particulier en même temps
"""
InstructionRoleFactory(user=authenticate.user)

emma = InstructionRoleFactory()
edouard = InstructionRoleFactory()
stephane = InstructionRoleFactory()

emma_declaration = OngoingInstructionDeclarationFactory(instructor=emma)
OngoingInstructionDeclarationFactory(instructor=edouard)
OngoingInstructionDeclarationFactory(instructor=stephane)
unassigned_declaration = AwaitingInstructionDeclarationFactory()

filter_url = f"{reverse('api:list_all_declarations')}?instructor=None,{emma.id}"
response = self.client.get(filter_url, format="json")
results = response.json()["results"]
self.assertEqual(len(results), 2)

self.assertIsNotNone(next(filter(lambda x: x["id"] == emma_declaration.id, results), None))
self.assertIsNotNone(next(filter(lambda x: x["id"] == unassigned_declaration.id, results), None))

@authenticate
def test_filter_visor_all_declarations(self):
"""
Expand All @@ -822,6 +871,53 @@ def test_filter_visor_all_declarations(self):
self.assertEqual(len(results), 1)
self.assertEqual(results[0]["id"], emma_declaration.id)

@authenticate
def test_filter_visor_not_assigned(self):
"""
Les déclarations peuvent être filtrées par celles qui n'ont pas encore de viseuse
"""
VisaRoleFactory(user=authenticate.user)

emma = VisaRoleFactory()
edouard = VisaRoleFactory()
stephane = VisaRoleFactory()

OngoingInstructionDeclarationFactory(visor=emma)
OngoingInstructionDeclarationFactory(visor=edouard)
OngoingInstructionDeclarationFactory(visor=stephane)
declaration = AwaitingVisaDeclarationFactory()

unassigned_filter_url = f"{reverse('api:list_all_declarations')}?visor=None"
response = self.client.get(unassigned_filter_url, format="json")
results = response.json()["results"]
self.assertEqual(len(results), 1)
self.assertEqual(results[0]["id"], declaration.id)

@authenticate
def test_filter_visor_not_assigned_and_assigned(self):
"""
Les déclarations peuvent être filtrées par celles qui n'ont pas encore d'instructrice
et par une instructrice en particulier en même temps
"""
VisaRoleFactory(user=authenticate.user)

emma = VisaRoleFactory()
edouard = VisaRoleFactory()
stephane = VisaRoleFactory()

emma_declaration = OngoingInstructionDeclarationFactory(visor=emma)
OngoingInstructionDeclarationFactory(visor=edouard)
OngoingInstructionDeclarationFactory(visor=stephane)
unassigned_declaration = AwaitingVisaDeclarationFactory()

filter_url = f"{reverse('api:list_all_declarations')}?visor=None,{emma.id}"
response = self.client.get(filter_url, format="json")
results = response.json()["results"]
self.assertEqual(len(results), 2)

self.assertIsNotNone(next(filter(lambda x: x["id"] == emma_declaration.id, results), None))
self.assertIsNotNone(next(filter(lambda x: x["id"] == unassigned_declaration.id, results), None))

@authenticate
def test_filter_company_all_declarations(self):
"""
Expand Down
47 changes: 45 additions & 2 deletions api/views/declaration/declaration.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from rest_framework.response import Response
from unidecode import unidecode

from api.exceptions import ProjectAPIException
from api.permissions import (
CanAccessIndividualDeclaration,
CanAccessUserDeclatarions,
Expand Down Expand Up @@ -42,8 +43,8 @@ class DeclarationPagination(LimitOffsetPagination):

class DeclarationFilterSet(django_filters.FilterSet):
author = BaseNumberInFilter(field_name="author__id")
instructor = BaseNumberInFilter(field_name="instructor__id")
visor = BaseNumberInFilter(field_name="visor__id")
instructor = django_filters.CharFilter(method="nullable_instructor")
visor = django_filters.CharFilter(method="nullable_visor")
company = BaseNumberInFilter(field_name="company__id")
company_name_start = django_filters.CharFilter(method="company_name_start__gte")
company_name_end = django_filters.CharFilter(method="company_name_end__lte")
Expand All @@ -61,6 +62,48 @@ class Meta:
"company_name_end",
]

def nullable_instructor(self, queryset, value, *args, **kwargs):
empty_term = "None"
filter_values = args[0].split(",")

if not filter_values:
return queryset

unassigned_declarations = (
queryset.filter(instructor__isnull=True) if empty_term in filter_values else Declaration.objects.none()
)

try:
declaration_ids = [int(x.strip()) for x in filter_values if x != empty_term]
except Exception as _:
raise ProjectAPIException(global_error="Vérifier votre filtre instructeur")
filtered_declarations = (
queryset.filter(instructor__id__in=declaration_ids) if declaration_ids else Declaration.objects.none()
)

return unassigned_declarations.union(filtered_declarations)

def nullable_visor(self, queryset, value, *args, **kwargs):
empty_term = "None"
filter_values = args[0].split(",")

if not filter_values:
return queryset

unassigned_declarations = (
queryset.filter(visor__isnull=True) if empty_term in filter_values else Declaration.objects.none()
)

try:
declaration_ids = [int(x.strip()) for x in filter_values if x != empty_term]
except Exception as _:
raise ProjectAPIException(global_error="Vérifier votre filtre viseur")
filtered_declarations = (
queryset.filter(visor__id__in=declaration_ids) if declaration_ids else Declaration.objects.none()
)

return unassigned_declarations.union(filtered_declarations)

def company_name_start__gte(self, queryset, value, *args, **kwargs):
return self._annotate_queryset(queryset).filter(name_unaccented_lower__gte=args[0].lower())

Expand Down
7 changes: 3 additions & 4 deletions frontend/src/views/InstructionDeclarationsPage/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,8 @@ const limit = 10
const pages = computed(() => getPagesForPagination(data.value?.count, limit, route.path))
const allInstructors = computed(() => data.value?.instructors)
const instructorSelectOptions = computed(() => {
const availableInstructors = allInstructors.value?.map((x) => ({ value: x.id, text: x.name })) || []
const availableInstructors = allInstructors.value?.map((x) => ({ value: "" + x.id, text: x.name })) || []
availableInstructors.unshift({ value: "None", text: "Déclarations non assignées" })
availableInstructors.unshift({ value: "", text: "Toutes les déclarations" })
return availableInstructors
})
Expand All @@ -93,9 +94,7 @@ const page = computed(() => parseInt(route.query.page))
const filteredStatus = computed(() => route.query.status)
const companyNameStart = computed(() => route.query.entrepriseDe)
const companyNameEnd = computed(() => route.query.entrepriseA)
const assignedInstructor = computed(() =>
route.query.personneAssignée === "" ? "" : parseInt(route.query.personneAssignée)
)
const assignedInstructor = computed(() => route.query.personneAssignée)
const updateQuery = (newQuery) => router.push({ query: { ...route.query, ...newQuery } })
Expand Down

0 comments on commit 0c2f0d3

Please sign in to comment.