Skip to content

Commit

Permalink
Merge pull request #21 from PurplShip/ReviewGeneralTypes
Browse files Browse the repository at this point in the history
Review general types
  • Loading branch information
danh91 committed Nov 3, 2018
2 parents c7ee502 + 19a4452 commit cc5101a
Show file tree
Hide file tree
Showing 35 changed files with 1,327 additions and 1,052 deletions.
4 changes: 2 additions & 2 deletions purplship/domain/entities/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ class Quote:
""" manage quotes operations """

@staticmethod
def create(**args) -> quote_request:
def create(**args) -> shipment_request:
""" Create a quote request payload """
return quote_request(**quote_request_type(**args)._asdict())
return shipment_request(**shipment_request_type(**args)._asdict())

class Tracking:
""" manage tracking operations """
Expand Down
26 changes: 1 addition & 25 deletions purplship/domain/entities/datatypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ class party(NamedTuple):
city: str = None
type: str = None
tax_id: str = None
account_number: str = None
person_name: str = None
company_name: str = None
country_name: str = None
Expand Down Expand Up @@ -60,25 +61,6 @@ class invoice_type(NamedTuple):
copies: int = None
extra: Dict = {}

class quote_options(NamedTuple):
packages: List[package_type]
insured_amount: float = None
number_of_packages: int = None
packaging_type: str = None
is_document: bool = False
total_weight: float = None
weight_unit: str = "LB"
dimension_unit: str = "IN"
currency: str = None
paid_by: str = None
declared_value: float = None
duty_paid_by: str = None
payment_country_code: str = None
payment_account_number: str = None
shipper_account_number: str = None
services: List[str] = []
extra: Dict = {}

class shipment_options(NamedTuple):
packages: List[package_type]
insured_amount: float = None
Expand All @@ -97,7 +79,6 @@ class shipment_options(NamedTuple):
duty_payment_account: str = None
payment_country_code: str = None
payment_account_number: str = None
shipper_account_number: str = None

ship_date: str = None
customs: customs_type = None
Expand All @@ -110,11 +91,6 @@ class shipment_options(NamedTuple):
label: label_type = None
extra: Dict = {}

class quote_request(NamedTuple):
shipper: party
recipient: party
shipment: quote_options

class shipment_request(NamedTuple):
shipper: party
recipient: party
Expand Down
41 changes: 2 additions & 39 deletions purplship/domain/entities/factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,34 +17,9 @@ def __new__(cls, no_eei: str = None, aes: str = None, description: str = None, t
)


''' quote options Type definition '''
class quote_options_type(namedtuple("quote_options_type", "packages insured_amount number_of_packages packaging_type is_document currency total_weight weight_unit dimension_unit paid_by declared_value duty_paid_by payment_country_code payment_account_number shipper_account_number services extra")):
def __new__(cls, packages: List[package_type], insured_amount: float = None, number_of_packages: int = None, packaging_type: str = None, is_document: bool = False, currency: str = None, total_weight: float = None, weight_unit: str = "LB", dimension_unit: str = "IN", paid_by: str = None, declared_value: float = None, duty_paid_by: str = None, payment_country_code: str = None, payment_account_number: str = None, shipper_account_number: str = None, services: List[str] = [], extra: Dict = {}):
return super(cls, quote_options_type).__new__(
cls,
[package_type(**p) for p in packages],
insured_amount,
number_of_packages,
packaging_type,
is_document,
currency,
total_weight,
weight_unit,
dimension_unit,
paid_by,
declared_value,
duty_paid_by,
payment_country_code,
payment_account_number,
shipper_account_number,
services,
extra
)


