from datetime import datetime from urbantz.utils import Coordinates from urbantz.exceptions import APIError import json import requests class Delivery(object): """ A UrbanTZ delivery with a unique ID. """ def __init__(self, tracking_code=None): """ :param tracking_code: A delivery public tracking code. :type tracking_code: str or None """ self.tracking_code = tracking_code """ The delivery public tracking code. :type: str or None """ self.last_updated = None """ Last API update date/time. Is None if data has never been fetched from the API. :type: datetime or None """ self.payload = None """ Latest parsed JSON payload from the API. Is None if data has never been fetched from the API or loaded via :meth:`Delivery.use`. :type: dict or None """ self.position = None """ Coordinates of the delivery truck's position. :type: urbantz.utils.Coordinates """ self.destination = None """ Coordinates of the delivery destination. :type: urbantz.utils.Coordinates """ def __repr__(self): return '{}({})'.format( self.__class__.__name__, repr(self.tracking_code)) @property def api_url(self): """ URL pointing to the API endpoint to use for the specific delivery. :type: str """ return 'https://backend.urbantz.com/public/task/tracking/{}'.format( self.tracking_code) def update(self): """ Fetch the latest delivery information from the API. :raises requests.exceptions.HTTPError: If the response has an HTTP 4xx or 5xx code. :raises urbantz.exceptions.APIError: If the API returned an error. """ resp = requests.get(self.api_url) resp.raise_for_status() data = resp.json() if 'error' in data: raise APIError(self.payload['error']) self.use(data) # TODO: See if the payload holds a last update value self.last_updated = datetime.now() def use(self, payload): """ Use a parsed JSON payload to update the properties. :param dict payload: A parsed JSON payload from the API. """ self.payload = payload self.position = Coordinates.fromJSON(self.payload['position']) self.destination = Coordinates.fromJSON( self.payload['location']['location']['geometry']) @classmethod def from_payload(cls, payload): """ Create a Delivery instance from an existing payload. :param payload: A parsed JSON payload, a str holding a JSON payload, or an open file-like object. :type payload: dict or str or file-like object """ if isinstance(payload, str): payload = json.loads(payload) if not isinstance(payload, dict): payload = json.load(payload) instance = cls() instance.use(payload) return instance