Ajout du frontend et branchement sur l'API axione #9
16 changed files with 618 additions and 114 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,2 +1,3 @@
|
||||||
__pycache__
|
__pycache__
|
||||||
/elig-test.ini
|
/elig-test.ini
|
||||||
|
.vscode
|
|
@ -45,3 +45,9 @@ curl -v http://127.0.0.1:5000/addresses/communes?s=29530
|
||||||
# Chercher les voies d'une commune via son code insee
|
# Chercher les voies d'une commune via son code insee
|
||||||
curl -v http://127.0.0.1:5000/addresses/fantoirvoies/29036
|
curl -v http://127.0.0.1:5000/addresses/fantoirvoies/29036
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Jeux de données
|
||||||
|
|
||||||
|
#### Insee
|
||||||
|
|
||||||
|
Importez ce CSV https://www.data.gouv.fr/fr/datasets/base-officielle-des-codes-postaux/ dans la base de données
|
||||||
|
|
|
@ -1,79 +1,110 @@
|
||||||
import sqlite3
|
import sqlite3
|
||||||
import sys
|
import sys
|
||||||
import json
|
import json
|
||||||
from .model import Commune,FantoirVoie
|
from .model import Commune, FantoirVoie
|
||||||
|
import re
|
||||||
|
|
||||||
# DB with addresses info
|
# DB with addresses info
|
||||||
DB_ADDRESSES_PATH_ENV="DB_ADDRESSES_PATH"
|
DB_ADDRESSES_PATH_ENV = "DB_ADDRESSES_PATH"
|
||||||
DB_ADDRESSES_DEFAULT_PATH="/etc/fantoir.sqlite"
|
DB_ADDRESSES_DEFAULT_PATH = "/etc/fantoir.sqlite"
|
||||||
|
|
||||||
# Table for insee codes
|
# Table for insee codes
|
||||||
DB_TABLE_INSEE_NAME="insee"
|
DB_TABLE_INSEE_NAME = "insee"
|
||||||
DB_COL_COMMUNE_INSEE="Code_commune_INSEE"
|
DB_COL_COMMUNE_INSEE = "Code_commune_INSEE"
|
||||||
DB_COL_COMMUNE_NAME="Nom_commune"
|
DB_COL_COMMUNE_NAME = "Nom_commune"
|
||||||
DB_COL_COMMUNE_POSTE="Code_postal"
|
DB_COL_COMMUNE_POSTE = "Code_postal"
|
||||||
|
|
||||||
# Table for Fantoir voies (code Rivoli)
|
|
||||||
DB_TABLE_FANTOIR_NAME="keyv"
|
|
||||||
DB_COL_FANTOIR_INSEE="key"
|
|
||||||
DB_FANTOIR_INSEE_KEY_SUFFIX="keyv:"
|
|
||||||
|
|
||||||
# Utility to find an address
|
# Utility to find an address
|
||||||
class AddressFinder:
|
class AddressFinder:
|
||||||
|
|
||||||
def __init__(self, db_addresses_sqlite_path: str):
|
def __init__(self, db_insee_communes_sqlite_path: str, db_fantoir_voies_sqlite_path: str):
|
||||||
self.dbPath = db_addresses_sqlite_path
|
self.dbCommunesPath = db_insee_communes_sqlite_path
|
||||||
print("DB addresses Path : " + self.dbPath)
|
self.dbFantoirPath = db_fantoir_voies_sqlite_path
|
||||||
|
print("DB insee communes Path : " + self.dbCommunesPath)
|
||||||
|
print("DB Fantoir voies Path : " + self.dbFantoirPath)
|
||||||
|
|
||||||
def getCommunesFromNameOrZip(self, communeNameOrZip: str) -> list[Commune]:
|
def getCommunesFromNameOrZip(self, communeNameOrZip: str, limit: int = None) -> list[Commune]:
|
||||||
con = sqlite3.connect(self.dbPath)
|
con = sqlite3.connect(self.dbCommunesPath)
|
||||||
con.row_factory = sqlite3.Row
|
con.row_factory = sqlite3.Row
|
||||||
cur = con.cursor()
|
cur = con.cursor()
|
||||||
communes: list[Commune] = []
|
|
||||||
try:
|
|
||||||
if communeNameOrZip is None:
|
|
||||||
cur.execute(f"SELECT * from \"{DB_TABLE_INSEE_NAME}\"")
|
|
||||||
else:
|
|
||||||
cur.execute(f"SELECT * from \"{DB_TABLE_INSEE_NAME}\" WHERE {DB_COL_COMMUNE_NAME}=\"{communeNameOrZip}\" COLLATE nocase OR {DB_COL_COMMUNE_POSTE}=\"{communeNameOrZip}\"")
|
|
||||||
except sqlite3.OperationalError as err:
|
|
||||||
print("Error querying DB : {0}".format(err), file=sys.stderr)
|
|
||||||
return []
|
|
||||||
rows = [dict(row) for row in cur.fetchall()]
|
|
||||||
con.close()
|
|
||||||
for row in rows:
|
|
||||||
commune=Commune(
|
|
||||||
codeInsee=row[DB_COL_COMMUNE_INSEE],
|
|
||||||
nom=row[DB_COL_COMMUNE_NAME],
|
|
||||||
codeZip=row[DB_COL_COMMUNE_POSTE])
|
|
||||||
communes.append(commune)
|
|
||||||
return communes
|
|
||||||
|
|
||||||
def getCommuneFantoirVoies(self, communeInseeCode: str) -> list[FantoirVoie]:
|
# Check if a search limit is specified, make sure it is an integer
|
||||||
|
select_limit = ""
|
||||||
|
if limit is not None:
|
||||||
|
try:
|
||||||
|
select_limit = f"LIMIT {int(limit)}"
|
||||||
|
except ValueError:
|
||||||
|
print("Error, limit arg not a valid int: ", limit)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# If no search parameter, select all
|
||||||
|
if communeNameOrZip is None:
|
||||||
|
cur.execute(
|
||||||
|
f"SELECT * from \"{DB_TABLE_INSEE_NAME}\" {select_limit}")
|
||||||
|
else:
|
||||||
|
communeSearch = communeNameOrZip
|
||||||
|
zipSearch = communeNameOrZip
|
||||||
|
searchOpertor = "OR"
|
||||||
|
|
||||||
|
# Allow search zip and commune at the same time, in the format "29530 PLO"
|
||||||
|
regexCommuneAndZip = r"[0-9]{5} .+"
|
||||||
|
if re.match(regexCommuneAndZip, communeNameOrZip):
|
||||||
|
splitSearch = communeNameOrZip.split(' ')
|
||||||
|
zipSearch = splitSearch[0]
|
||||||
|
communeSearch = ' '.join(splitSearch[1:])
|
||||||
|
searchOpertor = "AND"
|
||||||
|
cur.execute(
|
||||||
|
f"SELECT * from \"{DB_TABLE_INSEE_NAME}\" WHERE {DB_COL_COMMUNE_NAME} LIKE \"%{communeSearch}%\" COLLATE nocase {searchOpertor} {DB_COL_COMMUNE_POSTE} LIKE \"{zipSearch}%\" {select_limit}")
|
||||||
|
except sqlite3.OperationalError as err:
|
||||||
|
print("Error querying DB : {0}".format(err), file=sys.stderr)
|
||||||
|
return []
|
||||||
|
|
||||||
|
communesMap = dict()
|
||||||
|
for row in cur.fetchall():
|
||||||
|
row_obj = dict(row)
|
||||||
|
commune = Commune(
|
||||||
|
codeInsee=row_obj[DB_COL_COMMUNE_INSEE],
|
||||||
|
nom=row_obj[DB_COL_COMMUNE_NAME],
|
||||||
|
codeZip=row_obj[DB_COL_COMMUNE_POSTE])
|
||||||
|
# This way we avoid duplicates in DB
|
||||||
|
communesMap[commune["codeInsee"]] = commune
|
||||||
|
|
||||||
|
con.close()
|
||||||
|
return list(communesMap.values())
|
||||||
|
|
||||||
|
def getCommuneFantoirVoies(self, communeInseeCode: str, voieSearch: str = None, limit: int = None) -> list[FantoirVoie]:
|
||||||
|
|
||||||
# Extract data from DB
|
# Extract data from DB
|
||||||
con = sqlite3.connect(self.dbPath)
|
con = sqlite3.connect(self.dbFantoirPath)
|
||||||
con.row_factory = sqlite3.Row
|
con.row_factory = sqlite3.Row
|
||||||
cur = con.cursor()
|
cur = con.cursor()
|
||||||
try:
|
if voieSearch is None:
|
||||||
cur.execute(f"SELECT value from \"{DB_TABLE_FANTOIR_NAME}\" WHERE {DB_COL_FANTOIR_INSEE}=\"{DB_FANTOIR_INSEE_KEY_SUFFIX}{communeInseeCode}\"")
|
voieSearch=''
|
||||||
except sqlite3.OperationalError as err:
|
|
||||||
print("Error querying DB : {0}".format(err), file=sys.stderr)
|
|
||||||
return []
|
|
||||||
data_raw = cur.fetchone()
|
|
||||||
con.close()
|
|
||||||
|
|
||||||
## Get JSON payload
|
# Check if a search limit is specified, make sure it is an integer
|
||||||
|
select_limit = ""
|
||||||
|
if limit is not None:
|
||||||
|
try:
|
||||||
|
select_limit = f"LIMIT {int(limit)}"
|
||||||
|
except ValueError:
|
||||||
|
print("Error, limit arg not a valid int: ", limit)
|
||||||
|
|
||||||
|
try:
|
||||||
|
cur.execute(
|
||||||
|
f"SELECT trim(libelle), rivoli_with_key from fantoir WHERE full_insee=\"{communeInseeCode}\" AND libelle like \"%{voieSearch}%\" {select_limit}")
|
||||||
|
except sqlite3.OperationalError as err:
|
||||||
|
print("Error querying DB : {0}".format(err), file=sys.stderr)
|
||||||
|
return []
|
||||||
|
data_raw = cur.fetchall()
|
||||||
|
con.close()
|
||||||
|
|
||||||
fantoir_dict = []
|
fantoir_dict = []
|
||||||
# Check if data where found
|
# Check if data where found
|
||||||
if data_raw is not None:
|
if data_raw is not None:
|
||||||
data = dict(data_raw)
|
fantoir_dict = dict(data_raw)
|
||||||
# Extract the data behind "value" which is a JSON structure
|
|
||||||
data_dict=json.loads(data.get("value"))
|
|
||||||
# In extracted JSON data, the interesting payload is behind "value" key
|
|
||||||
fantoir_dict = data_dict.get("value")
|
|
||||||
else:
|
else:
|
||||||
print("Did not found any data matching Insee code " + str(communeInseeCode))
|
print("Did not found any data matching Insee code " +
|
||||||
|
str(communeInseeCode))
|
||||||
|
|
||||||
# Return the json dump
|
# Return the json dump
|
||||||
return fantoir_dict
|
return fantoir_dict
|
||||||
|
|
|
@ -6,12 +6,5 @@ class Commune(TypedDict):
|
||||||
codeZip: str
|
codeZip: str
|
||||||
|
|
||||||
class FantoirVoie(TypedDict):
|
class FantoirVoie(TypedDict):
|
||||||
id: str
|
libelle: str
|
||||||
dateAjout: int
|
rivoli_with_key: int
|
||||||
libelle: list[str]
|
|
||||||
typeVoie:str
|
|
||||||
codeCommune: str
|
|
||||||
codeFantoir: str
|
|
||||||
cleRivoli: str
|
|
||||||
nomCommune: str
|
|
||||||
predecesseur: bool
|
|
||||||
|
|
|
@ -23,14 +23,37 @@ def ptoRequest(ptoRef):
|
||||||
</soapenv:Envelope>
|
</soapenv:Envelope>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
def fantoirRequest(insee, rivoli, numVoie):
|
||||||
|
ts = datetime.now(timezone.utc).isoformat()
|
||||||
|
return f"""
|
||||||
|
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ent="http://structureadresseftth.axione.fr/model/entreprise" xmlns:com="http://structureadresseftth.axione.fr/model/commun">
|
||||||
|
<soapenv:Header/>
|
||||||
|
<soapenv:Body>
|
||||||
|
<ent:obtentionStructureAdresseDemandeSoap>
|
||||||
|
<ent:entete versionWS="3.0" horodatageRequete="{ts}">
|
||||||
|
<com:operateurCommercial nom="AQUILENET" identifiant=""/>
|
||||||
|
</ent:entete>
|
||||||
|
<ent:referenceAdresse>
|
||||||
|
<com:referenceRivoli codeInsee="{insee}" codeRivoli="{rivoli}" numeroVoie="{numVoie}"/>
|
||||||
|
</ent:referenceAdresse>
|
||||||
|
</ent:obtentionStructureAdresseDemandeSoap>
|
||||||
|
</soapenv:Body>
|
||||||
|
</soapenv:Envelope>
|
||||||
|
"""
|
||||||
|
|
||||||
|
def query_axione_fantoir(cfg, insee, rivoli, numVoie):
|
||||||
|
body = fantoirRequest(insee, rivoli, numVoie)
|
||||||
|
return query_axione(cfg, body)
|
||||||
|
|
||||||
|
|
||||||
def query_axione_pto(cfg, ptoRef):
|
def query_axione_pto(cfg, ptoRef):
|
||||||
body = ptoRequest(ptoRef)
|
body = ptoRequest(ptoRef)
|
||||||
|
return query_axione(cfg, body)
|
||||||
|
|
||||||
|
def query_axione(cfg, body):
|
||||||
# Note: the password should be the base64 of username:password.
|
# Note: the password should be the base64 of username:password.
|
||||||
# Don't ask why.
|
# Don't ask why.
|
||||||
passwd = base64.b64encode(f"{cfg.username}:{cfg.password}".encode("utf8")).decode(
|
passwd = cfg.password
|
||||||
"utf8"
|
|
||||||
)
|
|
||||||
headers = {
|
headers = {
|
||||||
"User-Agent": "aquilenet-elig-test/0.1",
|
"User-Agent": "aquilenet-elig-test/0.1",
|
||||||
"Accept": "*/*",
|
"Accept": "*/*",
|
||||||
|
@ -38,7 +61,7 @@ def query_axione_pto(cfg, ptoRef):
|
||||||
"Connection": "Keep-Alive",
|
"Connection": "Keep-Alive",
|
||||||
"Authorization": passwd,
|
"Authorization": passwd,
|
||||||
}
|
}
|
||||||
resp = None
|
respData = None
|
||||||
if not cfg.debug:
|
if not cfg.debug:
|
||||||
try:
|
try:
|
||||||
conn = http.client.HTTPSConnection(
|
conn = http.client.HTTPSConnection(
|
||||||
|
@ -67,11 +90,10 @@ def query_axione_pto(cfg, ptoRef):
|
||||||
print(headers)
|
print(headers)
|
||||||
print("BODY: ")
|
print("BODY: ")
|
||||||
print(body)
|
print(body)
|
||||||
print("===================")
|
|
||||||
with open("./fixtures/dummy-data-1.xml", "r") as f:
|
with open("./fixtures/dummy-data-1.xml", "r") as f:
|
||||||
dummyData = f.read()
|
dummyData = f.read()
|
||||||
return dummyData
|
return dummyData
|
||||||
return resp
|
return respData
|
||||||
|
|
||||||
|
|
||||||
class LigneResult(TypedDict):
|
class LigneResult(TypedDict):
|
||||||
|
@ -98,17 +120,45 @@ class BatimentResult(TypedDict):
|
||||||
referenceBatiment: str
|
referenceBatiment: str
|
||||||
etages: list[EtageResult]
|
etages: list[EtageResult]
|
||||||
|
|
||||||
|
class AxioneErreur(TypedDict):
|
||||||
|
codeErreur: str
|
||||||
|
libelleErreur: str
|
||||||
|
|
||||||
def parse_response(resp_str) -> list[BatimentResult]:
|
class AxioneResult(TypedDict):
|
||||||
|
codeRetour: int
|
||||||
|
axioneErreur: AxioneErreur
|
||||||
|
batiments: list[BatimentResult]
|
||||||
|
|
||||||
|
|
||||||
|
def parse_response(resp_str) -> AxioneResult:
|
||||||
root = ET.fromstring(resp_str)
|
root = ET.fromstring(resp_str)
|
||||||
parsedBatiments = [
|
codeRetourXml = root.find(
|
||||||
parse_batiment(b)
|
".//{http://structureadresseftth.axione.fr/model/commun}codeRetour"
|
||||||
for b in root.findall(
|
)
|
||||||
".//{http://structureadresseftth.axione.fr/model/commun}batiment"
|
codeRetour = int(codeRetourXml.text.strip())
|
||||||
)
|
axioneErreur = None
|
||||||
]
|
parsedBatiments = []
|
||||||
return parsedBatiments
|
if codeRetour == 0:
|
||||||
|
parsedBatiments = [
|
||||||
|
parse_batiment(b)
|
||||||
|
for b in root.findall(
|
||||||
|
".//{http://structureadresseftth.axione.fr/model/commun}batiment"
|
||||||
|
)
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
axioneErreur = AxioneErreur(
|
||||||
|
codeErreur = root.find(
|
||||||
|
".//{http://structureadresseftth.axione.fr/model/commun}codeErreur"
|
||||||
|
).text.strip(),
|
||||||
|
libelleErreur = root.find(
|
||||||
|
".//{http://structureadresseftth.axione.fr/model/commun}libelleErreur"
|
||||||
|
).text.strip()
|
||||||
|
)
|
||||||
|
return AxioneResult(
|
||||||
|
codeRetour=codeRetour,
|
||||||
|
axioneErreur=axioneErreur,
|
||||||
|
batiments=parsedBatiments
|
||||||
|
)
|
||||||
|
|
||||||
def parse_batiment(batiment) -> BatimentResult:
|
def parse_batiment(batiment) -> BatimentResult:
|
||||||
etatBatiment = batiment.get("etatBatiment", None)
|
etatBatiment = batiment.get("etatBatiment", None)
|
||||||
|
|
|
@ -2,11 +2,12 @@ import configparser
|
||||||
|
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
def __init__(self, username, password, source_addr, db_addresses_sqlite_path):
|
def __init__(self, username, password, source_addr, db_insee_communes_sqlite_path, db_fantoir_voies_sqlite_path):
|
||||||
self.username = username
|
self.username = username
|
||||||
self.password = password
|
self.password = password
|
||||||
self.source_addr = source_addr
|
self.source_addr = source_addr
|
||||||
self.db_addresses_sqlite_path = db_addresses_sqlite_path
|
self.db_insee_communes_sqlite_path = db_insee_communes_sqlite_path
|
||||||
|
self.db_fantoir_voies_sqlite_path = db_fantoir_voies_sqlite_path
|
||||||
self.debug = False
|
self.debug = False
|
||||||
|
|
||||||
|
|
||||||
|
@ -17,5 +18,6 @@ def parse_config(cfgPath):
|
||||||
username = cfg.get("API", "username")
|
username = cfg.get("API", "username")
|
||||||
passwd = cfg.get("API", "password")
|
passwd = cfg.get("API", "password")
|
||||||
source_addr = cfg.get("API", "source_addr")
|
source_addr = cfg.get("API", "source_addr")
|
||||||
db_addresses_sqlite_path = cfg.get("ADDRESSES","db_addresses_sqlite_path")
|
db_insee_communes_sqlite_path = cfg.get("ADDRESSES","db_insee_communes_sqlite_path")
|
||||||
return Config(username, passwd, source_addr,db_addresses_sqlite_path)
|
db_fantoir_voies_sqlite_path = cfg.get("ADDRESSES","db_fantoir_voies_sqlite_path")
|
||||||
|
return Config(username, passwd, source_addr,db_insee_communes_sqlite_path, db_fantoir_voies_sqlite_path)
|
||||||
|
|
|
@ -5,4 +5,5 @@
|
||||||
# to send the requests from.
|
# to send the requests from.
|
||||||
source_addr = xxx.xxx.xxx.xxx
|
source_addr = xxx.xxx.xxx.xxx
|
||||||
[ADDRESSES]
|
[ADDRESSES]
|
||||||
db_addresses_sqlite_path = path/to/db.sqlite
|
db_insee_communes_sqlite_path = path/to/db.sqlite
|
||||||
|
db_fantoir_voies_sqlite_path = path/to/db.sqlite
|
46
fixtures/dummy-data-2.xml
Normal file
46
fixtures/dummy-data-2.xml
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
|
||||||
|
<SOAP-ENV:Header />
|
||||||
|
<SOAP-ENV:Body>
|
||||||
|
<ns3:obtentionStructureAdresseReponseSoap xmlns:ns2="http://structureadresseftth.axione.fr/model/commun" xmlns:ns3="http://structureadresseftth.axione.fr/model/entreprise">
|
||||||
|
<ns3:entete horodatageReponse="2022-02-13T00:57:50.773+01:00" horodatageRequete="2022-02-12T23:57:48.974060Z" identifiantReponse="1644710270773" versionWS="3.0">
|
||||||
|
<ns2:operateurCommercial identifiant="" nom="AQUILENET" />
|
||||||
|
</ns3:entete>
|
||||||
|
<ns3:codeRetour>
|
||||||
|
<ns2:codeRetour>0</ns2:codeRetour>
|
||||||
|
</ns3:codeRetour>
|
||||||
|
<ns3:codeOI>BEFO</ns3:codeOI>
|
||||||
|
<ns3:etatImmeuble>true</ns3:etatImmeuble>
|
||||||
|
<ns3:structureDetaillee>
|
||||||
|
<ns2:adresse referenceHexacle="361992226N">
|
||||||
|
<ns2:referenceRivoli codeInsee="36199" codeRivoli="0031" numeroVoie="48" />
|
||||||
|
</ns2:adresse>
|
||||||
|
<ns2:batiment conditionsSyndic="false" dateDebutAcceptationCmdAcces="2021-09-14T00:00:00.000+02:00" dateDebutFournitureCRCmdAcces="2021-09-14T00:00:00.000+02:00" etatBatiment="COMMERCIALISABLE" identifiantImmeuble="BAT36-7C32FE6AD11F4A6797E4F29E" nombreLogementsImmeuble="1" referenceBatiment="NA">
|
||||||
|
<ns2:referenceGeographique coordonneeImmeubleX="2.027143270510003" coordonneeImmeubleY="47.01243491007431" typeProjection="WGS84" />
|
||||||
|
<ns2:escalier reference="NA">
|
||||||
|
<ns2:etage nombreLignesActives="0" nombreLignesExistantes="0" nombreLocauxFTTH="1" refPriseCommandeObligatoire="false" reference="RDC">
|
||||||
|
<ns2:listePbo>
|
||||||
|
<ns2:pbo referencePBO="PBO36_ISS1_139" typePbo="AERIEN" />
|
||||||
|
</ns2:listePbo>
|
||||||
|
<ns2:listeLignesFTTH>
|
||||||
|
<ns2:ligneFTTH>
|
||||||
|
<ns2:prise etiquetteAPoser="Non" referencePBO="PBO36_ISS1_139">
|
||||||
|
<ns2:referencePTO>BF-ISS1-0110</ns2:referencePTO>
|
||||||
|
<ns2:referencePrisePromoteur />
|
||||||
|
<ns2:statutLigneFTTH actif="false" commercialisable="true" existant="false" raccordable="true" rompu="false" />
|
||||||
|
</ns2:prise>
|
||||||
|
</ns2:ligneFTTH>
|
||||||
|
<ns2:ligneFTTH>
|
||||||
|
<ns2:local localisationLocalOC="" localisationLocalOI="" />
|
||||||
|
</ns2:ligneFTTH>
|
||||||
|
</ns2:listeLignesFTTH>
|
||||||
|
<ns2:pm referencePM="ADR_36199_ISS1" referencePMT="PMT_36199_ISS1">
|
||||||
|
<ns2:typeEmplacementPM>PME</ns2:typeEmplacementPM>
|
||||||
|
<ns2:responsableBrassage>OC</ns2:responsableBrassage>
|
||||||
|
</ns2:pm>
|
||||||
|
</ns2:etage>
|
||||||
|
</ns2:escalier>
|
||||||
|
</ns2:batiment>
|
||||||
|
</ns3:structureDetaillee>
|
||||||
|
</ns3:obtentionStructureAdresseReponseSoap>
|
||||||
|
</SOAP-ENV:Body>
|
||||||
|
</SOAP-ENV:Envelope>
|
36
fixtures/dummy-data-3.xml
Normal file
36
fixtures/dummy-data-3.xml
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
|
||||||
|
<SOAP-ENV:Header />
|
||||||
|
<SOAP-ENV:Body>
|
||||||
|
<ns3:obtentionStructureAdresseReponseSoap xmlns:ns2="http://structureadresseftth.axione.fr/model/commun" xmlns:ns3="http://structureadresseftth.axione.fr/model/entreprise">
|
||||||
|
<ns3:entete horodatageReponse="2022-02-13T00:55:44.339+01:00" horodatageRequete="2022-02-12T23:55:42.382844Z" identifiantReponse="1644710144339" versionWS="3.0">
|
||||||
|
<ns2:operateurCommercial identifiant="" nom="AQUILENET" />
|
||||||
|
</ns3:entete>
|
||||||
|
<ns3:codeRetour>
|
||||||
|
<ns2:codeRetour>1</ns2:codeRetour>
|
||||||
|
<ns2:codeErreur>I01</ns2:codeErreur>
|
||||||
|
<ns2:libelleErreur>Code Rivoli introuvable, manquant ou incomplet</ns2:libelleErreur>
|
||||||
|
</ns3:codeRetour>
|
||||||
|
<ns3:codeOI />
|
||||||
|
<ns3:structureDetaillee>
|
||||||
|
<ns2:adresse />
|
||||||
|
<ns2:batiment referenceBatiment="">
|
||||||
|
<ns2:referenceGeographique coordonneeImmeubleX="" coordonneeImmeubleY="" typeProjection="" />
|
||||||
|
<ns2:escalier reference="">
|
||||||
|
<ns2:etage refPriseCommandeObligatoire="false" reference="">
|
||||||
|
<ns2:listePbo>
|
||||||
|
<ns2:pbo referencePBO="" typePbo="" typeRaccoPbPTO="" />
|
||||||
|
</ns2:listePbo>
|
||||||
|
<ns2:listeLignesFTTH>
|
||||||
|
<ns2:ligneFTTH />
|
||||||
|
</ns2:listeLignesFTTH>
|
||||||
|
<ns2:pm referencePM="" referencePMT="">
|
||||||
|
<ns2:typeEmplacementPM>PME</ns2:typeEmplacementPM>
|
||||||
|
<ns2:responsableBrassage>OI</ns2:responsableBrassage>
|
||||||
|
</ns2:pm>
|
||||||
|
</ns2:etage>
|
||||||
|
</ns2:escalier>
|
||||||
|
</ns2:batiment>
|
||||||
|
</ns3:structureDetaillee>
|
||||||
|
</ns3:obtentionStructureAdresseReponseSoap>
|
||||||
|
</SOAP-ENV:Body>
|
||||||
|
</SOAP-ENV:Envelope>
|
|
@ -1,3 +1,3 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
DEBUG=true CONFIG=./elig-test.ini FLASK_APP=webapp poetry run flask run --reload
|
DEBUG=true TEMPLATES_AUTO_RELOAD=true FLASK_ENV=development CONFIG=./elig-test.ini FLASK_APP=webapp poetry run flask run --reload
|
||||||
|
|
29
scripts/import-laposte-insee.sh
Executable file
29
scripts/import-laposte-insee.sh
Executable file
|
@ -0,0 +1,29 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -eau -o pipefail
|
||||||
|
|
||||||
|
if [ "$#" -ne 2 ]; then
|
||||||
|
echo "Usage: import-laposte-insee.sh path-to-laposte-insee-CSV fantoir-sqlite-db-path"
|
||||||
|
echo ""
|
||||||
|
echo "ERROR: Missing laposte CSV."
|
||||||
|
echo "You can download it at https://www.data.gouv.fr/fr/datasets/base-officielle-des-codes-postaux/"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
tmpDir=$(mktemp -d)
|
||||||
|
clean_tmp () {
|
||||||
|
rm -r "${tmpDir}"
|
||||||
|
}
|
||||||
|
trap clean_tmp EXIT
|
||||||
|
tmpSql="${tmpDir}"/import-laposte-hexasmal.sql
|
||||||
|
|
||||||
|
echo "Importing laposte/insee hexasmal data into the fantoir db."
|
||||||
|
|
||||||
|
cat >"${tmpSql}" <<EOF
|
||||||
|
.separator ";"
|
||||||
|
.import $1 insee
|
||||||
|
EOF
|
||||||
|
|
||||||
|
sqlite3 "${2}" < "${tmpSql}"
|
||||||
|
|
||||||
|
|
||||||
|
echo "Data imported"
|
BIN
static/find_pto.jpg
Normal file
BIN
static/find_pto.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 26 KiB |
|
@ -1,25 +1,235 @@
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
|
|
||||||
<html lang="fr">
|
<html lang="fr">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
|
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
|
||||||
|
integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
|
||||||
|
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"
|
||||||
|
integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"
|
||||||
|
integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1"
|
||||||
|
crossorigin="anonymous"></script>
|
||||||
|
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"
|
||||||
|
integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM"
|
||||||
|
crossorigin="anonymous"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/@tarekraafat/autocomplete.js@10.2.6/dist/autoComplete.min.js"></script>
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tarekraafat/autocomplete.js@10.2.6/dist/css/autoComplete.02.min.css">
|
||||||
|
|
||||||
<title>Aquilenet: Éligibilité FTTH</title>
|
<title>Aquilenet: Éligibilité FTTH</title>
|
||||||
<style>
|
<style>
|
||||||
{% include 'style.css' %}
|
{% include 'style.css'%}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<h1 id="aquilenet-title">Aquilenet</h1>
|
<h1 class="text-center">
|
||||||
<div id="container">
|
<a href="https://www.aquilenet.fr/" id="aquilenet-title">AQUILENET</a>
|
||||||
<h1 id="main-title">Test d'Éligibilité FTTH Aquilenet</h1>
|
</h1>
|
||||||
<form method="post" action="/result">
|
<div id="mainContainer" class="container">
|
||||||
<label>Numéro de PTO :
|
|
||||||
<input name="pto"/>
|
<div class="row d-flex justify-content-between align-items-center" data-parent="#mainContainer">
|
||||||
</label>
|
|
||||||
<button>Tester</button>
|
<div class="col-12 text-intro">
|
||||||
</form>
|
<div id="methodPto" class="collapse show testMethods">
|
||||||
|
|
||||||
|
<h2 class="text-center form-title">Test d'éligibilité par PTO</h2>
|
||||||
|
<form id="formPtoTest" method="post" action="/test/pto">
|
||||||
|
<div class="form-group" id="ptoForm">
|
||||||
|
<label class="form-label" for="pto-number">Numéro PTO</label>
|
||||||
|
<input autofocus required type="text" class="form-control" name="pto" id="pto-number" aria-describedby="ptoHelp"
|
||||||
|
placeholder="OOOO-XXXX-XXXX" oninvalid="this.setCustomValidity('Veuillez renseigner le PTO')"
|
||||||
|
oninput="setCustomValidity('')">
|
||||||
|
|
||||||
|
<small id="ptoHelp" class="form-text btn btn-link" data-toggle="collapse" data-target="#ptoInfo"
|
||||||
|
aria-expanded="true" aria-controls="ptoInfo">Où trouver mon numéro de PTO ?</small>
|
||||||
|
<div id="ptoInfo" class="collapse" aria-labelledby="ptoForm" data-parent="#ptoForm">
|
||||||
|
<div class="card-body">
|
||||||
|
PTO (Point de terminaison optique) est un numéro unique que vous pouvez trouver sur le boîtier de
|
||||||
|
raccordement de la fibre.
|
||||||
|
C'est un petit boîtier blanc installé dans la maison
|
||||||
|
<br>
|
||||||
|
<img src="{{url_for('static', filename='find_pto.jpg')}}" class="img-fluid" alt="Responsive image">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button id="buttonPtoTest" type="submit" class="btn btn-sable">Tester le PTO</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
<button id="buttonNoPto" type="button" data-toggle="collapse" data-target=".testMethods"
|
||||||
|
class="btn btn-link page-nav-btn" aria-expanded="false" aria-controls="methodPto methodAddress">Je n'ai pas/ne trouve
|
||||||
|
pas le PTO, tester autrement</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="methodAddress" class="collapse testMethods">
|
||||||
|
<h2 class="text-center form-title">Test d'éligibilité par adresse</h2>
|
||||||
|
<form id="formAddressTest" method="post" action="/test/address">
|
||||||
|
<div class="form-group row" id="communeForm">
|
||||||
|
<label class="form-label col-sm-2 my-1" for="commune-autocomplete">Commune</label>
|
||||||
|
<input id="commune-autocomplete" class="col-sm-9" type="search" dir="ltr" spellcheck=false autocorrect="off" autocomplete="off" class="form-control" autocapitalize="off"/>
|
||||||
|
</div>
|
||||||
|
<div class="form-row collapse" id="voieForm">
|
||||||
|
<div class="col-sm-3 my-1">
|
||||||
|
<label class="form-label" for="numeroVoieInput">Numéro de voie</label>
|
||||||
|
<input required type="text" name="numeroVoie" class="form-control" id="numeroVoieInput"
|
||||||
|
aria-describedby="numeroVoieHelp" placeholder="Numéro de voie"
|
||||||
|
oninvalid="this.setCustomValidity('Veuillez renseigner le numéro de voie')"
|
||||||
|
oninput="setCustomValidity('')">
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-7 my-1">
|
||||||
|
<label class="form-label" for="fantoir-autocomplete">Nom de voie</label>
|
||||||
|
<input id="fantoir-autocomplete" class="form-control" type="search" dir="ltr" spellcheck=false autocorrect="off" autocomplete="off" autocapitalize="off"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<button id="btnTestAdresse" type="submit" class="btn btn-sable collapse">Tester l'adresse</button>
|
||||||
|
</form>
|
||||||
|
<br>
|
||||||
|
<button id="buttonReturnPto" type="button" data-toggle="collapse" data-target=".testMethods"
|
||||||
|
class="btn btn-link page-nav-btn" aria-expanded="false" aria-controls="methodPto methodAddress">Revenir au test par
|
||||||
|
PTO</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
$(document).ready(function () {
|
||||||
|
|
||||||
|
// AbortController allows to cancel promises
|
||||||
|
const controllerCommunes = new AbortController();
|
||||||
|
const { signalCommunes } = controllerCommunes;
|
||||||
|
const controllerVoies = new AbortController();
|
||||||
|
const { signalVoies } = controllerVoies;
|
||||||
|
|
||||||
|
// Global variables
|
||||||
|
var communes = []
|
||||||
|
var codeInsee = 0
|
||||||
|
var codeRivoli = ""
|
||||||
|
var voies = []
|
||||||
|
var voie = ""
|
||||||
|
|
||||||
|
function sanitizeInputStr(inputStr) {
|
||||||
|
|
||||||
|
inputStr = inputStr.replace(/[ÀÁÂÃÄÅàáâãäå]/g, "a");
|
||||||
|
inputStr = inputStr.replace(/[Ææ]/g, "ae");
|
||||||
|
inputStr = inputStr.replace(/[Çç]/g, "c");
|
||||||
|
inputStr = inputStr.replace(/[ÈÉÊËèéêë]/g, "e");
|
||||||
|
inputStr = inputStr.replace(/[ÌÍÎÏìíîï]/g, "i");
|
||||||
|
inputStr = inputStr.replace(/[Ññ]/g, "n");
|
||||||
|
inputStr = inputStr.replace(/[ÒÓÔÕÖòóôõö]/g, "o");
|
||||||
|
inputStr = inputStr.replace(/[ÙÚÛÜùúûü]/g, "u");
|
||||||
|
inputStr = inputStr.replace(/[Ýý]/g, "y");
|
||||||
|
inputStr = inputStr.replace(/[ß]/g, "ss");
|
||||||
|
|
||||||
|
return inputStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setButtonSpinner(buttonSelector, buttonText) {
|
||||||
|
// disable button
|
||||||
|
$(buttonSelector).prop("disabled", true);
|
||||||
|
// add spinner to button
|
||||||
|
$(buttonSelector).html(
|
||||||
|
`<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>${buttonText}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#methodAddress').on('show.bs.collapse', function () {
|
||||||
|
$('#communeInput').trigger('input')
|
||||||
|
$('#communeInput').trigger('keyup')
|
||||||
|
});
|
||||||
|
$('#methodAddress').on('shown.bs.collapse', function () {
|
||||||
|
$('#communeInput').focus();
|
||||||
|
});
|
||||||
|
$('#voieForm').on('show.bs.collapse', function () {
|
||||||
|
$('#voieInput').trigger('input')
|
||||||
|
$('#voieInput').trigger('keyup')
|
||||||
|
});
|
||||||
|
$('#voieForm').on('shown.bs.collapse', function () {
|
||||||
|
$('#numeroVoieInput').focus();
|
||||||
|
});
|
||||||
|
|
||||||
|
const autoCompleteCommune = new autoComplete({
|
||||||
|
selector: "#commune-autocomplete",
|
||||||
|
placeHolder: "Code postal/nom de commune...",
|
||||||
|
data: {
|
||||||
|
src: async (query) => {
|
||||||
|
const api = "addresses/communes?limit=100";
|
||||||
|
const reqUrl = query === '' ? api : api + "&s=" + query
|
||||||
|
const source = await fetch(reqUrl, { signalCommunes });
|
||||||
|
const data = await source.json();
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
keys: [ "codeZip", "nom" ],
|
||||||
|
},
|
||||||
|
resultList: {
|
||||||
|
element: (list, data) => {
|
||||||
|
}
|
||||||
|
},
|
||||||
|
resultItem: {
|
||||||
|
highlight: true
|
||||||
|
},
|
||||||
|
debounce: 300,
|
||||||
|
events: {
|
||||||
|
input: {
|
||||||
|
selection: (event) => {
|
||||||
|
const selection = event.detail.selection.value;
|
||||||
|
autoCompleteCommune.input.value = selection.codeZip + " - " + selection.nom ;
|
||||||
|
codeInsee = selection.codeInsee;
|
||||||
|
$("#voieForm").collapse("show");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const autoCompleteFantoir = new autoComplete({
|
||||||
|
selector: "#fantoir-autocomplete",
|
||||||
|
placeHolder: "Nom de la voie...",
|
||||||
|
data: {
|
||||||
|
src: async (query) => {
|
||||||
|
const api = "addresses/fantoirvoies/" + codeInsee + "?limit=100";
|
||||||
|
const reqUrl = query === '' ? api : api + "&s=" + query
|
||||||
|
const source = await fetch(reqUrl, { signalVoies });
|
||||||
|
const data = await source.json();
|
||||||
|
return Object.entries(data).map(e => {return {"name": e[0], "value": e[1]}; });
|
||||||
|
},
|
||||||
|
keys: [ "name" ],
|
||||||
|
},
|
||||||
|
resultList: {
|
||||||
|
element: (list, data) => {
|
||||||
|
}
|
||||||
|
},
|
||||||
|
resultItem: {
|
||||||
|
highlight: true
|
||||||
|
},
|
||||||
|
debounce: 300,
|
||||||
|
events: {
|
||||||
|
input: {
|
||||||
|
selection: (event) => {
|
||||||
|
const selection = event.detail.selection.value;
|
||||||
|
autoCompleteFantoir.input.value = selection.name;
|
||||||
|
codeRivoli = selection.value;
|
||||||
|
$('#btnTestAdresse').collapse('show');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#formAddressTest').submit(function(eventObj) {
|
||||||
|
setButtonSpinner("#btnTestAdresse", "Test...");
|
||||||
|
$('#formAddressTest').append(`<input type="hidden" class="form-control" name="codeInsee" value="${codeInsee}" />`);
|
||||||
|
$('#formAddressTest').append(`<input type="hidden" class="form-control" name="codeRivoli" value="${codeRivoli}" />`);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
$('#formPtoTest').submit(function(eventObj) {
|
||||||
|
setButtonSpinner("#buttonPtoTest", "Test...");
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -3,7 +3,19 @@
|
||||||
<html lang="fr">
|
<html lang="fr">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
|
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
|
||||||
|
integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
|
||||||
|
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"
|
||||||
|
integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"
|
||||||
|
integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1"
|
||||||
|
crossorigin="anonymous"></script>
|
||||||
|
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"
|
||||||
|
integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM"
|
||||||
|
crossorigin="anonymous"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/@tarekraafat/autocomplete.js@10.2.6/dist/autoComplete.min.js"></script>
|
||||||
|
|
||||||
<title>Aquilenet: Éligibilité FTTH</title>
|
<title>Aquilenet: Éligibilité FTTH</title>
|
||||||
<style>
|
<style>
|
||||||
{% include 'style.css' %}
|
{% include 'style.css' %}
|
||||||
|
@ -11,11 +23,20 @@
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<h1 id="aquilenet-title">Aquilenet</h1>
|
<h1 class="text-center">
|
||||||
<div id="container">
|
<a href="https://www.aquilenet.fr/" id="aquilenet-title">AQUILENET</a>
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
|
||||||
|
<div id="containerResults" class="container">
|
||||||
|
<a id="buttonReturnTest" type="button" class="btn btn-link page-nav-btn" href="/">
|
||||||
|
Retour test
|
||||||
|
</a>
|
||||||
|
|
||||||
|
{% if result['codeRetour'] == 0 %}
|
||||||
<h1 id="main-title">Test d'Éligibilité FTTH Aquilenet: Résultats</h1>
|
<h1 id="main-title">Test d'Éligibilité FTTH Aquilenet: Résultats</h1>
|
||||||
<p>Résultat pour le PTO: {{ pto }}</p>
|
<p>Résultat pour le PTO: {{ pto }}</p>
|
||||||
{% for batiment in result %}
|
{% for batiment in result['batiments'] %}
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -57,6 +78,12 @@
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</table>
|
</table>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
|
{% else %}
|
||||||
|
<h1 id="main-title">Erreur</h1>
|
||||||
|
<p>Code d'erreur: {{ result['axioneErreur']['codeErreur'] }}</p>
|
||||||
|
<p>Description de l'erreur: {{ result['axioneErreur']['libelleErreur'] }}</p>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -1,16 +1,71 @@
|
||||||
|
html, body {
|
||||||
|
height : 100%;
|
||||||
|
/* font-size: 20px; */
|
||||||
|
}
|
||||||
body {
|
body {
|
||||||
background-color: #1787c2;
|
background-color: #1787c2;
|
||||||
display: flex;
|
/* display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column; */
|
||||||
font-family: 'Titillium Web', sans-serif;
|
font-family: 'Titillium Web', sans-serif;
|
||||||
}
|
}
|
||||||
#aquilenet-title {
|
#aquilenet-title {
|
||||||
color: white;
|
color: whitesmoke;
|
||||||
align-self: center;
|
align-self: center;
|
||||||
font-size: 4em;
|
font-size: 2em;
|
||||||
|
margin-top: .3em;
|
||||||
|
margin-bottom: .5em;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
.form-title {
|
||||||
|
color: whitesmoke;
|
||||||
|
align-self: center;
|
||||||
|
font-size: 2em;
|
||||||
margin-top: .3em;
|
margin-top: .3em;
|
||||||
margin-bottom: .5em;
|
margin-bottom: .5em;
|
||||||
}
|
}
|
||||||
|
.form-label, .card-body, #containerResults {
|
||||||
|
color: whitesmoke;
|
||||||
|
}
|
||||||
|
#ptoHelp {
|
||||||
|
color: rgb(192, 192, 192);
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sable .btn-ciel, .ciel .btn-sable{
|
||||||
|
padding: 8px;
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: 3px 3px 3px rgba(0,0,0,.3);
|
||||||
|
font-size: 1.1em
|
||||||
|
}
|
||||||
|
.sable .btn-ciel:hover, .ciel .btn-sable:hover{
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-nav-btn {
|
||||||
|
color: whitesmoke;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-sable:hover {
|
||||||
|
background-position: right center; /* change the direction of the change here */
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-sable {
|
||||||
|
/* box-shadow: 0 0 20px #eee; */
|
||||||
|
/* border-radius: 10px; */
|
||||||
|
color: darkslategray;
|
||||||
|
transition: 0.5s;
|
||||||
|
background-size: 200% auto;
|
||||||
|
text-shadow: dimgray 0 1px 1px;
|
||||||
|
background-image: linear-gradient(to bottom, #ffd38c 0%, #ffedd0 51%, #e0e0e0 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Autocomplete */
|
||||||
|
|
||||||
|
|
||||||
|
/* #fda085 51%, */
|
||||||
|
|
||||||
|
/*
|
||||||
#container {
|
#container {
|
||||||
width: 80%;
|
width: 80%;
|
||||||
background-color: #ffd38c;
|
background-color: #ffd38c;
|
||||||
|
@ -29,7 +84,7 @@ form {
|
||||||
align-self: center;
|
align-self: center;
|
||||||
margin-bottom: 2em;
|
margin-bottom: 2em;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
table,td {
|
table,td {
|
||||||
border: 2px solid #333;
|
border: 2px solid #333;
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
|
|
39
webapp.py
39
webapp.py
|
@ -3,7 +3,7 @@ from flask import Flask, render_template, request, escape
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from config.config import parse_config
|
from config.config import parse_config
|
||||||
from axione_api.api import query_axione_pto, parse_response
|
from axione_api.api import query_axione_pto, parse_response, query_axione_fantoir
|
||||||
from address_finder.api import AddressFinder
|
from address_finder.api import AddressFinder
|
||||||
|
|
||||||
def load_config():
|
def load_config():
|
||||||
|
@ -22,7 +22,7 @@ def load_config():
|
||||||
|
|
||||||
cfg = load_config()
|
cfg = load_config()
|
||||||
|
|
||||||
addressFinder = AddressFinder(cfg.db_addresses_sqlite_path)
|
addressFinder = AddressFinder(cfg.db_insee_communes_sqlite_path, cfg.db_fantoir_voies_sqlite_path)
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
@ -30,17 +30,11 @@ app = Flask(__name__)
|
||||||
def get_form():
|
def get_form():
|
||||||
return render_template("landing_form.html")
|
return render_template("landing_form.html")
|
||||||
|
|
||||||
@app.route("/result", methods=['POST'])
|
|
||||||
def show_result():
|
|
||||||
pto = escape(request.form['pto'])
|
|
||||||
result = parse_response(query_axione_pto(cfg, pto))
|
|
||||||
return render_template("result.html", pto=pto, result=result)
|
|
||||||
|
|
||||||
@app.route("/addresses/communes", methods=['GET'])
|
@app.route("/addresses/communes", methods=['GET'])
|
||||||
def get_communes():
|
def get_communes():
|
||||||
to_search=request.args.get('s')
|
to_search=request.args.get('s')
|
||||||
print(to_search)
|
limit=request.args.get('limit')
|
||||||
communes=addressFinder.getCommunesFromNameOrZip(to_search)
|
communes=addressFinder.getCommunesFromNameOrZip(to_search,limit)
|
||||||
response = app.response_class(
|
response = app.response_class(
|
||||||
response=json.dumps(communes),
|
response=json.dumps(communes),
|
||||||
mimetype='application/json'
|
mimetype='application/json'
|
||||||
|
@ -49,9 +43,32 @@ def get_communes():
|
||||||
|
|
||||||
@app.route("/addresses/fantoirvoies/<codeInsee>", methods=['GET'])
|
@app.route("/addresses/fantoirvoies/<codeInsee>", methods=['GET'])
|
||||||
def get_fantoir_voies(codeInsee):
|
def get_fantoir_voies(codeInsee):
|
||||||
fantoirVoies=addressFinder.getCommuneFantoirVoies(codeInsee)
|
to_search=request.args.get('s')
|
||||||
|
limit=request.args.get('limit')
|
||||||
|
fantoirVoies=addressFinder.getCommuneFantoirVoies(codeInsee,to_search,limit)
|
||||||
response = app.response_class(
|
response = app.response_class(
|
||||||
response=json.dumps(fantoirVoies),
|
response=json.dumps(fantoirVoies),
|
||||||
mimetype='application/json'
|
mimetype='application/json'
|
||||||
)
|
)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
@app.route("/test/address", methods=['POST'])
|
||||||
|
def test_address():
|
||||||
|
codeInsee = escape(request.form['codeInsee'])
|
||||||
|
numeroVoie = escape(request.form['numeroVoie'])
|
||||||
|
# Trimming rivoli's key
|
||||||
|
codeRivoli = escape(request.form['codeRivoli'])[:-1]
|
||||||
|
result = parse_response(query_axione_fantoir(cfg, codeInsee, codeRivoli, numeroVoie))
|
||||||
|
if cfg.debug:
|
||||||
|
print("===================")
|
||||||
|
print("Dummy response: ")
|
||||||
|
print(result)
|
||||||
|
print("===================")
|
||||||
|
return render_template("result.html", pto="", result=result)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/test/pto", methods=['POST'])
|
||||||
|
def test_pto():
|
||||||
|
pto = escape(request.form['pto'])
|
||||||
|
result = parse_response(query_axione_pto(cfg, pto))
|
||||||
|
return render_template("result.html", pto=pto, result=result)
|
||||||
|
|
Loading…
Reference in a new issue