Source code for pyticketswitch.mixins

import datetime
import decimal
import json

from pyticketswitch import utils


[docs]class JSONMixin(object): """Adds json encoding functionality to objects."""
[docs] def __jsondict__(self, hide_none=True, hide_empty=True): def sanitise(obj): if isinstance(obj, datetime.datetime): return obj.isoformat() if isinstance(obj, datetime.date): return obj.isoformat() if isinstance(obj, decimal.Decimal): return float(obj) if hasattr(obj, '__jsondict__'): return obj.__jsondict__(hide_none=hide_none, hide_empty=hide_empty) if isinstance(obj, list): return [sanitise(item) for item in obj] if isinstance(obj, dict): return {key: sanitise(value) for key, value in obj.items()} return obj return { key: sanitise(obj) for key, obj in self.__dict__.items() # when hiding None's and the object is None, skip the object if not (hide_none and obj is None) # when hiding empty iterators and the object is an iterator and # it's empty, skip the object. if not (hide_empty and hasattr(obj, '__iter__') and not obj) }
[docs] def as_dict_for_json(self, hide_none=True, hide_empty=True): """Generate a json serialisable dictionary from the object Dates are changed to strings in ISO 8601 format. Lists children are recursively serialised. Dictionary values are recursively serialised, keys are left as is. Objects that implement __jsondict__ return the result of calling __jsondict__ on the object. Args: hide_none (bool, optional): when :obj:`True` the returned dictionary will not include attributes who's value is :obj:`None`. hide_empty (bool, optional): when :obj:`True` the returned dictionary will not include attributes who's value is iterable and has a length of zero. Returns: dict: a dictionary representation of the object. """ return self.__jsondict__(hide_none=hide_none, hide_empty=hide_empty)
[docs] def as_json(self, hide_none=True, hide_empty=True, **kwargs): """Generate a json represetation of an object. Args: hide_none (bool): when :obj:`True` the returned dictionary will not include attributes who's value is :obj:`None`. hide_empty (bool): when :obj:`True` the returned dictionary will not include attributes who's value is iterable and has a length of zero. **kwargs: passed to :py:func:`json.dumps`. Returns: str: a json representation of an object """ return json.dumps( self.as_dict_for_json(hide_none=hide_none, hide_empty=hide_empty), **kwargs )
[docs]class PaginationMixin(object): """Adds pagination information to a responses meta data. Attributes: page_length (int): the number of items per page. page_number (int): the current page. pages_remaining (int): the number of pages remaining. results_remaining (int): the total number of remaining results after the current page. total_results (int): the total number of results. """
[docs] def __init__(self, page_length=None, page_number=None, pages_remaining=None, total_results=None, results_remaining=None, *args, **kwargs): self.page_length = page_length self.page_number = page_number self.pages_remaining = pages_remaining self.results_remaining = results_remaining self.total_results = total_results super(PaginationMixin, self).__init__(*args, **kwargs)
[docs] @classmethod def from_api_data(cls, data, result_key=None, *args, **kwargs): inst = super(PaginationMixin, cls).from_api_data(data) if not result_key: result_key = 'results' results = data.get(result_key, {}) paging_data = results.get('paging_status', {}) inst.page_length = paging_data.get('page_length') inst.page_number = paging_data.get('page_number') inst.pages_remaining = paging_data.get('pages_remaining') inst.results_remaining = paging_data.get('results_remaining') inst.total_results = paging_data.get('total_unpaged_results') return inst
[docs] def is_paginated(self): """Indicates that the response is paginated Returns: bool: :obj:`True` when the response is omitting results due to pagination otherwise :obj:``False`` """ if not self.total_results or not self.page_length: return False if self.total_results < self.page_length: return False return True
[docs]class SeatPricingMixin(object): """Adds seat pricing to an object Attributes: seatprice (float): the price per seat/ticket. surcharge (float): additional charges per seat/ticket. non_offer_seatprice (float): the original price per seat/ticket when not on offer. non_offer_surcharge (float): the original additional charges per seat/ticket when not on offer. """
[docs] def __init__(self, seatprice=None, surcharge=None, non_offer_seatprice=None, non_offer_surcharge=None, *args, **kwargs): super(SeatPricingMixin, self).__init__(*args, **kwargs) self.seatprice = seatprice self.surcharge = surcharge self.non_offer_seatprice = non_offer_seatprice self.non_offer_surcharge = non_offer_surcharge
[docs] @staticmethod def kwargs_from_api_data(data): kwargs = { 'seatprice': data.get('sale_seatprice'), 'surcharge': data.get('sale_surcharge'), 'non_offer_seatprice': data.get('non_offer_sale_seatprice'), 'non_offer_surcharge': data.get('non_offer_sale_surcharge'), } return kwargs
[docs] def combined_price(self): """Returns the combined seatprice and surcharge. This method assumes that we have both a seatprice and surcharge. In the situation where are missing either a seatprice or a surcharge then we don't have all the information to be able provide this information. Returns: float: the combined seatprice and surcharge Raises: AssertionError: It might seem like the obvious thing to do would be to assume the missing data was in fact zero and simply allow the addition to continue. However that would be somewhat dangerous when we are talking about prices, and it's better to actually raise an exception to indicate that there was a problem with the objects data, than to inform a customer that the tickets are free or have no booking fees """ assert self.seatprice is not None, 'seatprice data missing' assert self.surcharge is not None, 'surcharge data missing' return utils.add_prices(self.seatprice, self.surcharge)
[docs] def non_offer_combined_price(self): """Returns the combined non offer seatprice and surcharge. This method assumes that we have both a seatprice and surcharge. In the situation where are missing either a seatprice or a surcharge then we don't have all the information to be able provide this information. Returns: float: the combined seatprice and surcharge Raises: AssertionError: It might seem like the obvious thing to do would be to assume the missing data was in fact zero and simply allow the addition to continue. However that would be somewhat dangerous when we are talking about prices, and it's better to actually raise an exception to indicate that there was a problem with the objects data, than to inform a customer that the tickets are free or have no booking fees """ assert self.non_offer_seatprice is not None, 'non_offer_seatprice data missing' assert self.non_offer_surcharge is not None, 'non_offer_surcharge data missing' return utils.add_prices(self.non_offer_seatprice, self.non_offer_surcharge)