''' shipment options Type definition '''
class shipment_options_type(namedtuple("shipment_options_type", "packages insured_amount number_of_packages packaging_type is_document currency date total_weight weight_unit dimension_unit paid_by duty_paid_by payment_type payment_country_code duty_payment_account declared_value payment_account_number shipper_account_number billing_account_number services customs invoice references commodities label, extra")):
def __new__(cls, packages: List, insured_amount: float = None, number_of_packages: int = None, packaging_type: str = None, is_document: bool = False, currency: str = None, date: str = None, total_weight: float = None, weight_unit: str = "LB", dimension_unit: str = "IN", paid_by: str = None, duty_paid_by: str = None, payment_type: str = None, payment_country_code: str = None, duty_payment_account: str = None, declared_value: float = None, payment_account_number: str = None, shipper_account_number: str = None, billing_account_number: str = None, services: List[str] = [], customs: Dict = None, invoice: dict = None, references: List[str] = [], commodities: List[Dict] = [], label: Dict = None, extra: Dict = {}):
class shipment_options_type(namedtuple("shipment_options_type", "packages insured_amount number_of_packages packaging_type is_document currency date total_weight weight_unit dimension_unit paid_by duty_paid_by payment_type payment_country_code duty_payment_account declared_value payment_account_number billing_account_number services customs invoice references commodities label, extra")):
def __new__(cls, packages: List, insured_amount: float = None, number_of_packages: int = None, packaging_type: str = None, is_document: bool = False, currency: str = None, date: str = None, total_weight: float = None, weight_unit: str = "LB", dimension_unit: str = "IN", paid_by: str = None, duty_paid_by: str = None, payment_type: str = None, payment_country_code: str = None, duty_payment_account: str = None, declared_value: float = None, payment_account_number: str = None, billing_account_number: str = None, services: List[str] = [], customs: Dict = None, invoice: dict = None, references: List[str] = [], commodities: List[Dict] = [], label: Dict = None, extra: Dict = {}):
return super(cls, shipment_options_type).__new__(
cls,
[package_type(**p) for p in packages],
Expand All @@ -64,7 +39,6 @@ def __new__(cls, packages: List, insured_amount: float = None, number_of_package
duty_payment_account,
declared_value,
payment_account_number,
shipper_account_number,
billing_account_number,
services,
customs_details_type(**customs) if customs else None,
Expand All @@ -76,17 +50,6 @@ def __new__(cls, packages: List, insured_amount: float = None, number_of_package
)


''' quote request Type definition '''
class quote_request_type(namedtuple("quote_request_type", "shipper recipient shipment")):
def __new__(cls, shipper: Dict, recipient: Dict, shipment: Dict):
return super(cls, quote_request_type).__new__(
cls,
party(**shipper),
party(**recipient),
quote_options_type(**shipment)
)


''' shipment request Type definition '''
class shipment_request_type(namedtuple("shipment_request_type", "shipper recipient shipment")):
def __new__(cls, shipper: Dict, recipient: Dict, shipment: Dict):
Expand Down
2 changes: 1 addition & 1 deletion purplship/domain/mapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ class Mapper:
""" Unitied API to carrier API Mapper (Interface) """
client: Client

def create_quote_request(self, payload: E.quote_request):
def create_quote_request(self, payload: E.shipment_request):
""" Create carrier specific quote request xml data from payload """
raise Exception("Not Supported")

Expand Down
6 changes: 3 additions & 3 deletions purplship/mappers/caps/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
from .caps_client import CanadaPostClient
from .caps_mapper import CanadaPostMapper
from .caps_proxy import CanadaPostProxy
from purplship.mappers.caps.caps_client import CanadaPostClient
from purplship.mappers.caps.caps_mapper import CanadaPostMapper
from purplship.mappers.caps.caps_proxy import CanadaPostProxy
39 changes: 39 additions & 0 deletions purplship/mappers/caps/caps_mapper/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
from typing import Tuple, List, Union
from purplship.domain.mapper import Mapper
from purplship.domain import entities as E
from pycaps.rating import mailing_scenario
from pycaps.shipment import ShipmentType
from pycaps.ncshipment import NonContractShipmentType
from .partials import (
CanadaPostRateMapperPartial,
CanadaPostTrackMapperPartial,
CanadaPostShipmentMapperPartial
)


class CanadaPostMapper(
Mapper,
CanadaPostRateMapperPartial,
CanadaPostTrackMapperPartial,
CanadaPostShipmentMapperPartial
):

def create_quote_request(self, payload: E.shipment_request) -> mailing_scenario:
return self.create_mailing_scenario(payload)

def create_tracking_request(self, payload: E.tracking_request) -> List[str]:
return self.create_tracking_pins(payload)

def create_shipment_request(self, payload: E.shipment_request) -> Union[ShipmentType, NonContractShipmentType]:
return self.create_shipment(payload)



def parse_quote_response(self, response: 'XMLElement') -> Tuple[List[E.QuoteDetails], List[E.Error]]:
return self.parse_price_quotes(response)

def parse_tracking_response(self, response: 'XMLElement') -> Tuple[List[E.TrackingDetails], List[E.Error]]:
return self.parse_tracking_summary(response)

def parse_shipment_response(self, response: 'XMLElement') -> Tuple[E.ShipmentDetails, List[E.Error]]:
return self.parse_shipment_info(response)
3 changes: 3 additions & 0 deletions purplship/mappers/caps/caps_mapper/partials/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from .rate import CanadaPostMapperPartial as CanadaPostRateMapperPartial
from .track import CanadaPostMapperPartial as CanadaPostTrackMapperPartial
from .shipment import CanadaPostMapperPartial as CanadaPostShipmentMapperPartial
57 changes: 57 additions & 0 deletions purplship/mappers/caps/caps_mapper/partials/interface.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
from typing import Tuple, List, Union
from functools import reduce
from purplship.mappers.caps import CanadaPostClient
from purplship.domain import entities as E
from pycaps.rating import mailing_scenario
from pycaps.shipment import ShipmentType
from pycaps.ncshipment import NonContractShipmentType
from pycaps.messages import messageType


class CanadaPostCapabilities:
"""
CanadaPost native service request types
"""

""" Requests """

def create_mailing_scenario(self, payload: E.shipment_request) -> mailing_scenario:
pass

def create_tracking_pins(self, payload: E.tracking_request) -> List[str]:
pass

def create_shipment(self, payload: E.shipment_request) -> Union[ShipmentType, NonContractShipmentType]:
pass


""" Replys """

def parse_price_quotes(self, response: 'XMLElement') -> Tuple[List[E.QuoteDetails], List[E.Error]]:
pass

def parse_tracking_summary(self, response: 'XMLElement') -> Tuple[List[E.TrackingDetails], List[E.Error]]:
pass

def parse_shipment_info(self, response: 'XMLElement') -> Tuple[E.ShipmentDetails, List[E.Error]]:
pass


class CanadaPostMapperBase(CanadaPostCapabilities):
"""
CanadaPost mapper base class
"""
def __init__(self, client: CanadaPostClient):
self.client = client

def parse_error_response(self, response: 'XMLElement') -> List[E.Error]:
messages = response.xpath('.//*[local-name() = $name]', name="message")
return reduce(self._extract_error, messages, [])

def _extract_error(self, errors: List[E.Error], messageNode: 'XMLElement') -> List[E.Error]:
message = messageType()
message.build(messageNode)
return errors + [
E.Error(code=message.code,
message=message.description, carrier=self.client.carrier_name)
]
84 changes: 84 additions & 0 deletions purplship/mappers/caps/caps_mapper/partials/rate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
from pycaps.rating import *
from datetime import datetime
from .interface import reduce, Tuple, List, E, CanadaPostMapperBase


class CanadaPostMapperPartial(CanadaPostMapperBase):

def parse_price_quotes(self, response: 'XMLElement') -> Tuple[List[E.QuoteDetails], List[E.Error]]:
price_quotes = response.xpath('.//*[local-name() = $name]', name="price-quote")
quotes = reduce(self._extract_quote, price_quotes, [])
return (quotes, self.parse_error_response(response))

def _extract_quote(self, quotes: List[E.QuoteDetails], price_quoteNode: 'XMLElement') -> List[E.QuoteDetails]:
price_quote = price_quoteType()
price_quote.build(price_quoteNode)
discounts = [E.ChargeDetails(name=d.adjustment_name, currency="CAD", amount=float(d.adjustment_cost or 0)) for d in price_quote.price_details.adjustments.adjustment]
return quotes + [
E.QuoteDetails(
carrier=self.client.carrier_name,
currency="CAD",
delivery_date=str(price_quote.service_standard.expected_delivery_date),
service_name=price_quote.service_name,
service_type=price_quote.service_code,
base_charge=float(price_quote.price_details.base or 0),
total_charge=float(price_quote.price_details.due or 0),
discount=reduce(lambda sum, d: sum + d.amount, discounts, 0),
duties_and_taxes=float(price_quote.price_details.taxes.gst.valueOf_ or 0) +
float(price_quote.price_details.taxes.pst.valueOf_ or 0) +
float(price_quote.price_details.taxes.hst.valueOf_ or 0),
extra_charges=list(map(lambda a: E.ChargeDetails(
name=a.adjustment_name, currency="CAD", amount=float(a.adjustment_cost or 0)), price_quote.price_details.adjustments.adjustment)
)
)
]


def create_mailing_scenario(self, payload: E.shipment_request) -> mailing_scenario:
package = payload.shipment.packages[0]

if len(payload.shipment.services) > 0:
services = servicesType()
for code in payload.shipment.services:
services.add_service_code(code)

if 'options' in payload.shipment.extra:
options = optionsType()
for option in payload.shipment.extra.get('options'):
options.add_option(optionType(
option_amount=option.get('option-amount'),
option_code=option.get('option-code')
))

return mailing_scenario(
customer_number=payload.shipper.account_number or payload.shipment.payment_account_number or self.client.customer_number,
contract_id=payload.shipment.extra.get('contract-id'),
promo_code=payload.shipment.extra.get('promo-code'),
quote_type=payload.shipment.extra.get('quote-type'),
expected_mailing_date=payload.shipment.extra.get('expected-mailing-date'),
options=options if ('options' in payload.shipment.extra) else None,
parcel_characteristics=parcel_characteristicsType(
weight=payload.shipment.total_weight or package.weight,
dimensions=dimensionsType(
length=package.length,
width=package.width,
height=package.height
),
unpackaged=payload.shipment.extra.get('unpackaged'),
mailing_tube=payload.shipment.extra.get('mailing-tube'),
oversized=payload.shipment.extra.get('oversized')
),
services=services if (len(payload.shipment.services) > 0) else None,
origin_postal_code=payload.shipper.postal_code,
destination=destinationType(
domestic=domesticType(
postal_code=payload.recipient.postal_code
) if (payload.recipient.country_code == 'CA') else None,
united_states=united_statesType(
zip_code=payload.recipient.postal_code
) if (payload.recipient.country_code == 'US') else None,
international=internationalType(
country_code=payload.shipment.country_code
) if (payload.recipient.country_code not in ['US', 'CA']) else None
)
)
Loading

0 comments on commit cc5101a

Please sign in to comment.