def formula(famille, period, parameters): annee = periods.instant(period).year annee_n = periods.instant(annee).period('year') annee_n1 = annee_n.offset(-1, 'year') base_ressource_annuelle_membres_famille = famille.sum(famille.members('base_ressource_annuelle_individu', annee_n1)) # base_ressource_mensuelle_famille_n1 = famille.sum( # famille('base_ressource_mensuelle_famille', annee_n1, options = [ADD]) # ) return ( # famille('base_ressource_mensuelle_famille', period.offset(-1, 'month')) + famille('base_ressource_mensuelle_famille', period.offset(-2, 'month')) + famille('base_ressource_mensuelle_famille', period.offset(-3, 'month')) + famille('base_ressource_mensuelle_famille', period.offset(-4, 'month')) + famille('base_ressource_mensuelle_famille', period.offset(-5, 'month')) + famille('base_ressource_mensuelle_famille', period.offset(-6, 'month')) + famille('base_ressource_mensuelle_famille', period.offset(-7, 'month')) + famille('base_ressource_mensuelle_famille', period.offset(-8, 'month')) + famille('base_ressource_mensuelle_famille', period.offset(-9, 'month')) + famille('base_ressource_mensuelle_famille', period.offset(-10, 'month')) + famille('base_ressource_mensuelle_famille', period.offset(-11, 'month')) + famille('base_ressource_mensuelle_famille', period.offset(-12, 'month')) + famille('base_ressource_mensuelle_famille', period.offset(-13, 'month')) # base_ressource_mensuelle_famille_n1 + base_ressource_annuelle_membres_famille )
def inflate_parameter_leaf(sub_parameter, base_year, inflator, unit_type = 'unit'): """ Inflate a Parameter leaf according to unit type Basic unit type are supposed by default Other admissible unit types are threshold_unit and rate_unit """ if isinstance(sub_parameter, Scale): if unit_type == 'threshold_unit': for bracket in sub_parameter.brackets: threshold = bracket.children['threshold'] inflate_parameter_leaf(threshold, base_year, inflator) return else: # Remove new values for year > base_year kept_instants_str = [ parameter_at_instant.instant_str for parameter_at_instant in sub_parameter.values_list if periods.instant(parameter_at_instant.instant_str).year <= base_year ] if not kept_instants_str: return last_admissible_instant_str = max(kept_instants_str) sub_parameter.update( start = last_admissible_instant_str, value = sub_parameter(last_admissible_instant_str) ) restricted_to_base_year_value_list = [ parameter_at_instant for parameter_at_instant in sub_parameter.values_list if periods.instant(parameter_at_instant.instant_str).year == base_year ] # When value is changed in the base year if restricted_to_base_year_value_list: for parameter_at_instant in reversed(restricted_to_base_year_value_list): if parameter_at_instant.instant_str.startswith(str(base_year)): value = ( parameter_at_instant.value * (1 + inflator) if parameter_at_instant.value is not None else None ) sub_parameter.update( start = parameter_at_instant.instant_str.replace( str(base_year), str(base_year + 1) ), value = value, ) # Or use the value at that instant even when it is defined earlier tahn the base year else: value = ( sub_parameter("{}-12-31".format(base_year)) * (1 + inflator) if sub_parameter("{}-12-31".format(base_year)) is not None else None ) sub_parameter.update( start = "{}-01-01".format(base_year + 1), value = value )
def inflate_parameter_leaf(sub_parameter, base_year, inflator, unit_type='unit'): """ Inflate a Parameter leaf according to unit type Basic unit type are supposed by default Other admissible unit types are threshold_unit and rate_unit """ if isinstance(sub_parameter, Scale): if unit_type == 'threshold_unit': for bracket in sub_parameter.brackets: threshold = bracket.children['threshold'] inflate_parameter_leaf(threshold, base_year, inflator) return else: # Remove new values for year > base_year kept_instants_str = [ parameter_at_instant.instant_str for parameter_at_instant in sub_parameter.values_list if periods.instant(parameter_at_instant.instant_str).year <= base_year ] if not kept_instants_str: return last_admissible_instant_str = max(kept_instants_str) sub_parameter.update(start=last_admissible_instant_str, value=sub_parameter(last_admissible_instant_str)) restricted_to_base_year_value_list = [ parameter_at_instant for parameter_at_instant in sub_parameter.values_list if periods.instant(parameter_at_instant.instant_str).year == base_year ] # When value is changed in the base year if restricted_to_base_year_value_list: for parameter_at_instant in reversed( restricted_to_base_year_value_list): if parameter_at_instant.instant_str.startswith(str(base_year)): value = (parameter_at_instant.value * (1 + inflator) if parameter_at_instant.value is not None else None) sub_parameter.update( start=parameter_at_instant.instant_str.replace( str(base_year), str(base_year + 1)), value=value, ) # Or use the value at that instant even when it is defined earlier tahn the base year else: value = (sub_parameter("{}-12-31".format(base_year)) * (1 + inflator) if sub_parameter( "{}-12-31".format(base_year)) is not None else None) sub_parameter.update(start="{}-01-01".format(base_year + 1), value=value)
def get_formula(self, period=None): """ Returns the formula used to compute the variable at the given period. If no period is given and the variable has several formula, return the oldest formula. :returns: Formula used to compute the variable :rtype: function """ if not self.formulas: return None if period is None: return self.formulas.peekitem( index=0 )[1] # peekitem gets the 1st key-value tuple (the oldest start_date and formula). Return the formula. if isinstance(period, periods.Period): instant = period.start else: try: instant = periods.period(period).start except ValueError: instant = periods.instant(period) if self.end and instant.date > self.end: return None instant = str(instant) for start_date in reversed(self.formulas): if start_date <= instant: return self.formulas[start_date] return None
def variables_asof(tax_benefit_system, instant, variables_list=[]): if isinstance(instant, str): instant = periods.instant(instant) assert isinstance(instant, periods.Instant) if variables_list == []: variables_list = tax_benefit_system.variables.keys() for variable_name, variable in tax_benefit_system.variables.items(): if variable_name in variables_list: formulas = variable.formulas for instant_str in list(formulas.keys()): if periods.instant(instant_str) > instant: del formulas[instant_str] if variable.end is not None: if periods.instant(variable.end) >= instant: variable.end = None
def variables_asof(tax_benefit_system, instant, variables_list = []): if isinstance(instant, str): instant = periods.instant(instant) assert isinstance(instant, periods.Instant) if variables_list == []: variables_list = tax_benefit_system.variables.keys() for variable_name, variable in tax_benefit_system.variables.items(): if variable_name in variables_list: formulas = variable.formulas for instant_str in list(formulas.keys()): if periods.instant(instant_str) > instant: del formulas[instant_str] if variable.end is not None: if periods.instant(variable.end) >= instant: variable.end = None
def formula(famille, period, parameters): annee = periods.instant(period).year annee_n = periods.instant(annee).period('year') annee_n1 = annee_n.offset(-1, 'year') base_ressource_annuelle_membres_famille = famille.sum( famille.members('base_ressource_annuelle_individu', annee_n1)) # base_ressource_mensuelle_famille_n1 = famille.sum( # famille('base_ressource_mensuelle_famille', annee_n1, options = [ADD]) # ) return ( # famille('base_ressource_mensuelle_famille', period.offset(-1, 'month')) +famille('base_ressource_mensuelle_famille', period.offset(-2, 'month')) + famille('base_ressource_mensuelle_famille', period.offset(-3, 'month')) + famille('base_ressource_mensuelle_famille', period.offset(-4, 'month')) + famille('base_ressource_mensuelle_famille', period.offset(-5, 'month')) + famille('base_ressource_mensuelle_famille', period.offset(-6, 'month')) + famille('base_ressource_mensuelle_famille', period.offset(-7, 'month')) + famille('base_ressource_mensuelle_famille', period.offset(-8, 'month')) + famille('base_ressource_mensuelle_famille', period.offset(-9, 'month')) + famille('base_ressource_mensuelle_famille', period.offset(-10, 'month')) + famille('base_ressource_mensuelle_famille', period.offset(-11, 'month')) + famille('base_ressource_mensuelle_famille', period.offset(-12, 'month')) + famille('base_ressource_mensuelle_famille', period.offset(-13, 'month')) # base_ressource_mensuelle_famille_n1 + base_ressource_annuelle_membres_famille)
def leaf_asof(sub_parameter, instant): kept_instants_str = [ parameter_at_instant.instant_str for parameter_at_instant in sub_parameter.values_list if periods.instant(parameter_at_instant.instant_str) <= instant ] if not kept_instants_str: sub_parameter.values_list = [] return last_admissible_instant_str = max(kept_instants_str) sub_parameter.update(start=last_admissible_instant_str, value=sub_parameter(last_admissible_instant_str)) return
def leaf_asof(sub_parameter, instant): kept_instants_str = [ parameter_at_instant.instant_str for parameter_at_instant in sub_parameter.values_list if periods.instant(parameter_at_instant.instant_str) <= instant ] if not kept_instants_str: sub_parameter.values_list = [] return last_admissible_instant_str = max(kept_instants_str) sub_parameter.update( start = last_admissible_instant_str, value = sub_parameter(last_admissible_instant_str) ) return
def parameters_asof(parameters, instant): if isinstance(instant, str): instant = periods.instant(instant) assert isinstance(instant, periods.Instant) for name, sub_parameter in parameters.children.items(): if isinstance(sub_parameter, ParameterNode): parameters_asof(sub_parameter, instant) else: if isinstance(sub_parameter, Scale): for bracket in sub_parameter.brackets: threshold = bracket.children['threshold'] rate = bracket.children['rate'] leaf_asof(threshold, instant) leaf_asof(rate, instant) else: leaf_asof(sub_parameter, instant)
def test_inflate_scale_with_changing_number_of_brackets(): """ Test parameters inflator on a scale parameter when the number of brackets changes Use parameters_asof to use the present legislation the future pre-inflated legislation Test on the social security contributions tax_scale """ tax_benefit_system = CountryTaxBenefitSystem() parameters = tax_benefit_system.parameters parameters_asof( parameters, instant=periods.instant(2016)) # Remove post 2016 legislation changes inflate_parameters(parameters, inflator=.3, base_year=2016, last_year=2017) for (threshold_2017, threshold_2016) in zip( parameters.taxes.social_security_contribution(2017).thresholds, parameters.taxes.social_security_contribution(2016).thresholds): assert threshold_2017 == threshold_2016 * 1.3, "{} != {}".format( threshold_2017, threshold_2016 * 1.3)
def test_inflate_scale_with_changing_number_of_brackets(): """ Test parameters inflator on a scale parameter when the number of brackets changes Use parameters_asof to use the present legislation the future pre-inflated legislation Test on the social security contributions tax_scale """ tax_benefit_system = CountryTaxBenefitSystem() parameters = tax_benefit_system.parameters parameters_asof(parameters, instant = periods.instant(2016)) # Remove post 2016 legislation changes inflate_parameters(parameters, inflator = .3, base_year = 2016, last_year = 2017) for (threshold_2017, threshold_2016) in zip( parameters.taxes.social_security_contribution(2017).thresholds, parameters.taxes.social_security_contribution(2016).thresholds ): assert threshold_2017 == threshold_2016 * 1.3, "{} != {}".format( threshold_2017, threshold_2016 * 1.3 )
def get_parameters_at_instant(self, instant): """ Get the parameters of the legislation at a given instant :param instant: string of the format 'YYYY-MM-DD' or `openfisca_core.periods.Instant` instance. :returns: The parameters of the legislation at a given instant. :rtype: :any:`ParameterNodeAtInstant` """ if isinstance(instant, periods.Period): instant = instant.start elif isinstance(instant, (str, int)): instant = periods.instant(instant) else: assert isinstance(instant, periods.Instant), "Expected an Instant (e.g. Instant((2017, 1, 1)) ). Got: {}.".format(instant) parameters_at_instant = self._parameters_at_instant_cache.get(instant) if parameters_at_instant is None and self.parameters is not None: parameters_at_instant = self.parameters.get_at_instant(str(instant)) self._parameters_at_instant_cache[instant] = parameters_at_instant return parameters_at_instant
def parameters_asof(parameters, instant): if isinstance(instant, str): instant = periods.instant(instant) assert isinstance(instant, periods.Instant) for sub_parameter in parameters.children.values(): if isinstance(sub_parameter, ParameterNode): parameters_asof(sub_parameter, instant) else: if isinstance(sub_parameter, Scale): for bracket in sub_parameter.brackets: threshold = bracket.children['threshold'] rate = bracket.children.get('rate') amount = bracket.children.get('amount') leaf_asof(threshold, instant) if rate: leaf_asof(rate, instant) if amount: leaf_asof(amount, instant) else: leaf_asof(sub_parameter, instant)
def get_parameters_at_instant(self, instant): """ Get the parameters of the legislation at a given instant :param instant: string of the format 'YYYY-MM-DD' or `openfisca_core.periods.Instant` instance. :returns: The parameters of the legislation at a given instant. """ if isinstance(instant, periods.Period): instant = instant.start elif isinstance(instant, (basestring_type, int)): instant = periods.instant(instant) else: assert isinstance( instant, periods.Instant ), "Expected an Instant (e.g. Instant((2017, 1, 1)) ). Got: {}.".format( instant) parameters_at_instant = self._parameters_at_instant_cache.get(instant) if parameters_at_instant is None and self.parameters is not None: parameters_at_instant = self.parameters.get_at_instant( str(instant)) self._parameters_at_instant_cache[instant] = parameters_at_instant return parameters_at_instant
def get_parameters_at_instant(self, instant): """ Get the parameters of the legislation at a given instant :param instant: :obj:`str` of the format 'YYYY-MM-DD' or :class:`.Instant` instance. :returns: The parameters of the legislation at a given instant. :rtype: :class:`.ParameterNodeAtInstant` """ if isinstance(instant, Period): instant = instant.start elif isinstance(instant, (str, int)): instant = periods.instant(instant) else: assert isinstance( instant, Instant ), "Expected an Instant (e.g. Instant((2017, 1, 1)) ). Got: {}.".format( instant) parameters_at_instant = self._parameters_at_instant_cache.get(instant) if parameters_at_instant is None and self.parameters is not None: parameters_at_instant = self.parameters.get_at_instant( str(instant)) self._parameters_at_instant_cache[instant] = parameters_at_instant return parameters_at_instant
def instant(): return periods.instant("2018")
def generateSituation(file): parser = etree.XMLParser(remove_blank_text=True) tree = etree.parse(file, parser) dateDemande = periods.instant( tree.find('//ns1:dateCreationDemande', namespaces=namespaces).text) moisDemande = dateDemande.period('month') moisDemandeKey = str(moisDemande) last4Years = [ moisDemande.offset(offset, 'month') for offset in range(-12 * 4 + 1, 1) ] def duplicateMonthly(value): return {str(m): value for m in last4Years} def expand(entity, additions={}): result = {field: duplicateMonthly(entity[field]) for field in entity} for k in additions: result[k] = additions[k] return result details = tree.find('//ns1:personnePhysique', namespaces=namespaces) famille = { 'aide_logement': 0, # Met à zéro l'aide au logement perçu par défaut } sourceDemandeur = details.find('ns1:demandeur', namespaces=namespaces) demandeur = processIndividu(sourceDemandeur, famille) rfr = getRFR( sourceDemandeur.find('ns1:revenuFiscal', namespaces=namespaces)) situation = { 'individus': { 'demandeur': expand(demandeur) }, 'familles': { '_': expand(famille, {'parents': ['demandeur']}) }, 'foyers_fiscaux': { '_': { 'declarants': ['demandeur'], 'rfr': rfr, } }, 'menages': { '_': { 'personne_de_reference': ['demandeur'], 'depcom': duplicateMonthly('75113'), 'loyer': duplicateMonthly(800), 'statut_occupation_logement': duplicateMonthly('locataire_hlm'), } } } situation['familles']['_']['aide_logement'][moisDemandeKey] = None print(json.dumps(situation, indent=2)) return situation
def check_max_instant_leaf(sub_parameter, instant): for parameter_at_instant in sub_parameter.values_list: assert periods.instant(parameter_at_instant.instant_str) <= instant, "Error for {}: \n {}".format( sub_parameter.name, sub_parameter.values_list)
def test_variables_as_of(): tax_benefit_system = CountryTaxBenefitSystem() instant = periods.instant("2015-12-31") variables_asof(tax_benefit_system, instant)
def test_parameters_as_of(): tax_benefit_system = CountryTaxBenefitSystem() parameters = tax_benefit_system.parameters instant = periods.instant("2012-12-31") parameters_asof(parameters, instant) check_max_instant(parameters, instant)
def api1_parameters(req): ctx = contexts.Ctx(req) headers = wsgihelpers.handle_cross_origin_resource_sharing(ctx) assert req.method == 'GET', req.method params = req.GET inputs = dict( instant = params.get('instant'), names = params.getall('name'), ) parameters_name = [ parameter_json['name'] for parameter_json in model.parameters_json_cache ] data, errors = conv.pipe( conv.struct( dict( instant = conv.pipe( conv.empty_to_none, conv.test_isinstance(basestring), conv.function(lambda str: periods.instant(str)), ), names = conv.pipe( conv.uniform_sequence( conv.pipe( conv.empty_to_none, conv.test_in(parameters_name, error = u'Parameter does not exist'), ), drop_none_items = True, ), conv.empty_to_none, ), ), default = 'drop', ), )(inputs, state = ctx) if errors is not None: return wsgihelpers.respond_json(ctx, collections.OrderedDict(sorted(dict( apiVersion = 1, error = collections.OrderedDict(sorted(dict( code = 400, # Bad Request errors = [conv.jsonify_value(errors)], message = ctx._(u'Bad parameters in request'), ).iteritems())), method = req.script_name, params = inputs, url = req.url.decode('utf-8'), ).iteritems())), headers = headers, ) tax_benefit_system = model.tax_benefit_system if data['instant'] is None: if data['names'] is None: parameters_json = model.parameters_json_cache else: parameters_json = [ parameter_json for parameter_json in model.parameters_json_cache if parameter_json['name'] in data['names'] ] else: instant = data['instant'] parameters_json = [] dated_legislation_json = legislations.generate_dated_legislation_json( tax_benefit_system.legislation_json, instant, ) for name in data['names']: name_fragments = name.split('.') parameter_json = dated_legislation_json for name_fragment in name_fragments: parameter_json = parameter_json['children'][name_fragment] parameter_json['name'] = name parameter_json_in_cache = [ parameter_json1 for parameter_json1 in model.parameters_json_cache if parameter_json1['name'] == name ][0] parameter_json['description'] = parameter_json_in_cache['description'] parameters_json.append(parameter_json) return wsgihelpers.respond_json(ctx, collections.OrderedDict(sorted(dict( apiVersion = 1, country_package_git_head_sha = environment.country_package_git_head_sha, currency = tax_benefit_system.CURRENCY, method = req.script_name, parameters = parameters_json, parameters_file_path = model.parameters_file_path, url = req.url.decode('utf-8'), ).iteritems())), headers = headers, )
def load_environment(global_conf, app_conf): """Configure the application environment.""" conf.update(strings.deep_decode(global_conf)) conf.update(strings.deep_decode(app_conf)) conf.update( conv.check( conv.struct( { 'app_conf': conv.set_value(app_conf), 'app_dir': conv.set_value(app_dir), 'country_package': conv.pipe( conv.make_input_to_slug(separator=u'_'), conv.test_in(( u'openfisca_france', u'openfisca_tunisia', u'openfisca_tunisia_pension', )), conv.not_none, ), 'debug': conv.pipe(conv.guess_bool, conv.default(False)), 'global_conf': conv.set_value(global_conf), 'i18n_dir': conv.default(os.path.join(app_dir, 'i18n')), 'load_alert': conv.pipe(conv.guess_bool, conv.default(False)), 'log_level': conv.pipe( conv.default('WARNING'), conv.function(lambda log_level: getattr( logging, log_level.upper())), ), 'package_name': conv.default('openfisca-web-api'), 'realm': conv.default(u'OpenFisca Web API'), 'reforms': conv.ini_str_to_list, # Another validation is done below. }, default='drop', ))(conf)) # Configure logging. logging.basicConfig(level=conf['log_level'], stream=sys.stderr) errorware = conf.setdefault('errorware', {}) errorware['debug'] = conf['debug'] if not errorware['debug']: errorware['error_email'] = conf['email_to'] errorware['error_log'] = conf.get('error_log', None) errorware['error_message'] = conf.get( 'error_message', 'An internal server error occurred') errorware['error_subject_prefix'] = conf.get( 'error_subject_prefix', 'OpenFisca Web API Error: ') errorware['from_address'] = conf['from_address'] errorware['smtp_server'] = conf.get('smtp_server', 'localhost') # Initialize tax-benefit system. country_package = importlib.import_module(conf['country_package']) CountryTaxBenefitSystem = country_package.init_country() class Scenario(CountryTaxBenefitSystem.Scenario): instance_and_error_couple_cache = {} if conf[ 'debug'] else weakref.WeakValueDictionary() # class attribute @classmethod def make_json_to_cached_or_new_instance(cls, ctx, repair, tax_benefit_system): def json_to_cached_or_new_instance(value, state=None): key = (unicode(ctx.lang), unicode(value), repair, tax_benefit_system) instance_and_error_couple = cls.instance_and_error_couple_cache.get( key) if instance_and_error_couple is None: instance_and_error_couple = cls.make_json_to_instance( repair, tax_benefit_system)(value, state=state or conv.default_state) # Note: Call to ValueAndError() is needed below, otherwise it raises TypeError: cannot create # weak reference to 'tuple' object. cls.instance_and_error_couple_cache[key] = ValueAndError( instance_and_error_couple) return instance_and_error_couple return json_to_cached_or_new_instance class TaxBenefitSystem(CountryTaxBenefitSystem): pass TaxBenefitSystem.Scenario = Scenario model.TaxBenefitSystem = TaxBenefitSystem model.tax_benefit_system = tax_benefit_system = TaxBenefitSystem() tax_benefit_system.prefill_cache() # Initialize reforms build_reform_functions = conv.check( conv.uniform_sequence(conv.module_and_function_str_to_function, ))( conf['reforms']) if build_reform_functions is not None: api_reforms = [ build_reform(tax_benefit_system) for build_reform in build_reform_functions ] api_reforms = conv.check( conv.uniform_sequence(conv.test_isinstance( reforms.AbstractReform)))(api_reforms) model.build_reform_function_by_key = { reform.key: build_reform for build_reform, reform in zip(build_reform_functions, api_reforms) } model.reform_by_full_key = { reform.full_key: reform for reform in api_reforms } # Cache default decomposition. if hasattr(tax_benefit_system, 'DEFAULT_DECOMP_FILE'): model.get_cached_or_new_decomposition_json(tax_benefit_system) # Compute and cache compact legislation for each first day of month since at least 2 legal years. today = periods.instant(datetime.date.today()) first_day_of_year = today.offset('first-of', 'year') instant = first_day_of_year.offset(-2, 'year') two_years_later = first_day_of_year.offset(2, 'year') while instant < two_years_later: tax_benefit_system.get_compact_legislation(instant) instant = instant.offset(1, 'month') # Initialize lib2to3-based input variables extractor. if input_variables_extractors is not None: model.input_variables_extractor = input_variables_extractors.setup( tax_benefit_system) global country_package_dir_path country_package_dir_path = pkg_resources.get_distribution( conf['country_package']).location # Store Git last commit SHA global git_head_sha git_head_sha = get_git_head_sha() global country_package_git_head_sha country_package_git_head_sha = get_git_head_sha( cwd=country_package.__path__[0]) # Cache legislation JSON with references to original XML legislation_json_with_references_to_xml = tax_benefit_system.get_legislation_json( with_source_file_infos=True) parameters_json = [] walk_legislation_json( legislation_json_with_references_to_xml, descriptions=[], parameters_json=parameters_json, path_fragments=[], ) model.parameters_json_cache = parameters_json # Initialize multiprocessing and load_alert if conf['load_alert']: global cpu_count cpu_count = multiprocessing.cpu_count()
def load_environment(global_conf, app_conf): """Configure the application environment.""" conf.update(strings.deep_decode(global_conf)) conf.update(strings.deep_decode(app_conf)) conf.update(conv.check(conv.struct( { 'app_conf': conv.set_value(app_conf), 'app_dir': conv.set_value(app_dir), 'country_package': conv.pipe( conv.make_input_to_slug(separator = u'_'), conv.test_in(( u'openfisca_france', u'openfisca_tunisia', u'openfisca_tunisia_pension', )), conv.not_none, ), 'debug': conv.pipe(conv.guess_bool, conv.default(False)), 'global_conf': conv.set_value(global_conf), 'i18n_dir': conv.default(os.path.join(app_dir, 'i18n')), 'load_alert': conv.pipe(conv.guess_bool, conv.default(False)), 'log_level': conv.pipe( conv.default('WARNING'), conv.function(lambda log_level: getattr(logging, log_level.upper())), ), 'package_name': conv.default('openfisca-web-api'), 'realm': conv.default(u'OpenFisca Web API'), 'reforms': conv.ini_str_to_list, # Another validation is done below. 'extensions': conv.ini_str_to_list, }, default = 'drop', ))(conf)) # Configure logging. logging.basicConfig(level = conf['log_level'], stream = sys.stderr) errorware = conf.setdefault('errorware', {}) errorware['debug'] = conf['debug'] if not errorware['debug']: errorware['error_email'] = conf['email_to'] errorware['error_log'] = conf.get('error_log', None) errorware['error_message'] = conf.get('error_message', 'An internal server error occurred') errorware['error_subject_prefix'] = conf.get('error_subject_prefix', 'OpenFisca Web API Error: ') errorware['from_address'] = conf['from_address'] errorware['smtp_server'] = conf.get('smtp_server', 'localhost') errorware['show_exceptions_in_wsgi_errors'] = conf.get('show_exceptions_in_wsgi_errors', True) # Initialize tax-benefit system. country_package = importlib.import_module(conf['country_package']) tax_benefit_system = country_package.CountryTaxBenefitSystem() extensions = conf['extensions'] if extensions is not None: for extension in extensions: tax_benefit_system.load_extension(extension) class Scenario(tax_benefit_system.Scenario): instance_and_error_couple_cache = {} if conf['debug'] else weakref.WeakValueDictionary() # class attribute @classmethod def make_json_to_cached_or_new_instance(cls, ctx, repair, tax_benefit_system): def json_to_cached_or_new_instance(value, state = None): key = (unicode(ctx.lang), unicode(value), repair, tax_benefit_system) instance_and_error_couple = cls.instance_and_error_couple_cache.get(key) if instance_and_error_couple is None: instance_and_error_couple = cls.make_json_to_instance(repair, tax_benefit_system)( value, state = state or conv.default_state) # Note: Call to ValueAndError() is needed below, otherwise it raises TypeError: cannot create # weak reference to 'tuple' object. cls.instance_and_error_couple_cache[key] = ValueAndError(instance_and_error_couple) return instance_and_error_couple return json_to_cached_or_new_instance tax_benefit_system.Scenario = Scenario model.tax_benefit_system = tax_benefit_system log.debug(u'Pre-fill tax and benefit system cache.') tax_benefit_system.prefill_cache() log.debug(u'Initialize reforms.') reforms = conv.check( conv.uniform_sequence( conv.module_and_function_str_to_function, ) )(conf['reforms']) model.reforms = {} model.reformed_tbs = {} if reforms is not None: for reform in reforms: reformed_tbs = reform(tax_benefit_system) key = reformed_tbs.key full_key = reformed_tbs.full_key model.reforms[key] = reform model.reformed_tbs[full_key] = reformed_tbs log.debug(u'Cache default decomposition.') if tax_benefit_system.decomposition_file_path is not None: # Ignore the returned value, because we just want to pre-compute the cache. model.get_cached_or_new_decomposition_json(tax_benefit_system) log.debug(u'Initialize lib2to3-based input variables extractor.') if input_variables_extractors is not None: model.input_variables_extractor = input_variables_extractors.setup(tax_benefit_system) global country_package_dir_path # Using pkg_resources.get_distribution(conf["country_package"]).location # returns a wrong path in virtualenvs (<venv>/lib versus <venv>/local/lib). country_package_dir_path = country_package.__path__[0] global api_package_version api_package_version = pkg_resources.get_distribution('openfisca_web_api').version global country_package_version country_package_version = pkg_resources.get_distribution(conf["country_package"]).version log.debug(u'Cache legislation JSON with references to original XML.') legislation_json = tax_benefit_system.get_legislation(with_source_file_infos=True) parameters_json = [] walk_legislation_json( legislation_json, descriptions = [], parameters_json = parameters_json, path_fragments = [], ) model.parameters_json_cache = parameters_json if not conf['debug']: # Do this after tax_benefit_system.get_legislation(with_source_file_infos=True). log.debug(u'Compute and cache compact legislation for each first day of month since at least 2 legal years.') today = periods.instant(datetime.date.today()) first_day_of_year = today.offset('first-of', 'year') instant = first_day_of_year.offset(-2, 'year') two_years_later = first_day_of_year.offset(2, 'year') while instant < two_years_later: tax_benefit_system.get_compact_legislation(instant) instant = instant.offset(1, 'month') # Initialize multiprocessing and load_alert if conf['load_alert']: global cpu_count cpu_count = multiprocessing.cpu_count()
def __init__(self, tbs: FranceTaxBenefitSystem, payload: dict, period: str) -> None: self.payload = payload.get("impot_revenu", {}) self.instant = periods.instant(period) self.period = periods.period("year:1900:200") super().__init__(tbs)
def load_environment(global_conf, app_conf): """Configure the application environment.""" conf = openfisca_web_api.conf # Empty dictionary conf.update(strings.deep_decode(global_conf)) conf.update(strings.deep_decode(app_conf)) conf.update(conv.check(conv.struct( { 'app_conf': conv.set_value(app_conf), 'app_dir': conv.set_value(app_dir), 'country_package': conv.pipe( conv.make_input_to_slug(separator = u'_'), conv.test_in(( u'openfisca_france', u'openfisca_tunisia', u'openfisca_tunisia_pension', )), conv.not_none, ), 'debug': conv.pipe(conv.guess_bool, conv.default(False)), 'global_conf': conv.set_value(global_conf), 'i18n_dir': conv.default(os.path.join(app_dir, 'i18n')), 'load_alert': conv.pipe(conv.guess_bool, conv.default(False)), 'log_level': conv.pipe( conv.default('WARNING'), conv.function(lambda log_level: getattr(logging, log_level.upper())), ), 'package_name': conv.default('openfisca-web-api'), 'realm': conv.default(u'OpenFisca Web API'), 'reforms': conv.ini_items_list_to_ordered_dict, # Another validation is done below. }, default = 'drop', ))(conf)) # Configure logging. logging.basicConfig(level = conf['log_level'], stream = sys.stderr) errorware = conf.setdefault('errorware', {}) errorware['debug'] = conf['debug'] if not errorware['debug']: errorware['error_email'] = conf['email_to'] errorware['error_log'] = conf.get('error_log', None) errorware['error_message'] = conf.get('error_message', 'An internal server error occurred') errorware['error_subject_prefix'] = conf.get('error_subject_prefix', 'OpenFisca Web API Error: ') errorware['from_address'] = conf['from_address'] errorware['smtp_server'] = conf.get('smtp_server', 'localhost') # Initialize tax-benefit system. country_package = importlib.import_module(conf['country_package']) CountryTaxBenefitSystem = country_package.init_country() class Scenario(CountryTaxBenefitSystem.Scenario): instance_and_error_couple_by_json_str_cache = weakref.WeakValueDictionary() # class attribute @classmethod def cached_or_new(cls): return conv.check(cls.json_to_cached_or_new_instance)(None) @classmethod def make_json_to_cached_or_new_instance(cls, repair, tax_benefit_system): def json_to_cached_or_new_instance(value, state = None): json_str = json.dumps(value, separators = (',', ':')) if value is not None else None instance_and_error_couple = cls.instance_and_error_couple_by_json_str_cache.get(json_str) if instance_and_error_couple is None: instance_and_error_couple = cls.make_json_to_instance(repair, tax_benefit_system)( value, state = state or conv.default_state) # Note: Call to ValueAndError() is needed below, otherwise it raises TypeError: cannot create # weak reference to 'tuple' object. cls.instance_and_error_couple_by_json_str_cache[json_str] = ValueAndError( instance_and_error_couple) return instance_and_error_couple return json_to_cached_or_new_instance class TaxBenefitSystem(CountryTaxBenefitSystem): pass TaxBenefitSystem.Scenario = Scenario model.TaxBenefitSystem = TaxBenefitSystem model.tax_benefit_system = tax_benefit_system = TaxBenefitSystem() tax_benefit_system.prefill_cache() # Cache default decomposition. model.get_cached_or_new_decomposition_json(tax_benefit_system) # Compute and cache compact legislation for each first day of month since at least 2 legal years. today = periods.instant(datetime.date.today()) first_day_of_year = today.offset('first-of', 'year') instant = first_day_of_year.offset(-2, 'year') two_years_later = first_day_of_year.offset(2, 'year') while instant < two_years_later: tax_benefit_system.get_compact_legislation(instant) instant = instant.offset(1, 'month') # Initialize lib2to3-based input variables extractor. if input_variables_extractors is not None: model.input_variables_extractor = input_variables_extractors.setup(tax_benefit_system) # Store Git last commit SHA global last_commit_sha last_commit_sha = get_git_last_commit_sha() # Load reform modules and store build_reform functions. model.build_reform_function_by_key = build_reform_function_by_key = conv.check( conv.uniform_mapping( conv.noop, conv.module_function_str_to_function, )(conf['reforms']) ) # Check that each reform builds and cache instances. Must not be used with composed reforms. model.reform_by_key = conv.check( conv.uniform_mapping( conv.noop, conv.pipe( conv.function(lambda build_reform: build_reform(tax_benefit_system)), conv.test_isinstance(reforms.AbstractReform), ), )(build_reform_function_by_key) )
def load_environment(global_conf, app_conf): """Configure the application environment.""" conf.update(strings.deep_decode(global_conf)) conf.update(strings.deep_decode(app_conf)) conf.update(conv.check(conv.struct( { 'app_conf': conv.set_value(app_conf), 'app_dir': conv.set_value(app_dir), 'country_package': conv.pipe( conv.make_input_to_slug(separator = u'_'), conv.test_in(( u'openfisca_france', u'openfisca_tunisia', u'openfisca_tunisia_pension', )), conv.not_none, ), 'debug': conv.pipe(conv.guess_bool, conv.default(False)), 'global_conf': conv.set_value(global_conf), 'i18n_dir': conv.default(os.path.join(app_dir, 'i18n')), 'load_alert': conv.pipe(conv.guess_bool, conv.default(False)), 'log_level': conv.pipe( conv.default('WARNING'), conv.function(lambda log_level: getattr(logging, log_level.upper())), ), 'package_name': conv.default('openfisca-web-api'), 'realm': conv.default(u'OpenFisca Web API'), 'reforms': conv.ini_str_to_list, # Another validation is done below. }, default = 'drop', ))(conf)) # Configure logging. logging.basicConfig(level = conf['log_level'], stream = sys.stderr) errorware = conf.setdefault('errorware', {}) errorware['debug'] = conf['debug'] if not errorware['debug']: errorware['error_email'] = conf['email_to'] errorware['error_log'] = conf.get('error_log', None) errorware['error_message'] = conf.get('error_message', 'An internal server error occurred') errorware['error_subject_prefix'] = conf.get('error_subject_prefix', 'OpenFisca Web API Error: ') errorware['from_address'] = conf['from_address'] errorware['smtp_server'] = conf.get('smtp_server', 'localhost') # Initialize tax-benefit system. country_package = importlib.import_module(conf['country_package']) CountryTaxBenefitSystem = country_package.init_country() class Scenario(CountryTaxBenefitSystem.Scenario): instance_and_error_couple_cache = {} if conf['debug'] else weakref.WeakValueDictionary() # class attribute @classmethod def make_json_to_cached_or_new_instance(cls, ctx, repair, tax_benefit_system): def json_to_cached_or_new_instance(value, state = None): key = (unicode(ctx.lang), unicode(value), repair, tax_benefit_system) instance_and_error_couple = cls.instance_and_error_couple_cache.get(key) if instance_and_error_couple is None: instance_and_error_couple = cls.make_json_to_instance(repair, tax_benefit_system)( value, state = state or conv.default_state) # Note: Call to ValueAndError() is needed below, otherwise it raises TypeError: cannot create # weak reference to 'tuple' object. cls.instance_and_error_couple_cache[key] = ValueAndError(instance_and_error_couple) return instance_and_error_couple return json_to_cached_or_new_instance class TaxBenefitSystem(CountryTaxBenefitSystem): pass TaxBenefitSystem.Scenario = Scenario model.TaxBenefitSystem = TaxBenefitSystem model.tax_benefit_system = tax_benefit_system = TaxBenefitSystem() tax_benefit_system.prefill_cache() # Initialize reforms build_reform_functions = conv.check( conv.uniform_sequence( conv.module_and_function_str_to_function, ) )(conf['reforms']) if build_reform_functions is not None: api_reforms = [ build_reform(tax_benefit_system) for build_reform in build_reform_functions ] api_reforms = conv.check( conv.uniform_sequence(conv.test_isinstance(reforms.AbstractReform)) )(api_reforms) model.build_reform_function_by_key = { reform.key: build_reform for build_reform, reform in zip(build_reform_functions, api_reforms) } model.reform_by_full_key = { reform.full_key: reform for reform in api_reforms } # Cache default decomposition. if hasattr(tax_benefit_system, 'DEFAULT_DECOMP_FILE'): model.get_cached_or_new_decomposition_json(tax_benefit_system) # Compute and cache compact legislation for each first day of month since at least 2 legal years. today = periods.instant(datetime.date.today()) first_day_of_year = today.offset('first-of', 'year') instant = first_day_of_year.offset(-2, 'year') two_years_later = first_day_of_year.offset(2, 'year') while instant < two_years_later: tax_benefit_system.get_compact_legislation(instant) instant = instant.offset(1, 'month') # Initialize lib2to3-based input variables extractor. if input_variables_extractors is not None: model.input_variables_extractor = input_variables_extractors.setup(tax_benefit_system) global country_package_dir_path country_package_dir_path = pkg_resources.get_distribution(conf['country_package']).location # Store Git last commit SHA global git_head_sha git_head_sha = get_git_head_sha() global country_package_git_head_sha country_package_git_head_sha = get_git_head_sha(cwd = country_package.__path__[0]) # Cache legislation JSON with references to original XML legislation_json_with_references_to_xml = tax_benefit_system.get_legislation_json(with_source_file_infos = True) parameters_json = [] walk_legislation_json( legislation_json_with_references_to_xml, descriptions = [], parameters_json = parameters_json, path_fragments = [], ) model.parameters_json_cache = parameters_json # Initialize multiprocessing and load_alert if conf['load_alert']: global cpu_count cpu_count = multiprocessing.cpu_count()
def get_at_instant(self, instant): instant = str(periods.instant(instant)) return self._get_at_instant(instant)
def api1_parameters(req): ctx = contexts.Ctx(req) headers = wsgihelpers.handle_cross_origin_resource_sharing(ctx) assert req.method == 'GET', req.method params = req.GET inputs = dict( instant=params.get('instant'), names=params.getall('name'), ) parameters_name = [ parameter_json['name'] for parameter_json in model.parameters_json_cache ] data, errors = conv.pipe( conv.struct( dict( instant=conv.pipe( conv.empty_to_none, conv.test_isinstance(basestring), conv.function(lambda str: periods.instant(str)), ), names=conv.pipe( conv.uniform_sequence( conv.pipe( conv.empty_to_none, conv.test_in(parameters_name, error=u'Parameter does not exist'), ), drop_none_items=True, ), conv.empty_to_none, ), ), default='drop', ), )(inputs, state=ctx) if errors is not None: return wsgihelpers.respond_json( ctx, collections.OrderedDict( sorted( dict( apiVersion=1, error=collections.OrderedDict( sorted( dict( code=400, # Bad Request errors=[conv.jsonify_value(errors)], message=ctx._( u'Bad parameters in request'), ).iteritems())), method=req.script_name, params=inputs, url=req.url.decode('utf-8'), ).iteritems())), headers=headers, ) tax_benefit_system = model.tax_benefit_system if data['instant'] is None: if data['names'] is None: parameters_json = model.parameters_json_cache else: parameters_json = [ parameter_json for parameter_json in model.parameters_json_cache if parameter_json['name'] in data['names'] ] else: instant = data['instant'] parameters_json = [] dated_legislation_json = legislations.generate_dated_legislation_json( tax_benefit_system.get_legislation(), instant, ) for name in data['names']: name_fragments = name.split('.') parameter_json = dated_legislation_json for name_fragment in name_fragments: parameter_json = parameter_json['children'][name_fragment] parameter_json['name'] = name parameter_json_in_cache = [ parameter_json1 for parameter_json1 in model.parameters_json_cache if parameter_json1['name'] == name ][0] parameter_json['description'] = parameter_json_in_cache[ 'description'] parameters_json.append(parameter_json) response_dict = dict( apiVersion=1, country_package_name=conf['country_package'], country_package_version=environment.country_package_version, method=req.script_name, parameters=parameters_json, url=req.url.decode('utf-8'), ) if hasattr(tax_benefit_system, 'CURRENCY'): response_dict['currency'] = tax_benefit_system.CURRENCY return wsgihelpers.respond_json( ctx, collections.OrderedDict(sorted(response_dict.iteritems())), headers=headers, )
def check_max_instant_leaf(sub_parameter, instant): for parameter_at_instant in sub_parameter.values_list: assert periods.instant(parameter_at_instant.instant_str ) <= instant, "Error for {}: \n {}".format( sub_parameter.name, sub_parameter.values_list)