Skip to content

Commit

Permalink
Merge pull request #31 from PurplShip/CleanUpUnifiedTypesAndFreightIn…
Browse files Browse the repository at this point in the history
…tegrations

Clean up unified types and freight integrations
  • Loading branch information
danh91 authored Jan 12, 2019
2 parents ed1dee3 + 04c7392 commit 1ef50a7
Show file tree
Hide file tree
Showing 34 changed files with 491 additions and 328 deletions.
5 changes: 4 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ python:
install:
- pip install coverage
- pip install codecov
- pip install mypy
- pip install . --process-dependency-links

script: coverage run --include='purplship/*' -m unittest -v
script:
- coverage run --include='purplship/*' -m unittest -v
- mypy purplship/ --no-strict-optional --no-warn-return-any --no-warn-unused-configs

after_success:
- coverage report -m
Expand Down
29 changes: 29 additions & 0 deletions mypy.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Global options:

[mypy]
python_version = 3.6
warn_return_any = True
warn_unused_configs = True

# Per-module options:

[mypy-lxml]
ignore_missing_imports = True

[mypy-gds_helpers]
ignore_missing_imports = True

[mypy-pycaps.*]
ignore_missing_imports = True

[mypy-pydhl.*]
ignore_missing_imports = True

[mypy-pyfedex.*]
ignore_missing_imports = True

[mypy-pyups.*]
ignore_missing_imports = True

[mypy-pysoap.*]
ignore_missing_imports = True
16 changes: 12 additions & 4 deletions purplship/domain/Types/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,13 @@ class Quote:
""" manage quotes operations """

@staticmethod
def create(**args) -> shipment_request:
def create(shipper: dict, recipient: dict, shipment: dict) -> shipment_request:
""" Create a quote request payload """
return shipment_request(**shipment_request_type(**args)._asdict())
return shipment_request(
shipper=party(**shipper),
recipient=party(**recipient),
shipment=create_shipment_options(**shipment)
)

class Tracking:
""" manage tracking operations """
Expand All @@ -21,9 +25,13 @@ class Shipment:
""" manage shipment operations """

@staticmethod
def create(**args) -> shipment_request:
def create(shipper: dict, recipient: dict, shipment: dict) -> shipment_request:
""" Create a shipment details request payload """
return shipment_request(**shipment_request_type(**args)._asdict())
return shipment_request(
shipper=party(**shipper),
recipient=party(**recipient),
shipment=create_shipment_options(**shipment)
)

class Pickup:
""" manage pickup operations """
Expand Down
6 changes: 3 additions & 3 deletions purplship/domain/Types/datatypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class party(NamedTuple):

class item_type(NamedTuple):
"""
item type is a package of commodity
item type is a package or a commodity
"""

weight: float
Expand Down Expand Up @@ -90,7 +90,7 @@ class shipment_options(NamedTuple):
payment_country_code: str = None
payment_account_number: str = None

ship_date: str = None
date: str = None
customs: customs_type = None
invoice: invoice_type = None
doc_images: List[doc_image] = []
Expand Down Expand Up @@ -161,7 +161,7 @@ def __init__(self, message: str = None, code: str = None, carrier: str = None):
self.carrier = carrier

class ChargeDetails:
def __init__(self, name: str = None, amount: str = None, currency: str = None):
def __init__(self, name: str = None, amount: float = None, currency: str = None):
self.name = name
self.amount = amount
self.currency = currency
Expand Down
98 changes: 41 additions & 57 deletions purplship/domain/Types/factories.py
Original file line number Diff line number Diff line change
@@ -1,63 +1,47 @@
from collections import namedtuple
from typing import List, Dict
from purplship.domain.Types.datatypes import item_type, party, invoice_type, doc_image, option_type
from purplship.domain.Types.datatypes import item_type, party, invoice_type, doc_image, option_type, shipment_options, customs_type

