Skip to content

Commit

Permalink
Merge pull request #38 from PurplShip/IntegrateAustraliaPost
Browse files Browse the repository at this point in the history
WIP: integrating Australia post mapper
  • Loading branch information
danh91 committed Mar 3, 2019
2 parents aa3aaf9 + c99ceb9 commit df9be4c
Show file tree
Hide file tree
Showing 40 changed files with 901 additions and 138 deletions.
36 changes: 21 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,26 @@ Shipping carriers API integrations Library
PurplSHip prevents you from reinventing the wheel and is easy to use:

```shell
>>> from purplship.mappers.dhl import DHLClient, DHLProxy
>>> from purplship.domain.Types import Tracking
>>> client = DHLClient(
"https://xmlpi-ea.dhl.com/XMLShippingServlet",
"YOUR_DHL_SITE_ID",
"YOUR_DHL_SITE_PASSWORD",
"YOUR_DHL_ACCOUNT_NUMBER",
"CARRIER_NAME"
)
>>> proxy = DHLProxy(client)
>>> payload = Tracking.create(tracking_numbers=["8346088391"])
>>> tracking_req_xml_obj = proxy.mapper.create_tracking_request(payload)
>>> response = proxy.get_trackings(tracking_req_xml_obj)
>>> trackings = proxy.mapper.parse_tracking_response(response)
import purplship

proxy = purplship.gateway['aups'].create({
"server_url": "https://digitalapi.auspost.com.au/test",
"username": "username",
"password": "password",
"account_number": "1234567"
})

rate_request = purplship.rating.fetch(
shipper={"postal_code": "H3N1S4", "country_code": "CA"},
recipient = {"city": "Lome", "country_code": "TG"},
shipment = {
"items": [
{"id": "1", "height": 3, "length": 10, "width": 3, "weight": 4.0}
]
}
).from_(proxy)

rates = rate_request.parse()
```

