Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,8 @@ def set_revision_source(
# once the values are chosen, we need to reconstruct the perimetre_adomicile
# and lieu_prestation
if service_a_domicile := result.get("service_a_domicile"):
result["perimetre_adomiciles"] = service_a_domicile["perimetre_adomicile"]
if perimetre_adomiciles := service_a_domicile["perimetre_adomicile"]:
result["perimetre_adomiciles"] = perimetre_adomiciles
result["lieu_prestation"] = service_a_domicile["lieu_prestation"]
if "service_a_domicile" in result:
del result["service_a_domicile"]
Expand Down
53 changes: 33 additions & 20 deletions dags/sources/tasks/transform/transform_df.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,6 @@
ACTEUR_TYPE_ESS = "ess"
LABEL_ESS = "ess"

# FIXME : Utiliser Django pour toutes ces constantes ?
A_DOMICILE = "A_DOMICILE"
SUR_PLACE = "SUR_PLACE"
SUR_PLACE_OU_A_DOMICILE = "SUR_PLACE_OU_A_DOMICILE"

LABEL_TO_IGNORE = ["non applicable", "na", "n/a", "null", "aucun", "non"]
MANDATORY_COLUMNS_AFTER_NORMALISATION = [
Expand All @@ -47,6 +43,14 @@
]
REGEX_BAN_SEPARATORS = r"\s,;-"
STRIP_BAN = REGEX_BAN_SEPARATORS.replace("\\s", " ")
REGEXPS_SERVICE_A_DOMICILE_UNIQUEMENT = [
r"SERVICE\s*A\s*DOMICILE\s*UNIQUEMENT",
r"OUI\s*EXCLUSIVEMENT",
]
REGEXPS_SERVICE_SUR_PLACE_OU_A_DOMICILE = [
r"OUI",
r"SERVICE\s*A\s*DOMICILE\s*ET\s*EN\s*BOUTIQUE",
]


def merge_duplicates(
Expand Down Expand Up @@ -323,10 +327,8 @@ def get_point_from_location(longitude, latitude):


def clean_proposition_services(row, _):
# formater les propositions de service selon les colonnes
# action_codes and sous_categorie_codes
#
# [{'action': 'CODE_ACTION','sous_categories': ['CODE_SSCAT']}] ou []
# Format proposition de service following action_codes and sous_categorie_codes
# ex: [{'action': 'CODE_ACTION','sous_categories': ['CODE_SSCAT']}] ou []
if row["sous_categorie_codes"]:
row["proposition_service_codes"] = [
{
Expand All @@ -341,24 +343,27 @@ def clean_proposition_services(row, _):
return row[["proposition_service_codes"]]


### Fonctions de résolution de l'adresse au format BAN et avec vérification via l'API
# adresse.data.gouv.fr en option
# TODO : A déplacer ?
def _sanitize_string(str_to_sanitize):
return unidecode(str_to_sanitize).upper().strip()


def _clean_lieu_prestation(service_a_domicile):
from qfdmo.models.acteur import Acteur

service_a_domicile = service_a_domicile.lower().strip()
service_a_domicile = _sanitize_string(service_a_domicile)

if re.match(r"oui\s*exclusivement", service_a_domicile):
if any(
re.match(regexp, service_a_domicile)
for regexp in REGEXPS_SERVICE_A_DOMICILE_UNIQUEMENT
):
return Acteur.LieuPrestation.A_DOMICILE
elif re.match(r"^oui$", service_a_domicile):
elif any(
re.match(regexp, service_a_domicile)
for regexp in REGEXPS_SERVICE_SUR_PLACE_OU_A_DOMICILE
):
return Acteur.LieuPrestation.SUR_PLACE_OU_A_DOMICILE
elif re.match(r"^non$", service_a_domicile):
return Acteur.LieuPrestation.SUR_PLACE
else:
return Acteur.LieuPrestation.UNKNOWN
return Acteur.LieuPrestation.SUR_PLACE


def _clean_departement_code(departement_code):
Expand All @@ -373,7 +378,7 @@ def _clean_perimetre_adomicile_codes(perimetre_dinterventions):
perimetre_prestation = []
perimetre_dinterventions = perimetre_dinterventions.split("|")
for perimetre in perimetre_dinterventions:
perimetre = unidecode(perimetre).upper().strip()
perimetre = _sanitize_string(perimetre)
if matches := re.match(r"(\d+)\s*KM", perimetre):
perimetre_prestation.append(
{
Expand Down Expand Up @@ -429,17 +434,25 @@ def _clean_perimetre_adomicile_codes(perimetre_dinterventions):


def clean_service_a_domicile(row, _):
from qfdmo.models.acteur import Acteur

row["lieu_prestation"] = _clean_lieu_prestation(row["service_a_domicile"])

row["perimetre_adomicile_codes"] = []
if row["lieu_prestation"] in [A_DOMICILE, SUR_PLACE_OU_A_DOMICILE]:
if row["lieu_prestation"] in [
Acteur.LieuPrestation.A_DOMICILE,
Acteur.LieuPrestation.SUR_PLACE_OU_A_DOMICILE,
]:
row["perimetre_adomicile_codes"] = _clean_perimetre_adomicile_codes(
row["perimetre_dintervention"]
)

return row[["lieu_prestation", "perimetre_adomicile_codes"]]


### Functions to resolve BAN formatted address and check it with APIs
# adresse.data.gouv.fr en option


def _get_address(
adresse_format_ban: str,
) -> tuple[str, str, str]:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,11 @@ def test_cluster_acteurs_parents_choose_data_parent_create(
# Retrieve parent data
assert df.loc[df["identifiant_unique"] == "p1", "parent_data_new"].values[
0
] == {"nom": "prio 1", "email": "email.acteur@source.3"}
] == {
"nom": "prio 1",
"email": "email.acteur@source.3",
"lieu_prestation": "SUR_PLACE",
}
assert (
df.loc[df["identifiant_unique"] != "p1", "parent_data_new"].isnull().all()
), "tester que tous les autres sont None"
Expand Down Expand Up @@ -288,6 +292,7 @@ def test_cluster_acteurs_parents_choose_data_parent_create_keep_empty(
] == {
"nom": "prio 1",
"email": "email.acteur@source.3",
"lieu_prestation": "SUR_PLACE",
}, (
"keep_empty is forced to False and the empty email is ignored until"
" found `email.acteur@source.3`"
Expand Down
15 changes: 15 additions & 0 deletions dags/tests/sources/tasks/transform/test_transform_df.py
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,12 @@ class TestCleanServiceADomicile:
),
[
# Nominal cases : one perimeter
(
"Service à domicile et en Boutique",
"10 km",
"SUR_PLACE_OU_A_DOMICILE",
[{"type": "KILOMETRIQUE", "valeur": 10}],
),
(
"oui",
"10 km",
Expand All @@ -457,6 +463,12 @@ class TestCleanServiceADomicile:
"SUR_PLACE_OU_A_DOMICILE",
[{"type": "FRANCE_METROPOLITAINE", "valeur": ""}],
),
(
"Service à domicile uniquement",
"10 km",
"A_DOMICILE",
[{"type": "KILOMETRIQUE", "valeur": 10}],
),
(
"oui exclusivement",
"10 km",
Expand All @@ -478,6 +490,9 @@ class TestCleanServiceADomicile:
{"type": "DROM_TOM", "valeur": ""},
],
),
# Nominal cases empty
("", "", "SUR_PLACE", []),
("", "10 km", "SUR_PLACE", []),
# Nominal cases : non
("non", "", "SUR_PLACE", []),
# other cases : non
Expand Down
73 changes: 73 additions & 0 deletions qfdmo/migrations/0173_alter_acteur_lieu_prestation_and_more.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# Generated by Django 5.2.5 on 2025-09-11 15:08

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("qfdmo", "0172_vueacteur_enfants_liste_vueacteur_enfants_nombre_and_more"),
]

operations = [
migrations.AlterField(
model_name="acteur",
name="lieu_prestation",
field=models.CharField(
blank=True,
choices=[
("A_DOMICILE", "À domicile"),
("SUR_PLACE", "Sur place"),
("SUR_PLACE_OU_A_DOMICILE", "Sur place ou à domicile"),
],
default="SUR_PLACE",
max_length=255,
null=True,
),
),
migrations.AlterField(
model_name="displayedacteur",
name="lieu_prestation",
field=models.CharField(
blank=True,
choices=[
("A_DOMICILE", "À domicile"),
("SUR_PLACE", "Sur place"),
("SUR_PLACE_OU_A_DOMICILE", "Sur place ou à domicile"),
],
default="SUR_PLACE",
max_length=255,
null=True,
),
),
migrations.AlterField(
model_name="revisionacteur",
name="lieu_prestation",
field=models.CharField(
blank=True,
choices=[
("A_DOMICILE", "À domicile"),
("SUR_PLACE", "Sur place"),
("SUR_PLACE_OU_A_DOMICILE", "Sur place ou à domicile"),
],
default="SUR_PLACE",
max_length=255,
null=True,
),
),
migrations.AlterField(
model_name="vueacteur",
name="lieu_prestation",
field=models.CharField(
blank=True,
choices=[
("A_DOMICILE", "À domicile"),
("SUR_PLACE", "Sur place"),
("SUR_PLACE_OU_A_DOMICILE", "Sur place ou à domicile"),
],
default="SUR_PLACE",
max_length=255,
null=True,
),
),
]
4 changes: 2 additions & 2 deletions qfdmo/models/acteur.py
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,6 @@ class LieuPrestation(models.TextChoices):
A_DOMICILE = "A_DOMICILE", "À domicile"
SUR_PLACE = "SUR_PLACE", "Sur place"
SUR_PLACE_OU_A_DOMICILE = "SUR_PLACE_OU_A_DOMICILE", "Sur place ou à domicile"
UNKNOWN = "", ""

class Meta:
abstract = True
Expand Down Expand Up @@ -467,8 +466,9 @@ class Meta:
lieu_prestation = models.CharField(
max_length=255,
choices=LieuPrestation.choices,
default=LieuPrestation.UNKNOWN,
default=LieuPrestation.SUR_PLACE,
blank=True,
null=True,
)

@property
Expand Down
4 changes: 1 addition & 3 deletions unit_tests/core/test_qfdmo_tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,5 @@ def test_acteur_pinpoint_tag_carte_config(
sous_categorie.id,
)

assert result_context["marker_icon_file"] == (
"/media/config/groupeaction/icones/top.svg"
)
assert result_context["marker_icon_file"].endswith("top.svg")
assert result_context["marker_icon"] == ""
Loading