""" Split rules for revenue distribution. This module contains functions to look up and apply split rules in order to determine Cable revenue distribution between The Orchard and its clients. """ from collections import namedtuple import datetime PVOD_UNIT_PRICE = 6.94 Store = namedtuple('Store', ['id', 'name', 'provider_ids']) class SplitRule: """Split Rule class. Representation of a Cable Split Rule. Those rules are set to determine the revenue distribution between The Orchard and its clients for a given gross revenue amount. Each rule depends on certain criteria and parameters. """ def __init__( self, rule_id, store_id, start, rule_definition, terminated=False): """Create a Split Rule. Args: rule_id (str): rule id store_id (int): store id start (date): start date of the rule rule_definition (callable): rule definition function terminated (bool): whether or not the rule is active """ self.id = rule_id self.store_id = store_id self.start = start self.rule_definition = rule_definition self.terminated = terminated def apply(self, **kwargs): """Apply a split rule according to its definition.""" return self.rule_definition.execute(**kwargs) class SplitRuleDefinition: """Split Rule Definition. Representation of a calculation rule based on input parameters and split type. """ def __init__(self, schema, rule_callable): """Construct a new Split Rule Definition. Args: schema (dict): defined rule parameters. rule_callable (callable): callable used for calculation """ self.schema = schema self.rule_callable = rule_callable def validate_param_schema(self, **kwargs): """Validate rule defintion param types. raises (AssertionError): if params don't match the schema. """ for key in self.schema: assert key in kwargs, 'Missing param: \'{}\''.format(key) assert isinstance( kwargs[key], self.schema[key]['type'] ), 'Wrong type for param \'{}\', expected is {}'.format( key, self.schema[key]['type'].__name__) def validate_param_values(self, **kwargs): """Validate rule param values. raises (NotImplementedError): if function not implemented by the child. """ raise NotImplementedError('Must be implemented in the child.') def validate_params(self, **kwargs): """Validate rule params.""" self.validate_param_schema(**kwargs) self.validate_param_values(**kwargs) def execute(self, **kwargs): """Validate and execute calculation rule.""" self.validate_params(**kwargs) return self.rule_callable(**kwargs) class SplitRuleDbo(SplitRuleDefinition): """Split rule definition for DBO only type.""" def __init__(self, rule_callable): """Construct a new SplitRuleDboDates object. Args: rule_callable (callable): callable used for calculation """ params = { 'dbo': {'type': float}} super().__init__(params, rule_callable) def validate_param_values(self, **kwargs): """Validate rule params for split rule definition. raises (AssertionError): if one of the params is not valid. """ assert kwargs['dbo'] >= 0 class SplitRuleDboDates(SplitRuleDefinition): """Split rule definition for DBO with dates type.""" def __init__(self, rule_callable): """Construct a new SplitRuleDboDates object. Args: rule_callable (callable): callable used for calculation """ params = { 'dbo': {'type': float}, 'vod_date': {'type': datetime.date}, 'est_date': {'type': datetime.date}} super().__init__(params, rule_callable) def validate_param_values(self, **kwargs): """Validate rule params for split rule definition. raises (AssertionError): if one of the params is not valid. """ assert kwargs['dbo'] >= 0 assert kwargs['vod_date'] >= kwargs['est_date'] class SplitRulePremiumUltra(SplitRuleDefinition): """Split rule definition for Premium and Ultra.""" pass class SplitRuleStraight(SplitRuleDefinition): """Split rule definition for straight split.""" def __init__(self, rule_callable): """Construct a new SplitRuleStraight object. Args: rule_callable (callable): callable used for calculation """ # No params params = {} super().__init__(params, rule_callable) def validate_param_values(self, **kwargs): """Validate rule params for split rule definition.""" assert True class SplitRulePpv(SplitRuleDefinition): """Split rule definition for PPV.""" pass class SplitRuleLibraryTitles(SplitRuleDefinition): """Split rule definition for library titles.""" pass class SplitRuleDvdVodDates(SplitRuleDefinition): """Split rule definition for DVD and VOD with dates.""" pass def in_demand_dbo_dates( gross, dbo=None, vod_date=None, est_date=None, unit_price=None, **kwargs): """Calculation function based on DBO, VOD date and EST date. if PVOD then # PVOD rule 60% on Premium VOD ($6.95) else # standard VOD 25% where DBO is $0 and VOD is 15-30 days after EST. 30% where DBO is $0 and VOD is 1-14 days after EST. 35% where DBO is $0 and VOD is same day as EST. 35% where DBO is $0.01-$5M and VOD is 15-30 days after EST. 40% where DBO is $0.01-$5M and VOD is 1-14 days after EST. 45% where DBO is $0.01-$5M and VOD is same day as EST. 40% where DBO $5M-$20M and VOD is 15-30 days after EST. 45% where DBO $5M-$20M and VOD is 1-14 days after EST. 50% where DBO $5M-$20M and VOD is same day as EST. 45% where DBO >$20M and VOD is 15-30 days after EST. 50% where DBO >$20M and VOD is 1-14 days after EST. 55% where DBO >$20M and VOD is same day as EST. Args: gross (int): gross amount in USD. dbo (int): DBO (Domestic Box Office) amount in USD. vod_date (datetime.date): VOD start date. est_date (datetime.date): EST start date. unit_price (float): unit price for a single transaction. Returns: float: orchard split in USD. """ if unit_price > PVOD_UNIT_PRICE: # Premium VOD rule split_rate = 0.60 else: # Standard VOD rules delta = (vod_date - est_date).days if dbo == 0: if 15 <= delta < 30: split_rate = 0.25 elif 1 <= delta < 15: split_rate = 0.30 elif delta == 0: split_rate = 0.35 elif 0 < dbo < 5000000: if 15 <= delta < 30: split_rate = 0.35 elif 1 <= delta < 15: split_rate = 0.40 elif delta == 0: split_rate = 0.45 elif 5000000 <= dbo < 20000000: if 15 <= delta < 30: split_rate = 0.40 elif 1 <= delta < 15: split_rate = 0.45 elif delta == 0: split_rate = 0.50 else: if 15 <= delta < 30: split_rate = 0.45 elif 1 <= delta < 15: split_rate = 0.50 elif delta == 0: split_rate = 0.55 return split_rate * gross def in_vubiquity_dbo_only(gross, dbo=None, **kwargs): """Calculation function based on DBO only. 45% where DBO is <$1M. 50% where DBO is $1M-$9,999,999. 55% where DBO is $10M or greater. Args: gross (int): gross amount in USD. dbo (int): DBO (Domestic Box Office) amount in USD. Returns: float: orchard split in USD. """ if 0 <= dbo < 1000000: split_rate = 0.45 elif 1000000 <= dbo < 10000000: split_rate = 0.50 else: split_rate = 0.55 return split_rate * gross def in_vubiquity_internationl_dbo_only(gross, **kwargs): """Calculation function based on DBO only. Straight cut. Straight split of 40%. Args: gross (int): gross amount in USD. Returns: float: orchard split in USD. """ return 0.40 * gross def in_direct_tv_dbo_only(gross, dbo=None, **kwargs): """Calculation function based on DBO only. 50% of retail with DBO $1M or less. 55% of retail with DBO >$1M. Args: gross (int): gross amount in USD. dbo (int): DBO (Domestic Box Office) amount in USD. Returns: float: orchard split in USD. """ if 0 <= dbo <= 1000000: split_rate = 0.50 else: split_rate = 0.55 return split_rate * gross def dish_dbo_only(gross, dbo=None, unit_price=None, **kwargs): """Calculation function based on DBO only. 50% of retail where DBO <$1M. 55% of retail where DBO is $1M or greater. 60% of retail on Premium VOD and Ultra VOD price point. Args: gross (int): gross amount in USD. dbo (int): DBO (Domestic Box Office) amount in USD. unit_price (float): unit price for a single transaction. Returns: float: orchard split in USD. """ if unit_price > PVOD_UNIT_PRICE: # Premium VOD rule split_rate = 0.60 else: # Standard VOD rule if 0 <= dbo < 1000000: split_rate = 0.50 else: split_rate = 0.55 return split_rate * gross def bell_dbo_dvd_dates( gross, dbo=None, vod_date=None, dvd_date=None, unit_price=None, **kwargs): """Calculation function based on DBO only. 50% of retail where VOD is 1 day or later than DVD Release Date. 60% of retail where VOD is same day or earlier than DVD Release Date. 60% of retail on Premium VOD or Ultra VOD releases Args: gross (int): gross amount in USD. dbo (int): DBO (Domestic Box Office) amount in USD. vod_date (datetime.date): VOD start date. dvd_date (datetime.date): DVD start date. unit_price (float): unit price for a single transaction. Returns: float: orchard split in USD. """ delta = (vod_date - dvd_date).days if unit_price > PVOD_UNIT_PRICE: # Premium VOD rule split_rate = 0.60 else: # VOD date vs. DVVD date rule if delta >= 1: split_rate = 0.50 else: split_rate = 0.60 return split_rate * gross def sample_dbo_2(): """.""" pass