## Getting Started
Expand All @@ -47,7 +53,7 @@ PurplShip can be installed with [pip](https://pip.pypa.io/):
For released version (specify a purplship==version if needed)

```shell
pip install -f https://git.io/fxTZ6 purplship
pip install -f https://git.io/purplship purplship
```

Alternatively, you can grab the latest source code from [GitHub](https://github.com/PurplShip/purplship):
Expand Down
6 changes: 6 additions & 0 deletions mypy.ini
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,15 @@ warn_unused_configs = True
[mypy-lxml]
ignore_missing_imports = True

[mypy-jstruct]
ignore_missing_imports = True

[mypy-gds_helpers]
ignore_missing_imports = True

[mypy-pyaups.*]
ignore_missing_imports = True

[mypy-pycaps.*]
ignore_missing_imports = True

Expand Down
60 changes: 13 additions & 47 deletions purplship/domain/Types/models.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""PurplShip Unified datatypes module."""
import attr
from typing import List, Dict, Callable, Any
from typing import List, Dict
from jstruct import JList, JStruct


@attr.s(auto_attribs=True)
Expand All @@ -21,15 +22,12 @@ class Party:

state: str = None
state_code: str = None
suburb: str = None

address_lines: List[str] = []
extra: Dict = {}


def party_converter(args) -> Party:
return Party(**args) if isinstance(args, dict) else args


@attr.s(auto_attribs=True)
class Item:
"""item type (can be a package or a commodity)."""
Expand All @@ -51,13 +49,6 @@ class Item:
extra: Dict = {}


def item_converter(args) -> Item:
return Item(**args) if isinstance(args, dict) else args

def item_list(args) -> List[Item]:
return [item_converter(arg) for arg in args]


@attr.s(auto_attribs=True)
class Customs:
"""customs type."""
Expand All @@ -66,15 +57,11 @@ class Customs:
aes: str = None
description: str = None
terms_of_trade: str = None
items: List[Item] = attr.ib(default=[], converter=item_list)
items: List[Item] = JList[Item]
commercial_invoice: bool = False
extra: Dict = {}


def customs_converter(args) -> Customs:
return Customs(**args) if isinstance(args, dict) else args


@attr.s(auto_attribs=True)
class Invoice:
"""invoice type."""
Expand All @@ -86,10 +73,6 @@ class Invoice:
extra: Dict = {}


def invoice_converter(args) -> Invoice:
return Invoice(**args) if isinstance(args, dict) else args


@attr.s(auto_attribs=True)
class Doc:
"""document image type."""
Expand All @@ -100,10 +83,6 @@ class Doc:
extra: Dict = {}


def doc_converter(args) -> Doc:
return Doc(**args) if isinstance(args, dict) else args


@attr.s(auto_attribs=True)
class Option:
"""shipment option type."""
Expand All @@ -113,20 +92,11 @@ class Option:
extra: Dict = {}


def option_converter(args) -> Option:
return Option(**args) if isinstance(args, dict) else args

def option_list(args) -> List[Option]:
return [option_converter(arg) for arg in args]

def doc_list(args) -> List[Doc]:
return [doc_converter(arg) for arg in args]

@attr.s(auto_attribs=True)
class Shipment:
"""shipment configuration type."""

items: List[Item] = attr.ib(default=[], converter=item_list)
items: List[Item] = JList[Item]
insured_amount: float = None
total_items: int = None
packaging_type: str = None
Expand All @@ -145,29 +115,25 @@ class Shipment:
payment_account_number: str = None

date: str = None
customs: Customs = attr.ib(default=None, converter=customs_converter)
invoice: Invoice = attr.ib(default=None, converter=invoice_converter)
doc_images: List[Doc] = attr.ib(default=[], converter=doc_list)
customs: Customs = JStruct[Customs]
invoice: Invoice = JStruct[Invoice]
doc_images: List[Doc] = JList[Doc]

references: List[str] = []
services: List[str] = []
options: List[Option] = attr.ib(default=[], converter=option_list)
options: List[Option] = JList[Option]

label: Doc = attr.ib(default=None, converter=doc_converter)
label: Doc = JStruct[Doc]
extra: Dict = {}


def shipment_converter(args) -> Shipment:
return Shipment(**args) if isinstance(args, dict) else args


@attr.s(auto_attribs=True)
class ShipmentRequest:
"""shipment request type."""

shipper: Party = attr.ib(converter=party_converter)
recipient: Party = attr.ib(converter=party_converter)
shipment: Shipment = attr.ib(converter=shipment_converter)
shipper: Party = JStruct[Party]
recipient: Party = JStruct[Party]
shipment: Shipment = JStruct[Shipment]


class RateRequest(ShipmentRequest):
Expand Down
5 changes: 3 additions & 2 deletions purplship/domain/interface.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
"""Interface."""

import attr
from typing import Callable, TypeVar, Dict, Any
from typing import Callable, TypeVar
from purplship.domain.proxy import Proxy
from purplship.domain.mapper import Mapper
from purplship.domain.Types.models import (
RateRequest,
ShipmentRequest,
TrackingRequest,
PickupRequest,
Expand Down Expand Up @@ -96,7 +97,7 @@ class rating:
def fetch(**args):

def action(proxy: Proxy):
payload = ShipmentRequest(**args)
payload = RateRequest(**args)
mapper: Mapper = proxy.mapper
request = mapper.create_quote_request(payload)
response = proxy.get_quotes(request)
Expand Down
2 changes: 1 addition & 1 deletion purplship/domain/mapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class Mapper(ABC):
a carrier client (holding connection settings)
"""

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

Expand Down
2 changes: 2 additions & 0 deletions purplship/mappers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@
from enum import Enum
from typing import Callable
from purplship.domain import Proxy
from purplship.mappers.aups import AustraliaPostProxy, AustraliaPostClient
from purplship.mappers.caps import CanadaPostProxy, CanadaPostClient
from purplship.mappers.dhl import DHLProxy, DHLClient
from purplship.mappers.fedex import FedexProxy, FedexClient
from purplship.mappers.ups import UPSProxy, UPSClient


class Providers(Enum):
aups = (AustraliaPostProxy, AustraliaPostClient)
caps = (CanadaPostProxy, CanadaPostClient)
dhl = (DHLProxy, DHLClient)
fedex = (FedexProxy, FedexClient)
Expand Down
3 changes: 3 additions & 0 deletions purplship/mappers/aups/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from .aups_client import AustraliaPostClient
from .aups_mapper import AustraliaPostMapper
from .aups_proxy import AustraliaPostProxy
15 changes: 15 additions & 0 deletions purplship/mappers/aups/aups_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
"""PurplShip Australia post client settings."""

import attr
from purplship.domain.client import Client


@attr.s(auto_attribs=True)
class AustraliaPostClient(Client):
"""Australia post connection settings."""

username: str
password: str
account_number: str
carrier_name: str = "AustraliaPost"
server_url: str = "https://digitalapi.auspost.com.au/shipping/v1"
32 changes: 32 additions & 0 deletions purplship/mappers/aups/aups_mapper/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from typing import Tuple, List
from purplship.domain.mapper import Mapper
from purplship.domain.Types import (
RateRequest,
TrackingRequest,
Error,
QuoteDetails,
TrackingDetails
)
from .partials import (
AustraliaPostRateMapperPartial,
AustraliaPostTrackMapperPartial,
)
from pyaups.shipping_price_request import ShippingPriceRequest


class AustraliaPostMapper(
Mapper,
AustraliaPostRateMapperPartial,
AustraliaPostTrackMapperPartial,
):
def create_quote_request(self, payload: RateRequest) -> ShippingPriceRequest:
return self.create_shipping_price_request(payload)

def create_tracking_request(self, payload: TrackingRequest) -> List[str]:
return self.create_track_items_request(payload)

def parse_quote_response(self, response: dict) -> Tuple[List[QuoteDetails], List[Error]]:
return self.parse_shipping_price_response(response)

def parse_tracking_response(self, response: dict) -> Tuple[List[TrackingDetails], List[Error]]:
return self.parse_track_items_response(response)
2 changes: 2 additions & 0 deletions purplship/mappers/aups/aups_mapper/partials/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from .rate import AustraliaPostMapperPartial as AustraliaPostRateMapperPartial
from .track import AustraliaPostMapperPartial as AustraliaPostTrackMapperPartial
54 changes: 54 additions & 0 deletions purplship/mappers/aups/aups_mapper/partials/interface.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
from typing import Tuple, List
from purplship.mappers.aups import AustraliaPostClient
from purplship.domain.Types import (
RateRequest,
TrackingRequest,
Error,
TrackingDetails,
QuoteDetails
)
from pyaups.shipping_price_request import ShippingPriceRequest
from pyaups.shipping_price_response import Errors


class AustraliaPostCapabilities:
"""
AustraliaPost native service request types
"""

""" Requests """

def create_shipping_price_request(self, payload: RateRequest) -> ShippingPriceRequest:
pass

def create_track_items_request(self, payload: TrackingRequest) -> List[str]:
pass

""" Replys """

def parse_shipping_price_response(self, response: dict) -> Tuple[List[QuoteDetails], List[Error]]:
pass

def parse_track_items_response(self, response: dict) -> Tuple[List[TrackingDetails], List[Error]]:
pass


class AustraliaPostMapperBase(AustraliaPostCapabilities):
"""
AustraliaPost mapper base class
"""

def __init__(self, client: AustraliaPostClient):
self.client: AustraliaPostClient = client

def parse_error_response(self, response: dict) -> List[Error]:
if 'errors' in response:
errorResponse: Errors = Errors(**response)
return [
Error(
message=error.message,
carrier=self.client.carrier_name,
code=error.code
) for error in errorResponse.errors
]
return []
Loading

0 comments on commit df9be4c

Please sign in to comment.