diff --git a/dags/sources/tasks/business_logic/source_data_normalize.py b/dags/sources/tasks/business_logic/source_data_normalize.py index 8846f6e5b..140c5a80d 100755 --- a/dags/sources/tasks/business_logic/source_data_normalize.py +++ b/dags/sources/tasks/business_logic/source_data_normalize.py @@ -85,15 +85,13 @@ def source_data_normalize( # TODO : un peu crado, à revoir # A cause de la résolution de l'identifiant unique qui dépend du code de la source if "source_id" in df.columns: - df["source_code"] = df["source_id"].str.lower() - df["source_id"] = df["source_code"].map(source_id_by_code) + df["source_code"] = df["source_id"] + df["source_id"] = df["source_id"].map(source_id_by_code) elif source_code is not None: df["source_code"] = source_code df["source_id"] = source_id_by_code[source_code] else: ValueError("Pas de colonne 'source_id'") - if df["source_id"].isna().sum() > 0: - raise ValueError("Valeur nan dans 'source_id'") # Identifiant unique # TODO: on dévrait pouvoir utiliser les modèles django/DB pour automatiser cela diff --git a/dags_unit_tests/sources/tasks/business_logic/test_source_data_normalize.py b/dags_unit_tests/sources/tasks/business_logic/test_source_data_normalize.py index 6a8fae6fd..515afca44 100755 --- a/dags_unit_tests/sources/tasks/business_logic/test_source_data_normalize.py +++ b/dags_unit_tests/sources/tasks/business_logic/test_source_data_normalize.py @@ -303,7 +303,7 @@ def test_statut(self, statut, statut_expected, source_data_normalize_kwargs): { "identifiant_externe": ["1"], "ecoorganisme": ["source1"], - "source_id": ["source1"], + "source_id": ["source_id1"], "acteur_type_id": ["decheterie"], "produitsdechets_acceptes": ["Plastic Box"], "statut": [statut], @@ -318,7 +318,7 @@ def test_statut_no_column(self, source_data_normalize_kwargs): { "identifiant_externe": ["1"], "ecoorganisme": ["source1"], - "source_id": ["source1"], + "source_id": ["source_id1"], "acteur_type_id": ["decheterie"], "produitsdechets_acceptes": ["Plastic Box"], } @@ -345,7 +345,7 @@ def test_label_bonus( { "identifiant_externe": ["1"], "ecoorganisme": ["source1"], - "source_id": ["source1"], + "source_id": ["source_id1"], "acteur_type_id": ["decheterie"], "labels_etou_bonus": [label_et_bonus], "produitsdechets_acceptes": ["Plastic Box"], @@ -375,7 +375,7 @@ def test_public_accueilli( { "identifiant_externe": ["1"], "ecoorganisme": ["source1"], - "source_id": ["source1"], + "source_id": ["source_id1"], "public_accueilli": [public_accueilli], "acteur_type_id": ["decheterie"], "produitsdechets_acceptes": ["Plastic Box"], @@ -401,7 +401,7 @@ def test_public_accueilli_filtre_pro( { "identifiant_externe": ["1"], "ecoorganisme": ["source1"], - "source_id": ["source1"], + "source_id": ["source_id1"], "public_accueilli": [public_accueilli], "acteur_type_id": ["decheterie"], "produitsdechets_acceptes": ["Plastic Box"], @@ -437,7 +437,7 @@ def test_uniquement_sur_rdv( { "identifiant_externe": ["1"], "ecoorganisme": ["source1"], - "source_id": ["source1"], + "source_id": ["source_id1"], "uniquement_sur_rdv": [uniquement_sur_rdv], "acteur_type_id": ["decheterie"], "produitsdechets_acceptes": ["Plastic Box"], @@ -468,7 +468,7 @@ def test_reprise( { "identifiant_externe": ["1"], "ecoorganisme": ["source1"], - "source_id": ["source1"], + "source_id": ["source_id1"], "reprise": [reprise], "acteur_type_id": ["decheterie"], "produitsdechets_acceptes": ["Plastic Box"], @@ -504,7 +504,7 @@ def test_exclusivite_de_reprisereparation( { "identifiant_externe": ["1"], "ecoorganisme": ["source1"], - "source_id": ["source1"], + "source_id": ["source_id1"], "exclusivite_de_reprisereparation": [exclusivite_de_reprisereparation], "acteur_type_id": ["decheterie"], "produitsdechets_acceptes": ["Plastic Box"], @@ -534,7 +534,7 @@ def test_column_transformations_is_called(self, source_data_normalize_kwargs): { "identifiant_externe": ["1"], "ecoorganisme": ["source1"], - "source_id": ["source1"], + "source_id": ["source_id1"], "acteur_type_id": ["decheterie"], "produitsdechets_acceptes": ["Plastic Box"], "nom origin": ["nom origin 1"], diff --git a/qfdmo/migrations/0108_refacto_source_codes.py b/qfdmo/migrations/0108_refacto_source_codes.py deleted file mode 100644 index e226e31fc..000000000 --- a/qfdmo/migrations/0108_refacto_source_codes.py +++ /dev/null @@ -1,168 +0,0 @@ -# Generated by Django 5.1.1 on 2024-10-12 07:55 - -from django.db import migrations - -mapping = { - "ECODDS": {"old_code": "", "new_code": "ecodds"}, - "Emmaus Connect": {"old_code": "emmaus_connect", "new_code": "emmausconnect"}, - "RecyclOptics": {"old_code": "RecyclOptics", "new_code": "recycloptics"}, - "Emmaüs Défi": {"old_code": "", "new_code": "emmausdefi"}, - "Lunettes de Zac": {"old_code": "lunettes_de_zac", "new_code": "lunettesdezac"}, - "CRAR Normandie": {"old_code": "crar_normandie", "new_code": "crarnormandie"}, - "Syvadec": {"old_code": "", "new_code": "syvadec"}, - "ALIAPUR": {"old_code": "", "new_code": "aliapur"}, - "Ville de Paris": {"old_code": "ville_de_paris", "new_code": "villedeparis"}, - "Le REFER": {"old_code": "le_refer", "new_code": "lerefer"}, - "Origine Cycles": {"old_code": "origine_cycles", "new_code": "originecycles"}, - "Réseau National des Ressourceries et Recycleries": { - "old_code": "reseau_national_des_ressourceries_et_recycleries", - "new_code": "ressourceries", - }, - "Recyclivre - Boîtes à lire": { - "old_code": "recyclivre_-_boites_a_lire", - "new_code": "recyclivrebal", - }, - "Association des Ludothèques Françaises": { - "old_code": "", - "new_code": "ludotheques", - }, # cas spécial, pas de code -> correction id unique -> id externe - "Bibliothèques - Ministère de la culture": { - "old_code": "", - "new_code": "bibliotheques", - }, # cas spécial, pas de code -> correction id unique -> id externe - "ADEME - SINOE": { - "old_code": "", - "new_code": "sinoe", - }, # cas spécial, code au milieu de l'identifiant - "ADEME - Initiatives locales": { - "old_code": "", - "new_code": "ademelocales", - }, # cas spécial, pas de code - "ADEME - Acteurs digitaux": { - "old_code": "", - "new_code": "ademedigitaux", - }, # cas spécial, pas de code - "ADEME - Initiatives nationales": { - "old_code": "", - "new_code": "ademenationales", - }, # cas spécial, pas de code - "CartEco - ESS France": { - "old_code": "carteco_-_ess_france", - "new_code": "carteco", - }, # carteco & carteco_-_ess_france - "ADEME - Locations": { - "old_code": "", - "new_code": "ademelocation", - }, # cas spécial, pas de code - # "openstreetmap": { - # "old_code": "", - # "new_code": "", - # }, # cas spécial, que 4 points 'DEL', n'est ce pas de la contribution équipe ? - "ADELPHE": {"old_code": "adelphe", "new_code": "adelphe"}, # 1 seul acteur ? - "ALCOME": {"old_code": "", "new_code": "alcome"}, # 0 acteur - "ECOMAISON": {"old_code": "ecomaison", "new_code": "ecomaison"}, - "APER": {"old_code": "", "new_code": "aper"}, # 0 acteur - "Communauté Longue Vie Aux Objets": { - "old_code": "communaute_longue_vie_aux_objets", - "new_code": "communautelvao", - }, - "Groupe Findis": {"old_code": "", "new_code": "findis"}, # cas special, pas de code - "CITEO": {"old_code": "", "new_code": "citeo"}, - "Collectif Encore": {"old_code": "", "new_code": "encore"}, - "CYCLAMED": {"old_code": "", "new_code": "cyclamed"}, - "CYCLEVIA": {"old_code": "", "new_code": "cyclevia"}, # 0 acteur - "DASTRI": {"old_code": "", "new_code": "dastri"}, # 0 acteur - "Collectivité Ambert Livradois Forez": { - "old_code": "collectivite_ambert_livradois_forez", - "new_code": "alv", - }, - "CMA - Chambre des métiers et de l'artisanat": { - "old_code": "cma_reparacteur", - "new_code": "cma", - }, - "Fédération Envie": {"old_code": "", "new_code": "envie"}, - # "equipe": {"old_code": "", "new_code": ""}, - "COREPILE": {"old_code": "", "new_code": "corepile"}, - "ECOLOGIC": {"old_code": "", "new_code": "ecologic"}, - "VALDELIA": {"old_code": "", "new_code": "valdelia"}, - "VALOBAT": {"old_code": "", "new_code": "valobat"}, - "ECOMINERO": {"old_code": "", "new_code": "ecominero"}, - "PYREO": {"old_code": "", "new_code": "pyreo"}, - "GIE FRP": {"old_code": "", "new_code": "giefrp"}, - "LEKO": {"old_code": "", "new_code": "leko"}, - "SCRELEC": {"old_code": "", "new_code": "screlec"}, - "TYVAL": {"old_code": "", "new_code": "tyval"}, - "REFASHION": {"old_code": "", "new_code": "refashion"}, - "Recyclivre - Point livres": { - "old_code": "recyclivre_-_point_livres", - "new_code": "recyclivrepl", - }, - "ADEME_SINOE_Decheteries": { - "old_code": "ademe_sinoe_decheteries", - "new_code": "ademesinoedecheteries", - }, - "Longue Vie Aux Objets": { - "old_code": "", - "new_code": "lvao", - }, # il y a à boire et à manger - "ECOSYSTEM": {"old_code": "", "new_code": "ecosystem"}, - "SOREN": {"old_code": "", "new_code": "soren"}, - "Ordre National Des Pharmaciens": { - "old_code": "ordredespharmaciens", - "new_code": "ordredespharmaciens", - }, - "Leroy Merlin": {"old_code": "", "new_code": "leroymerlin"}, - "la_poste": {"old_code": "", "new_code": "laposte"}, - "cma_reparacteur": {"old_code": "cma_reparacteur", "new_code": "cmareparacteur"}, -} - - -def update_acteur_source_code(apps, schema_editor): - Source = apps.get_model("qfdmo", "Source") - Acteur = apps.get_model("qfdmo", "Acteur") - RevisionActeur = apps.get_model("qfdmo", "RevisionActeur") - DisplayedActeur = apps.get_model("qfdmo", "DisplayedActeur") - for code, map in mapping.items(): - Source.objects.filter(code=code).update(code=map["new_code"]) - for cls in [Acteur, RevisionActeur, DisplayedActeur]: - if map["old_code"] and map["new_code"] != map["old_code"]: - # tous ceux dont l'identifiant_unique commence par old_code, on remplace old_code par new_code dans l'identifiant unique - cls.objects.filter(identifiant_unique__startswith=map["old_code"]) - for acteur in cls.objects.filter( - identifiant_unique__startswith=map["old_code"] - ): - acteur.identifiant_unique = acteur.identifiant_unique.replace( - map["old_code"], map["new_code"] - ) - acteur.save() - - -def rollback_acteur_source_code(apps, schema_editor): - Source = apps.get_model("qfdmo", "Source") - Acteur = apps.get_model("qfdmo", "Acteur") - RevisionActeur = apps.get_model("qfdmo", "RevisionActeur") - DisplayedActeur = apps.get_model("qfdmo", "DisplayedActeur") - for code, map in mapping.items(): - Source.objects.filter(code=map["new_code"]).update(code=code) - for cls in [Acteur, RevisionActeur, DisplayedActeur]: - if map["old_code"]: - # tous ceux dont l'identifiant_unique commence par old_code, on remplace old_code par new_code dans l'identifiant unique - cls.objects.filter(identifiant_unique__startswith=map["new_code"]) - for acteur in cls.objects.filter( - identifiant_unique__startswith=map["new_code"] - ): - acteur.identifiant_unique = acteur.identifiant_unique.replace( - map["new_code"], map["old_code"] - ) - acteur.save() - - -class Migration(migrations.Migration): - - dependencies = [ - ("qfdmo", "0107_alter_source_licence"), - ] - - operations = [ - migrations.RunPython(update_acteur_source_code, rollback_acteur_source_code), - ] diff --git a/qfdmo/migrations/0109_alter_source_code.py b/qfdmo/migrations/0109_alter_source_code.py deleted file mode 100644 index a547bfb79..000000000 --- a/qfdmo/migrations/0109_alter_source_code.py +++ /dev/null @@ -1,25 +0,0 @@ -# Generated by Django 5.1.1 on 2024-12-10 17:14 - -from django.db import migrations, models - -import qfdmo.validators - - -class Migration(migrations.Migration): - - dependencies = [ - ("qfdmo", "0108_refacto_source_codes"), - ] - - operations = [ - migrations.AlterField( - model_name="source", - name="code", - field=models.CharField( - help_text="Ce champ est utilisé lors de l'import de données, il ne doit pas être mis à jour sous peine de casser l'import de données", - max_length=255, - unique=True, - validators=[qfdmo.validators.CodeValidator()], - ), - ), - ] diff --git a/qfdmo/models/acteur.py b/qfdmo/models/acteur.py index 9baa76c99..f351b1f3e 100644 --- a/qfdmo/models/acteur.py +++ b/qfdmo/models/acteur.py @@ -131,13 +131,10 @@ class Meta: code = models.CharField( max_length=255, unique=True, - blank=False, - null=False, help_text=( - "Ce champ est utilisé lors de l'import de données, il ne doit pas être" - " mis à jour sous peine de casser l'import de données" + "This field is used to manage the import of data." + " Any update can break the import data process" ), - validators=[CodeValidator()], ) afficher = models.BooleanField(default=True) url = models.CharField(max_length=2048, blank=True, null=True)