Skip to content
This repository has been archived by the owner on Jun 24, 2024. It is now read-only.

Commit

Permalink
Merge pull request #969 from yverdon/feature/domain_filter_agenda
Browse files Browse the repository at this point in the history
add filter by domain and make it work with ids
  • Loading branch information
AlexandreJunod authored Apr 22, 2024
2 parents 254682d + e7983f1 commit 7674a57
Show file tree
Hide file tree
Showing 10 changed files with 194 additions and 46 deletions.
1 change: 1 addition & 0 deletions geocity/apps/accounts/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -781,6 +781,7 @@ class AdministrativeEntityAdmin(IntegratorFilterMixin, admin.ModelAdmin):
{
"fields": (
"name",
"agenda_name",
"tags",
"ofs_id",
"is_single_form_submissions",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Generated by Django 4.2.10 on 2024-04-16 07:47
# Generated by Django 4.2.10 on 2024-04-18 06:19

import django.core.validators
from django.db import migrations
from django.db import migrations, models

import geocity.apps.accounts.fields
import geocity.fields
Expand All @@ -14,6 +14,16 @@ class Migration(migrations.Migration):
]

operations = [
migrations.AddField(
model_name="administrativeentity",
name="agenda_name",
field=models.CharField(
blank=True,
help_text="Nom visible dans le filtre de l'agenda",
max_length=128,
verbose_name="Nom dans l'api agenda",
),
),
migrations.AlterField(
model_name="templatecustomization",
name="background_image",
Expand Down
6 changes: 6 additions & 0 deletions geocity/apps/accounts/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,12 @@ def associated_to_user(self, user):

class AdministrativeEntity(models.Model):
name = models.CharField(_("name"), max_length=128)
agenda_name = models.CharField(
_("Nom dans l'api agenda"),
help_text=_("Nom visible dans le filtre de l'agenda"),
max_length=128,
blank=True,
)
ofs_id = models.PositiveIntegerField(_("Numéro OFS"))
link = models.URLField(_("Lien"), max_length=200, blank=True)
archive_link = models.URLField(_("Archives externes"), max_length=1024, blank=True)
Expand Down
5 changes: 3 additions & 2 deletions geocity/apps/api/pagination.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,9 @@ class AgendaResultsSetPagination(PageNumberPagination):
max_page_size = 100

def get_paginated_response(self, data):
domain = self.request.GET.get("domain")
agenda_filters = get_available_filters_for_agenda_as_json(domain)
domains = self.request.GET.get("domain")
domains = domains.split(",") if domains else None
agenda_filters = get_available_filters_for_agenda_as_json(domains)
return Response(
{
"type": "FeatureCollection",
Expand Down
58 changes: 40 additions & 18 deletions geocity/apps/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -951,14 +951,16 @@ def get_agenda_form_fields(value, detailed, available_filters):
return result


def get_available_filters_for_agenda_as_qs(domain):
def get_available_filters_for_agenda_as_qs(domains):
"""
Returns a list of filters available for a specific entity.
The order is important, agenda-embed has no logic, everything is set here
"""

if not domain:
if not domains or len(domains) > 1:
return None
else:
domain = domains[0]

entity = (
AdministrativeEntity.objects.filter(
Expand All @@ -982,30 +984,50 @@ def get_available_filters_for_agenda_as_qs(domain):
return available_filters


def get_available_filters_for_agenda_as_json(domain):
def get_available_filters_for_agenda_as_json(domains):
"""
Returns the list of filters for api
"""
available_filters = get_available_filters_for_agenda_as_qs(domain)

if not available_filters:
return None

available_filters = get_available_filters_for_agenda_as_qs(domains)
agenda_filters = []
for available_filter in available_filters:
actual_filter = {
"label": available_filter.name,
"slug": available_filter.api_name,

# Category filter available for simple and detailed agenda. Example : Sport, Culture, Économie, etc...
if domains and len(domains) > 1:
domain_filter = {
"label": "Catégorie",
"slug": "domain_filter",
}
actual_filter["options"] = [
entities = AdministrativeEntity.objects.filter(
forms__agenda_visible=True, forms__is_public=True, tags__name__in=domains
)
domain_filter["options"] = [
{
"id": key,
"label": choice.strip(),
"id": entity.id,
"label": entity.agenda_name
if entity.agenda_name
else "Valeur non définie",
}
for key, choice in enumerate(available_filter.choices.strip().splitlines())
for entity in entities
]
agenda_filters.append(actual_filter)
return agenda_filters
agenda_filters.append(domain_filter)

if available_filters:
for available_filter in available_filters:
actual_filter = {
"label": available_filter.name,
"slug": available_filter.api_name,
}
actual_filter["options"] = [
{
"id": key,
"label": choice.strip(),
}
for key, choice in enumerate(
available_filter.choices.strip().splitlines()
)
]
agenda_filters.append(actual_filter)
return agenda_filters if agenda_filters != [] else None


class AgendaSerializer(serializers.Serializer):
Expand Down
49 changes: 27 additions & 22 deletions geocity/apps/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,23 @@ def image_thumbor_display(request, submission_id, image_name):
return thumbor_response


def get_agenda_submissions(entities, submissions):
# To validate a request and show it in agenda, an user need to be pilot of his own entity and validator for other entities.
# Retrieve pilots of entity
pilot_of_entity = User.objects.filter(
groups__permit_department__administrative_entity__in=entities,
groups__permit_department__is_backoffice=True,
).values("id")

# Check agenda submissions is validated by any user on the pilot group of it's own entity
submissions = submissions.filter(
Q(administrative_entity__in=entities)
| Q(validations__validated_by__in=pilot_of_entity)
)

return submissions


class AgendaViewSet(viewsets.ReadOnlyModelViewSet):
"""
This api provides :
Expand Down Expand Up @@ -577,7 +594,7 @@ def get_queryset(self):
This view has a detailed result and a simple result
The detailed result is built with AgendaResultsSetPagination,
this is required to be able to make pagination and return features and filters
The simple result just return informations for une submission
The simple result just return informations for une submission and lists available domains
The order is important, agenda-embed has no logic, everything is set here
"""
submissions = (
Expand All @@ -596,22 +613,16 @@ def get_queryset(self):
# Filter domain (administrative_entity) to permit sites to filter on their own domain (e.g.: sports, culture)
domains = None

if "domain" in query_params:
domains = query_params["domain"].split(",")
entities = AdministrativeEntity.objects.filter(tags__name__in=domains)

# To validate a request and show it in agenda, an user need to be pilot of his own entity and validator for other entities.
# Retrieve pilots of entity
pilot_of_entity = User.objects.filter(
groups__permit_department__administrative_entity__in=entities,
groups__permit_department__is_backoffice=True,
).values("id")
if "domain_filter" in query_params:
domain_filter = query_params.getlist("domain_filter")
entities = AdministrativeEntity.objects.filter(id__in=domain_filter)
submissions = get_agenda_submissions(entities, submissions)

# Check agenda submissions is validated by any user on the pilot group of it's own entity
submissions = submissions.filter(
Q(administrative_entity__in=entities)
| Q(validations__validated_by__in=pilot_of_entity)
)
elif "domain" in query_params:
domains = query_params["domain"]
domains = domains.split(",") if domains else None
entities = AdministrativeEntity.objects.filter(tags__name__in=domains)
submissions = get_agenda_submissions(entities, submissions)

if "starts_at" in query_params:
starts_at = datetime.datetime.strptime(
Expand Down Expand Up @@ -640,12 +651,6 @@ def get_queryset(self):
| Q(selected_forms__field_values__value__val__icontains=query)
)

# There's no filters available if there's multiple domains
if domains and len(domains) > 1:
return submissions
elif domains:
domains = domains[0]

# List every available filter
available_filters = serializers.get_available_filters_for_agenda_as_qs(domains)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Generated by Django 4.2.11 on 2024-04-18 13:00

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
(
"submissions",
"0029_change_default_amend_field_visibility_by_author_to_false",
),
]

operations = [
migrations.AlterField(
model_name="servicefeetype",
name="fix_price_editable",
field=models.BooleanField(
default=False,
help_text="Exemple: montant demandant un calcul spécifique à réaliser en dehors de l'application",
verbose_name="Montant à saisir manuellement",
),
),
]
2 changes: 1 addition & 1 deletion geocity/apps/submissions/payments/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ class ServiceFeeType(models.Model):
help_text=_("Le tarif forfaitaire de cette prestation."),
)
fix_price_editable = models.BooleanField(
verbose_name=_("Montant à saisir manuelllement"),
verbose_name=_("Montant à saisir manuellement"),
help_text=_(
"Exemple: montant demandant un calcul spécifique à réaliser en dehors de l'application"
),
Expand Down
78 changes: 78 additions & 0 deletions geocity/tests/api/test_agenda_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -645,6 +645,44 @@ def test_filters_only_appears_with_domain(self):
# Check if filters is not None
self.assertNotEqual(response_json["filters"], None)

# Check if category filter is present
is_category_present = any(
item.get("slug") == "category" for item in response_json["filters"]
)
self.assertTrue(is_category_present)

# Check if domain_filter filter is not present
is_category_present = any(
item.get("slug") == "domain_filter" for item in response_json["filters"]
)
self.assertFalse(is_category_present)

# ////////////////////////////////////#
# With multiple domains
# ////////////////////////////////////#

# Request to agenda-list (light API) on ?domain=sit,fin
response = self.client.get(reverse("agenda-list"), {"domain": "sit,fin"})
response_json = response.json()

# Check if request is ok
self.assertEqual(response.status_code, 200)

# Check if filters is not None
self.assertNotEqual(response_json["filters"], None)

# Check if category filter is not present
is_category_present = any(
item.get("slug") == "category" for item in response_json["filters"]
)
self.assertFalse(is_category_present)

# Check if domain_filter filter is present
is_category_present = any(
item.get("slug") == "domain_filter" for item in response_json["filters"]
)
self.assertTrue(is_category_present)

def test_elements_are_ordered_by_featured_and_dates(self):
"""
Order of elements logic is in the backend.
Expand Down Expand Up @@ -743,3 +781,43 @@ def test_filters_work_correctly(self):
response_json["features"][0]["properties"]["id"],
self.sit_first_submission.pk,
)

# ////////////////////////////////////#
# Multiple domain
# ////////////////////////////////////#

# Request to agenda-list (light API) on ?domain=sit,fin
response = self.client.get(reverse("agenda-list"), {"domain": "sit,fin"})
response_json = response.json()

# Check if request is ok
self.assertEqual(response.status_code, 200)

# Check if there's 5 features
self.assertEqual(response_json["count"], 5)

# ////////////////////////////////////#
# Filter by domain_filter
# ////////////////////////////////////#

# Request to agenda-list (light API) on ?domain=sit,fin
response = self.client.get(
reverse("agenda-list"),
{
"domain": "sit,fin",
"domain_filter": self.sit_first_submission.administrative_entity.pk,
},
)
response_json = response.json()

# Check if request is ok
self.assertEqual(response.status_code, 200)

# Check if there's 4 features and not 5 (4 sit and 1 fin)
self.assertEqual(response_json["count"], 4)

# Check if this is the first_submission
self.assertEqual(
response_json["features"][0]["properties"]["id"],
self.sit_first_submission.pk,
)
2 changes: 1 addition & 1 deletion services/external_statics/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,5 @@ RUN cd /app/geocity-wc-map \
# Install agenda-embed web component
RUN mkdir /app/agenda-embed \
&& cd /app/agenda-embed \
&& npm install @geocity/agenda-embed \
&& npm install @geocity/agenda-embed@latest \
&& echo "static files for agenda embed imported successfully!"

0 comments on commit 7674a57

Please sign in to comment.