karrio-dicom 2025.5rc1__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.dicom.mapper import Mapper
2
+ from karrio.mappers.dicom.proxy import Proxy
3
+ from karrio.mappers.dicom.settings import Settings
@@ -0,0 +1,107 @@
1
+ from typing import List, Tuple
2
+ from karrio.core.utils.serializable import Serializable, Deserializable
3
+ from karrio.api.mapper import Mapper as BaseMapper
4
+ from karrio.core.models import (
5
+ # ShipmentCancelRequest,
6
+ # PickupUpdateRequest,
7
+ # PickupCancelRequest,
8
+ # ShipmentRequest,
9
+ TrackingRequest,
10
+ # PickupRequest,
11
+ # RateRequest,
12
+ #
13
+ # ConfirmationDetails,
14
+ TrackingDetails,
15
+ # ShipmentDetails,
16
+ # PickupDetails,
17
+ # RateDetails,
18
+ Message,
19
+ )
20
+ from karrio.providers.dicom import (
21
+ # parse_shipment_cancel_response,
22
+ # parse_pickup_update_response,
23
+ # parse_pickup_cancel_response,
24
+ # parse_shipment_response,
25
+ parse_tracking_response,
26
+ # parse_pickup_response,
27
+ # parse_rate_response,
28
+ #
29
+ # shipment_cancel_request,
30
+ # pickup_update_request,
31
+ # pickup_cancel_request,
32
+ tracking_request,
33
+ # shipment_request,
34
+ # pickup_request,
35
+ # rate_request,
36
+ )
37
+ from karrio.mappers.dicom.settings import Settings
38
+
39
+
40
+ class Mapper(BaseMapper):
41
+ settings: Settings
42
+
43
+ # def create_rate_request(
44
+ # self, payload: RateRequest
45
+ # ) -> Serializable:
46
+ # return rate_request(payload, self.settings)
47
+
48
+ def create_tracking_request(self, payload: TrackingRequest) -> Serializable:
49
+ return tracking_request(payload, self.settings)
50
+
51
+ # def create_shipment_request(
52
+ # self, payload: ShipmentRequest
53
+ # ) -> Serializable:
54
+ # return shipment_request(payload, self.settings)
55
+ #
56
+ # def create_pickup_request(
57
+ # self, payload: PickupRequest
58
+ # ) -> Serializable:
59
+ # return pickup_request(payload, self.settings)
60
+ #
61
+ # def create_pickup_update_request(
62
+ # self, payload: PickupUpdateRequest
63
+ # ) -> Serializable:
64
+ # return pickup_update_request(payload, self.settings)
65
+ #
66
+ # def create_cancel_pickup_request(
67
+ # self, payload: PickupCancelRequest
68
+ # ) -> Serializable:
69
+ # return pickup_cancel_request(payload, self.settings)
70
+ #
71
+ # def create_cancel_shipment_request(self, payload: ShipmentCancelRequest) -> Serializable:
72
+ # return shipment_cancel_request(payload, self.settings)
73
+
74
+ # def parse_cancel_pickup_response(
75
+ # self, response: Deserializable
76
+ # ) -> Tuple[ConfirmationDetails, List[Message]]:
77
+ # return parse_pickup_cancel_response(response, self.settings)
78
+ #
79
+ # def parse_cancel_shipment_response(
80
+ # self, response: Deserializable
81
+ # ) -> Tuple[ConfirmationDetails, List[Message]]:
82
+ # return parse_shipment_cancel_response(response, self.settings)
83
+ #
84
+ # def parse_pickup_response(
85
+ # self, response: Deserializable
86
+ # ) -> Tuple[PickupDetails, List[Message]]:
87
+ # return parse_pickup_response(response, self.settings)
88
+ #
89
+ # def parse_pickup_update_response(
90
+ # self, response: Deserializable
91
+ # ) -> Tuple[PickupDetails, List[Message]]:
92
+ # return parse_pickup_update_response(response, self.settings)
93
+ #
94
+ # def parse_rate_response(
95
+ # self, response: Deserializable
96
+ # ) -> Tuple[List[RateDetails], List[Message]]:
97
+ # return parse_rate_response(response, self.settings)
98
+ #
99
+ # def parse_shipment_response(
100
+ # self, response: Deserializable
101
+ # ) -> Tuple[ShipmentDetails, List[Message]]:
102
+ # return parse_shipment_response(response, self.settings)
103
+
104
+ def parse_tracking_response(
105
+ self, response: Deserializable
106
+ ) -> Tuple[List[TrackingDetails], List[Message]]:
107
+ return parse_tracking_response(response, self.settings)
@@ -0,0 +1,31 @@
1
+ from typing import List
2
+ from karrio.core.utils import (
3
+ Serializable,
4
+ Deserializable,
5
+ request as http,
6
+ exec_async,
7
+ DP,
8
+ )
9
+ from karrio.api.proxy import Proxy as BaseProxy
10
+ from karrio.mappers.dicom.settings import Settings
11
+
12
+
13
+ class Proxy(BaseProxy):
14
+ settings: Settings
15
+
16
+ def get_tracking(self, request: Serializable) -> Deserializable:
17
+ def _get_tracking(tracking_number: str):
18
+ return http(
19
+ url=f"{self.settings.server_url}/v1/tracking/{tracking_number}",
20
+ trace=self.trace_as("json"),
21
+ method="GET",
22
+ headers={
23
+ "Accept": "application/json",
24
+ "Authorization": f"Basic {self.settings.authorization}",
25
+ },
26
+ )
27
+
28
+ responses: List[dict] = exec_async(_get_tracking, request.serialize())
29
+ return Deserializable(
30
+ responses, lambda res: [DP.to_dict(r) for r in res if any(r.strip())]
31
+ )
@@ -0,0 +1,22 @@
1
+ """Karrio Dicom client settings."""
2
+
3
+ import attr
4
+ from karrio.providers.dicom.utils import Settings as BaseSettings
5
+
6
+
7
+ @attr.s(auto_attribs=True)
8
+ class Settings(BaseSettings):
9
+ """Dicom connection settings."""
10
+
11
+ # Carrier specific properties
12
+ username: str
13
+ password: str
14
+ billing_account: str = None
15
+
16
+ # Base properties
17
+ id: str = None
18
+ test_mode: bool = False
19
+ carrier_id: str = "dicom"
20
+ account_country_code: str = None
21
+ metadata: dict = {}
22
+ config: dict = {}
@@ -0,0 +1,16 @@
1
+ import karrio.core.metadata as metadata
2
+ import karrio.mappers.dicom as mappers
3
+
4
+
5
+ METADATA = metadata.PluginMetadata(
6
+ status="beta",
7
+ id="dicom",
8
+ label="Dicom",
9
+
10
+ # Integrations
11
+ Mapper=mappers.Mapper,
12
+ Proxy=mappers.Proxy,
13
+ Settings=mappers.Settings,
14
+
15
+ # Data Units
16
+ )
@@ -0,0 +1,20 @@
1
+ # from karrio.providers.dicom.utils import Settings
2
+ # from karrio.providers.dicom.rate import parse_rate_response, rate_request
3
+ # from karrio.providers.dicom.shipment import (
4
+ # parse_shipment_cancel_response,
5
+ # parse_shipment_response,
6
+ # shipment_cancel_request,
7
+ # shipment_request,
8
+ # )
9
+ # from karrio.providers.dicom.pickup import (
10
+ # parse_pickup_cancel_response,
11
+ # parse_pickup_update_response,
12
+ # parse_pickup_response,
13
+ # pickup_update_request,
14
+ # pickup_cancel_request,
15
+ # pickup_request,
16
+ # )
17
+ from karrio.providers.dicom.tracking import (
18
+ parse_tracking_response,
19
+ tracking_request,
20
+ )
@@ -0,0 +1,19 @@
1
+ from typing import List
2
+ from karrio.providers.sendle import Settings
3
+ from karrio.core.models import Message
4
+
5
+
6
+ def parse_error_response(response: List[dict], settings: Settings) -> List[Message]:
7
+ return [_extract_error(e, settings) for e in response]
8
+
9
+
10
+ def _extract_error(carrier_error: dict, settings: Settings) -> Message:
11
+ return Message(
12
+ # context info
13
+ carrier_name=settings.carrier_name,
14
+ carrier_id=settings.carrier_id,
15
+
16
+ # carrier error info
17
+ code=carrier_error.get('Code'),
18
+ message=carrier_error.get('Message')
19
+ )
@@ -0,0 +1,3 @@
1
+ from karrio.providers.dicom.pickup.create import parse_pickup_response, pickup_request
2
+ from karrio.providers.dicom.pickup.update import parse_pickup_update_response, pickup_update_request
3
+ from karrio.providers.dicom.pickup.cancel import parse_pickup_cancel_response, pickup_cancel_request
@@ -0,0 +1,33 @@
1
+ from typing import Tuple, List
2
+ from karrio.core.utils import Serializable
3
+ from karrio.core.models import PickupCancelRequest, ConfirmationDetails, Message
4
+
5
+ from karrio.providers.dicom.error import parse_error_response
6
+ from karrio.providers.dicom.utils import Settings
7
+ import karrio.lib as lib
8
+
9
+
10
+ def parse_pickup_cancel_response(
11
+ _response: lib.Deserializable[dict],
12
+ settings: Settings,
13
+ ) -> Tuple[ConfirmationDetails, List[Message]]:
14
+ response = _response.deserialize()
15
+ errors = parse_error_response(response, settings)
16
+ details = (
17
+ ConfirmationDetails(
18
+ carrier_id=settings.carrier_id,
19
+ carrier_name=settings.carrier_name,
20
+ success=True,
21
+ operation="Pickup Cancel",
22
+ )
23
+ if not any(errors)
24
+ else None
25
+ )
26
+
27
+ return details, errors
28
+
29
+
30
+ def pickup_cancel_request(payload: PickupCancelRequest, _) -> Serializable:
31
+ request = payload.confirmation_number
32
+
33
+ return Serializable(request)
@@ -0,0 +1,102 @@
1
+ import urllib.parse
2
+ from typing import Tuple, List
3
+ from functools import partial
4
+ from karrio.schemas.dicom.pickups import (
5
+ PickupRequest as DicomPickupRequest,
6
+ Sender,
7
+ Contact,
8
+ Pickup,
9
+ )
10
+ from karrio.core.utils import Serializable, Pipeline, Job, DP, SF
11
+ from karrio.core.models import PickupRequest, PickupDetails, Message
12
+
13
+ from karrio.providers.dicom.error import parse_error_response
14
+ from karrio.providers.dicom.utils import Settings
15
+ import karrio.lib as lib
16
+
17
+
18
+ def parse_pickup_response(
19
+ _response: lib.Deserializable[dict], settings: Settings
20
+ ) -> Tuple[PickupDetails, List[Message]]:
21
+ response = _response.deserialize()
22
+ errors = parse_error_response(response, settings)
23
+ pickup = next(
24
+ (DP.to_object(Pickup, pickup) for pickup in response.get("pickups", [])), None
25
+ )
26
+ details = _extract_details(pickup, settings) if pickup is not None else None
27
+
28
+ return details, errors
29
+
30
+
31
+ def _extract_details(pickup: Pickup, settings: Settings) -> PickupDetails:
32
+ return PickupDetails(
33
+ carrier_id=settings.carrier_id,
34
+ carrier_name=settings.carrier_name,
35
+ confirmation_number=str(pickup.id),
36
+ closing_time=pickup.officeClose,
37
+ pickup_date=pickup.date,
38
+ ready_time=pickup.ready,
39
+ )
40
+
41
+
42
+ def pickup_request(payload: PickupRequest, settings: Settings) -> Serializable:
43
+ request: Pipeline = Pipeline(
44
+ create_pickup=lambda *_: _create_pickup(payload),
45
+ retrieve_pickup=partial(_retrieve_pickup, payload=payload, settings=settings),
46
+ )
47
+
48
+ return Serializable(request)
49
+
50
+
51
+ def _create_pickup(payload: PickupRequest) -> Job:
52
+ request = DicomPickupRequest(
53
+ date=payload.pickup_date,
54
+ ready=payload.ready_time,
55
+ category=payload.options.get("category", "Parcel"),
56
+ officeClose=payload.closing_time,
57
+ sender=Sender(
58
+ city=payload.address.city,
59
+ provinceCode=payload.address.state_code,
60
+ postalCode=payload.address.postal_code,
61
+ countryCode=payload.address.country_code,
62
+ customerName=payload.address.company_name,
63
+ streetNumber=payload.address.street_number,
64
+ contact=Contact(
65
+ fullName=payload.address.person_name,
66
+ email=payload.address.email,
67
+ telephone=payload.address.phone_number,
68
+ ),
69
+ ),
70
+ location=payload.options.get("dicom_location", "OT"),
71
+ otherLocation=payload.package_location,
72
+ )
73
+
74
+ return Job(id="create_pickup", data=Serializable(request, DP.to_dict))
75
+
76
+
77
+ def _retrieve_pickup(
78
+ creation_response: str, payload: PickupRequest, settings: Settings
79
+ ) -> Job:
80
+ errors = parse_error_response(DP.to_dict(creation_response), settings)
81
+ data = (
82
+ Serializable(
83
+ dict(
84
+ category=payload.options.get("category", "Parcel"),
85
+ pickupDate=payload.pickup_date,
86
+ streetNumber=SF.concat_str(
87
+ payload.address.address_line1,
88
+ payload.address.address_line2,
89
+ join=True,
90
+ ),
91
+ postalCode=payload.address.postal_code,
92
+ offset=10,
93
+ ),
94
+ urllib.parse.urlencode,
95
+ )
96
+ if not any(errors)
97
+ else None
98
+ )
99
+
100
+ return Job(
101
+ id="retrieve_pickup", data=data, fallback=("{}" if data is None else None)
102
+ )
@@ -0,0 +1,57 @@
1
+ from typing import Tuple, List, cast
2
+ from functools import partial
3
+ from karrio.core.utils import Serializable, Pipeline, Job
4
+ from karrio.core.models import (
5
+ PickupCancelRequest,
6
+ PickupUpdateRequest,
7
+ PickupRequest,
8
+ PickupDetails,
9
+ Message,
10
+ )
11
+
12
+ from karrio.providers.dicom.pickup.create import (
13
+ _create_pickup as _create_pickup_job,
14
+ parse_pickup_response,
15
+ _retrieve_pickup,
16
+ )
17
+ from karrio.providers.dicom.pickup.cancel import pickup_cancel_request
18
+ from karrio.providers.dicom.error import parse_error_response
19
+ from karrio.providers.dicom.utils import Settings
20
+ import karrio.lib as lib
21
+
22
+
23
+ def parse_pickup_update_response(
24
+ _response: lib.Deserializable[dict], settings: Settings
25
+ ) -> Tuple[PickupDetails, List[Message]]:
26
+ response = _response.deserialize()
27
+ return parse_pickup_response(response, settings)
28
+
29
+
30
+ def pickup_update_request(
31
+ payload: PickupUpdateRequest, settings: Settings
32
+ ) -> Serializable:
33
+ request: Pipeline = Pipeline(
34
+ delete_pickup=lambda *_: _delete_pickup(payload, settings),
35
+ create_pickup=partial(_create_pickup_job, payload=payload, settings=settings),
36
+ retrieve_pickup=partial(_retrieve_pickup, payload=payload, settings=settings),
37
+ )
38
+
39
+ return Serializable(request)
40
+
41
+
42
+ def _delete_pickup(payload: PickupUpdateRequest, settings: Settings) -> Job:
43
+ data = pickup_cancel_request(
44
+ settings=settings, payload=cast(PickupCancelRequest, payload)
45
+ )
46
+
47
+ return Job(id="delete_pickup", data=data)
48
+
49
+
50
+ def _create_pickup(deletion_response: str, payload: PickupUpdateRequest) -> Job:
51
+ errors = parse_error_response(deletion_response)
52
+ create_job: Job = (
53
+ _create_pickup_job(cast(PickupRequest, payload)) if not any(errors) else None
54
+ )
55
+ data = create_job.data if create_job is not None else None
56
+
57
+ return Job(id="create_pickup", data=data, fallback=("{}" if data is None else None))
@@ -0,0 +1,127 @@
1
+ from karrio.schemas.dicom.rates import (
2
+ RateRequest as DicomRateRequest,
3
+ Address,
4
+ Parcel,
5
+ Surcharge,
6
+ Rate,
7
+ RateResponse,
8
+ )
9
+
10
+ import typing
11
+ import karrio.lib as lib
12
+ import karrio.core.models as models
13
+ import karrio.providers.dicom.error as provider_error
14
+ import karrio.providers.dicom.units as provider_units
15
+ import karrio.providers.dicom.utils as provider_utils
16
+
17
+
18
+ def parse_rate_response(
19
+ _response: lib.Deserializable[dict], settings: provider_utils.Settings
20
+ ) -> typing.Tuple[typing.List[models.RateDetails], typing.List[models.Message]]:
21
+ response = _response.deserialize()
22
+ errors = provider_error.parse_error_response(response, settings)
23
+ rate_response = (
24
+ lib.to_object(RateResponse, response) if "rates" in response else RateResponse()
25
+ )
26
+ details = [
27
+ _extract_details(rate, rate_response, settings)
28
+ for rate in (rate_response.rates or [])
29
+ ]
30
+
31
+ return details, errors
32
+
33
+
34
+ def _extract_details(
35
+ rate: Rate, response: RateResponse, settings: provider_utils.Settings
36
+ ) -> models.RateDetails:
37
+ charges = [
38
+ ("Base Charge", rate.basicCharge),
39
+ ("Discount", rate.discountAmount),
40
+ ("Taxes", rate.taxes),
41
+ *((charge.name, charge.amount) for charge in rate.surcharges),
42
+ ]
43
+
44
+ return models.RateDetails(
45
+ carrier_id=settings.carrier_id,
46
+ carrier_name=settings.carrier_name,
47
+ currency="CAD",
48
+ transit_days=response.delay,
49
+ service=provider_units.Service(rate.rateType),
50
+ total_charge=lib.to_decimal(rate.total),
51
+ extra_charges=[
52
+ provider_units.ChargeDetails(
53
+ currency="CAD",
54
+ name=name,
55
+ amount=lib.to_decimal(charge),
56
+ )
57
+ for name, charge in charges
58
+ if charge
59
+ ],
60
+ meta=dict(accountType=rate.accountType),
61
+ )
62
+
63
+
64
+ def rate_request(
65
+ payload: models.RateRequest, settings: provider_utils.Settings
66
+ ) -> lib.Serializable:
67
+ packages = lib.to_packages(payload.parcels)
68
+ service = (
69
+ provider_units.Services(payload.services, provider_units.Service).first
70
+ or provider_units.Service.dicom_ground_delivery.value
71
+ ).value
72
+ options = lib.to_shipping_options(
73
+ payload.options,
74
+ package_options=packages.options,
75
+ initializer=provider_units.shipping_options_initializer,
76
+ )
77
+
78
+ request = DicomRateRequest(
79
+ category="Parcel",
80
+ paymentType=provider_units.PaymentType.prepaid.value,
81
+ deliveryType=service,
82
+ unitOfMeasurement=provider_units.UnitOfMeasurement.KC.value,
83
+ sender=Address(
84
+ postalCode=payload.shipper.postal_code,
85
+ provinceCode=payload.shipper.state_code,
86
+ countryCode=payload.shipper.country_code,
87
+ name=(payload.shipper.company_name or payload.shipper.person_name),
88
+ ),
89
+ consignee=Address(
90
+ postalCode=payload.recipient.postal_code,
91
+ provinceCode=payload.recipient.state_code,
92
+ countryCode=payload.recipient.country_code,
93
+ name=(payload.recipient.company_name or payload.recipient.person_name),
94
+ ),
95
+ parcels=[
96
+ Parcel(
97
+ quantity=1,
98
+ parcelType=provider_units.ParcelType[
99
+ package.packaging_type or "dicom_box"
100
+ ].value,
101
+ id=None,
102
+ weight=package.weight.KG,
103
+ length=package.height.CM,
104
+ depth=package.length.CM,
105
+ width=package.width.CM,
106
+ note=None,
107
+ status=None,
108
+ FCA_Class=None,
109
+ hazmat=None,
110
+ requestReturnLabel=None,
111
+ returnWaybill=None,
112
+ )
113
+ for package in packages
114
+ ],
115
+ billing=settings.billing_account,
116
+ promoCodes=None,
117
+ surcharges=[
118
+ Surcharge(
119
+ type=option.code,
120
+ value=lib.to_money(option.state),
121
+ )
122
+ for _, option in options.items()
123
+ ],
124
+ appointment=None,
125
+ )
126
+
127
+ return lib.Serializable(request, lib.to_dict)
@@ -0,0 +1,2 @@
1
+ from karrio.providers.dicom.shipment.create import parse_shipment_response, shipment_request
2
+ from karrio.providers.dicom.shipment.cancel import parse_shipment_cancel_response, shipment_cancel_request
@@ -0,0 +1,32 @@
1
+ from typing import Tuple, List
2
+ from karrio.core.utils import Serializable
3
+ from karrio.core.models import ShipmentCancelRequest, ConfirmationDetails, Message
4
+
5
+ from karrio.providers.dicom.error import parse_error_response
6
+ from karrio.providers.dicom.utils import Settings
7
+ import karrio.lib as lib
8
+
9
+
10
+ def parse_shipment_cancel_response(
11
+ response: lib.Deserializable[dict],
12
+ settings: Settings,
13
+ ) -> Tuple[ConfirmationDetails, List[Message]]:
14
+ errors = parse_error_response(response, settings)
15
+ details = (
16
+ ConfirmationDetails(
17
+ carrier_id=settings.carrier_id,
18
+ carrier_name=settings.carrier_name,
19
+ operation="Shipment Cancel",
20
+ success=True,
21
+ )
22
+ if not any(errors)
23
+ else None
24
+ )
25
+
26
+ return details, errors
27
+
28
+
29
+ def shipment_cancel_request(payload: ShipmentCancelRequest, _) -> Serializable:
30
+ request = payload.shipment_identifier
31
+
32
+ return Serializable(request)