Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Set Statiques optional values to a default #327

Merged
merged 2 commits into from
Jan 10, 2025
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
2 changes: 2 additions & 0 deletions src/api/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ and this project adheres to
command
- 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
8 changes: 6 additions & 2 deletions src/api/qualicharge/api/v1/routers/static.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""QualiCharge API v1 statique router."""

import logging
from io import StringIO
from io import BytesIO
from typing import Annotated, List, Optional, cast

import pandas as pd
Expand Down Expand Up @@ -285,8 +285,12 @@ async def bulk(

# Convert statiques to a Pandas DataFrame
df = pd.read_json(
StringIO(f"{'\n'.join([s.model_dump_json() for s in statiques])}"),
BytesIO(
bytes(f"{'\n'.join([s.model_dump_json() for s in statiques])}", "utf-8")
),
lines=True,
orient="records",
engine="pyarrow",
dtype_backend="pyarrow",
)

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
8 changes: 6 additions & 2 deletions src/api/qualicharge/schemas/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import logging
from enum import IntEnum
from io import StringIO
from io import BytesIO
from typing import Generator, List, Optional, Set, Tuple, Type, cast

import pandas as pd
Expand Down Expand Up @@ -215,8 +215,12 @@ def update_statique(
def save_statiques(db_session: Session, statiques: List[Statique]):
"""Save input statiques to database."""
df = pd.read_json(
StringIO(f"{'\n'.join([s.model_dump_json() for s in statiques])}"),
BytesIO(
bytes(f"{'\n'.join([s.model_dump_json() for s in statiques])}", "utf-8")
),
lines=True,
orient="records",
engine="pyarrow",
dtype_backend="pyarrow",
)
importer = StatiqueImporter(df, db_session.connection())
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"
Loading