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 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)