From 46b08973dc32e298d99e8b0df768ea8d5183ebab Mon Sep 17 00:00:00 2001 From: Florian Mounier Date: Fri, 13 Sep 2024 10:23:29 +0200 Subject: [PATCH] Add find_pickup_site for mondial relay and common schema --- roulier.py | 2 - roulier/carriersv2/helpers.py | 15 +++ roulier/carriersv2/mondialrelay/constants.py | 13 +++ roulier/carriersv2/mondialrelay/schema.py | 103 +++++++++++++++++- .../carriersv2/mondialrelay/transporter.py | 15 ++- roulier/carriersv2/schema.py | 28 +++++ 6 files changed, 169 insertions(+), 7 deletions(-) delete mode 100755 roulier.py diff --git a/roulier.py b/roulier.py deleted file mode 100755 index 2c59438..0000000 --- a/roulier.py +++ /dev/null @@ -1,2 +0,0 @@ -if __name__ == "__main__": - print("from Cli") diff --git a/roulier/carriersv2/helpers.py b/roulier/carriersv2/helpers.py index 1cb45ee..890c40b 100644 --- a/roulier/carriersv2/helpers.py +++ b/roulier/carriersv2/helpers.py @@ -21,3 +21,18 @@ def clean_empty(data): def none_as_empty(data): return {k: v if v is not None else "" for k, v in data.items()} + + +def merge(*dicts): + # Recursively merge dictionaries + result = {} + for d in dicts: + for k, v in d.items(): + if isinstance(v, dict): + result[k] = merge(result.get(k, {}), v) + else: + if not v and result.get(k): + # Do not override value with empty value + continue + result[k] = v + return result diff --git a/roulier/carriersv2/mondialrelay/constants.py b/roulier/carriersv2/mondialrelay/constants.py index e61de07..26fa2a8 100644 --- a/roulier/carriersv2/mondialrelay/constants.py +++ b/roulier/carriersv2/mondialrelay/constants.py @@ -146,4 +146,17 @@ "Montage", "Assurance", "Instructions", + "Pays", + "NumPointRelais", + "Ville", + "CP", + "Latitude", + "Longitude", + "Taille", + "Poids", + "Action", + "DelaiEnvoi", + "RayonRecherche", + "TypeActivite", + "NombreResultats", ] diff --git a/roulier/carriersv2/mondialrelay/schema.py b/roulier/carriersv2/mondialrelay/schema.py index 8ba8251..bcc7e2f 100644 --- a/roulier/carriersv2/mondialrelay/schema.py +++ b/roulier/carriersv2/mondialrelay/schema.py @@ -3,14 +3,18 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from ..helpers import prefix, clean_empty, REMOVED from ..schema import ( - LabelInput, Address, - LabelOutput, Auth, - Service, + Label, + LabelInput, + LabelOutput, Parcel, ParcelLabel, - Label, + PickupSite, + PickupSiteInput, + PickupSiteOutput, + PickupSiteSearch, + Service, ) from .constants import SORTED_KEYS from hashlib import md5 @@ -154,6 +158,48 @@ def soap(self): ) +class MondialRelayPickupSiteSearch(PickupSiteSearch): + id: int | None = None + weight: float | None = None + action: str | None = None + delay: int | None = None + searchRadius: int | None = None + actionType: str | None = None + resultsCount: int | None = None + + def soap(self): + return clean_empty( + { + "Pays": self.country, + "NumPointRelais": self.id, + "CP": self.zip, + "Latitude": self.lat, + "Longitude": self.lng, + "Poids": self.weight, + "Action": self.action, + "DelaiEnvoi": self.delay, + "RayonRecherche": self.searchRadius, + "TypeActivite": self.actionType, + "NombreResultats": self.resultsCount, + } + ) + + +class MondialRelayPickupSiteInput(PickupSiteInput): + auth: MondialRelayAuth + search: MondialRelayPickupSiteSearch + + def soap(self): + return self.auth.sign( + clean_empty( + { + **self.auth.soap(), + **self.search.soap(), + } + ) + ) + + class MondialRelayLabel(Label): @classmethod def from_soap(cls, result): @@ -181,3 +227,52 @@ class MondialRelayLabelOutput(LabelOutput): @classmethod def from_soap(cls, result): return cls.model_construct(parcels=[MondialRelayParcelLabel.from_soap(result)]) + + +class MondialRelayPickupSite(PickupSite): + actionType: str + hours: dict + url_pic: str + url_map: str + + @classmethod + def from_soap(cls, result): + return cls.model_construct( + id=result["Num"], + name="\n".join( + [part for part in [result["LgAdr1"], result["LgAdr2"]] if part] + ), + street="\n".join( + [part for part in [result["LgAdr3"], result["LgAdr4"]] if part] + ), + zip=result["CP"], + city=result["Ville"], + country=result["Pays"], + lat=result["Latitude"], + lng=result["Longitude"], + actionType=result["TypeActivite"], + hours={ + "monday": result["Horaires_Lundi"], + "tuesday": result["Horaires_Mardi"], + "wednesday": result["Horaires_Mercredi"], + "thursday": result["Horaires_Jeudi"], + "friday": result["Horaires_Vendredi"], + "saturday": result["Horaires_Samedi"], + "sunday": result["Horaires_Dimanche"], + }, + url_pic=result["URL_Photo"], + url_map=result["URL_Plan"], + ) + + +class MondialRelayPickupSiteOutput(PickupSiteOutput): + sites: list[MondialRelayPickupSite] + + @classmethod + def from_soap(cls, result): + return cls.model_construct( + sites=[ + MondialRelayPickupSite.from_soap(site) + for site in result["PointsRelais"]["PointRelais_Details"] + ] + ) diff --git a/roulier/carriersv2/mondialrelay/transporter.py b/roulier/carriersv2/mondialrelay/transporter.py index fd7d0b0..12b66d7 100644 --- a/roulier/carriersv2/mondialrelay/transporter.py +++ b/roulier/carriersv2/mondialrelay/transporter.py @@ -5,7 +5,12 @@ from ..api import Transporter, action from ...exception import CarrierError -from .schema import MondialRelayLabelInput, MondialRelayLabelOutput +from .schema import ( + MondialRelayLabelInput, + MondialRelayLabelOutput, + MondialRelayPickupSiteInput, + MondialRelayPickupSiteOutput, +) from .constants import STATUSES @@ -31,3 +36,11 @@ def get_label(self, input: MondialRelayLabelInput) -> MondialRelayLabelOutput: result = self.client.WSI2_CreationEtiquette(**input.soap()) self.raise_for_status(result) return MondialRelayLabelOutput.from_soap(result) + + @action + def find_pickup_site( + self, input: MondialRelayPickupSiteInput + ) -> MondialRelayPickupSiteOutput: + result = self.client.WSI4_PointRelais_Recherche(**input.soap()) + self.raise_for_status(result) + return MondialRelayPickupSiteOutput.from_soap(result) diff --git a/roulier/carriersv2/schema.py b/roulier/carriersv2/schema.py index d8583ca..beef5ae 100644 --- a/roulier/carriersv2/schema.py +++ b/roulier/carriersv2/schema.py @@ -57,6 +57,19 @@ class LabelInput(BaseModel): to_address: ToAddress +class PickupSiteSearch(BaseModel): + country: str + city: str | None = None + zip: str + lat: float | None = None + lng: float | None = None + + +class PickupSiteInput(BaseModel): + auth: Auth + search: PickupSiteSearch + + class Tracking(BaseModel): number: str url: str | None = None @@ -79,3 +92,18 @@ class ParcelLabel(BaseModel): class LabelOutput(BaseModel): parcels: list[ParcelLabel] annexes: list[Label] = [] + + +class PickupSite(BaseModel): + id: int + name: str + street: str + zip: str + city: str + country: str + lat: str + lng: str + + +class PickupSiteOutput(BaseModel): + sites: list[PickupSite]