Ajout du frontend et branchement sur l'API axione #9

Merged
felix.baylac merged 21 commits from johan/frontend into master 2022-02-13 19:39:12 +01:00
3 changed files with 143 additions and 78 deletions
Showing only changes of commit 7672a52239 - Show all commits

View file

@ -34,7 +34,7 @@ def fantoirRequest(insee, rivoli, numVoie):
<com:operateurCommercial nom="AQUILENET" identifiant=""/> <com:operateurCommercial nom="AQUILENET" identifiant=""/>
</ent:entete> </ent:entete>
<ent:referenceAdresse> <ent:referenceAdresse>
<com:referenceRivoli codeInsee="{insee}" codeRivoli="{rivoli}" numeroVoie="{numvoie}"/> <com:referenceRivoli codeInsee="{insee}" codeRivoli="{rivoli}" numeroVoie="{numVoie}"/>
</ent:referenceAdresse> </ent:referenceAdresse>
</ent:obtentionStructureAdresseDemandeSoap> </ent:obtentionStructureAdresseDemandeSoap>
</soapenv:Body> </soapenv:Body>

View file

@ -1,17 +1,24 @@
<!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, shrink-to-fit=no"> <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"> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script> integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<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://code.jquery.com/jquery-3.6.0.min.js"
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script> integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" 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>
<title>Aquilenet: Éligibilité FTTH</title> <title>Aquilenet: Éligibilité FTTH</title>
<style> <style>
{% include 'style.css' %} {% include 'style.css'%}
</style> </style>
</head> </head>
@ -24,16 +31,20 @@
<div class="col-12 text-intro"> <div class="col-12 text-intro">
<div id="methodPto" class="collapse show testMethods"> <div id="methodPto" class="collapse show testMethods">
<h2 class="text-center form-title">Test d'éligibilité par PTO</h1> <h2 class="text-center form-title">Test d'éligibilité par PTO</h2>
<form method="post" action="/test/pto"> <form method="post" action="/test/pto">
<div class="form-group" id="ptoForm"> <div class="form-group" id="ptoForm">
<label class="form-label" for="pto-number">Numéro PTO</label> <label class="form-label" for="pto-number">Numéro PTO</label>
<input 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('')"> <input 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> <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 id="ptoInfo" class="collapse" aria-labelledby="ptoForm" data-parent="#ptoForm">
<div class="card-body"> <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. 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 C'est un petit boîtier blanc installé dans la maison
<br> <br>
<img src="{{url_for('static', filename='find_pto.jpg')}}" class="img-fluid" alt="Responsive image"> <img src="{{url_for('static', filename='find_pto.jpg')}}" class="img-fluid" alt="Responsive image">
@ -44,105 +55,150 @@
</form> </form>
<br> <br>
<button id="buttonNoPto" type="button" data-toggle="collapse" data-target=".testMethods" class="btn btn-link" aria-expanded="false" aria-controls="methodPto methodAddress">Je n'ai pas/ne trouve pas le PTO, tester autrement</button> <button id="buttonNoPto" type="button" data-toggle="collapse" data-target=".testMethods"
class="btn btn-link" aria-expanded="false" aria-controls="methodPto methodAddress">Je n'ai pas/ne trouve
pas le PTO, tester autrement</button>
</div> </div>
<div id="methodAddress" class="collapse testMethods"> <div id="methodAddress" class="collapse testMethods">
<h2 class="text-center form-title">Test d'éligibilité par adresse</h1> <h2 class="text-center form-title">Test d'éligibilité par adresse</h2>
<form method="post" action="/test/address"> <form id="formAddressTest" method="post" action="/test/address">
<div class="form-group" id="communeForm"> <div class="form-group" id="communeForm">
<label class="form-label" for="communeInput">Commune</label> <label class="form-label" for="communeInput">Commune</label>
<input autocomplete="off" type="text" list="communes" name="commune" class="form-control" id="communeInput" ondblclick="this.focus();this.select()" aria-describedby="communeHelp" placeholder="Nom de la commune ou code postal"> <input autocomplete="off" type="text" list="communes" name="commune" class="form-control"
<datalist id="communes"> id="communeInput" ondblclick="this.focus();this.select()" aria-describedby="communeHelp"
</datalist> placeholder="Nom de la commune ou code postal">
</div> <datalist id="communes">
<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-9 my-1">
<label class="form-label" for="voieInput">Nom de voie</label>
<input autocomplete="off" type="text" name="voie" list="voies" class="form-control" id="voieInput" aria-describedby="voieHelp" placeholder="Nom de voie">
<datalist id="voies">
</datalist> </datalist>
</div> </div>
</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-9 my-1">
<label class="form-label" for="voieInput">Nom de voie</label>
<input autocomplete="off" type="text" name="voie" list="voies" class="form-control" id="voieInput"
aria-describedby="voieHelp" placeholder="Nom de voie">
<datalist id="voies">
</datalist>
</div>
</div>
<br>
<button id="btnTestAdresse" type="submit" class="btn btn-sable collapse">Tester l'adresse</button>
</form>
<br> <br>
<button id="btnTestAdresse" type="submit" class="btn btn-sable collapse">Tester l'adresse</button> <button id="buttonReturnPto" type="button" data-toggle="collapse" data-target=".testMethods"
</form> class="btn btn-link" aria-expanded="false" aria-controls="methodPto methodAddress">Revenir au test par
<br> PTO</button>
<button id="buttonReturnPto" type="button" data-toggle="collapse" data-target=".testMethods" class="btn btn-link" aria-expanded="false" aria-controls="methodPto methodAddress">Revenir au test par PTO</button>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<script> <script>
$(document).ready(function(){ $(document).ready(function () {
var communes=[]
var codeInsee=0 // AbortController allows to cancel promises
var voies=[] const controllerCommunes = new AbortController();
var voie="" const { signalCommunes } = controllerCommunes;
function fillCommunes(search='') { const controllerVoies = new AbortController();
var api="addresses/communes?limit=15"; const { signalVoies } = controllerVoies;
// Global variables
var communes = []
var codeInsee = 0
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 to update list of communes (calls backend API)
function updateCommunes(search = '') {
var api = "addresses/communes?limit=15";
if (search != '') { if (search != '') {
api+="&s="+search; api += "&s=" + search;
} }
$.ajax({ fetch(api, { signalCommunes })
type: 'GET', .then(response => response.json())
dataType:"json", .then(data => {
url: api, if (JSON.stringify(data) !== JSON.stringify(communes)) {
success: function (data, status, xhr) {
if (JSON.stringify(data) !== JSON.stringify(communes)){
$("#communes").empty(); $("#communes").empty();
communes=data communes = data
communes.forEach(commune => { communes.forEach(commune => {
$("#communes").append("<option codeInsee=" + commune.codeInsee + " value='" + $("#communes").append("<option codeInsee=" + commune.codeInsee + " value='" +
commune.codeZip + ' ' + commune.nom + "'></option>"); commune.codeZip + ' ' + commune.nom + "'></option>");
}); });
} }
if (communes.length == 1) { if (communes.length == 1) {
codeInsee=communes[0].codeInsee; codeInsee = communes[0].codeInsee;
$('#voieForm').collapse('show'); $('#voieForm').collapse('show');
} else { } else {
$('#voieInput').val(''); $('#voieInput').val('');
$('#voieForm').collapse('hide'); $('#voieForm').collapse('hide');
} }
} })
}); .catch(err => {
if (err.name === "AbortError") {
console.log("DEBUG: updateCommunes aborted !")
}
console.error("Error fetching communes:", err)
})
} }
var voies=[] // Function to update list of voies (calls backend API)
function fillVoies(codeInsee,search='') { function updateVoies(codeInsee, search = '') {
var api="addresses/fantoirvoies/" + codeInsee + '?limit=15'; var api = "addresses/fantoirvoies/" + codeInsee + '?limit=15';
if (search != '') { if (search != '') {
api+="&s="+search; api += "&s=" + search;
} }
$.ajax({ fetch(api, { signalVoies })
type: 'GET', .then(response => response.json())
dataType:"json", .then(data => {
url: api, if (JSON.stringify(data) !== JSON.stringify(voies)) {
success: function (data, status, xhr) {
if (JSON.stringify(data) !== JSON.stringify(voies)){
$("#voies").empty(); $("#voies").empty();
voies=Object.entries(data); voies = Object.entries(data);
let voie; console.log(voies)
for (i in voies) { let voie;
voie = voies[i] for (i in voies) {
$("#voies").append("<option id=" + voie[1] + " value='" + voie = voies[i]
voie[0] + "'></option>"); $("#voies").append("<option id=" + voie[1] + " value='" +
voie[0] + "'></option>");
} }
console.log(voies)
if (voies.length == 1) { if (voies.length == 1) {
$('#btnTestAdresse').collapse('show'); $('#btnTestAdresse').collapse('show');
voie=voies[0].libelle[0] voie = voies[0]
} else { } else {
$('#btnTestAdresse').collapse('hide'); $('#btnTestAdresse').collapse('hide');
} }
} }
} })
}); .catch(err => {
if (err.name === "AbortError") {
console.log("DEBUG: updateCommunes aborted !")
}
console.error("Error fetching communes:", err)
})
} }
$('#methodAddress').on('show.bs.collapse', function () { $('#methodAddress').on('show.bs.collapse', function () {
$('#communeInput').trigger('input') $('#communeInput').trigger('input')
@ -154,16 +210,20 @@
}); });
$('#communeInput').on('keyup', function () { $('#communeInput').on('keyup', function () {
fillCommunes($(this).val()); controllerCommunes.abort();
inputStr = sanitizeInputStr($(this).val());
updateCommunes(inputStr);
}); });
$('#communeInput').on('input', function() { $('#communeInput').on('input', function () {
if ($(this).val() === '') { if ($(this).val() === '') {
$('#voieForm').collapse('hide'); $('#voieForm').collapse('hide');
} }
}); });
$('#voieInput').on('keyup', function () { $('#voieInput').on('keyup', function () {
fillVoies(codeInsee,$(this).val()); controllerVoies.abort();
inputStr = sanitizeInputStr($(this).val());
updateVoies(codeInsee, inputStr);
}); });
$('#voieInput').on('input', function () { $('#voieInput').on('input', function () {
if ($(this).val() === '') { if ($(this).val() === '') {
@ -171,7 +231,13 @@
} }
}); });
$('#formAddressTest').submit(function(eventObj) {
console.log("SUBMIT",$('#formAddressTest'))
$('#formAddressTest').append(`<input type="hidden" class="form-control" name="codeInsee" value="${codeInsee}" />`);
return true;
});
}); });
</script> </script>
</body> </body>
</html>
</html>

View file

@ -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():
@ -54,12 +54,11 @@ def get_fantoir_voies(codeInsee):
@app.route("/test/address", methods=['POST']) @app.route("/test/address", methods=['POST'])
def test_address(): def test_address():
commune = escape(request.form['commune']) codeInsee = escape(request.form['codeInsee'])
numeroVoie = escape(request.form['numeroVoie']) numeroVoie = escape(request.form['numeroVoie'])
# Trimming rivoli's key # Trimming rivoli's key
voie = escape(request.form['voie'])[:-1] voie = escape(request.form['voie'])[:-1]
result = query_axione_fantoir(cfg, commune, voie, numeroVoie) result = query_axione_fantoir(cfg, codeInsee, voie, numeroVoie)
# result = parse_response(query_axione_pto(cfg, pto))
return render_template("result.html", pto="", result=result) return render_template("result.html", pto="", result=result)