Skip to content

Commit

Permalink
0.1.1 (#69)
Browse files Browse the repository at this point in the history
* Version 0.1.1
  • Loading branch information
hparfr authored Mar 20, 2017
1 parent ef99847 commit 325b2b4
Show file tree
Hide file tree
Showing 37 changed files with 738 additions and 318 deletions.
14 changes: 14 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
language: python
python:
- "2.7"
script:
- echo "1"
deploy:
provider: pypi
user: hparfr
password:
secure: yyroV8BdvIaQZSbDf/6zKXmu0uL4u5SdyxCPLTJU+qyTlhtS/IJAJZK+W00zu6rOimHUFSgkhi0s21kuck9m4gYuDZnl9ghSuTCkmlra1l4pY565XkIutu1ArGBViQb5A6EkWf8oX9JPXY2fnYr4R/7PMQbDEyY/Ft9yG5zLsug5DtmUJgYU3M9r3lweN8Jv9TFRXjBEFwhbtgpe+/0mWHAMyDQmZE/+CocSf2F7LtILPaqIGvVVXJg3mItV1vCzGi0FbHolIWRnSpLCSun/G2ldQcNUrtKnZY+2vPFfnOGLOwHn8iKqY7Rxid7cI8vYFE65tWkJZ+g6gKI8Rk0JG0IBE1hUl86Smy3+g84Q5SfVDiCXRFQwpAx77SPsfrUD7uYzFQAf+n43inF1X/r26hdZnhr0FjWVUVvtJIVeUGOtO1Dp1NFIpLmCvXHDT8nV6qva7+yIMU2+6HPI2Sy41xMpDAKPJWCCq7rdKd4t0sPnpj2PFdDQ1nY8ZuQV1iQfuqv82Vz89fFCW5S5OHoN01EC6zThAjfRFkAefPtGP7QCZnaAGMcsLhDDG9J/S5Np4sx33hrFuvGtrv/P6zQCPOS7iBn3UA0xEzZORUEImVKL5eFJtQdS7Firl2HYYvPkv7SsDHUvONjk0Drv3Gsk4AtB7Wxx26bXgoqLDrdt0Pk=
distributions: sdist bdist_wheel
on:
tags: true
branch: master
20 changes: 18 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,31 @@

# 0.1.1

Roadmap / TODO:
- Add Kuehne & Nagel carrier
- Add Geodis EDI
- Add UPS carrier
- Support test_mode for some carriers
- Improve documentation
- Support additionnal methods of api
- Support carrier tracking
- Write tests

# 0.1.1 2017-03-20


### Features / Refactorings
- Refactoring of error handling: exception are now always raised, returns code (success | warning) removed
- Simplification of return code
- Remove duplicate code of get_parts
- Handling of additionnal attachments ("annexes")
- tracking code is always in tracking.number
- label is always in label.data

### BREAKING CHANGES
- errors are all raised with exceptions
- raise Exception removed in favor of better defined exceptions
- return of get_label harmonized


# 0.1.0 2016-12-19

### BREAKING CHANGES
Expand Down
39 changes: 36 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Roulier will get a label + tracking number to your carrier for you.
* Roulier runs on your server and call each carrier API directly.
* You have to use your own credentials provided by each carriers.
* Roulier is Open Source software, AGPL-3
* Roulier integrate a multitude of carriers : Laposte, Geodis, DPD, K&N... more to come.
* Roulier integrate a multitude of carriers : Laposte, Geodis, DPD, K&N, TRS... more to come.



Expand Down Expand Up @@ -74,7 +74,38 @@ pprint(laposte.api())

```

Advanced usage for Laposte
### Return

```python
from roulier import roulier
laposte = roulier.get('laposte')
api = laposte.api()
api['auth']['login'] = '12345'
...
print laposte.get_label(api)

# {
# label: {
# 'name':'label',
# 'type': 'zpl',
# 'data': 'base64 here',
# },
# tracking: {
# 'number': 'tracking code here',
# },
# annexes: [
# {
# 'name': 'cn23',
# 'type': 'pdf',
# 'data': 'base64 here'
# }, ...
# ],
#}
```



### Advanced usage for Laposte

Usefull for debugging: get the xml before the call, send an xml directly, analyse the response

Expand Down Expand Up @@ -138,7 +169,7 @@ l_api._parcel()
```


###Contributors
### Contributors


* [@hparfr](https://github.com/hparfr) ([Akretion.com](https://akretion.com))
Expand All @@ -153,3 +184,5 @@ l_api._parcel()
* [Jinja2](http://jinja.pocoo.org/) - templating
* [Requests](http://docs.python-requests.org/) - HTTP requests
* [zplgrf](https://github.com/kylemacfarlane/zplgrf) - PNG to ZPL conversion
* [unidecode](https://pypi.python.org/pypi/Unidecode) - Remove accents from ZPL
* [unicodecsv](https://github.com/jdunck/python-unicodecsv) - CSV generation
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.1.0
0.1.1
21 changes: 21 additions & 0 deletions roulier/api.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
"""API interface."""
from cerberus import Validator
from unidecode import unidecode


class MyValidator(Validator):
Expand All @@ -14,6 +15,26 @@ def _validate_description(self, description, field, value):
"""
pass

def _normalize_coerce_zpl(self, value):
"""Sanitze input for ZPL.
Remove ZPL ctrl caraters
Remove accents
"""
if not isinstance(value, basestring):
return value

ctrl_cars = [
0xFE, # Tilde ~
0x5E, # Caret ^
0x1E, # RS (^ substitution)
0x10, # DLE (~ substitution)
]
val = unidecode(value)
for ctrl in ctrl_cars:
val = val.replace("%c" % ctrl, "")
return val


class Api(object):
"""Define expected fields of carriers.
Expand Down
2 changes: 2 additions & 0 deletions roulier/carriers/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# -*- coding: utf-8 -*-
from . import laposte
from . import dummy
from . import geodis
from . import dummy
from . import dpd
from . import trs
29 changes: 2 additions & 27 deletions roulier/carriers/dpd/dpd.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@
from .dpd_decoder import DpdDecoder
from .dpd_encoder import DpdEncoder
from .dpd_transport import DpdTransport
from .dpd_api import DpdApi
from roulier.carrier import Carrier
from roulier import ws_tools as tools


class Dpd(Carrier):
Expand All @@ -24,33 +22,10 @@ def get(self, data, action):
"""Run an action with data against Dpd WS."""
request = self.encoder.encode(data, action)
response = self.ws.send(request)
if not response['payload']:
return response
payload = self.decoder.decode(response, {})
zpl = self.handle_zpl(data, payload['label'])
payload['label'] = zpl if zpl else payload['label']
return payload
return self.decoder.decode(
response['body'], request['output_format'])

# shortcuts
def get_label(self, data):
"""Genereate a createShipmentWithLabels."""
return self.get(data, 'createShipmentWithLabels')

# utils
def handle_zpl(self, data, png):
"""Convert a png in zpl.
if labelFormat was asked as ZPL, WS returns a png
This function rotate it and convert it an suitable zpl format
@params:
data : full dict with all the params of the get method
png : a base64 formatted string (returned by ws)
@returns:
a zpl in a string
"""
label_format = DpdApi().normalize(data)['service']['labelFormat']

if label_format == 'ZPL':
return tools.png_to_zpl(png, True)
return None
39 changes: 32 additions & 7 deletions roulier/carriers/dpd/dpd_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,13 @@
'No',
'Predict',
'AutomaticSMS',
'AutomaticEmail',
'AutomaticMail',
)
DPD_PRODUCTS = (
'DPD_Classic',
'DPD_Relais',
'DPD_Predict',
# 'DPD Retour en Relais', # Not implemented yet
)


Expand All @@ -22,16 +28,35 @@ def _service(self):
schema['labelFormat']['allowed'] = DPD_LABEL_FORMAT
schema['labelFormat']['default'] = 'ZPL'
schema['labelFormat'].update({'required': True, 'empty': False})
schema['agencyId'].update({'required': True, 'empty': False, 'description': 'Agency code int(3)'})
schema['customerCountry'] = {'required': True, 'empty': False, 'description': 'Customer country code (France = 250) int(3)'}
schema['customerId'].update({'required': True, 'empty': False, 'description': 'Customer number int(6)'})
schema['agencyId'].update({
'required': True, 'empty': False,
'description': 'Agency code int(3)'})
schema['customerCountry'] = {
'required': True, 'empty': False,
'description': 'Customer country code (France = 250) int(3)'}
schema['customerId'].update({
'required': True, 'empty': False,
'description': 'Customer number int(6)'})
schema['shippingDate'].update({'required': False, 'empty': True})

# mettre ça ensemble ?
schema['notifications'] = {'default': 'Predict', 'allowed': DPD_ALLOWED_NOTIFICATIONS}
schema['product'].update({'description': 'N/A for DPD'})
schema['notifications'] = {
'default': DPD_ALLOWED_NOTIFICATIONS[0],
'allowed': DPD_ALLOWED_NOTIFICATIONS}
schema['product'].update({
'empty': False,
'required': True,
'default': DPD_PRODUCTS[0],
'description': 'Type de produit',
'allowed': DPD_PRODUCTS
})

schema['dropOffLocation'] = {'default': '', 'description': 'Drop-off Location id (Relais Colis)' }
schema['dropOffLocation'] = {
'default': '',
'empty': True,
'required': False,
'description': 'Drop-off Location id (Relais Colis)'
}

return schema

Expand Down
42 changes: 30 additions & 12 deletions roulier/carriers/dpd/dpd_decoder.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,13 @@
"""Dpd XML -> Python."""
from lxml import objectify
from roulier.codec import Decoder
from roulier import ws_tools as tools


class DpdDecoder(Decoder):
"""Dpd XML -> Python."""

def decode(self, response, parts):
payload_xml = response['payload']
tag, content = self.decode_payload(payload_xml)
return content

def decode_payload(self, xml_string):
def decode(self, body, output_format):
"""Dpd XML -> Python."""
def create_shipment_with_labels(msg):
"""Understand a CreateShipmentWithLabelsResponse."""
Expand All @@ -21,19 +17,41 @@ def create_shipment_with_labels(msg):
)
shipment = shipments.getchildren()[0]
label, attachment = labels.getchildren()
label_data = self.handle_zpl(label.label.text, output_format)
# .text because we want str instead of objectify.StringElement
x = {
'barcode': shipment.barcode.text,
'parcelnumber': shipment.parcelnumber.text,
'label': label.label.text,
'attachment': attachment.label.text
"tracking": {
'number': shipment.barcode.text,
'parcelnumber': shipment.parcelnumber.text,
},
"label": {
"data": label_data,
"name": "label",
"type": output_format,
},
"annexes": [{
"data": attachment.label.text,
"name": "Summary",
"type": output_format
}]
}
return x

xml = objectify.fromstring(xml_string)
xml = objectify.fromstring(body)
tag = xml.tag
lookup = {
"{http://www.cargonet.software}CreateShipmentWithLabelsResponse":
create_shipment_with_labels,
}
return tag, lookup[tag](xml)
return lookup[tag](xml)

def handle_zpl(self, png, label_format):
"""Convert a png in zpl.
if labelFormat was asked as ZPL, WS returns a png
This function rotate it and convert it an suitable zpl format
"""
if label_format == 'ZPL':
return tools.png_to_zpl(png, True)
else:
return png
Loading

0 comments on commit 325b2b4

Please sign in to comment.