Playing mouse and cat with the CNAF form

This commit is contained in:
Denis Merigoux 2022-09-26 15:44:38 +02:00
parent 309dfb6c3e
commit 7ab74501b8
No known key found for this signature in database
GPG Key ID: EE99DCFA365C3EE3
3 changed files with 73 additions and 73 deletions

View File

@ -135,8 +135,8 @@ class LogementChambre(Logement):
class SeulOuCouple(Enum):
Seul = 'SEUL'
EnCouple = 'COUPLE'
Seul = 'CEL'
EnCouple = 'VIM'
class Enfant():

View File

@ -0,0 +1,19 @@
from input import AppartementOuMaison, AppartementOuMaisonType, CnafSimulatorInput, Enfant, SeulOuCouple
from pupeteer import run_simulator
# Output identical to the JS test of the housing benefits
sample_input = CnafSimulatorInput(
code_postal="69001",
logement=AppartementOuMaison(
AppartementOuMaisonType.Location, meuble=False),
loyer=450,
seul_ou_couple=SeulOuCouple.EnCouple,
enfants=[Enfant(age=7, remuneration_derniere_annee=0),
Enfant(age=8, remuneration_derniere_annee=0)],
revenu_pris_en_compte=11_500
)
print(sample_input)
housing_benefits = run_simulator(sample_input)
print("Aides au logement : {}".format(housing_benefits))

View File

