Skip to content

Commit

Permalink
🗃️(api) add default values for optional Statique model fields
Browse files Browse the repository at this point in the history
The importation process may create many duplicates as the upsert
technique we use does not compare rows with missing fields. Two tables
are mostly concerned: Operateur and Amenageur. Hence we decided to fill
optional (non filled) fields with default generic strings (e.g. "NA").
  • Loading branch information
jmaupetit committed Jan 10, 2025
1 parent c11b6d8 commit a6a8949
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 5 deletions.
1 change: 1 addition & 0 deletions src/api/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ and this project adheres to
- Decrease the number of database queries for dynamic endpoints
- Cache the "get PointDeCharge id from its `id_pdc_itinerance`" database query
- Improve JSON string parsing using pyarrow engine
- Add default values for optional Statique model fields
- Upgrade pydantic to `2.10.4`
- Upgrade pydantic-settings to `2.7.1`
- Upgrade python-multipart to `0.0.20`
Expand Down
16 changes: 11 additions & 5 deletions src/api/qualicharge/models/static.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,11 +120,17 @@ def not_future(value: date):
# A date not in the future (today or in the past)
NotFutureDate = Annotated[date, AfterValidator(not_future)]

# Default values (if not provided)
DEFAULT_CHAR_VALUE: str = "NA"
DEFAULT_EMAIL_ADDRESS: str = "[email protected]"
DEFAULT_PHONE_NUMBER: FrenchPhoneNumber = FrenchPhoneNumber("+33.123456789")
DEFAULT_SIREN_NUMBER: str = "123456789"


class Statique(ModelSchemaMixin, BaseModel):
"""IRVE static model."""

nom_amenageur: Optional[str]
nom_amenageur: Optional[str] = DEFAULT_CHAR_VALUE
siren_amenageur: Optional[
Annotated[
str,
Expand All @@ -135,11 +141,11 @@ class Statique(ModelSchemaMixin, BaseModel):
],
),
]
]
contact_amenageur: Optional[EmailStr]
nom_operateur: Optional[str]
] = DEFAULT_SIREN_NUMBER
contact_amenageur: Optional[EmailStr] = DEFAULT_EMAIL_ADDRESS
nom_operateur: Optional[str] = DEFAULT_CHAR_VALUE
contact_operateur: EmailStr
telephone_operateur: Optional[FrenchPhoneNumber]
telephone_operateur: Optional[FrenchPhoneNumber] = DEFAULT_PHONE_NUMBER
nom_enseigne: str
id_station_itinerance: Annotated[
str, Field(pattern="(?:(?:^|,)(^[A-Z]{2}[A-Z0-9]{4,33}$|Non concerné))+$")
Expand Down
80 changes: 80 additions & 0 deletions src/api/tests/api/v1/routers/test_statique.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from qualicharge.auth.schemas import GroupOperationalUnit, ScopesEnum, User, UserGroup
from qualicharge.conf import settings
from qualicharge.factories.static import StatiqueFactory
from qualicharge.models.static import Statique
from qualicharge.schemas.core import (
OperationalUnit,
PointDeCharge,
Expand Down Expand Up @@ -383,6 +384,43 @@ def test_create_for_superuser(client_auth):
assert json_response["items"][0] == id_pdc_itinerance


def test_create_without_optional_fields(client_auth):
"""Test the /statique/ create endpoint when optional fields are not provided."""
id_pdc_itinerance = "FR911E1111ER1"
data = Statique(
**StatiqueFactory.build(
id_pdc_itinerance=id_pdc_itinerance,
).model_dump(
exclude={
"nom_amenageur",
"siren_amenageur",
"contact_amenageur",
"nom_operateur",
"telephone_operateur",
}
)
)

# Create the Statique without optional fields
response = client_auth.post("/statique/", json=json.loads(data.model_dump_json()))
assert response.status_code == status.HTTP_201_CREATED
json_response = response.json()
assert json_response["message"] == "Statique items created"
assert json_response["size"] == 1
assert json_response["items"][0] == id_pdc_itinerance

# Get created Statique and check defaults
response = client_auth.get(f"/statique/{id_pdc_itinerance}")
assert response.status_code == status.HTTP_200_OK
json_response = response.json()
statique = Statique(**json_response)
assert statique.nom_amenageur == "NA"
assert statique.siren_amenageur == "123456789"
assert statique.contact_amenageur == "[email protected]"
assert statique.nom_operateur == "NA"
assert statique.telephone_operateur == "tel:+33-1-23-45-67-89"


def test_create_twice(client_auth):
"""Test the /statique/ create endpoint with the same payload twice."""
id_pdc_itinerance = "FR911E1111ER1"
Expand Down Expand Up @@ -656,6 +694,48 @@ def test_bulk_for_superuser(client_auth):
assert json_response["items"][1] == data[1].id_pdc_itinerance


def test_bulk_without_optional_fields(client_auth):
"""Test the /statique/bulk create endpoint when optional fields are not provided."""
data = StatiqueFactory.batch(
size=2,
)

payload = [
json.loads(
d.model_dump_json(
exclude={
"nom_amenageur",
"siren_amenageur",
"contact_amenageur",
"nom_operateur",
"telephone_operateur",
}
)
)
for d in data
]
response = client_auth.post("/statique/bulk", json=payload)

assert response.status_code == status.HTTP_201_CREATED
json_response = response.json()
assert json_response["message"] == "Statique items created"
assert json_response["size"] == len(payload)
assert json_response["items"][0] == data[0].id_pdc_itinerance
assert json_response["items"][1] == data[1].id_pdc_itinerance

# Get created Statique and check defaults
response = client_auth.get("/statique/")
assert response.status_code == status.HTTP_200_OK
json_response = response.json()
for item in json_response["items"]:
statique = Statique(**item)
assert statique.nom_amenageur == "NA"
assert statique.siren_amenageur == "123456789"
assert statique.contact_amenageur == "[email protected]"
assert statique.nom_operateur == "NA"
assert statique.telephone_operateur == "tel:+33-1-23-45-67-89"


@pytest.mark.parametrize(
"client_auth",
(
Expand Down
22 changes: 22 additions & 0 deletions src/api/tests/models/test_static.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,3 +123,25 @@ def test_statique_model_date_maj():
tomorrow = today + timedelta(days=1)
with pytest.raises(ValueError, match=f"{tomorrow} is in the future"):
StatiqueFactory.build(date_maj=tomorrow)


def test_statique_model_defaults():
"""Test the Statique model defaut values (when not provided)."""
example = StatiqueFactory.build()
statique = Statique(
**example.model_dump(
exclude={
"nom_amenageur",
"siren_amenageur",
"contact_amenageur",
"nom_operateur",
"telephone_operateur",
}
)
)

assert statique.nom_amenageur == "NA"
assert statique.siren_amenageur == "123456789"
assert statique.contact_amenageur == "[email protected]"
assert statique.nom_operateur == "NA"
assert statique.telephone_operateur == "+33.123456789"

0 comments on commit a6a8949

Please sign in to comment.