''' customs Type definition '''
class customs_details_type(namedtuple("customs_details_type", "no_eei aes description terms_of_trade items commercial_invoice extra")):
def __new__(cls, no_eei: str = None, aes: str = None, description: str = None, terms_of_trade: str = None, items: List[dict] = [], commercial_invoice: bool = None, extra: dict = None):
return super(cls, customs_details_type).__new__(
cls,
no_eei,
aes,
description,
terms_of_trade,
[item_type(**i) for i in items],
commercial_invoice,
extra,
)
def create_customs_type(no_eei: str = None, aes: str = None, description: str = None, terms_of_trade: str = None, items: List[dict] = [], commercial_invoice: bool = False, extra: dict = {}) -> customs_type:
''' customs Type factory function '''
return customs_type(
no_eei=no_eei,
aes=aes,
description=description,
terms_of_trade=terms_of_trade,
items=[item_type(**i) for i in items],
commercial_invoice=commercial_invoice,
extra=extra,
)


''' shipment options Type definition '''
class shipment_options_type(namedtuple("shipment_options_type", "items insured_amount total_items 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 options customs invoice doc_images references label, extra")):
def __new__(cls, items: List, insured_amount: float = None, total_items: 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] = [], options: List[dict] = [], customs: Dict = None, invoice: dict = None, doc_images: List[dict] = [], references: List[str] = [], label: Dict = None, extra: Dict = {}):
return super(cls, shipment_options_type).__new__(
cls,
[item_type(**p) for p in items],
insured_amount,
total_items,
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,
[option_type(**option) for option in options],
customs_details_type(**customs) if customs else None,
invoice_type(**invoice) if invoice else None,
[doc_image(**doc) for doc in doc_images],
references,
doc_image(**label) if label else None,
extra
)


''' shipment request Type definition '''
class shipment_request_type(namedtuple("shipment_request_type", "shipper recipient shipment")):
def __new__(cls, shipper: Dict, recipient: Dict, shipment: Dict):
return super(cls, shipment_request_type).__new__(
cls,
party(**shipper),
party(**recipient),
shipment_options_type(**shipment)
)
def create_shipment_options(items: List, insured_amount: float = None, total_items: 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, 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, services: List[str] = [], options: List[dict] = [], date : str = None, customs: dict = None, invoice: dict = None, doc_images: List[dict] = [], references: List[str] = [], label: Dict = None, extra: Dict = {}) -> shipment_options:
''' shipment options Type factory function '''
return shipment_options(
items=[item_type(**p) for p in items],
insured_amount=insured_amount,
total_items=total_items,
packaging_type=packaging_type,
is_document=is_document,
currency=currency,
total_weight=total_weight,
weight_unit=weight_unit,
dimension_unit=dimension_unit,
paid_by=paid_by,
duty_paid_by=duty_paid_by,
payment_type=payment_type,
payment_country_code=payment_country_code,
duty_payment_account=duty_payment_account,
declared_value=declared_value,
payment_account_number=payment_account_number,
services=services,
options=[option_type(**option) for option in options],
date=date,
customs=create_customs_type(**customs) if customs else None,
invoice=invoice_type(**invoice) if invoice else None,
doc_images=[doc_image(**doc) for doc in doc_images],
references=references,
label=doc_image(**label) if label else None,
extra=extra
)

15 changes: 13 additions & 2 deletions purplship/domain/client.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
from abc import ABC

class Client:
""" Unitied API carrier Client (Interface) """
class Client(ABC):
"""
Unitied API carrier Client (Interface)
...
Attributes
----------
server_url : str
a carrier server url address (can be test or prod)
carrier_name : str
a custom name to identified the carrier client instance (set to carrier name by default)
"""
server_url: str
carrier_name: str
37 changes: 22 additions & 15 deletions purplship/domain/mapper.py
Original file line number Diff line number Diff line change
@@ -1,51 +1,58 @@
from abc import ABC, abstractmethod
from typing import List, Tuple
from .client import Client
from ..domain import Types as T

class Mapper:
""" Unitied API to carrier API Mapper (Interface) """
client: Client
class Mapper(ABC):
"""
United API to carrier data Mapper (Interface)
...
Attributes
----------
client : Client
a carrier client (holding connection settings)
"""

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

def parse_quote_response(self, response) -> Tuple[List[T.QuoteDetails], List[T.Error]]:
""" Create united API quote result list from carrier xml response """
""" Create a united API quote result list from carrier xml response """
raise Exception("Not Supported")

