diff --git a/Makefile b/Makefile index bcb8c88e..ac3fb5b3 100644 --- a/Makefile +++ b/Makefile @@ -186,20 +186,17 @@ FRENCH_LAW_OCAML_LIB_DIR=french_law/ocaml FRENCH_LAW_JS_LIB_DIR=french_law/js FRENCH_LAW_PYTHON_LIB_DIR=french_law/python -$(FRENCH_LAW_PYTHON_LIB_DIR)/allocations_familiales.py: .FORCE +$(FRENCH_LAW_PYTHON_LIB_DIR)/src/allocations_familiales.py: .FORCE CATALA_OPTS="$(CATALA_OPTS) -O -t" $(MAKE) -C $(ALLOCATIONS_FAMILIALES_DIR) allocations_familiales.py - cp -f $(ALLOCATIONS_FAMILIALES_DIR)/allocations_familiales.py \ - $(FRENCH_LAW_PYTHON_LIB_DIR)/ + cp -f $(ALLOCATIONS_FAMILIALES_DIR)/allocations_familiales.py $@ $(FRENCH_LAW_OCAML_LIB_DIR)/law_source/allocations_familiales.ml: .FORCE CATALA_OPTS="$(CATALA_OPTS) -O -t" $(MAKE) -C $(ALLOCATIONS_FAMILIALES_DIR) allocations_familiales.ml - cp -f $(ALLOCATIONS_FAMILIALES_DIR)/allocations_familiales.ml \ - $(FRENCH_LAW_OCAML_LIB_DIR)/law_source + cp -f $(ALLOCATIONS_FAMILIALES_DIR)/allocations_familiales.ml $@ $(FRENCH_LAW_OCAML_LIB_DIR)/law_source/unit_tests/tests_allocations_familiales.ml: .FORCE CATALA_OPTS="$(CATALA_OPTS) -O -t" $(MAKE) -s -C $(ALLOCATIONS_FAMILIALES_DIR) tests/tests_allocations_familiales.ml - cp -f $(ALLOCATIONS_FAMILIALES_DIR)/tests/tests_allocations_familiales.ml \ - $(FRENCH_LAW_OCAML_LIB_DIR)/law_source/unit_tests/ + cp -f $(ALLOCATIONS_FAMILIALES_DIR)/tests/tests_allocations_familiales.ml $@ #> generate_french_law_library_ocaml : Generates the French law library OCaml sources from Catala generate_french_law_library_ocaml:\ @@ -227,7 +224,7 @@ build_french_law_library_js: generate_french_law_library_ocaml format #> generate_french_law_library_python : Generates the French law library Python sources from Catala generate_french_law_library_python:\ - $(FRENCH_LAW_PYTHON_LIB_DIR)/allocations_familiales.py + $(FRENCH_LAW_PYTHON_LIB_DIR)/src/allocations_familiales.py . $(FRENCH_LAW_PYTHON_LIB_DIR)/env/bin/activate ;\ $(MAKE) -C $(FRENCH_LAW_PYTHON_LIB_DIR) format diff --git a/compiler/scalc/to_python.ml b/compiler/scalc/to_python.ml index df54624a..fc2a3f91 100644 --- a/compiler/scalc/to_python.ml +++ b/compiler/scalc/to_python.ml @@ -299,11 +299,21 @@ and format_block (ctx : Dcalc.Ast.decl_ctx) (fmt : Format.formatter) (b : block) let format_ctx (type_ordering : Scopelang.Dependency.TVertex.t list) (fmt : Format.formatter) (ctx : D.decl_ctx) : unit = let format_struct_decl fmt (struct_name, struct_fields) = - if List.length struct_fields = 0 then - Format.fprintf fmt "class %a(Unit):@\n\tpass@\n@\n" format_struct_name struct_name + if List.length struct_fields = 0 then failwith "no fields in the struct" else - Format.fprintf fmt "class %a:@\n\tdef __init__(self, %a) -> None:@\n%a@\n@\n" - format_struct_name struct_name + Format.fprintf fmt + "class %a:@\n\ + \tdef __init__(self, %a) -> None:@\n\ + %a@\n\ + @\n\ + \tdef __eq__(self, other: object) -> bool:@\n\ + \t\tif isinstance(other, %a):@\n\ + \t\t\treturn @[(%a)@]@\n\ + \t\telse:@\n\ + \t\t\treturn False@\n\ + @\n\ + \tdef __ne__(self, other: object) -> bool:@\n\ + \t\treturn not (self == other)" format_struct_name struct_name (Format.pp_print_list ~pp_sep:(fun fmt () -> Format.fprintf fmt ", ") (fun _fmt (struct_field, struct_field_type) -> @@ -315,6 +325,12 @@ let format_ctx (type_ordering : Scopelang.Dependency.TVertex.t list) (fmt : Form (fun _fmt (struct_field, _) -> Format.fprintf fmt "\t\tself.%a = %a" format_struct_field_name struct_field format_struct_field_name struct_field)) + struct_fields format_struct_name struct_name + (Format.pp_print_list + ~pp_sep:(fun fmt () -> Format.fprintf fmt " and@ ") + (fun _fmt (struct_field, _) -> + Format.fprintf fmt "self.%a == other.%a" format_struct_field_name struct_field + format_struct_field_name struct_field)) struct_fields in let format_enum_decl fmt (enum_name, enum_cons) = @@ -327,13 +343,24 @@ let format_ctx (type_ordering : Scopelang.Dependency.TVertex.t list) (fmt : Form class %a:@\n\ \tdef __init__(self, code: %a_Code, value: Any) -> None:@\n\ \t\tself.code = code@\n\ - \t\tself.value = value" format_enum_name enum_name + \t\tself.value = value@\n\ + @\n\ + @\n\ + \tdef __eq__(self, other: object) -> bool:@\n\ + \t\tif isinstance(other, %a):@\n\ + \t\t\treturn self.code == other.code and self.value == other.value@\n\ + \t\telse:@\n\ + \t\t\treturn False@\n\ + @\n\ + @\n\ + \tdef __ne__(self, other: object) -> bool:@\n\ + \t\treturn not (self == other)" format_enum_name enum_name (Format.pp_print_list ~pp_sep:(fun fmt () -> Format.fprintf fmt "@\n") (fun _fmt (i, enum_cons, enum_cons_type) -> Format.fprintf fmt "%a = %d" format_enum_cons_name enum_cons i)) (List.mapi (fun i (x, y) -> (i, x, y)) enum_cons) - format_enum_name enum_name format_enum_name enum_name + format_enum_name enum_name format_enum_name enum_name format_enum_name enum_name in let is_in_type_ordering s = @@ -367,7 +394,7 @@ let format_program (fmt : Format.formatter) (p : Ast.program) Format.fprintf fmt "# This file has been generated by the Catala compiler, do not edit!\n\ @\n\ - from .catala_runtime import *@\n\ + from .catala import *@\n\ from typing import Any, List, Callable, Tuple\n\ from enum import Enum\n\ @\n\ diff --git a/french_law/python/.gitignore b/french_law/python/.gitignore index 9063d14e..a1ba2597 100644 --- a/french_law/python/.gitignore +++ b/french_law/python/.gitignore @@ -1,6 +1,3 @@ env/ .mypy_cache/ -_static/ -_templates/ -doc/ **/__pycache__ diff --git a/french_law/python/Makefile b/french_law/python/Makefile index 35c24ad8..fb021685 100644 --- a/french_law/python/Makefile +++ b/french_law/python/Makefile @@ -1,4 +1,4 @@ -SOURCES=catala_runtime.py allocations_familiales.py +SOURCES=src/catala.py src/allocations_familiales.py main.py dependencies: pip install -r dependencies.txt @@ -7,9 +7,4 @@ type: mypy $(SOURCES) format: - autopep8 --in-place $(SOURCES) - -doc: - mkdir -p doc - sphinx-build ./ doc - ln -sf doc/index.html doc.html + autopep8 --in-place $(SOURCES) \ No newline at end of file diff --git a/french_law/python/catala_runtime.rst b/french_law/python/catala_runtime.rst deleted file mode 100644 index 65ad7f47..00000000 --- a/french_law/python/catala_runtime.rst +++ /dev/null @@ -1,7 +0,0 @@ -catala\_runtime module -====================== - -.. automodule:: catala_runtime - :members: - :undoc-members: - :show-inheritance: diff --git a/french_law/python/conf.py b/french_law/python/conf.py deleted file mode 100644 index 5f67f532..00000000 --- a/french_law/python/conf.py +++ /dev/null @@ -1,58 +0,0 @@ -# Configuration file for the Sphinx documentation builder. -# -# This file only contains a selection of the most common options. For a full -# list see the documentation: -# https://www.sphinx-doc.org/en/master/usage/configuration.html - -# -- Path setup -------------------------------------------------------------- - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -# -import os -import sys -sys.path.insert(0, os.path.abspath('.')) - - -# -- Project information ----------------------------------------------------- - -project = 'French law' -copyright = '2021, Denis Merigoux' -author = 'Denis Merigoux' - -# The full version, including alpha/beta/rc tags -release = '0.4.0' - - -# -- General configuration --------------------------------------------------- - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = [ - 'sphinx.ext.autodoc' -] - -# Add any paths that contain templates here, relative to this directory. -# templates_path = ['_templates'] - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -# This pattern also affects html_static_path and html_extra_path. -exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', 'doc', 'env'] - - -# -- Options for HTML output ------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -# -html_theme = 'alabaster' - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -# html_static_path = ['_static'] - -autodoc_member_order = 'bysource' diff --git a/french_law/python/dependencies.txt b/french_law/python/dependencies.txt index 8d8b0fb1..9b2ff2e7 100644 --- a/french_law/python/dependencies.txt +++ b/french_law/python/dependencies.txt @@ -3,5 +3,4 @@ typing mypy python-dateutil types-python-dateutil -sphinx autopep8 \ No newline at end of file diff --git a/french_law/python/index.rst b/french_law/python/index.rst deleted file mode 100644 index e4b8b45d..00000000 --- a/french_law/python/index.rst +++ /dev/null @@ -1,26 +0,0 @@ -.. French law documentation master file, created by - sphinx-quickstart on Mon Jun 21 16:33:54 2021. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. - -Welcome to French law's documentation! -====================================== - -.. toctree:: - :maxdepth: 2 - :caption: Contents: - -modules -======= - -.. toctree:: - :maxdepth: 2 - - modules - -Indices and tables -================== - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` diff --git a/french_law/python/main.py b/french_law/python/main.py new file mode 100755 index 00000000..a8cf096a --- /dev/null +++ b/french_law/python/main.py @@ -0,0 +1,34 @@ +#!python3 + +from src.catala import date_of_numbers, Unit, integer_of_int, money_of_units_int, no_input +from src.allocations_familiales import interface_allocations_familiales, InterfaceAllocationsFamilialesIn, EnfantEntree, PriseEnCharge, PriseEnCharge_Code, Collectivite, Collectivite_Code + + +def main(): + print(interface_allocations_familiales( + InterfaceAllocationsFamilialesIn( + date_courante_in=lambda _: date_of_numbers(2020, 4, 20), + enfants_in=lambda _: [ + EnfantEntree(d_identifiant=integer_of_int(0), d_remuneration_mensuelle=integer_of_int(0), + d_date_de_naissance=date_of_numbers(2003, 2, 2), + d_prise_en_charge=PriseEnCharge( + PriseEnCharge_Code.EffectiveEtPermanente, Unit()), + d_a_deja_ouvert_droit_aux_allocations_familiales=True), + EnfantEntree(d_identifiant=integer_of_int(1), d_remuneration_mensuelle=integer_of_int(300), + d_date_de_naissance=date_of_numbers(2013, 9, 30), + d_prise_en_charge=PriseEnCharge( + PriseEnCharge_Code.GardeAlterneePartageAllocations, Unit()), + d_a_deja_ouvert_droit_aux_allocations_familiales=True) + ], + ressources_menage_in=lambda _: money_of_units_int(30000), + residence_in=lambda _: Collectivite( + Collectivite_Code.Metropole, Unit()), + personne_charge_effective_permanente_est_parent_in=lambda _: True, + personne_charge_effective_permanente_remplit_titre_I_in=lambda _: True, + enfants_a_charge_in=no_input(), + montant_verse_in=no_input() + ))) + + +if __name__ == '__main__': + main() diff --git a/french_law/python/modules.rst b/french_law/python/modules.rst deleted file mode 100644 index bf37a93d..00000000 --- a/french_law/python/modules.rst +++ /dev/null @@ -1,4 +0,0 @@ -.. toctree:: - :maxdepth: 4 - - catala_runtime diff --git a/french_law/python/__init__.py b/french_law/python/src/__init__.py similarity index 100% rename from french_law/python/__init__.py rename to french_law/python/src/__init__.py diff --git a/french_law/python/allocations_familiales.py b/french_law/python/src/allocations_familiales.py similarity index 93% rename from french_law/python/allocations_familiales.py rename to french_law/python/src/allocations_familiales.py index 09b38b01..147bb57a 100644 --- a/french_law/python/allocations_familiales.py +++ b/french_law/python/src/allocations_familiales.py @@ -1,6 +1,6 @@ # This file has been generated by the Catala compiler, do not edit! -from .catala_runtime import * +from .catala import * from typing import Any, List, Callable, Tuple from enum import Enum @@ -18,6 +18,15 @@ class PriseEnCharge: self.code = code self.value = value + def __eq__(self, other: object) -> bool: + if isinstance(other, PriseEnCharge): + return self.code == other.code and self.value == other.value + else: + return False + + def __ne__(self, other: object) -> bool: + return not (self == other) + class SituationObligationScolaire_Code(Enum): Avant = 0 @@ -30,6 +39,15 @@ class SituationObligationScolaire: self.code = code self.value = value + def __eq__(self, other: object) -> bool: + if isinstance(other, SituationObligationScolaire): + return self.code == other.code and self.value == other.value + else: + return False + + def __ne__(self, other: object) -> bool: + return not (self == other) + class Collectivite_Code(Enum): Guadeloupe = 0 @@ -48,6 +66,15 @@ class Collectivite: self.code = code self.value = value + def __eq__(self, other: object) -> bool: + if isinstance(other, Collectivite): + return self.code == other.code and self.value == other.value + else: + return False + + def __ne__(self, other: object) -> bool: + return not (self == other) + class PriseEnCompte_Code(Enum): Complete = 0 @@ -60,6 +87,15 @@ class PriseEnCompte: self.code = code self.value = value + def __eq__(self, other: object) -> bool: + if isinstance(other, PriseEnCompte): + return self.code == other.code and self.value == other.value + else: + return False + + def __ne__(self, other: object) -> bool: + return not (self == other) + class VersementAllocations_Code(Enum): Normal = 0 @@ -71,6 +107,15 @@ class VersementAllocations: self.code = code self.value = value + def __eq__(self, other: object) -> bool: + if isinstance(other, VersementAllocations): + return self.code == other.code and self.value == other.value + else: + return False + + def __ne__(self, other: object) -> bool: + return not (self == other) + class ElementPrestationsFamiliales_Code(Enum): PrestationAccueilJeuneEnfant = 0 @@ -88,6 +133,15 @@ class ElementPrestationsFamiliales: self.code = code self.value = value + def __eq__(self, other: object) -> bool: + if isinstance(other, ElementPrestationsFamiliales): + return self.code == other.code and self.value == other.value + else: + return False + + def __ne__(self, other: object) -> bool: + return not (self == other) + class EnfantEntree: def __init__(self, d_identifiant: Integer, d_remuneration_mensuelle: Money, d_date_de_naissance: Date, d_prise_en_charge: PriseEnCharge, d_a_deja_ouvert_droit_aux_allocations_familiales: bool) -> None: @@ -97,6 +151,19 @@ class EnfantEntree: self.d_prise_en_charge = d_prise_en_charge self.d_a_deja_ouvert_droit_aux_allocations_familiales = d_a_deja_ouvert_droit_aux_allocations_familiales + def __eq__(self, other: object) -> bool: + if isinstance(other, EnfantEntree): + return (self.d_identifiant == other.d_identifiant and + self.d_remuneration_mensuelle == other.d_remuneration_mensuelle and + self.d_date_de_naissance == other.d_date_de_naissance and + self.d_prise_en_charge == other.d_prise_en_charge and + self.d_a_deja_ouvert_droit_aux_allocations_familiales == other.d_a_deja_ouvert_droit_aux_allocations_familiales) + else: + return False + + def __ne__(self, other: object) -> bool: + return not (self == other) + class Enfant: def __init__(self, identifiant: Integer, obligation_scolaire: SituationObligationScolaire, remuneration_mensuelle: Money, date_de_naissance: Date, age: Integer, prise_en_charge: PriseEnCharge, a_deja_ouvert_droit_aux_allocations_familiales: bool) -> None: @@ -108,6 +175,21 @@ class Enfant: self.prise_en_charge = prise_en_charge self.a_deja_ouvert_droit_aux_allocations_familiales = a_deja_ouvert_droit_aux_allocations_familiales + def __eq__(self, other: object) -> bool: + if isinstance(other, Enfant): + return (self.identifiant == other.identifiant and + self.obligation_scolaire == other.obligation_scolaire and + self.remuneration_mensuelle == other.remuneration_mensuelle and + self.date_de_naissance == other.date_de_naissance and + self.age == other.age and + self.prise_en_charge == other.prise_en_charge and + self.a_deja_ouvert_droit_aux_allocations_familiales == other.a_deja_ouvert_droit_aux_allocations_familiales) + else: + return False + + def __ne__(self, other: object) -> bool: + return not (self == other) + class SmicOut: def __init__(self, date_courante_out: Date, residence_out: Collectivite, brut_horaire_out: Money) -> None: @@ -115,6 +197,17 @@ class SmicOut: self.residence_out = residence_out self.brut_horaire_out = brut_horaire_out + def __eq__(self, other: object) -> bool: + if isinstance(other, SmicOut): + return (self.date_courante_out == other.date_courante_out and + self.residence_out == other.residence_out and + self.brut_horaire_out == other.brut_horaire_out) + else: + return False + + def __ne__(self, other: object) -> bool: + return not (self == other) + class SmicIn: def __init__(self, date_courante_in: Callable[[Unit], Date], residence_in: Callable[[Unit], Collectivite], brut_horaire_in: Callable[[Unit], Money]) -> None: @@ -122,6 +215,17 @@ class SmicIn: self.residence_in = residence_in self.brut_horaire_in = brut_horaire_in + def __eq__(self, other: object) -> bool: + if isinstance(other, SmicIn): + return (self.date_courante_in == other.date_courante_in and + self.residence_in == other.residence_in and + self.brut_horaire_in == other.brut_horaire_in) + else: + return False + + def __ne__(self, other: object) -> bool: + return not (self == other) + class PrestationsFamilialesOut: def __init__(self, droit_ouvert_out: Callable[[Enfant], bool], conditions_hors_age_out: Callable[[Enfant], bool], plafond_l512_3_2_out: Money, age_l512_3_2_out: Integer, regime_outre_mer_l751_1_out: bool, date_courante_out: Date, prestation_courante_out: ElementPrestationsFamiliales, residence_out: Collectivite, base_mensuelle_out: Money) -> None: @@ -135,6 +239,23 @@ class PrestationsFamilialesOut: self.residence_out = residence_out self.base_mensuelle_out = base_mensuelle_out + def __eq__(self, other: object) -> bool: + if isinstance(other, PrestationsFamilialesOut): + return (self.droit_ouvert_out == other.droit_ouvert_out and + self.conditions_hors_age_out == other.conditions_hors_age_out and + self.plafond_l512_3_2_out == other.plafond_l512_3_2_out and + self.age_l512_3_2_out == other.age_l512_3_2_out and + self.regime_outre_mer_l751_1_out == other.regime_outre_mer_l751_1_out and + self.date_courante_out == other.date_courante_out and + self.prestation_courante_out == other.prestation_courante_out and + self.residence_out == other.residence_out and + self.base_mensuelle_out == other.base_mensuelle_out) + else: + return False + + def __ne__(self, other: object) -> bool: + return not (self == other) + class PrestationsFamilialesIn: def __init__(self, droit_ouvert_in: Callable[[Unit], (Callable[[Enfant], bool])], conditions_hors_age_in: Callable[[Unit], (Callable[[Enfant], bool])], plafond_l512_3_2_in: Callable[[Unit], Money], age_l512_3_2_in: Callable[[Unit], Integer], regime_outre_mer_l751_1_in: Callable[[Unit], bool], date_courante_in: Callable[[Unit], Date], prestation_courante_in: Callable[[Unit], ElementPrestationsFamiliales], residence_in: Callable[[Unit], Collectivite], base_mensuelle_in: Callable[[Unit], Money]) -> None: @@ -148,28 +269,83 @@ class PrestationsFamilialesIn: self.residence_in = residence_in self.base_mensuelle_in = base_mensuelle_in + def __eq__(self, other: object) -> bool: + if isinstance(other, PrestationsFamilialesIn): + return (self.droit_ouvert_in == other.droit_ouvert_in and + self.conditions_hors_age_in == other.conditions_hors_age_in and + self.plafond_l512_3_2_in == other.plafond_l512_3_2_in and + self.age_l512_3_2_in == other.age_l512_3_2_in and + self.regime_outre_mer_l751_1_in == other.regime_outre_mer_l751_1_in and + self.date_courante_in == other.date_courante_in and + self.prestation_courante_in == other.prestation_courante_in and + self.residence_in == other.residence_in and + self.base_mensuelle_in == other.base_mensuelle_in) + else: + return False + + def __ne__(self, other: object) -> bool: + return not (self == other) + class AllocationFamilialesAvril2008Out: def __init__(self, age_minimum_alinea_1_l521_3_out: Integer) -> None: self.age_minimum_alinea_1_l521_3_out = age_minimum_alinea_1_l521_3_out + def __eq__(self, other: object) -> bool: + if isinstance(other, AllocationFamilialesAvril2008Out): + return (self.age_minimum_alinea_1_l521_3_out == other.age_minimum_alinea_1_l521_3_out) + else: + return False + + def __ne__(self, other: object) -> bool: + return not (self == other) + class AllocationFamilialesAvril2008In: def __init__(self, age_minimum_alinea_1_l521_3_in: Callable[[Unit], Integer]) -> None: self.age_minimum_alinea_1_l521_3_in = age_minimum_alinea_1_l521_3_in + def __eq__(self, other: object) -> bool: + if isinstance(other, AllocationFamilialesAvril2008In): + return (self.age_minimum_alinea_1_l521_3_in == other.age_minimum_alinea_1_l521_3_in) + else: + return False + + def __ne__(self, other: object) -> bool: + return not (self == other) + class EnfantLePlusAgeOut: def __init__(self, enfants_out: List[Enfant], le_plus_age_out: Enfant) -> None: self.enfants_out = enfants_out self.le_plus_age_out = le_plus_age_out + def __eq__(self, other: object) -> bool: + if isinstance(other, EnfantLePlusAgeOut): + return (self.enfants_out == other.enfants_out and + self.le_plus_age_out == other.le_plus_age_out) + else: + return False + + def __ne__(self, other: object) -> bool: + return not (self == other) + class EnfantLePlusAgeIn: def __init__(self, enfants_in: Callable[[Unit], (List[Enfant])], le_plus_age_in: Callable[[Unit], Enfant]) -> None: self.enfants_in = enfants_in self.le_plus_age_in = le_plus_age_in + def __eq__(self, other: object) -> bool: + if isinstance(other, EnfantLePlusAgeIn): + return (self.enfants_in == other.enfants_in and + self.le_plus_age_in == other.le_plus_age_in) + else: + return False + + def __ne__(self, other: object) -> bool: + return not (self == other) + class AllocationsFamilialesOut: def __init__(self, personne_charge_effective_permanente_est_parent_out: bool, personne_charge_effective_permanente_remplit_titre_I_out: bool, ressources_menage_out: Money, residence_out: Collectivite, date_courante_out: Date, enfants_a_charge_out: List[Enfant], enfants_a_charge_droit_ouvert_prestation_familiale_out: List[Enfant], prise_en_compte_out: Callable[[Enfant], PriseEnCompte], versement_out: Callable[[Enfant], VersementAllocations], montant_verse_out: Money, droit_ouvert_base_out: bool, montant_initial_base_out: Money, montant_initial_base_premier_enfant_out: Money, montant_initial_base_deuxieme_enfant_out: Money, montant_initial_base_troisieme_enfant_et_plus_out: Money, rapport_enfants_total_moyen_out: Decimal, nombre_moyen_enfants_out: Decimal, nombre_total_enfants_out: Decimal, montant_avec_garde_alternee_base_out: Money, montant_verse_base_out: Money, droit_ouvert_forfaitaire_out: Callable[[Enfant], bool], montant_verse_forfaitaire_par_enfant_out: Money, montant_verse_forfaitaire_out: Money, droit_ouvert_majoration_out: Callable[[Enfant], bool], montant_initial_metropole_majoration_out: Callable[[Enfant], Money], montant_initial_majoration_out: Callable[[Enfant], Money], montant_avec_garde_alternee_majoration_out: Callable[[Enfant], Money], montant_verse_majoration_out: Money, droit_ouvert_complement_out: bool, montant_base_complement_pour_base_et_majoration_out: Money, complement_degressif_out: Callable[[Money], Money], montant_verse_complement_pour_base_et_majoration_out: Money, montant_verse_complement_pour_forfaitaire_out: Money, nombre_enfants_l521_1_out: Integer, age_minimum_alinea_1_l521_3_out: Callable[[Enfant], Integer], nombre_enfants_alinea_2_l521_3_out: Integer, est_enfant_le_plus_age_out: Callable[[Enfant], bool], plafond_I_d521_3_out: Money, plafond_II_d521_3_out: Money) -> None: @@ -213,6 +389,53 @@ class AllocationsFamilialesOut: self.plafond_I_d521_3_out = plafond_I_d521_3_out self.plafond_II_d521_3_out = plafond_II_d521_3_out + def __eq__(self, other: object) -> bool: + if isinstance(other, AllocationsFamilialesOut): + return (self.personne_charge_effective_permanente_est_parent_out == other.personne_charge_effective_permanente_est_parent_out and + self.personne_charge_effective_permanente_remplit_titre_I_out == other.personne_charge_effective_permanente_remplit_titre_I_out and + self.ressources_menage_out == other.ressources_menage_out and + self.residence_out == other.residence_out and + self.date_courante_out == other.date_courante_out and + self.enfants_a_charge_out == other.enfants_a_charge_out and + self.enfants_a_charge_droit_ouvert_prestation_familiale_out == other.enfants_a_charge_droit_ouvert_prestation_familiale_out and + self.prise_en_compte_out == other.prise_en_compte_out and + self.versement_out == other.versement_out and + self.montant_verse_out == other.montant_verse_out and + self.droit_ouvert_base_out == other.droit_ouvert_base_out and + self.montant_initial_base_out == other.montant_initial_base_out and + self.montant_initial_base_premier_enfant_out == other.montant_initial_base_premier_enfant_out and + self.montant_initial_base_deuxieme_enfant_out == other.montant_initial_base_deuxieme_enfant_out and + self.montant_initial_base_troisieme_enfant_et_plus_out == other.montant_initial_base_troisieme_enfant_et_plus_out and + self.rapport_enfants_total_moyen_out == other.rapport_enfants_total_moyen_out and + self.nombre_moyen_enfants_out == other.nombre_moyen_enfants_out and + self.nombre_total_enfants_out == other.nombre_total_enfants_out and + self.montant_avec_garde_alternee_base_out == other.montant_avec_garde_alternee_base_out and + self.montant_verse_base_out == other.montant_verse_base_out and + self.droit_ouvert_forfaitaire_out == other.droit_ouvert_forfaitaire_out and + self.montant_verse_forfaitaire_par_enfant_out == other.montant_verse_forfaitaire_par_enfant_out and + self.montant_verse_forfaitaire_out == other.montant_verse_forfaitaire_out and + self.droit_ouvert_majoration_out == other.droit_ouvert_majoration_out and + self.montant_initial_metropole_majoration_out == other.montant_initial_metropole_majoration_out and + self.montant_initial_majoration_out == other.montant_initial_majoration_out and + self.montant_avec_garde_alternee_majoration_out == other.montant_avec_garde_alternee_majoration_out and + self.montant_verse_majoration_out == other.montant_verse_majoration_out and + self.droit_ouvert_complement_out == other.droit_ouvert_complement_out and + self.montant_base_complement_pour_base_et_majoration_out == other.montant_base_complement_pour_base_et_majoration_out and + self.complement_degressif_out == other.complement_degressif_out and + self.montant_verse_complement_pour_base_et_majoration_out == other.montant_verse_complement_pour_base_et_majoration_out and + self.montant_verse_complement_pour_forfaitaire_out == other.montant_verse_complement_pour_forfaitaire_out and + self.nombre_enfants_l521_1_out == other.nombre_enfants_l521_1_out and + self.age_minimum_alinea_1_l521_3_out == other.age_minimum_alinea_1_l521_3_out and + self.nombre_enfants_alinea_2_l521_3_out == other.nombre_enfants_alinea_2_l521_3_out and + self.est_enfant_le_plus_age_out == other.est_enfant_le_plus_age_out and + self.plafond_I_d521_3_out == other.plafond_I_d521_3_out and + self.plafond_II_d521_3_out == other.plafond_II_d521_3_out) + else: + return False + + def __ne__(self, other: object) -> bool: + return not (self == other) + class AllocationsFamilialesIn: def __init__(self, personne_charge_effective_permanente_est_parent_in: Callable[[Unit], bool], personne_charge_effective_permanente_remplit_titre_I_in: Callable[[Unit], bool], ressources_menage_in: Callable[[Unit], Money], residence_in: Callable[[Unit], Collectivite], date_courante_in: Callable[[Unit], Date], enfants_a_charge_in: Callable[[Unit], (List[Enfant])], enfants_a_charge_droit_ouvert_prestation_familiale_in: Callable[[Unit], (List[Enfant])], prise_en_compte_in: Callable[[Unit], (Callable[[Enfant], PriseEnCompte])], versement_in: Callable[[Unit], (Callable[[Enfant], VersementAllocations])], montant_verse_in: Callable[[Unit], Money], droit_ouvert_base_in: Callable[[Unit], bool], montant_initial_base_in: Callable[[Unit], Money], montant_initial_base_premier_enfant_in: Callable[[Unit], Money], montant_initial_base_deuxieme_enfant_in: Callable[[Unit], Money], montant_initial_base_troisieme_enfant_et_plus_in: Callable[[Unit], Money], rapport_enfants_total_moyen_in: Callable[[Unit], Decimal], nombre_moyen_enfants_in: Callable[[Unit], Decimal], nombre_total_enfants_in: Callable[[Unit], Decimal], montant_avec_garde_alternee_base_in: Callable[[Unit], Money], montant_verse_base_in: Callable[[Unit], Money], droit_ouvert_forfaitaire_in: Callable[[Unit], (Callable[[Enfant], bool])], montant_verse_forfaitaire_par_enfant_in: Callable[[Unit], Money], montant_verse_forfaitaire_in: Callable[[Unit], Money], droit_ouvert_majoration_in: Callable[[Unit], (Callable[[Enfant], bool])], montant_initial_metropole_majoration_in: Callable[[Unit], (Callable[[Enfant], Money])], montant_initial_majoration_in: Callable[[Unit], (Callable[[Enfant], Money])], montant_avec_garde_alternee_majoration_in: Callable[[Unit], (Callable[[Enfant], Money])], montant_verse_majoration_in: Callable[[Unit], Money], droit_ouvert_complement_in: Callable[[Unit], bool], montant_base_complement_pour_base_et_majoration_in: Callable[[Unit], Money], complement_degressif_in: Callable[[Unit], (Callable[[Money], Money])], montant_verse_complement_pour_base_et_majoration_in: Callable[[Unit], Money], montant_verse_complement_pour_forfaitaire_in: Callable[[Unit], Money], nombre_enfants_l521_1_in: Callable[[Unit], Integer], age_minimum_alinea_1_l521_3_in: Callable[[Unit], (Callable[[Enfant], Integer])], nombre_enfants_alinea_2_l521_3_in: Callable[[Unit], Integer], est_enfant_le_plus_age_in: Callable[[Unit], (Callable[[Enfant], bool])], plafond_I_d521_3_in: Callable[[Unit], Money], plafond_II_d521_3_in: Callable[[Unit], Money]) -> None: @@ -256,6 +479,53 @@ class AllocationsFamilialesIn: self.plafond_I_d521_3_in = plafond_I_d521_3_in self.plafond_II_d521_3_in = plafond_II_d521_3_in + def __eq__(self, other: object) -> bool: + if isinstance(other, AllocationsFamilialesIn): + return (self.personne_charge_effective_permanente_est_parent_in == other.personne_charge_effective_permanente_est_parent_in and + self.personne_charge_effective_permanente_remplit_titre_I_in == other.personne_charge_effective_permanente_remplit_titre_I_in and + self.ressources_menage_in == other.ressources_menage_in and + self.residence_in == other.residence_in and + self.date_courante_in == other.date_courante_in and + self.enfants_a_charge_in == other.enfants_a_charge_in and + self.enfants_a_charge_droit_ouvert_prestation_familiale_in == other.enfants_a_charge_droit_ouvert_prestation_familiale_in and + self.prise_en_compte_in == other.prise_en_compte_in and + self.versement_in == other.versement_in and + self.montant_verse_in == other.montant_verse_in and + self.droit_ouvert_base_in == other.droit_ouvert_base_in and + self.montant_initial_base_in == other.montant_initial_base_in and + self.montant_initial_base_premier_enfant_in == other.montant_initial_base_premier_enfant_in and + self.montant_initial_base_deuxieme_enfant_in == other.montant_initial_base_deuxieme_enfant_in and + self.montant_initial_base_troisieme_enfant_et_plus_in == other.montant_initial_base_troisieme_enfant_et_plus_in and + self.rapport_enfants_total_moyen_in == other.rapport_enfants_total_moyen_in and + self.nombre_moyen_enfants_in == other.nombre_moyen_enfants_in and + self.nombre_total_enfants_in == other.nombre_total_enfants_in and + self.montant_avec_garde_alternee_base_in == other.montant_avec_garde_alternee_base_in and + self.montant_verse_base_in == other.montant_verse_base_in and + self.droit_ouvert_forfaitaire_in == other.droit_ouvert_forfaitaire_in and + self.montant_verse_forfaitaire_par_enfant_in == other.montant_verse_forfaitaire_par_enfant_in and + self.montant_verse_forfaitaire_in == other.montant_verse_forfaitaire_in and + self.droit_ouvert_majoration_in == other.droit_ouvert_majoration_in and + self.montant_initial_metropole_majoration_in == other.montant_initial_metropole_majoration_in and + self.montant_initial_majoration_in == other.montant_initial_majoration_in and + self.montant_avec_garde_alternee_majoration_in == other.montant_avec_garde_alternee_majoration_in and + self.montant_verse_majoration_in == other.montant_verse_majoration_in and + self.droit_ouvert_complement_in == other.droit_ouvert_complement_in and + self.montant_base_complement_pour_base_et_majoration_in == other.montant_base_complement_pour_base_et_majoration_in and + self.complement_degressif_in == other.complement_degressif_in and + self.montant_verse_complement_pour_base_et_majoration_in == other.montant_verse_complement_pour_base_et_majoration_in and + self.montant_verse_complement_pour_forfaitaire_in == other.montant_verse_complement_pour_forfaitaire_in and + self.nombre_enfants_l521_1_in == other.nombre_enfants_l521_1_in and + self.age_minimum_alinea_1_l521_3_in == other.age_minimum_alinea_1_l521_3_in and + self.nombre_enfants_alinea_2_l521_3_in == other.nombre_enfants_alinea_2_l521_3_in and + self.est_enfant_le_plus_age_in == other.est_enfant_le_plus_age_in and + self.plafond_I_d521_3_in == other.plafond_I_d521_3_in and + self.plafond_II_d521_3_in == other.plafond_II_d521_3_in) + else: + return False + + def __ne__(self, other: object) -> bool: + return not (self == other) + class InterfaceAllocationsFamilialesOut: def __init__(self, date_courante_out: Date, enfants_out: List[EnfantEntree], enfants_a_charge_out: List[Enfant], ressources_menage_out: Money, residence_out: Collectivite, montant_verse_out: Money, personne_charge_effective_permanente_est_parent_out: bool, personne_charge_effective_permanente_remplit_titre_I_out: bool) -> None: @@ -268,6 +538,22 @@ class InterfaceAllocationsFamilialesOut: self.personne_charge_effective_permanente_est_parent_out = personne_charge_effective_permanente_est_parent_out self.personne_charge_effective_permanente_remplit_titre_I_out = personne_charge_effective_permanente_remplit_titre_I_out + def __eq__(self, other: object) -> bool: + if isinstance(other, InterfaceAllocationsFamilialesOut): + return (self.date_courante_out == other.date_courante_out and + self.enfants_out == other.enfants_out and + self.enfants_a_charge_out == other.enfants_a_charge_out and + self.ressources_menage_out == other.ressources_menage_out and + self.residence_out == other.residence_out and + self.montant_verse_out == other.montant_verse_out and + self.personne_charge_effective_permanente_est_parent_out == other.personne_charge_effective_permanente_est_parent_out and + self.personne_charge_effective_permanente_remplit_titre_I_out == other.personne_charge_effective_permanente_remplit_titre_I_out) + else: + return False + + def __ne__(self, other: object) -> bool: + return not (self == other) + class InterfaceAllocationsFamilialesIn: def __init__(self, date_courante_in: Callable[[Unit], Date], enfants_in: Callable[[Unit], (List[EnfantEntree])], enfants_a_charge_in: Callable[[Unit], (List[Enfant])], ressources_menage_in: Callable[[Unit], Money], residence_in: Callable[[Unit], Collectivite], montant_verse_in: Callable[[Unit], Money], personne_charge_effective_permanente_est_parent_in: Callable[[Unit], bool], personne_charge_effective_permanente_remplit_titre_I_in: Callable[[Unit], bool]) -> None: @@ -280,6 +566,22 @@ class InterfaceAllocationsFamilialesIn: self.personne_charge_effective_permanente_est_parent_in = personne_charge_effective_permanente_est_parent_in self.personne_charge_effective_permanente_remplit_titre_I_in = personne_charge_effective_permanente_remplit_titre_I_in + def __eq__(self, other: object) -> bool: + if isinstance(other, InterfaceAllocationsFamilialesIn): + return (self.date_courante_in == other.date_courante_in and + self.enfants_in == other.enfants_in and + self.enfants_a_charge_in == other.enfants_a_charge_in and + self.ressources_menage_in == other.ressources_menage_in and + self.residence_in == other.residence_in and + self.montant_verse_in == other.montant_verse_in and + self.personne_charge_effective_permanente_est_parent_in == other.personne_charge_effective_permanente_est_parent_in and + self.personne_charge_effective_permanente_remplit_titre_I_in == other.personne_charge_effective_permanente_remplit_titre_I_in) + else: + return False + + def __ne__(self, other: object) -> bool: + return not (self == other) + def smic(smic_in_1: SmicIn): date_courante_2 = smic_in_1.date_courante_in diff --git a/french_law/python/catala_runtime.py b/french_law/python/src/catala.py similarity index 92% rename from french_law/python/catala_runtime.py rename to french_law/python/src/catala.py index fa71254e..345ee748 100644 --- a/french_law/python/catala_runtime.py +++ b/french_law/python/src/catala.py @@ -163,8 +163,7 @@ class Date: return Date(self.value + other.value) def __sub__(self, other: 'Date') -> 'Duration': - # Careful: invert argument order - return Duration(dateutil.relativedelta.relativedelta(other.value, self.value)) + return Duration(dateutil.relativedelta.relativedelta(self.value, other.value)) def __lt__(self, other: 'Date') -> bool: return self.value < other.value @@ -253,6 +252,18 @@ class Unit: def __init__(self) -> None: ... + def __eq__(self, other: object) -> bool: + if isinstance(other, Unit): + return True + else: + return False + + def __ne__(self, other: object) -> bool: + if isinstance(other, Unit): + return False + else: + return True + class SourcePosition: def __init__(self, @@ -269,6 +280,10 @@ class SourcePosition: self.end_column = end_column self.law_headings = law_headings + def __str__(self) -> str: + return "in file {}, from {}:{} to {}:{} ({})".format( + self.filename, self.start_line, self.start_column, self.end_line, self.end_column, ", ".join(self.law_headings)) + # ========== # Exceptions # ========== @@ -303,8 +318,8 @@ def money_of_cents_string(v: str) -> Money: return Money(Integer(v)) -def money_of_cents_int(v: int) -> Money: - return Money(Integer(v)) +def money_of_units_int(v: int) -> Money: + return Money(Integer(v) * Integer(100)) def money_of_cents_integer(v: Integer) -> Money: @@ -396,7 +411,9 @@ def date_to_string(d: Date) -> str: def date_of_numbers(year: int, month: int, day: int) -> Date: - return Date(datetime.date(year, month, day)) + # The datetime.date does not take year=0 as an entry, we trick it into + # 1 in that case because year=0 cases don't care about the actual year + return Date(datetime.date(year if year != 0 else 1, month, day)) # --------- # Durations @@ -467,7 +484,7 @@ def handle_default( def no_input() -> Callable[[Unit], Alpha]: - def closure(_: Unit()): + def closure(_: Unit): raise EmptyError return closure @@ -488,5 +505,7 @@ def log_end_call(headings: List[str], value: Alpha) -> Alpha: return value -def log_decision_taken(pos: SourcePosition, value: Alpha) -> Alpha: +def log_decision_taken(pos: SourcePosition, value: bool) -> bool: + if value: + print(">> Decision taken {}".format(pos)) return value