Skip to content

Commit

Permalink
Merge branch 'staging' into 1465-history
Browse files Browse the repository at this point in the history
  • Loading branch information
pletelli authored Jan 21, 2025
2 parents 09cb9db + cb33709 commit 2c1cfca
Show file tree
Hide file tree
Showing 41 changed files with 687 additions and 250 deletions.
1 change: 0 additions & 1 deletion api/serializers/blogpost.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ class Meta:
"modification_date",
"title",
"tagline",
"body",
"content",
"published",
"display_date",
Expand Down
4 changes: 4 additions & 0 deletions api/serializers/condition.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from rest_framework import serializers

from data.models import Condition


Expand All @@ -9,5 +10,8 @@ class Meta:
"name",
"id",
"name_en",
"min_age",
"max_age",
"category",
]
read_only_fields = fields
11 changes: 10 additions & 1 deletion api/serializers/declaration.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ class PassthroughSubstanceSerializer(IdPassthrough, SubstanceSerializer):
class DeclaredListSerializer(serializers.ListSerializer):
"""
Pour les modèles liés et les list serializers on a besoin de spécifier le comportement
dans une mise à jour car DRF ne peut pas le déviner:
dans une mise à jour car DRF ne peut pas le deviner:
https://www.django-rest-framework.org/api-guide/serializers/#customizing-multiple-update
"""

Expand Down Expand Up @@ -272,6 +272,8 @@ class Meta:
model = Declaration
fields = (
"id",
"siccrf_id",
"declared_in_teleicare",
"status",
"author",
"company",
Expand Down Expand Up @@ -441,6 +443,7 @@ class Meta:
model = Declaration
fields = (
"id",
"declared_in_teleicare",
"article",
"status",
"author",
Expand Down Expand Up @@ -519,10 +522,14 @@ def setup_eager_loading(queryset):
queryset = queryset.prefetch_related(
"declared_plants__plant__substances",
"declared_plants__plant",
"declared_plants__preparation",
"declared_plants__unit",
"declared_microorganisms__microorganism__substances",
"declared_ingredients__ingredient__substances",
"declared_substances__substance",
"declared_substances__unit",
"computed_substances__substance",
"computed_substances__unit",
"attachments",
)
return queryset
Expand Down Expand Up @@ -579,6 +586,8 @@ class Meta:
model = Declaration
fields = (
"id",
"siccrf_id",
"declared_in_teleicare",
"status",
"author",
"company",
Expand Down
2 changes: 2 additions & 0 deletions api/serializers/population.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from rest_framework import serializers

from data.models import Population


Expand All @@ -11,5 +12,6 @@ class Meta:
"is_obsolete",
"min_age",
"max_age",
"category",
]
read_only_fields = fields
30 changes: 30 additions & 0 deletions api/tests/test_declaration.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
SubstanceUnitFactory,
SupervisorRoleFactory,
VisaRoleFactory,
PlantSynonymFactory,
)
from data.models import Attachment, Declaration, DeclaredMicroorganism, DeclaredPlant, Snapshot

Expand Down Expand Up @@ -1948,3 +1949,32 @@ def test_cannot_replace_element_different_type(self):
declared_plant.refresh_from_db()
self.assertEqual(declared_plant.request_status, DeclaredPlant.AddableStatus.REQUESTED)
self.assertNotEqual(declared_plant.plant, microorganism)

@authenticate
def test_can_add_synonym_on_replace(self):
"""
C'est possible d'envoyer une liste avec un nouvel element pour
ajouter un synonyme et laisser des synonymes existantes non-modifiées
"""
InstructionRoleFactory(user=authenticate.user)

declaration = DeclarationFactory()
declared_plant = DeclaredPlantFactory(declaration=declaration, new=True)
plant = PlantFactory()
synonym = PlantSynonymFactory.create(name="Eucalyptus Plant", standard_name=plant)

response = self.client.post(
reverse("api:declared_element_replace", kwargs={"pk": declared_plant.id, "type": "plant"}),
{
"element": {"id": plant.id, "type": "plant"},
"synonyms": [{"id": synonym.id, "name": "Eucalyptus Plant"}, {"name": "New synonym"}],
},
format="json",
)
self.assertEqual(response.status_code, status.HTTP_200_OK, response.json())
declared_plant.refresh_from_db()
self.assertEqual(declared_plant.request_status, DeclaredPlant.AddableStatus.REPLACED)
plant.refresh_from_db()
self.assertEqual(plant.plantsynonym_set.count(), 2)
self.assertIsNotNone(plant.plantsynonym_set.get(name="New synonym"))
self.assertEqual(plant.plantsynonym_set.get(id=synonym.id).name, synonym.name)
23 changes: 23 additions & 0 deletions api/views/declaration/declared_element.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@
Microorganism,
Substance,
Ingredient,
PlantSynonym,
MicroorganismSynonym,
SubstanceSynonym,
IngredientSynonym,
)
from api.serializers import (
DeclaredElementSerializer,
Expand Down Expand Up @@ -59,21 +63,25 @@ class ElementMappingMixin:
"plant": {
"model": DeclaredPlant,
"element_model": Plant,
"synonym_model": PlantSynonym,
"serializer": DeclaredPlantSerializer,
},
"microorganism": {
"model": DeclaredMicroorganism,
"element_model": Microorganism,
"synonym_model": MicroorganismSynonym,
"serializer": DeclaredMicroorganismSerializer,
},
"substance": {
"model": DeclaredSubstance,
"element_model": Substance,
"synonym_model": SubstanceSynonym,
"serializer": DeclaredSubstanceSerializer,
},
"other-ingredient": {
"model": DeclaredIngredient,
"element_model": Ingredient,
"synonym_model": IngredientSynonym,
"serializer": DeclaredIngredientSerializer,
},
}
Expand Down Expand Up @@ -102,6 +110,10 @@ def type_serializer(self):
def element_model(self):
return self.type_info["element_model"]