def create_tracking_request(self, payload: T.tracking_request):
""" Create carrier specific tracking request xml data from payload """
""" Create a carrier specific tracking request xml data from payload """
raise Exception("Not Supported")

def parse_tracking_response(self, response) -> Tuple[List[T.TrackingDetails], List[T.Error]]:
""" Create united API tracking result list from carrier xml response """
""" Create a united API tracking result list from carrier xml response """
raise Exception("Not Supported")

def create_shipment_request(self, payload: T.shipment_request):
""" Create carrier specific shipment creation request xml data from payload """
""" Create a carrier specific shipment creation request xml data from payload """
raise Exception("Not Supported")

def parse_shipment_response(self, response) -> Tuple[T.ShipmentDetails, List[T.Error]]:
""" Create united API shipment creation result from carrier xml response """
""" Create a united API shipment creation result from carrier xml response """
raise Exception("Not Supported")

def create_pickup_request(self, payload: T.pickup_request):
""" Create carrier specific pickup request xml data from payload """
""" Create a carrier specific pickup request xml data from payload """
raise Exception("Not Supported")

def modify_pickup_request(self, payload: T.pickup_request):
""" Create carrier specific pickup modification request xml data from payload """
""" Create a carrier specific pickup modification request xml data from payload """
raise Exception("Not Supported")

def parse_pickup_response(self, response) -> Tuple[T.PickupDetails, List[T.Error]]:
""" Create united API pickup result from carrier xml response """
""" Create a united API pickup result from carrier xml response """
raise Exception("Not Supported")

def create_pickup_cancellation_request(self, payload: T.pickup_cancellation_request):
""" Create carrier specific pickup cancellation request xml data from payload """
""" Create a carrier specific pickup cancellation request xml data from payload """
raise Exception("Not Supported")

def parse_pickup_cancellation_response(self, response) -> Tuple[dict, List[T.Error]]:
""" Create united API pickup cancellation result from carrier xml response """
""" Create a united API pickup cancellation result from carrier xml response """
raise Exception("Not Supported")
18 changes: 15 additions & 3 deletions purplship/domain/proxy.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,19 @@
from .mapper import Mapper, Client
from abc import ABC
from purplship.domain.client import Client
from purplship.domain.mapper import Mapper

class Proxy:
""" Unitied API carrier Proxy (Interface) """
class Proxy(ABC):
"""
Unitied API carrier Proxy (Interface)
...
Attributes
----------
client : Client
a carrier client (holding connection settings)
mapper : Mapper
a carrier mapper for specific carrier data type mapping
"""
client: Client
mapper: Mapper

Expand Down
7 changes: 4 additions & 3 deletions purplship/mappers/caps/caps_mapper/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from typing import Tuple, List, Union
from lxml import etree
from purplship.domain.mapper import Mapper
from purplship.domain import Types as T
from pycaps.rating import mailing_scenario
Expand Down Expand Up @@ -29,11 +30,11 @@ def create_shipment_request(self, payload: T.shipment_request) -> Union[Shipment



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

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

def parse_shipment_response(self, response: 'XMLElement') -> Tuple[T.ShipmentDetails, List[T.Error]]:
def parse_shipment_response(self, response: etree.ElementBase) -> Tuple[T.ShipmentDetails, List[T.Error]]:
return self.parse_shipment_info(response)
15 changes: 8 additions & 7 deletions purplship/mappers/caps/caps_mapper/partials/interface.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from typing import Tuple, List, Union
from functools import reduce
from lxml import etree
from purplship.mappers.caps import CanadaPostClient
from purplship.domain import Types as T
from pycaps.rating import mailing_scenario
Expand Down Expand Up @@ -27,28 +28,28 @@ def create_shipment(self, payload: T.shipment_request) -> Union[ShipmentType, No

""" Replys """

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

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

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


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

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

def _extract_error(self, errors: List[T.Error], messageNode: 'XMLElement') -> List[T.Error]:
def _extract_error(self, errors: List[T.Error], messageNode: etree.ElementBase) -> List[T.Error]:
message = messageType()
message.build(messageNode)
return errors + [
Expand Down
Loading

0 comments on commit 1ef50a7

Please sign in to comment.