Skip to content

Commit 3d13b85

Browse files
authored
♻️(ingestion:presentation) refacto to prepare list of metiers endpoint (#603)
* ♻️(ingestion-infrastructure) make QuerySetPage generic * ♻️(ingestion-infrastructure) consistency in the naming domaine fonctionnel property across entity and model * ♻️(ingestion-presentation) simplify error serializer * ♻️(ingestion-presentation) simplify pagination in list offers refactor for upcoming #569 * 📝(presentation-ingestion) update schema
1 parent 5312450 commit 3d13b85

13 files changed

Lines changed: 64 additions & 64 deletions

File tree

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,8 @@
11
from dataclasses import dataclass
22
from typing import Optional
33

4-
from domain.entities.offer import Offer
5-
from domain.interfaces.page_interface import IPage
6-
74

85
@dataclass
96
class GetFilteredOffersInput:
107
active: bool
118
external_id_contains: Optional[str]
12-
13-
14-
class ListOffersPageResult:
15-
def __init__(self, page: IPage[Offer]):
16-
self.page = page
Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
1-
from application.ingestion.interfaces.list_offers_input import (
2-
GetFilteredOffersInput,
3-
ListOffersPageResult,
4-
)
1+
from application.ingestion.interfaces.list_offers_input import GetFilteredOffersInput
2+
from domain.entities.offer import Offer
3+
from domain.interfaces.page_interface import IPage
54
from domain.interfaces.usecase_interface import IUseCase
65
from domain.repositories.offers_repository_interface import IOffersRepository
76
from domain.services.logger_interface import ILogger
87

98

10-
class ListOffersUseCase(IUseCase[GetFilteredOffersInput, ListOffersPageResult]):
9+
class ListOffersUseCase(IUseCase[GetFilteredOffersInput, IPage[Offer]]):
1110
def __init__(
1211
self,
1312
offers_repository: IOffersRepository,
@@ -16,9 +15,8 @@ def __init__(
1615
self.offers_repository = offers_repository
1716
self.logger = logger
1817

19-
def execute(self, input_data: GetFilteredOffersInput) -> ListOffersPageResult:
20-
page = self.offers_repository.get_filtered(
18+
def execute(self, input_data: GetFilteredOffersInput) -> IPage[Offer]:
19+
return self.offers_repository.get_filtered(
2120
active=input_data.active,
2221
external_id_contains=input_data.external_id_contains,
2322
)
24-
return ListOffersPageResult(page=page)

src/web/infrastructure/django_apps/shared/admin.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,13 +125,13 @@ class MetierAdmin(admin.ModelAdmin):
125125
list_display = (
126126
"external_id",
127127
"libelle_long",
128-
"code_domaine_fonctionnel",
128+
"domaine_fonctionnel_code",
129129
"offer_family_code",
130130
"processed_at",
131131
"archived_at",
132132
)
133133
list_filter = (
134-
"code_domaine_fonctionnel",
134+
"domaine_fonctionnel_code",
135135
"offer_family_code",
136136
"processed_at",
137137
"archived_at",
@@ -140,7 +140,7 @@ class MetierAdmin(admin.ModelAdmin):
140140
"id",
141141
"external_id",
142142
"libelle_long",
143-
"code_domaine_fonctionnel",
143+
"domaine_fonctionnel_code",
144144
"offer_family_code",
145145
)
146146
readonly_fields = [f.name for f in MetierModel._meta.get_fields()]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Generated by Django 6.0.5 on 2026-05-19 15:20
2+
3+
from django.db import migrations
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
("shared", "0026_offermodel_area"),
10+
]
11+
12+
operations = [
13+
migrations.RenameField(
14+
model_name="metiermodel",
15+
old_name="code_domaine_fonctionnel",
16+
new_name="domaine_fonctionnel_code",
17+
),
18+
]

src/web/infrastructure/django_apps/shared/models/metier.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ class MetierModel(models.Model):
1111
external_id = models.CharField(max_length=8, unique=True)
1212
libelle_long = models.CharField(max_length=500)
1313
definition_synthetique = models.TextField(null=True, blank=True)
14-
code_domaine_fonctionnel = models.CharField(max_length=3)
14+
domaine_fonctionnel_code = models.CharField(max_length=3)
1515
offer_family_code = models.CharField(max_length=8)
1616
versants = models.JSONField(default=list, null=True, blank=True)
1717
conditions_particulieres = models.JSONField(default=list, null=True, blank=True)
@@ -41,7 +41,7 @@ def to_entity(self):
4141
external_id=self.external_id,
4242
libelle=self.libelle_long,
4343
description=self.definition_synthetique or "",
44-
domaine_fonctionnel_code=self.code_domaine_fonctionnel,
44+
domaine_fonctionnel_code=self.domaine_fonctionnel_code,
4545
versants=versants,
4646
activites=self.activites or [],
4747
conditions_particulieres=self.conditions_particulieres or [],
@@ -60,7 +60,7 @@ def from_entity(cls, metier):
6060
external_id=metier.external_id,
6161
libelle_long=metier.libelle,
6262
definition_synthetique=metier.description,
63-
code_domaine_fonctionnel=metier.domaine_fonctionnel_code,
63+
domaine_fonctionnel_code=metier.domaine_fonctionnel_code,
6464
offer_family_code=metier.offer_family_code or "",
6565
versants=versants,
6666
activites=metier.activites,

src/web/infrastructure/mappers/offer_queryset_page.py renamed to src/web/infrastructure/mappers/queryset_page.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1-
from domain.entities.offer import Offer
1+
from typing import Generic, TypeVar
2+
23
from domain.interfaces.page_interface import IPage
34

5+
T = TypeVar("T")
6+
47

5-
class OfferQuerySetPage(IPage[Offer]):
8+
class QuerySetPage(IPage[T], Generic[T]):
69
def __init__(self, qs):
710
self._qs = qs
811

src/web/infrastructure/repositories/shared/postgres_metier_repository.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ def upsert_batch(self, metiers: List[Metier]) -> IUpsertResult:
7272
fields=[
7373
"libelle_long",
7474
"definition_synthetique",
75-
"code_domaine_fonctionnel",
75+
"domaine_fonctionnel_code",
7676
"offer_family_code",
7777
"versants",
7878
"activites",

src/web/infrastructure/repositories/shared/postgres_offers_repository.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from domain.repositories.offers_repository_interface import IOffersRepository
1616
from domain.services.logger_interface import ILogger
1717
from infrastructure.django_apps.shared.models.offer import OfferModel
18-
from infrastructure.mappers.offer_queryset_page import OfferQuerySetPage
18+
from infrastructure.mappers.queryset_page import QuerySetPage
1919

2020

2121
class PostgresOffersRepository(IOffersRepository):
@@ -139,7 +139,7 @@ def get_filtered(
139139
if external_id_contains:
140140
qs = qs.filter(external_id__contains=external_id_contains)
141141

142-
return OfferQuerySetPage(qs.order_by("-updated_at"))
142+
return QuerySetPage(qs.order_by("-updated_at"))
143143

144144
@transaction.atomic
145145
def get_pending_processing(self, limit: int = 1000) -> List[Offer]:

src/web/presentation/ingestion/serializers.py

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,18 @@
11
from rest_framework import serializers
22

33

4-
class ValidationErrorSerializer(serializers.Serializer):
5-
row = serializers.IntegerField()
4+
class GenericErrorSerializer(serializers.Serializer):
65
error = serializers.CharField()
76

87

9-
class FileErrorSerializer(serializers.Serializer):
10-
error = serializers.CharField()
8+
class ValidationErrorSerializer(GenericErrorSerializer):
9+
row = serializers.IntegerField()
1110

1211

13-
class NoValidRowsErrorSerializer(serializers.Serializer):
14-
error = serializers.CharField()
12+
class NoValidRowsErrorSerializer(GenericErrorSerializer):
1513
validation_errors = ValidationErrorSerializer(many=True)
1614

1715

18-
class ServerErrorSerializer(serializers.Serializer):
19-
error = serializers.CharField()
20-
21-
2216
class TokenErrorMessageSerializer(serializers.Serializer):
2317
token_class = serializers.CharField()
2418
token_type = serializers.CharField()

src/web/presentation/ingestion/views.py

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,10 @@
4040
ArchiveOfferRequestSerializer,
4141
ArchiveOfferSuccessSerializer,
4242
ConcoursUploadResponseSerializer,
43-
FileErrorSerializer,
44-
ListOffersErrorSerializer,
43+
GenericErrorSerializer,
4544
ListOffersFiltersSerializer,
4645
ListOffersResponseSerializer,
4746
NoValidRowsErrorSerializer,
48-
ServerErrorSerializer,
4947
SourceSerializer,
5048
TokenErrorSerializer,
5149
)
@@ -144,11 +142,11 @@ class ConcoursUploadView(APIView):
144142
201: ConcoursUploadResponseSerializer,
145143
400: PolymorphicProxySerializer(
146144
component_name="ConcoursUpload400Error",
147-
serializers=[FileErrorSerializer, NoValidRowsErrorSerializer],
145+
serializers=[GenericErrorSerializer, NoValidRowsErrorSerializer],
148146
resource_type_field_name=None,
149147
),
150148
401: TokenErrorSerializer,
151-
500: ServerErrorSerializer,
149+
500: GenericErrorSerializer,
152150
},
153151
)
154152
def post(self, request):
@@ -335,9 +333,9 @@ def _create_success_response(
335333
tags=["offres"],
336334
responses={
337335
200: ListOffersResponseSerializer,
338-
400: ListOffersErrorSerializer,
336+
400: GenericErrorSerializer,
339337
401: TokenErrorSerializer,
340-
500: ServerErrorSerializer,
338+
500: GenericErrorSerializer,
341339
},
342340
)
343341
class OffersListView(APIView):
@@ -360,13 +358,13 @@ def get(self, request):
360358
result = self.usecase.execute(input_data)
361359

362360
paginator = IngestionPagination()
363-
items = paginator.paginate(result.page, request)
361+
items = paginator.paginate(result, request)
364362
return paginator.get_paginated_response(
365363
ListOffersResponseSerializer(items, many=True).data
366364
)
367365
except Exception as e:
368366
self.logger.error("Unexpected error in OffersListView: %s", str(e))
369-
serializer = ServerErrorSerializer({"error": "Unexpected error"})
367+
serializer = GenericErrorSerializer({"error": "Unexpected error"})
370368
return Response(
371369
serializer.data, status=status.HTTP_500_INTERNAL_SERVER_ERROR
372370
)

0 commit comments

Comments
 (0)