karrio-aramex 2025.5rc34__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,3 @@
1
+ from karrio.mappers.aramex.mapper import Mapper
2
+ from karrio.mappers.aramex.proxy import Proxy
3
+ from karrio.mappers.aramex.settings import Settings
@@ -0,0 +1,20 @@
1
+ import typing
2
+ import karrio.lib as lib
3
+ import karrio.api.mapper as mapper
4
+ import karrio.core.models as models
5
+ import karrio.providers.aramex as provider
6
+ import karrio.mappers.aramex.settings as settings
7
+
8
+
9
+ class Mapper(mapper.Mapper):
10
+ settings: settings.Settings
11
+
12
+ def create_tracking_request(
13
+ self, payload: models.TrackingRequest
14
+ ) -> lib.Serializable:
15
+ return provider.tracking_request(payload, self.settings)
16
+
17
+ def parse_tracking_response(
18
+ self, response: lib.Deserializable
19
+ ) -> typing.Tuple[typing.List[models.TrackingDetails], typing.List[models.Message]]:
20
+ return provider.parse_tracking_response(response, self.settings)
@@ -0,0 +1,22 @@
1
+ from karrio.core.utils import XP, request as http, Serializable, Deserializable
2
+ from karrio.api.proxy import Proxy as BaseProxy
3
+ from karrio.mappers.aramex.settings import Settings
4
+
5
+
6
+ class Proxy(BaseProxy):
7
+ settings: Settings
8
+
9
+ """ Proxy Methods """
10
+
11
+ def get_tracking(self, request: Serializable) -> Deserializable:
12
+ response = http(
13
+ url=f"{self.settings.server_url}/ShippingAPI.V2/Tracking/Service_1_0.svc",
14
+ data=request.serialize(),
15
+ trace=self.trace_as("xml"),
16
+ method="POST",
17
+ headers={
18
+ "Content-Type": "text/xml; charset=utf-8",
19
+ "soapAction": "http://ws.aramex.net/ShippingAPI/v1/Service_1_0/TrackShipments",
20
+ },
21
+ )
22
+ return Deserializable(response, XP.to_xml)
@@ -0,0 +1,24 @@
1
+ """Karrio Aramex settings."""
2
+
3
+ import attr
4
+ from karrio.providers.aramex.utils import Settings as BaseSettings
5
+
6
+
7
+ @attr.s(auto_attribs=True)
8
+ class Settings(BaseSettings):
9
+ """Aramex connection settings."""
10
+
11
+ # Carrier specific properties
12
+ username: str
13
+ password: str
14
+ account_pin: str
15
+ account_entity: str
16
+ account_number: str
17
+ account_country_code: str
18
+
19
+ # Base properties
20
+ id: str = None
21
+ test_mode: bool = False
22
+ carrier_id: str = "aramex"
23
+ metadata: dict = {}
24
+ config: dict = {}
@@ -0,0 +1,24 @@
1
+ import karrio.core.metadata as metadata
2
+ import karrio.mappers.aramex as mappers
3
+ # import karrio.providers.aramex.units as units
4
+
5
+
6
+ METADATA = metadata.PluginMetadata(
7
+ status="beta",
8
+ id="aramex",
9
+ label="Aramex",
10
+ # Integrations
11
+ Mapper=mappers.Mapper,
12
+ Proxy=mappers.Proxy,
13
+ Settings=mappers.Settings,
14
+ # Data Units
15
+ # options=units.OptionCode,
16
+ # package_presets=units.PackagePresets,
17
+ # packaging_types=units.PackagingType,
18
+ # services=units.Serives,
19
+ has_intl_accounts=True,
20
+ # New fields
21
+ website="https://www.aramex.com/ae/en",
22
+ documentation="https://www.aramex.com/us/en/developers-solution-center/aramex-apis",
23
+ description="Aramex is the leading global logistics provider.",
24
+ )
@@ -0,0 +1,24 @@
1
+ from karrio.providers.aramex.utils import Settings
2
+ # from karrio.providers.aramex.rate import parse_rate_response, rate_request
3
+ # from karrio.providers.aramex.address import (
4
+ # parse_address_validation_response,
5
+ # address_validation_request
6
+ # )
7
+ # from karrio.providers.aramex.shipment import (
8
+ # parse_shipment_cancel_response,
9
+ # parse_shipment_response,
10
+ # shipment_cancel_request,
11
+ # shipment_request,
12
+ # )
13
+ # from karrio.providers.aramex.pickup import (
14
+ # parse_pickup_cancel_response,
15
+ # parse_pickup_update_response,
16
+ # parse_pickup_response,
17
+ # pickup_update_request,
18
+ # pickup_cancel_request,
19
+ # pickup_request,
20
+ # )
21
+ from karrio.providers.aramex.tracking import (
22
+ parse_tracking_response,
23
+ tracking_request,
24
+ )
@@ -0,0 +1,23 @@
1
+ from typing import List
2
+ from karrio.schemas.aramex.tracking import Notification
3
+ from karrio.core.utils import Element, XP
4
+ from karrio.core.models import Message
5
+ from karrio.providers.aramex import Settings
6
+
7
+
8
+ def parse_error_response(response: Element, settings: Settings) -> List[Message]:
9
+ errors = response.xpath(".//*[local-name() = $name]", name="Notification")
10
+ return [_extract_error(node, settings) for node in errors]
11
+
12
+
13
+ def _extract_error(node: Element, settings: Settings) -> Message:
14
+ notification = XP.to_object(Notification, node)
15
+
16
+ return Message(
17
+ # context info
18
+ carrier_name=settings.carrier_name,
19
+ carrier_id=settings.carrier_id,
20
+ # carrier error info
21
+ code=notification.Code,
22
+ message=notification.Message,
23
+ )
@@ -0,0 +1,103 @@
1
+ from typing import List, Tuple
2
+ from functools import partial
3
+ from karrio.schemas.aramex.array_of_string import ArrayOfstring
4
+ from karrio.schemas.aramex.tracking import (
5
+ ShipmentTrackingRequest,
6
+ ClientInfo,
7
+ TrackingResult,
8
+ )
9
+ from karrio.core.utils import (
10
+ create_envelope,
11
+ Element,
12
+ Serializable,
13
+ XP,
14
+ DF,
15
+ )
16
+ from karrio.core.models import (
17
+ TrackingEvent,
18
+ TrackingDetails,
19
+ TrackingRequest,
20
+ Message,
21
+ )
22
+ from karrio.providers.aramex.utils import Settings
23
+ from karrio.providers.aramex.error import parse_error_response
24
+ import karrio.lib as lib
25
+
26
+
27
+ def parse_tracking_response(
28
+ _response: lib.Deserializable[lib.Element],
29
+ settings: Settings,
30
+ ) -> Tuple[List[TrackingDetails], List[Message]]:
31
+ response = _response.deserialize()
32
+ non_existents = next(
33
+ (
34
+ XP.to_object(ArrayOfstring, n)
35
+ for n in lib.find_element("NonExistingWaybills", response)
36
+ ),
37
+ ArrayOfstring(),
38
+ )
39
+ results = lib.find_element("TrackingResult", response)
40
+ tracking_details = [_extract_detail(node, settings) for node in results]
41
+ errors = _extract_errors(non_existents, settings) + parse_error_response(
42
+ response, settings
43
+ )
44
+
45
+ return tracking_details, errors
46
+
47
+
48
+ def _extract_errors(non_existents: ArrayOfstring, settings: Settings) -> List[Message]:
49
+ return [
50
+ Message(
51
+ carrier_name=settings.carrier_name,
52
+ carrier_id=settings.carrier_id,
53
+ message=f'Waybill "{waybill}" Not Found',
54
+ )
55
+ for waybill in non_existents.string
56
+ ]
57
+
58
+
59
+ def _extract_detail(node: Element, settings: Settings) -> TrackingDetails:
60
+ detail = XP.to_object(TrackingResult, node)
61
+
62
+ return TrackingDetails(
63
+ carrier_name=settings.carrier_name,
64
+ carrier_id=settings.carrier_id,
65
+ tracking_number=detail.WaybillNumber,
66
+ events=[
67
+ TrackingEvent(
68
+ date=DF.date(detail.UpdateDateTime, "%Y-%m-%dT%H:%M:%S"),
69
+ description=detail.UpdateDescription,
70
+ location=detail.UpdateLocation,
71
+ code=detail.UpdateCode,
72
+ time=DF.ftime(detail.UpdateDateTime, "%Y-%m-%dT%H:%M:%S"),
73
+ )
74
+ ],
75
+ )
76
+
77
+
78
+ def tracking_request(payload: TrackingRequest, settings: Settings) -> Serializable:
79
+ request = create_envelope(
80
+ body_content=ShipmentTrackingRequest(
81
+ ClientInfo=ClientInfo(
82
+ UserName=settings.username,
83
+ Password=settings.password,
84
+ Version="1.0",
85
+ AccountNumber=settings.account_number,
86
+ AccountPin=settings.account_pin,
87
+ AccountEntity=settings.account_entity,
88
+ AccountCountryCode=settings.account_country_code,
89
+ ),
90
+ Transaction=None,
91
+ Shipments=ArrayOfstring(string=payload.tracking_numbers),
92
+ GetLastTrackingUpdateOnly=False,
93
+ )
94
+ )
95
+
96
+ return Serializable(
97
+ request,
98
+ partial(
99
+ settings.standard_request_serializer,
100
+ extra_namespace='xmlns:arr="http://schemas.microsoft.com/2003/10/Serialization/Arrays',
101
+ special_prefixes=dict(string="arr"),
102
+ ),
103
+ )
@@ -0,0 +1,46 @@
1
+ """ Aramex Native Types """
2
+
3
+ # import karrio.lib as lib
4
+ # from karrio.core.utils import Enum, Flag
5
+ #
6
+ # PRESET_DEFAULTS = dict(dimension_unit="CM", weight_unit="KG")
7
+ #
8
+ #
9
+ # class PackagePresets(lib.Enum):
10
+ # # carrier_envelope = PackagePreset(
11
+ # # **dict(weight=0.5, width=35.0, height=27.5, length=1.0, packaging_type="envelope"),
12
+ # # **PRESET_DEFAULTS
13
+ # # )
14
+ # # carrier_box = PackagePreset(
15
+ # # **dict(weight=0.5, width=35.0, height=27.5, length=1.0, packaging_type="medium_box"),
16
+ # # **PRESET_DEFAULTS
17
+ # # )
18
+ # pass
19
+
20
+
21
+ # class PackageType(lib.StrEnum):
22
+ # carrier_envelope = "ENVELOPE CODE"
23
+ # carrier_box = "BOX CODE"
24
+ # carrier_your_packaging = "CUSTOM PACKAGING CODE"
25
+ #
26
+ # """ Unified Packaging type mapping """
27
+ # envelope = carrier_envelope
28
+ # pak = carrier_envelope
29
+ # tube = carrier_your_packaging
30
+ # pallet = carrier_your_packaging
31
+ # small_box = carrier_box
32
+ # medium_box = carrier_box
33
+ # large_box = carrier_box
34
+ # your_packaging = carrier_your_packaging
35
+ #
36
+ #
37
+ # class Service(Enum):
38
+ # carrier_standard = "STANDARD CODE"
39
+ # carrier_premium = "PREMIUM CODE"
40
+ # carrier_overnight = "OVERNIGHT CODE"
41
+ #
42
+ #
43
+ # class Option(lib.Enum):
44
+ # carrier_signature = "SIGNATURE CODE"
45
+ # carrier_saturday_delivery = "SATURDAY DELIVERY CODE"
46
+ # carrier_dry_ice = "DRY ICE CODE"
@@ -0,0 +1,48 @@
1
+ from karrio.core.utils import XP, apply_namespaceprefix, Envelope, Header
2
+ from karrio.core import Settings as BaseSettings
3
+
4
+
5
+ class Settings(BaseSettings):
6
+ """Aramex connection settings."""
7
+
8
+ # Carrier specific properties
9
+ username: str
10
+ password: str
11
+ account_pin: str
12
+ account_entity: str
13
+ account_number: str
14
+ account_country_code: str
15
+
16
+ id: str = None
17
+ metadata: dict = {}
18
+
19
+ @property
20
+ def carrier_name(self):
21
+ return "aramex"
22
+
23
+ @property
24
+ def server_url(self):
25
+ return "http://ws.dev.aramex.net" if self.test_mode else "http://ws.aramex.net"
26
+
27
+ @staticmethod
28
+ def standard_request_serializer(
29
+ envelope: Envelope,
30
+ version: str = "v1",
31
+ extra_namespace: str = "",
32
+ special_prefixes: dict = None,
33
+ ) -> str:
34
+
35
+ namespacedef_ = (
36
+ f'xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" '
37
+ f'xmlns:{version}="http://ws.aramex.net/ShippingAPI/{version}/" '
38
+ f"{extra_namespace}"
39
+ )
40
+ envelope.ns_prefix_ = "soap"
41
+ envelope.Header = Header()
42
+ envelope.Body.ns_prefix_ = envelope.ns_prefix_
43
+ envelope.Header.ns_prefix_ = envelope.ns_prefix_
44
+
45
+ for node in envelope.Body.anytypeobjs_ + envelope.Header.anytypeobjs_:
46
+ apply_namespaceprefix(node, version, special_prefixes)
47
+
48
+ return XP.export(envelope, namespacedef_=namespacedef_)
File without changes