mirror of
https://github.com/CatalaLang/catala.git
synced 2024-11-09 22:16:10 +03:00
Directly call the API
This commit is contained in:
parent
9b287cc0a5
commit
d2e4da144c
177
french_law/python/cnaf_cross_tester/call_cnaf.py
Normal file
177
french_law/python/cnaf_cross_tester/call_cnaf.py
Normal file
@ -0,0 +1,177 @@
|
||||
# This code is ported from https://github.com/jboillot/apl-fetcher
|
||||
|
||||
from .input import AppartementOuMaison, AppartementOuMaisonType, CnafSimulatorInput, LogementCrousType, LogementCrous, SeulOuCouple
|
||||
import json
|
||||
import requests # type: ignore
|
||||
|
||||
|
||||
def get_bearer():
|
||||
url = "https://wwwd.caf.fr/wps/s/GenerateTokenJwtPublic/"
|
||||
|
||||
payload = {}
|
||||
headers = {
|
||||
'Accept': 'application/json, text/plain, */*',
|
||||
'Connection': 'keep-alive',
|
||||
'Referer': 'https://wwwd.caf.fr/wps/portal/caffr/aidesetdemarches/mesdemarches/faireunesimulation/lelogement',
|
||||
'Sec-Fetch-Dest': 'empty',
|
||||
'Sec-Fetch-Mode': 'cors',
|
||||
'Sec-Fetch-Site': 'same-origin',
|
||||
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36',
|
||||
'Cookie': 'DYN_PERSYS=438624522.14119.0000; PERSYS=2801671596.47138.0000; TS015047ff=015e43680bb2f0a3e2996d95996eaa92a52fbad1221c1d64a9f10e25a1c12f8c7ec932a9aff3ba4fd7bdd4441e48f788df0b32610f'
|
||||
}
|
||||
|
||||
response = requests.request("GET", url, headers=headers, data=payload)
|
||||
data = json.loads(response.text)
|
||||
return data['cnafTokenJwt']
|
||||
|
||||
|
||||
def get_simulation(bearer, payload_json):
|
||||
import requests
|
||||
|
||||
url = "https://wwwd.caf.fr/api/prestationalmiddle/v1/simuler"
|
||||
|
||||
payload = json.dumps(payload_json)
|
||||
headers = {
|
||||
'Accept': 'application/json, text/plain, */*',
|
||||
'Accept-Language': 'en-GB,en-US;q=0.9,en;q=0.8,fr;q=0.7',
|
||||
'Authorization': bearer,
|
||||
'Connection': 'keep-alive',
|
||||
'Content-Type': 'application/json; charset=UTF-8',
|
||||
'Origin': 'https://wwwd.caf.fr',
|
||||
'Referer': 'https://wwwd.caf.fr/wps/portal/caffr/aidesetdemarches/mesdemarches/faireunesimulation/lelogement',
|
||||
'Sec-Fetch-Dest': 'empty',
|
||||
'Sec-Fetch-Mode': 'cors',
|
||||
'Sec-Fetch-Site': 'same-origin',
|
||||
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36',
|
||||
# 'Cookie': 'DYN_PERSYS=438624522.14119.0000; PERSYS=2801671596.47138.0000; TS015047ff=015e43680bb2f0a3e2996d95996eaa92a52fbad1221c1d64a9f10e25a1c12f8c7ec932a9aff3ba4fd7bdd4441e48f788df0b32610f'
|
||||
}
|
||||
|
||||
response = requests.request("POST", url, headers=headers, data=payload)
|
||||
response_json = response.json()
|
||||
print(response_json)
|
||||
if response_json['possedeDesDroits']:
|
||||
return int(response_json['montantDroit'])
|
||||
else:
|
||||
return 0
|
||||
|
||||
|
||||
def format_payload(input: CnafSimulatorInput):
|
||||
# C'est un exemple de fichier attendu
|
||||
# Les variables parlent d'elles mêmes et ont un type contraint
|
||||
flgLogementConventionne = False
|
||||
if isinstance(input.logement, LogementCrous):
|
||||
if input.logement.typ_v == LogementCrousType.Chambre_rehabilitee:
|
||||
flgLogementConventionne = False
|
||||
|
||||
if input.logement.meublee() is None:
|
||||
flgMeubleLogement = False
|
||||
else:
|
||||
flgMeubleLogement = input.logement.meublee()
|
||||
|
||||
logement = {
|
||||
"adresse": {
|
||||
"codeInsee": str(input.zone.value)
|
||||
},
|
||||
"loyer": {
|
||||
"mtLoyerFoyer": float(input.loyer)
|
||||
},
|
||||
# parmi ["APPARTEMENT_OU_MAISON", "FOYER", "LOGEMENT_CROUS",
|
||||
"cdTypeLogement": str(input.logement.residence_v),
|
||||
# "MAISON_RETRAITE_EHPAD", "RESIDENCE_SOCIALE_FJT", "CHAMBRE"]
|
||||
"flgLogementConventionne": flgLogementConventionne,
|
||||
"flgMeubleLogement": flgMeubleLogement
|
||||
}
|
||||
personne1 = {
|
||||
"personne": {
|
||||
"enActivite": False,
|
||||
"estAuChomage": False,
|
||||
"flgDemandeur": True, # Est ce la personne demandeuse ?
|
||||
"situationProfessionnelleList": [
|
||||
{
|
||||
"situationProfessionnelle": "ACT_INCONNUE"
|
||||
# parmi "ETU" (étudiant non boursier)
|
||||
# "CONGE_MATPAT",
|
||||
# "ST_RSA",
|
||||
# "EBO" (étudiant boursier)
|
||||
# "ACT_INCONNUE" (autre situation)
|
||||
# Et pour les enfants ça peut être
|
||||
# "ETU"
|
||||
# "APPRENTI"
|
||||
# "SALARIE_CP"
|
||||
}
|
||||
],
|
||||
"ressourceList": [
|
||||
{
|
||||
"groupeOrigine": "salaire",
|
||||
# parmi "salaire", "allocation_chomage",
|
||||
# "retraite_pension_imposable", "pension_alimentaire_recue"
|
||||
# "revenu_travailleur_independant"
|
||||
"mtRessource": int(float(input.revenu_pris_en_compte) / 0.9)
|
||||
},
|
||||
|
||||
]
|
||||
}
|
||||
}
|
||||
household_members = [personne1]
|
||||
if input.seul_ou_couple == SeulOuCouple.Seul:
|
||||
personne2 = None
|
||||
else:
|
||||
personne2 = {
|
||||
"personne": {
|
||||
"flgDemandeur": False,
|
||||
"situationProfessionnelleList": [
|
||||
{
|
||||
"situationProfessionnelle": "ACT_INCONNUE"
|
||||
}
|
||||
],
|
||||
"ressourceList": []
|
||||
}
|
||||
}
|
||||
household_members.append(personne2)
|
||||
children = [
|
||||
{
|
||||
"role": "ENF",
|
||||
"personne": {
|
||||
"role": "ENF",
|
||||
"enfantACharge": True,
|
||||
"flgDemandeur": False,
|
||||
"situationProfessionnelleList": [
|
||||
{
|
||||
"situationProfessionnelle": "ETU"
|
||||
}
|
||||
],
|
||||
"ressourceList": []
|
||||
}
|
||||
}
|
||||
for enfant in input.enfants
|
||||
]
|
||||
household_members.extend(children)
|
||||
|
||||
flgColocation = False
|
||||
if isinstance(logement, AppartementOuMaison):
|
||||
if logement.typ_v == AppartementOuMaisonType.Colocation:
|
||||
flgColocation = True
|
||||
result = {
|
||||
"rattachementLogementList": [
|
||||
{
|
||||
"flgColocation": flgColocation,
|
||||
"logement": logement
|
||||
}
|
||||
],
|
||||
"compoFoyerList": household_members,
|
||||
"situationFamilialeList": [
|
||||
{
|
||||
"estEnCouple": True if input.seul_ou_couple == SeulOuCouple.EnCouple else False,
|
||||
"situationFamille": str(input.seul_ou_couple.value)
|
||||
# Parmi "CEL" (célibataire)
|
||||
# "VIM" (vie maritale = couple)
|
||||
}
|
||||
]}
|
||||
return result
|
||||
|
||||
|
||||
def run_simulator(input: CnafSimulatorInput) -> int:
|
||||
payload = format_payload(input)
|
||||
print(payload)
|
||||
bearer = get_bearer()
|
||||
return (get_simulation(bearer, payload))
|
@ -1,6 +1,6 @@
|
||||
from .random_input_generator import generate_random_input
|
||||
from .input import AppartementOuMaison, AppartementOuMaisonType, CnafSimulatorInput, Enfant, SeulOuCouple, Zone
|
||||
from .pupeteer import run_simulator
|
||||
from .call_cnaf import run_simulator
|
||||
from .cnaf_to_catala import run_catala_by_converting_cnaf_input
|
||||
|
||||
# input identical to the JS test of the housing benefits
|
||||
|
@ -1,209 +0,0 @@
|
||||
# This code is ported from https://github.com/jboillot/apl-fetcher
|
||||
|
||||
from pdb import runeval
|
||||
from playwright.sync_api import sync_playwright
|
||||
from .input import AppartementOuMaison, AppartementOuMaisonType, CnafSimulatorInput, Enfant, LogementCrous, SeulOuCouple
|
||||
import re
|
||||
|
||||
HOME_PAGE = 'https://wwwd.caf.fr/wps/portal/caffr/aidesetservices/lesservicesenligne/estimervosdroits/lelogement'
|
||||
|
||||
|
||||
def run_simulator(input: CnafSimulatorInput) -> int:
|
||||
with sync_playwright() as p:
|
||||
browser = p.firefox.launch(headless=False, slow_mo=100)
|
||||
page = browser.new_page()
|
||||
|
||||
# Go to the CNAF simulator
|
||||
page.goto(HOME_PAGE)
|
||||
|
||||
# Click on cookie banner
|
||||
cookie_button = page.wait_for_selector(
|
||||
"div[id=\"popup-accept-cookies\"] >> button")
|
||||
if cookie_button is None:
|
||||
raise RuntimeError
|
||||
cookie_button.click(force=True)
|
||||
|
||||
# Click on 'Commencer'
|
||||
commencer_button = page.wait_for_selector('button[id="btn-suivant"]')
|
||||
if commencer_button is None:
|
||||
raise RuntimeError
|
||||
commencer_button.click(force=True)
|
||||
|
||||
# Code Postal
|
||||
code_postal_input = page.wait_for_selector('input[id="cpCommune"]')
|
||||
if code_postal_input is None:
|
||||
raise RuntimeError
|
||||
code_postal_input.fill(input.zone.value)
|
||||
|
||||
# Select first 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(
|
||||
"input[id=\"lieuResidence_{}\"]".format(input.logement.residence()))
|
||||
if type_residence_button is None:
|
||||
raise RuntimeError
|
||||
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(
|
||||
"input[id=\"typeOccupation_{}\"]".format(input.logement.typ()))
|
||||
if type_appartement_ou_maison_button is None:
|
||||
raise RuntimeError
|
||||
type_appartement_ou_maison_button.click(force=True)
|
||||
elif isinstance(input.logement, LogementCrous):
|
||||
type_crous_button = page.wait_for_selector(
|
||||
"input[id=\"typeCrous_{}\"]".format(input.logement.typ()))
|
||||
if type_crous_button is None:
|
||||
raise RuntimeError
|
||||
type_crous_button.click(force=True)
|
||||
|
||||
# Is the location meublee
|
||||
|
||||
if not (input.logement.meublee() is None):
|
||||
meuble_button = page.wait_for_selector(
|
||||
"input[id=\"estMeuble_{}\"]".format("true" if input.logement.meublee() else "false"))
|
||||
if meuble_button is None:
|
||||
raise RuntimeError
|
||||
meuble_button.click(force=True)
|
||||
|
||||
# Monthly rent
|
||||
loyer_mensuel_input = page.wait_for_selector(
|
||||
'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(
|
||||
"input[id=\"situationFamiliale_{}\"]".format(input.seul_ou_couple.value))
|
||||
if seul_button is None:
|
||||
raise RuntimeError
|
||||
seul_button.click(force=True)
|
||||
|
||||
# Number of children
|
||||
if len(input.enfants) < 0 or len(input.enfants) > 20:
|
||||
raise RuntimeError
|
||||
plus_button = page.wait_for_selector('button:has-text("+")')
|
||||
if plus_button is None:
|
||||
raise RuntimeError
|
||||
for i in range(len(input.enfants)):
|
||||
plus_button.click(force=True)
|
||||
|
||||
# Main applicant salary
|
||||
salaires_button = page.wait_for_selector(
|
||||
"button[id=\"SALAIRE_0_checkbox\"]")
|
||||
if salaires_button is None:
|
||||
raise RuntimeError
|
||||
salaires_button.click(force=True)
|
||||
salaires_input = page.wait_for_selector(
|
||||
"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)))
|
||||
salaires_input.wait_for_element_state(state="stable")
|
||||
# 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 = "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(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=\"aucunRevenu_1\"]")
|
||||
if sans_button is None:
|
||||
raise RuntimeError
|
||||
sans_button.click(force=True)
|
||||
status_button = page.wait_for_selector(
|
||||
"input[id=\"sitro_1_SITPRO_ACT_INCONNUE\"]")
|
||||
if status_button is None:
|
||||
raise RuntimeError
|
||||
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(force=True)
|
||||
|
||||
# Extra questions about children
|
||||
if len(input.enfants) > 0:
|
||||
for i in range(len(input.enfants)):
|
||||
if input.enfants[i].age < 21:
|
||||
yes_button = page.wait_for_selector(
|
||||
"input[id=\"QUESTION_AGE_MAX_{}_true\"]".format(i))
|
||||
if yes_button is None:
|
||||
raise RuntimeError
|
||||
yes_button.click(force=True)
|
||||
else:
|
||||
no_button = page.wait_for_selector(
|
||||
"input[id=\"QUESTION_AGE_MAX_{}_false\"]".format(i))
|
||||
if no_button is None:
|
||||
raise RuntimeError
|
||||
no_button.click(force=True)
|
||||
if int(float(input.enfants[i].remuneration_derniere_annee) / 0.9) > 4500:
|
||||
yes_button = page.wait_for_selector(
|
||||
"input[id=\"QUESTION_REVENUS_DERNIERSMOIS_{}_true\"]".format(i))
|
||||
if yes_button is None:
|
||||
raise RuntimeError
|
||||
yes_button.click(force=True)
|
||||
# We have to provide the exact remuneration
|
||||
salaires_button = page.wait_for_selector(
|
||||
"button[id=\"salaire{}_checkbox\"]".format(i))
|
||||
if salaires_button is None:
|
||||
raise RuntimeError
|
||||
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 = page.wait_for_selector(
|
||||
"input[id=\"QUESTION_SITPRO_{}_SALARIE_CP\"]".format(i))
|
||||
if salaires_status is None:
|
||||
raise RuntimeError
|
||||
salaires_status.click(force=True)
|
||||
else:
|
||||
no_button = page.wait_for_selector(
|
||||
"input[id=\"QUESTION_REVENUS_DERNIERSMOIS_{}_false\"]".format(i))
|
||||
if no_button is None:
|
||||
raise RuntimeError
|
||||
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(force=True)
|
||||
|
||||
# Wait for result page
|
||||
page.wait_for_selector("section[id=\"resultat\"]")
|
||||
|
||||
# Retrieve the amount
|
||||
result = page.query_selector('text=/\\d+ € par mois/')
|
||||
if result is None:
|
||||
# Then no benefits!
|
||||
housing_benefits = 0
|
||||
else:
|
||||
result_text = result.text_content()
|
||||
if result_text is None:
|
||||
housing_benefits = 0
|
||||
else:
|
||||
match = re.search("(\\d+) € par mois", result_text)
|
||||
if match is None:
|
||||
raise RuntimeError
|
||||
housing_benefits = int(match.group(1))
|
||||
if housing_benefits is None:
|
||||
raise RuntimeError
|
||||
|
||||
browser.close()
|
||||
return housing_benefits
|
@ -17,7 +17,7 @@ def generate_random_input() -> CnafSimulatorInput:
|
||||
zone = Zone.Zone2
|
||||
else: # zone_i == 3
|
||||
zone = Zone.Zone3
|
||||
loyer = random.randint(300, 1800)
|
||||
loyer = random.randint(300, 1000)
|
||||
revenus_pris_en_compte = random.randint(0, 200) * 100
|
||||
seul_ou_couple_i = random.randint(1, 2)
|
||||
if seul_ou_couple_i == 1:
|
||||
|
Loading…
Reference in New Issue
Block a user