@property
def synonym_model(self):
return self.type_info["synonym_model"]


class DeclaredElementView(RetrieveAPIView, ElementMappingMixin):
permission_classes = [(IsInstructor | IsVisor)]
Expand Down Expand Up @@ -161,3 +173,14 @@ def _update_element(self, element, request):
setattr(element, self.element_type, existing_element)
element.request_status = self.type_model.AddableStatus.REPLACED
element.new = False

synonyms = request.data.get("synonyms", [])

for synonym in synonyms:
if not synonym.get("id"):
# add new synonym
try:
name = synonym.get("name")
except KeyError:
raise ParseError(detail="Must provide 'name' to create new synonym")
self.synonym_model.objects.create(standard_name=existing_element, name=name)
3 changes: 3 additions & 0 deletions data/admin/condition.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,12 @@ class ConditionAdmin(admin.ModelAdmin):
fields = [
"name",
"ca_name",
"category",
"siccrf_name_en",
"is_obsolete",
"ca_is_obsolete",
"min_age",
"max_age",
"creation_date",
"modification_date",
]
Expand Down
1 change: 1 addition & 0 deletions data/admin/population.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class PopulationAdmin(admin.ModelAdmin):
fields = [
"name",
"ca_name",
"category",
"is_obsolete",
"ca_is_obsolete",
"is_defined_by_anses",
Expand Down
92 changes: 67 additions & 25 deletions data/etl/teleicare_history/extractor.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import contextlib
import logging
import re
from datetime import date, datetime
from datetime import date, datetime, timezone

from django.core.exceptions import ValidationError
from django.db import IntegrityError
Expand All @@ -22,6 +23,30 @@
logger = logging.getLogger(__name__)


@contextlib.contextmanager
def suppress_autotime(model, fields):
"""
Décorateur pour annuler temporairement le auto_now et auto_now_add de certains champs
Copié depuis https://stackoverflow.com/questions/7499767/temporarily-disable-auto-now-auto-now-add
"""
_original_values = {}
for field in model._meta.local_fields:
if field.name in fields:
_original_values[field.name] = {
"auto_now": field.auto_now,
"auto_now_add": field.auto_now_add,
}
field.auto_now = False
field.auto_now_add = False
try:
yield
finally:
for field in model._meta.local_fields:
if field.name in fields:
field.auto_now = _original_values[field.name]["auto_now"]
field.auto_now_add = _original_values[field.name]["auto_now_add"]


def convert_phone_number(phone_number_to_parse):
if phone_number_to_parse:
phone_number = PhoneNumber.from_string(phone_number_to_parse, region="FR")
Expand Down Expand Up @@ -124,9 +149,6 @@ def match_companies_on_siret_or_vat(create_if_not_exist=False):


def get_most_recent(list_of_declarations):
def convert_str_date(value):
return datetime.strptime(value, "%m/%d/%Y %H:%M:%S %p").date()

most_recent_date = date.min
for ica_declaration in list_of_declarations:
current_date = convert_str_date(ica_declaration.dcl_date)
Expand All @@ -137,6 +159,14 @@ def convert_str_date(value):
return list_of_declarations.get(dcl_date=most_recente_dcl_date)


def convert_str_date(value, aware=False):
dt = datetime.strptime(value, "%m/%d/%Y %H:%M:%S %p")
if aware:
return dt.replace(tzinfo=timezone.utc)
else:
return dt.date()


# Pour les déclarations TeleIcare, le status correspond au champ IcaVersionDeclaration.stattdcl_ident
DECLARATION_STATUS_MAPPING = {
1: Declaration.DeclarationStatus.ONGOING_INSTRUCTION, # 'en cours'
Expand Down Expand Up @@ -199,7 +229,18 @@ def create_declaration_from_teleicare_history():
f"Cette entreprise avec siccrf_id={ica_complement_alimentaire.etab_id} n'existe pas déjà en base"
)
continue
declaration_creation_date = (
convert_str_date(latest_ica_declaration.dcl_date, aware=True)
if latest_ica_declaration.dcl_date
else ""
)
declaration = Declaration(
creation_date=declaration_creation_date,
modification_date=convert_str_date(
latest_ica_declaration.dcl_date_fin_commercialisation, aware=True
)
if latest_ica_declaration.dcl_date_fin_commercialisation
else declaration_creation_date,
siccrf_id=ica_complement_alimentaire.cplalim_ident,
galenic_formulation=GalenicFormulation.objects.get(
siccrf_id=ica_complement_alimentaire.frmgal_ident
Expand Down Expand Up @@ -233,30 +274,31 @@ def create_declaration_from_teleicare_history():
)

try:
declaration.save()
declaration.populations.set(
[
Population.objects.get(siccrf_id=population.popcbl_ident)
for population in (
IcaPopulationCibleDeclaree.objects.filter(
vrsdecl_ident=latest_ica_version_declaration.vrsdecl_ident
with suppress_autotime(declaration, ["creation_date", "modification_date"]):
declaration.save()
declaration.populations.set(
[
Population.objects.get(siccrf_id=population.popcbl_ident)
for population in (
IcaPopulationCibleDeclaree.objects.filter(
vrsdecl_ident=latest_ica_version_declaration.vrsdecl_ident
)
)
)
]
)
declaration.conditions_not_recommended.set(
[
Condition.objects.get(siccrf_id=condition.poprs_ident)
for condition in (
IcaPopulationRisqueDeclaree.objects.filter(
vrsdecl_ident=latest_ica_version_declaration.vrsdecl_ident
]
)
declaration.conditions_not_recommended.set(
[
Condition.objects.get(siccrf_id=condition.poprs_ident)
for condition in (
IcaPopulationRisqueDeclaree.objects.filter(
vrsdecl_ident=latest_ica_version_declaration.vrsdecl_ident
)
)
)
]
)
declaration.save()
]
)
declaration.save()
nb_created_declarations += 1

nb_created_declarations += 1
except IntegrityError:
# cette Déclaration a déjà été créée
pass
Expand Down
1 change: 0 additions & 1 deletion data/factories/blogpost.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ class Meta:

title = factory.Faker("catch_phrase")
tagline = factory.Faker("catch_phrase")
body = factory.Faker("paragraph")
content = factory.Faker("paragraph")
published = factory.Faker("boolean")
author = factory.SubFactory(UserFactory)
Loading

0 comments on commit 2c1cfca

Please sign in to comment.