@ -10,7 +10,7 @@ HOME_PAGE = 'https://wwwd.caf.fr/wps/portal/caffr/aidesetservices/lesservicesenl
def run_simulator(input: CnafSimulatorInput) -> int:
with sync_playwright() as p:
browser = p.firefox.launch() # headless=False, slow_mo=1500)
browser = p.firefox.launch(headless=True, slow_mo=100)
page = browser.new_page()
# Go to the CNAF simulator
@ -21,67 +21,67 @@ def run_simulator(input: CnafSimulatorInput) -> int:
"div[id=\"popup-accept-cookies\"] >> button")
if cookie_button is None:
raise RuntimeError
cookie_button.click()
cookie_button.click(force=True)
# Click on 'Commencer'
commencer_button = page.wait_for_selector('button[id="btn-commencer"]')
commencer_button = page.wait_for_selector('button[id="btn-suivant"]')
if commencer_button is None:
raise RuntimeError
commencer_button.click()
commencer_button.click(force=True)
# Code Postal
code_postal_input = page.wait_for_selector('input[id="codePostal"]')
code_postal_input = page.wait_for_selector('input[id="cpCommune"]')
if code_postal_input is None:
raise RuntimeError
code_postal_input.fill(input.code_postal)
# Select first match
page.wait_for_selector('li[class="uib-typeahead-match"]')
page.wait_for_selector('a[class="ng-tns-c31-0"]')
page.keyboard.press('Enter')
# Select residence type
type_residence_button = page.wait_for_selector(
"label[for=\"nature_{}\"]".format(input.logement.residence()))
"input[id=\"lieuResidence_{}\"]".format(input.logement.residence()))
if type_residence_button is None:
raise RuntimeError
type_residence_button.click()
type_residence_button.click(force=True)
# Select housing type for some things
if isinstance(input.logement, AppartementOuMaison):
type_appartement_ou_maison_button = page.wait_for_selector(
"label[for=\"statut_{}\"]".format(input.logement.typ()))
"input[id=\"typeOccupation_{}\"]".format(input.logement.typ()))
if type_appartement_ou_maison_button is None:
raise RuntimeError
type_appartement_ou_maison_button.click()
type_appartement_ou_maison_button.click(force=True)
elif isinstance(input.logement, LogementCrous):
type_crous_button = page.wait_for_selector(
"label[for=\"typeLocal_{}\"]".format(input.logement.typ()))
"input[id=\"typeCrous_{}\"]".format(input.logement.typ()))
if type_crous_button is None:
raise RuntimeError
type_crous_button.click()
type_crous_button.click(force=True)
# Is the location meublee
if not (input.logement.meublee() is None):
meuble_button = page.wait_for_selector(
"label[for=\"estMeuble_{}\"]".format("true" if input.logement.meublee() else "false"))
"input[id=\"estMeuble_{}\"]".format("true" if input.logement.meublee() else "false"))
if meuble_button is None:
raise RuntimeError
meuble_button.click()
meuble_button.click(force=True)
# Monthly rent
loyer_mensuel_input = page.wait_for_selector(
'input[name="montantLoyer"]')
'input[id="mttDeclare"]')
if loyer_mensuel_input is None:
raise RuntimeError
loyer_mensuel_input.fill("{}".format(input.loyer))
# Couple or not
seul_button = page.wait_for_selector(
"label[for=\"situationFamiliale_{}\"]".format(input.seul_ou_couple.value))
"input[id=\"situationFamiliale_{}\"]".format(input.seul_ou_couple.value))
if seul_button is None:
raise RuntimeError
seul_button.click()
seul_button.click(force=True)
# Number of children
if len(input.enfants) < 0 or len(input.enfants) > 20:
@ -90,111 +90,107 @@ def run_simulator(input: CnafSimulatorInput) -> int:
if plus_button is None:
raise RuntimeError
for i in range(len(input.enfants)):
plus_button.click()
plus_button.click(force=True)
# Main applicant salary
salaires_button = page.wait_for_selector(
"button[id=\"allocataire_siSalaire\"]")
"button[id=\"SALAIRE_0_checkbox\"]")
if salaires_button is None:
raise RuntimeError
salaires_button.click()
salaires_button.click(force=True)
salaires_input = page.wait_for_selector(
"input[id=\"ressourceallocataire_SALAIRE\"]")
"input[id=\"SALAIRE_0_montant\"]")
if salaires_input is None:
raise RuntimeError
salaires_input.fill("{}".format(
int(float(input.revenu_pris_en_compte) / 0.9)))
# We divide by 0.9 because salaries have a 10% franchise
# Now if the salary is too low you have to click another button
status_selector = "cnaf-personne[profil=\"'ALLOCATAIRE'\"] >> label[for=\"aucunRevenuAUTRE_SITUATION\"]"
status_selector = "input[id=\"sitro_0_SITPRO_ACT_INCONNUE\"]"
if not (page.query_selector is None):
status_button = page.wait_for_selector(status_selector)
if status_button is None:
raise RuntimeError
status_button.click()
status_button.click(force=True)
# If there is a partner we assume no income for simplicity
if input.seul_ou_couple == SeulOuCouple.EnCouple:
sans_button = page.wait_for_selector(
"button[id=\"conjoint_siAucunRevenu\"]")
"button[id=\"aucunRevenu_1\"]")
if sans_button is None:
raise RuntimeError
sans_button.click()
sans_button.click(force=True)
status_button = page.wait_for_selector(
"cnaf-personne[profil=\"'CONJOINT'\"] >> label[for=\"aucunRevenuAUTRE_SITUATION\"]")
"input[id=\"sitro_1_SITPRO_ACT_INCONNUE\"]")
if status_button is None:
raise RuntimeError
status_button.click()
status_button.click(force=True)
# Continue
continuer_button = page.wait_for_selector('button[id="btn-suivant"]')
if continuer_button is None:
raise RuntimeError
continuer_button.click()
continuer_button.click(force=True)
# Extra questions about children
if len(input.enfants) > 0:
page.wait_for_selector(
selector="div[ng-form=\"vm.questionsEnfantsForm\"]", state='attached')
i = 0
for element in page.query_selector_all("cnaf-personne[profil=\"'ENFANT'\"]"):
for i in range(len(input.enfants)):
if input.enfants[i].age < 21:
yes_button = element.wait_for_selector(
"label[for=\"personneAGE_MINIMUM\"][uib-btn-radio=\"true\"]")
yes_button = page.wait_for_selector(
"input[id=\"QUESTION_AGE_MAX_{}_true\"]".format(i))
if yes_button is None:
raise RuntimeError
yes_button.click()
yes_button.click(force=True)
else:
no_button = element.wait_for_selector(
"label[for=\"personneAGE_MINIMUM\"][uib-btn-radio=\"false\"]")
no_button = page.wait_for_selector(
"input[id=\"QUESTION_AGE_MAX_{}_false\"]".format(i))
if no_button is None:
raise RuntimeError
no_button.click()
no_button.click(force=True)
if int(float(input.enfants[i].remuneration_derniere_annee) / 0.9) > 4500:
yes_button = element.wait_for_selector(
"label[for=\"ENFANT_MONTANT_MAX\"][uib-btn-radio=\"true\"]")
yes_button = page.wait_for_selector(
"input[id=\"QUESTION_REVENUS_DERNIERSMOIS_{}_true\"]".format(i))
if yes_button is None:
raise RuntimeError
yes_button.click()
yes_button.click(force=True)
# We have to provide the exact remuneration
salaires_button = element.wait_for_selector(
"label[for=\"enfant_{}_siSalaire\"]".format(i))
salaires_button = page.wait_for_selector(
"button[id=\"salaire{}_checkbox\"]".format(i))
if salaires_button is None:
raise RuntimeError
salaires_button.click()
salaires_input = element.wait_for_selector(
"input[id=\"ressourceenfant_{}_SALAIRE\"]".format(i))
salaires_button.click(force=True)
salaires_input = page.wait_for_selector(
"input[id=\"MONTANT_salaire{}\"]".format(i))
if salaires_input is None:
raise RuntimeError
salaires_input.fill("{}".format(
int(float(input.enfants[i].remuneration_derniere_annee) / 0.9)))
salaires_status = element.wait_for_selector(
"label[for=\"enfantSalarieSALARIE\"]")
salaires_status = page.wait_for_selector(
"input[id=\"QUESTION_SITPRO_{}_SALARIE_CP\"]".format(i))
if salaires_status is None:
raise RuntimeError
salaires_status.click()
salaires_status.click(force=True)
else:
no_button = element.wait_for_selector(
"label[for=\"ENFANT_MONTANT_MAX\"][uib-btn-radio=\"false\"]")
no_button = page.wait_for_selector(
"input[id=\"QUESTION_REVENUS_DERNIERSMOIS_{}_false\"]".format(i))
if no_button is None:
raise RuntimeError
no_button.click()
i += 1
no_button.click(force=True)
# Continue
continuer_button = page.wait_for_selector(
'button[id="btn-suivant"]')
if continuer_button is None:
raise RuntimeError
continuer_button.click()
continuer_button.click(force=True)
# Retrieve the amount
page.wait_for_selector('section[id="resultat"]')
result = page.locator('text=/\\d+€ par mois/').text_content()
result = page.locator('text=/\\d+ € par mois/').text_content()
if result is None:
raise RuntimeError
match = re.search("(\\d+)€ par mois", result)
match = re.search("(\\d+) € par mois", result)
if match is None:
raise RuntimeError
housing_benefits = match.group(1)
@ -203,18 +199,3 @@ def run_simulator(input: CnafSimulatorInput) -> int:
browser.close()
return int(housing_benefits)
input = CnafSimulatorInput(
code_postal="69001",
logement=AppartementOuMaison(
AppartementOuMaisonType.Location, meuble=False),
loyer=450,
seul_ou_couple=SeulOuCouple.EnCouple,
enfants=[Enfant(age=7, remuneration_derniere_annee=0),
Enfant(age=14, remuneration_derniere_annee=5000)],
revenu_pris_en_compte=11_500
)
print(input)
housing_benefits = run_simulator(input)
print("Aides au logement : {}".format(housing_benefits))