From 22f43fc95448262118290bc4f71c2c525c11f7d1 Mon Sep 17 00:00:00 2001 From: Johan Le Baut Date: Wed, 16 Nov 2022 16:10:30 +0100 Subject: [PATCH 01/17] Add API to get elig status per imm ref --- data-ingest/ingest | 31 +++++++++- webapp/eligibility_api/__init__.py | 0 webapp/eligibility_api/elig_api_exceptions.py | 39 +++++++++++++ webapp/eligibility_api/elig_api_routes.py | 21 +++++++ webapp/ipe_fetcher/axione.py | 57 ++++++++++++++++++- webapp/ipe_fetcher/sqlite_connector/cursor.py | 5 ++ webapp/main.py | 9 ++- 7 files changed, 155 insertions(+), 7 deletions(-) create mode 100644 webapp/eligibility_api/__init__.py create mode 100644 webapp/eligibility_api/elig_api_exceptions.py create mode 100644 webapp/eligibility_api/elig_api_routes.py diff --git a/data-ingest/ingest b/data-ingest/ingest index 13578e9..67c438e 100755 --- a/data-ingest/ingest +++ b/data-ingest/ingest @@ -23,12 +23,29 @@ cat > "${tmpSql}" <> "${tmpSql}" <> "${tmpSql}" < "${tmpSql}" < "${tmpSql}" < dict: @@ -59,11 +74,13 @@ class Axione: """, areaCoordinates, ) - + + res = cur.fetchall() + if not existing_buildings: existing_buildings = dict() buildings = existing_buildings - for b in cur.fetchall(): + for b in res: etatImm = b[3] idImm = b[2] isEligible = etatImm == AXIONE_ETAT_DEPLOYE @@ -99,3 +116,37 @@ class Axione: return buildings else: raise ValueError("The requested area is too wide, please reduce it") + + def get_eligibilite_per_id_immeuble(self, id_immeuble: str): + + # Try to get cursor on Axione database + try: + cur = getBasicCursor(self.db_axione_ipe_path) + except Exception as err: + print("Error while connecting to DB: ", err) + raise "Could not get Axione data" + + cur.execute( + f""" + SELECT EtatImmeuble + FROM {self.db_name_refimm} + WHERE IdentifiantImmeuble == '{id_immeuble}' + """ + ) + res = cur.fetchone() + if res: + imm_elig = res[0] + isEligible = imm_elig == AXIONE_ETAT_DEPLOYE + reasonNotEligible = "" if isEligible else "Pas encore deploye" + else: + isEligible = False + imm_elig = "NOT_AXIONE" + reasonNotEligible = "Axione ne gere pas ce batiment" + + eligStatus = FAIEligibilityStatus( + isEligible=isEligible, + ftthStatus=imm_elig, + reasonNotEligible=reasonNotEligible, + ) + + return eligStatus diff --git a/webapp/ipe_fetcher/sqlite_connector/cursor.py b/webapp/ipe_fetcher/sqlite_connector/cursor.py index b41527f..a7701ed 100644 --- a/webapp/ipe_fetcher/sqlite_connector/cursor.py +++ b/webapp/ipe_fetcher/sqlite_connector/cursor.py @@ -6,3 +6,8 @@ def getCursorWithSpatialite(db_path: str = None) -> sqlite3.Cursor: db.enable_load_extension(True) cur.execute('SELECT load_extension("mod_spatialite")') return cur + +def getBasicCursor(db_path: str = None) -> sqlite3.Cursor: + db = sqlite3.connect(db_path) + cur = db.cursor() + return cur diff --git a/webapp/main.py b/webapp/main.py index 3297793..42fe7a5 100644 --- a/webapp/main.py +++ b/webapp/main.py @@ -5,6 +5,10 @@ import configparser import sqlite3 import os from ipe_fetcher import Liazo,Axione,Arcep,AreaCoordinates +from eligibility_api.elig_api_exceptions import FlaskExceptions +from eligibility_api.elig_api_routes import EligibilityApiRoutes + + class Config(TypedDict): axione_ipe_path: str axione_ipe_db_name: str @@ -31,7 +35,10 @@ cfg: Config = parseConfig() axione = Axione(cfg.get("axione_ipe_path"), cfg.get("axione_ipe_db_name")) arcep = Arcep(cfg.get("arcep_ipe_path"), cfg.get("arcep_ipe_db_name")) liazo = Liazo() - +elig_api_routes = EligibilityApiRoutes(app, axione) +elig_api_routes.add_routes() +elig_api_exceptions = FlaskExceptions(app) +elig_api_exceptions.add_exceptions() @app.route("/", methods=["GET"]) def getMap(): -- 2.30.2 From f52e29463a7e6b891442df35dd8ff92d30ab170c Mon Sep 17 00:00:00 2001 From: Johan Le Baut Date: Wed, 16 Nov 2022 16:15:51 +0100 Subject: [PATCH 02/17] remove unused comment --- data-ingest/ingest | 2 -- 1 file changed, 2 deletions(-) diff --git a/data-ingest/ingest b/data-ingest/ingest index 67c438e..979a586 100755 --- a/data-ingest/ingest +++ b/data-ingest/ingest @@ -44,8 +44,6 @@ EOF done -#cat ${tmpSql} - echo "" rc=0 -- 2.30.2 From 270812f6d6d5e22aba80e035218cce98b72ebf4b Mon Sep 17 00:00:00 2001 From: Johan Le Baut Date: Wed, 30 Nov 2022 19:29:27 +0100 Subject: [PATCH 03/17] =?UTF-8?q?ajout=20de=20priorit=C3=A9=20dans=20l'aff?= =?UTF-8?q?ichage=20des=20immeubles=20selon=20leur=20statut=20de=20d=C3=A9?= =?UTF-8?q?ploiement?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- webapp/ipe_fetcher/arcep.py | 19 ++++++++++++++++++- webapp/ipe_fetcher/axione.py | 20 +++++++++++++++++--- webapp/ipe_fetcher/liazo.py | 4 ++-- webapp/ipe_fetcher/model.py | 1 + webapp/main.py | 13 +++++++------ webapp/templates/app.js | 6 ++++-- 6 files changed, 49 insertions(+), 14 deletions(-) diff --git a/webapp/ipe_fetcher/arcep.py b/webapp/ipe_fetcher/arcep.py index 55d9439..d0ed04d 100644 --- a/webapp/ipe_fetcher/arcep.py +++ b/webapp/ipe_fetcher/arcep.py @@ -1,6 +1,7 @@ +from os.path import exists + from ipe_fetcher.model import AreaCoordinates, Building, FAIEligibilityStatus from ipe_fetcher.sqlite_connector.cursor import getCursorWithSpatialite -from os.path import exists ARCEP_ETAT_DEPLOYE = "deploye" @@ -12,6 +13,18 @@ class Arcep: if not exists(self.db_arcep_ipe_path): raise ValueError(f"File {self.db_arcep_ipe_path} does not exist") + @staticmethod + def _get_etat_priority(etat_imm): + if etat_imm == ARCEP_ETAT_DEPLOYE: + return 0 + elif etat_imm == "en cours de deploiement": + return 1 + elif etat_imm != "abandonne": + return 3 + else: + return 4 + + def getAreaBuildings( self, areaCoordinates: AreaCoordinates, existing_buildings: dict ) -> dict: @@ -73,9 +86,12 @@ class Arcep: ftthStatus=etatImm, reasonNotEligible=None if isEligible else "Pas encore deploye", ) + + etat_priority = self._get_etat_priority(etatImm) if buildings.get(idImm): buildings[idImm]["othersEligStatus"] = othersEligStatus buildings[idImm]["bat_info"] = bat_info + buildings[idImm]['etat_imm_priority'] = etat_priority if buildings[idImm].get('found_in'): buildings[idImm]['found_in'].append("arcep") else: @@ -92,6 +108,7 @@ class Arcep: commune=commune, bat_info=bat_info, found_in = ["arcep"], + etat_imm_priority=etat_priority, aquilenetEligStatus=FAIEligibilityStatus(isEligible=False, reasonNotEligible="", ftthStatus=""), fdnEligStatus=FAIEligibilityStatus(isEligible=False, reasonNotEligible="", ftthStatus=""), othersEligStatus=othersEligStatus, diff --git a/webapp/ipe_fetcher/axione.py b/webapp/ipe_fetcher/axione.py index 5db90bf..e2b1665 100644 --- a/webapp/ipe_fetcher/axione.py +++ b/webapp/ipe_fetcher/axione.py @@ -1,9 +1,7 @@ -import time +from os.path import exists from ipe_fetcher.model import AreaCoordinates, Building, FAIEligibilityStatus from ipe_fetcher.sqlite_connector.cursor import getCursorWithSpatialite, getBasicCursor -from os.path import exists - AXIONE_ETAT_DEPLOYE = "DEPLOYE" AXIONE_ETAT_DEPLOIEMENT = "EN COURS DE DEPLOIEMENT" @@ -33,6 +31,19 @@ class Axione: if cur.fetchone()[0] == 1: self.db_name_refimm = AXIONE_REFIMM_TABLE_NAME + @staticmethod + def _get_etat_priority(etat_imm): + if etat_imm == AXIONE_ETAT_DEPLOYE: + return 0 + elif etat_imm == AXIONE_ETAT_DEPLOIEMENT: + return 1 + elif etat_imm == AXIONE_ETAT_RAD_DEPLOIEMENT: + return 2 + elif etat_imm != AXIONE_ETAT_ABANDONNE: + return 3 + else: + return 4 + def getAreaBuildings( self, areaCoordinates: AreaCoordinates, existing_buildings: dict @@ -89,8 +100,10 @@ class Axione: ftthStatus=etatImm, reasonNotEligible="" if isEligible else "Pas encore deploye", ) + etat_priority = self._get_etat_priority(etatImm) if buildings.get(idImm): buildings[idImm]["aquilenetEligStatus"] = aquilenetEligStatus + buildings[idImm]['etat_imm_priority'] = etat_priority if buildings[idImm].get('found_in'): buildings[idImm]['found_in'].append("axione") else: @@ -108,6 +121,7 @@ class Axione: commune=b[8], bat_info="", found_in = ["axione"], + etat_imm_priority=etat_priority, aquilenetEligStatus=aquilenetEligStatus, fdnEligStatus=FAIEligibilityStatus(isEligible=False, reasonNotEligible="", ftthStatus=""), othersEligStatus=FAIEligibilityStatus(isEligible=False, reasonNotEligible="", ftthStatus=""), diff --git a/webapp/ipe_fetcher/liazo.py b/webapp/ipe_fetcher/liazo.py index 7cfeafb..79f6cca 100644 --- a/webapp/ipe_fetcher/liazo.py +++ b/webapp/ipe_fetcher/liazo.py @@ -17,7 +17,7 @@ class Liazo: r = c.getresponse() if r.status < 200 or r.status >= 300: print("Erreur de serveur chez FDN.") - return + return existing_buildings d = r.read() c.close() v = json.loads(d.decode("utf-8")) @@ -38,7 +38,6 @@ class Liazo: else: buildings[idImm]['found_in'] = ["liazo"] if not buildings.get(idImm): - building = Building( y=building.get('lat'), x=building.get('lon'), @@ -50,6 +49,7 @@ class Liazo: commune="", bat_info="", found_in = ["liazo"], + etat_imm_priority=1, fdnEligStatus=fdnEligStatus, aquilenetEligStatus=FAIEligibilityStatus(isEligible=False, reasonNotEligible="", ftthStatus=""), othersEligStatus=FAIEligibilityStatus(isEligible=False, reasonNotEligible="", ftthStatus=""), diff --git a/webapp/ipe_fetcher/model.py b/webapp/ipe_fetcher/model.py index 8fc1e19..a13c645 100644 --- a/webapp/ipe_fetcher/model.py +++ b/webapp/ipe_fetcher/model.py @@ -18,6 +18,7 @@ class Building(TypedDict): commune: str bat_info: str found_in: list[str] + etat_imm_priority: int = 10 aquilenetEligStatus: FAIEligibilityStatus fdnEligStatus: FAIEligibilityStatus othersEligStatus: FAIEligibilityStatus diff --git a/webapp/main.py b/webapp/main.py index 42fe7a5..90c4f71 100644 --- a/webapp/main.py +++ b/webapp/main.py @@ -1,12 +1,12 @@ +import configparser +import os +from typing import TypedDict + from flask import Flask, request, render_template, redirect -from typing import TypedDict -import configparser -import sqlite3 -import os -from ipe_fetcher import Liazo,Axione,Arcep,AreaCoordinates from eligibility_api.elig_api_exceptions import FlaskExceptions from eligibility_api.elig_api_routes import EligibilityApiRoutes +from ipe_fetcher import Liazo, Axione, Arcep, AreaCoordinates class Config(TypedDict): @@ -81,8 +81,9 @@ def getEligData(): print("Could not get Axione data for this area:", err) buildings = liazo.getAreaBuildings(narrow_coordinates, buildings) + sorted_buildings = sorted(buildings.values(), key=lambda d: d.get('etat_imm_priority', 1), reverse=True) - return {"buildings": buildings} + return {"buildings": list(sorted_buildings)} else: return "Invalid bounding box coordinates", 400 diff --git a/webapp/templates/app.js b/webapp/templates/app.js index 1d9c066..00a6917 100644 --- a/webapp/templates/app.js +++ b/webapp/templates/app.js @@ -138,9 +138,11 @@ function initAddrSearch(map) { } function updateEligData(map, eligData) { - markers.map(marker => map.removeLayer(marker)); + if (markers) { + markers.map(marker => map.removeLayer(marker)); + } let buildings = eligData.buildings; - markers = Object.values(buildings).map(building => { + markers = buildings.forEach(building => { const latlng = new L.latLng(building.y, building.x); let addrImm = `${building.numVoieImm} ${building.typeVoieImm} ${building.nomVoieImm}` if (building.bat_info != "") { -- 2.30.2 From 90bcc500878dbc52de8aa6df114d157ebfd46c66 Mon Sep 17 00:00:00 2001 From: Johan Le Baut Date: Thu, 8 Dec 2022 12:28:33 +0100 Subject: [PATCH 04/17] update axione ingest script --- data-ingest/ingest | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/data-ingest/ingest b/data-ingest/ingest index 979a586..4d37cfe 100755 --- a/data-ingest/ingest +++ b/data-ingest/ingest @@ -1,6 +1,8 @@ #!/usr/bin/env bash set -eau -o pipefail +NEEDED_COLUMNS=("IdentifiantImmeuble" "EtatImmeuble" "CoordonneeImmeubleX" "CoordonneeImmeubleY" "NumeroVoieImmeuble" "TypeVoieImmeuble" "NomVoieImmeuble" "CodePostalImmeuble" "CommuneImmeuble" "DateDebutAcceptationCmdAcces" "DateMiseEnServiceCommercialeImmeuble") + if [ "$#" -ne 2 ]; then echo "Usage: ingest path-to-directory-containing-IPE-CSVs path-to-generated-db" echo "" @@ -35,13 +37,36 @@ for ipeFile in ${ipeFiles}; do fi if ! $firstFile; then import_opt="-skip 1" + else + header=$(head -n1 $ipeFile) + OLD_IFS=$IFS + export IFS=";" + idx=1 + idx_to_keep=() + for column in $header; do + export IFS=$OLD_IFS + if [[ " ${NEEDED_COLUMNS[*]} " =~ " ${column} " ]]; then + idx_to_keep+=("$idx") + fi + idx=$((idx+1)) + export IFS=";" + done + export IFS=$OLD_IFS + cut_idx_to_keep=$(IFS=',';echo "${idx_to_keep[*]}";IFS=$' \t\n') + echo " Column indexes that will be kept in csv files: $cut_idx_to_keep (matching columns ${NEEDED_COLUMNS[*]})" fi firstFile=false fi - cat >> "${tmpSql}" < ${ipeFile}.cut + useIpeFile=${ipeFile}.cut + fi + cat >> "${tmpSql}" < "${tmpSql}" < Date: Fri, 9 Dec 2022 10:24:44 +0100 Subject: [PATCH 05/17] fix ingest sxr --- data-ingest/ingest | 1 - 1 file changed, 1 deletion(-) diff --git a/data-ingest/ingest b/data-ingest/ingest index 4d37cfe..1446d99 100755 --- a/data-ingest/ingest +++ b/data-ingest/ingest @@ -66,7 +66,6 @@ for ipeFile in ${ipeFiles}; do cat >> "${tmpSql}" < Date: Fri, 9 Dec 2022 12:01:55 +0100 Subject: [PATCH 06/17] check if axione is commandable --- webapp/ipe_fetcher/axione.py | 37 ++++++++++++++++++++++++++++-------- webapp/ipe_fetcher/model.py | 3 ++- webapp/poetry.lock | 34 ++++++++++++++++++++++++++++++++- webapp/pyproject.toml | 1 + webapp/templates/app.js | 20 +++++++++++++------ 5 files changed, 79 insertions(+), 16 deletions(-) diff --git a/webapp/ipe_fetcher/axione.py b/webapp/ipe_fetcher/axione.py index e2b1665..e0f04c5 100644 --- a/webapp/ipe_fetcher/axione.py +++ b/webapp/ipe_fetcher/axione.py @@ -1,9 +1,11 @@ +from datetime import datetime from os.path import exists from ipe_fetcher.model import AreaCoordinates, Building, FAIEligibilityStatus from ipe_fetcher.sqlite_connector.cursor import getCursorWithSpatialite, getBasicCursor AXIONE_ETAT_DEPLOYE = "DEPLOYE" +AXIONE_ETAT_DEPLOYE_NON_COMMANDABLE = "DEPLOYE MAIS NON COMMANDABLE" AXIONE_ETAT_DEPLOIEMENT = "EN COURS DE DEPLOIEMENT" AXIONE_ETAT_ABANDONNE = "ABANDONNE" AXIONE_ETAT_CIBLE = "CIBLE" @@ -33,16 +35,18 @@ class Axione: @staticmethod def _get_etat_priority(etat_imm): - if etat_imm == AXIONE_ETAT_DEPLOYE: + if etat_imm in AXIONE_ETAT_DEPLOYE: return 0 - elif etat_imm == AXIONE_ETAT_DEPLOIEMENT: + elif etat_imm == AXIONE_ETAT_DEPLOYE_NON_COMMANDABLE: return 1 - elif etat_imm == AXIONE_ETAT_RAD_DEPLOIEMENT: + elif etat_imm == AXIONE_ETAT_DEPLOIEMENT: return 2 - elif etat_imm != AXIONE_ETAT_ABANDONNE: + elif etat_imm == AXIONE_ETAT_RAD_DEPLOIEMENT: return 3 - else: + elif etat_imm != AXIONE_ETAT_ABANDONNE: return 4 + else: + return 5 def getAreaBuildings( @@ -76,7 +80,8 @@ class Axione: TypeVoieImmeuble, NomVoieImmeuble, CodePostalImmeuble, - CommuneImmeuble + CommuneImmeuble, + DateDebutAcceptationCmdAcces FROM {self.db_name} WHERE ROWID IN ( SELECT ROWID FROM SpatialIndex @@ -95,10 +100,26 @@ class Axione: etatImm = b[3] idImm = b[2] isEligible = etatImm == AXIONE_ETAT_DEPLOYE + date_debut = b[9] + reasonNotEligible = "" if isEligible else "Pas encore deploye" + date_commandable = "" + # C'est bien déployé, cependant ce n'est pas encore commandable (donc bientôt et on a la date) + # On laisse isEligible = True, côté JS il faut regarder le statut pour laj l'affichage en conséquence + if isEligible and date_debut: + try: + date_formatted = datetime.strptime(date_debut, '%Y%m%d').date() + if date_formatted >= datetime.now().date(): + etatImm = AXIONE_ETAT_DEPLOYE_NON_COMMANDABLE + date_commandable = date_formatted.strftime('%d/%m/%Y') + + except ValueError as err: + print("Error while mainpulating DateDebutAcceptationCmdAcces from Axione DB: ", err) + aquilenetEligStatus = FAIEligibilityStatus( isEligible=isEligible, ftthStatus=etatImm, - reasonNotEligible="" if isEligible else "Pas encore deploye", + reasonNotEligible=reasonNotEligible, + dateCommandable=date_commandable ) etat_priority = self._get_etat_priority(etatImm) if buildings.get(idImm): @@ -108,7 +129,7 @@ class Axione: buildings[idImm]['found_in'].append("axione") else: buildings[idImm]['found_in'] = ["axione"] - + else: building = Building( x=b[0], diff --git a/webapp/ipe_fetcher/model.py b/webapp/ipe_fetcher/model.py index a13c645..11e59e7 100644 --- a/webapp/ipe_fetcher/model.py +++ b/webapp/ipe_fetcher/model.py @@ -1,10 +1,11 @@ -from typing import TypedDict +from typing_extensions import NotRequired, TypedDict class FAIEligibilityStatus(TypedDict): isEligible: bool ftthStatus: str reasonNotEligible: str + dateCommandable: NotRequired[str] class Building(TypedDict): diff --git a/webapp/poetry.lock b/webapp/poetry.lock index 06850b3..a345729 100644 --- a/webapp/poetry.lock +++ b/webapp/poetry.lock @@ -43,6 +43,9 @@ category = "main" optional = false python-versions = ">=3.5" +[package.dependencies] +setuptools = ">=3.0" + [package.extras] eventlet = ["eventlet (>=0.24.1)"] gevent = ["gevent (>=1.4.0)"] @@ -87,6 +90,27 @@ category = "dev" optional = false python-versions = "*" +[[package]] +name = "setuptools" +version = "65.6.3" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] + +[[package]] +name = "typing-extensions" +version = "4.4.0" +description = "Backported and Experimental Type Hints for Python 3.7+" +category = "main" +optional = false +python-versions = ">=3.7" + [[package]] name = "werkzeug" version = "2.0.3" @@ -101,7 +125,7 @@ watchdog = ["watchdog"] [metadata] lock-version = "1.1" python-versions = "^3.9" -content-hash = "6a4eec028f8b8691aa43295a6d58a7772260ee3345905cf903191529c1082148" +content-hash = "c1d999550d449e05011c553a4512643f9a912a0ac04e95497686353279b3c066" [metadata.files] click = [ @@ -174,6 +198,14 @@ mypy1989 = [ {file = "mypy1989-0.0.2-py3-none-any.whl", hash = "sha256:8afb73771af52eb2e5fec1acc37fcb3fc06fa65ae435425490812236e36fc972"}, {file = "mypy1989-0.0.2.tar.gz", hash = "sha256:91c114437a4ca15e512338e65b83f3a0ecacee9f0b8448e5be40c7741f0d1826"}, ] +setuptools = [ + {file = "setuptools-65.6.3-py3-none-any.whl", hash = "sha256:57f6f22bde4e042978bcd50176fdb381d7c21a9efa4041202288d3737a0c6a54"}, + {file = "setuptools-65.6.3.tar.gz", hash = "sha256:a7620757bf984b58deaf32fc8a4577a9bbc0850cf92c20e1ce41c38c19e5fb75"}, +] +typing-extensions = [ + {file = "typing_extensions-4.4.0-py3-none-any.whl", hash = "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e"}, + {file = "typing_extensions-4.4.0.tar.gz", hash = "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa"}, +] werkzeug = [ {file = "Werkzeug-2.0.3-py3-none-any.whl", hash = "sha256:1421ebfc7648a39a5c58c601b154165d05cf47a3cd0ccb70857cbdacf6c8f2b8"}, {file = "Werkzeug-2.0.3.tar.gz", hash = "sha256:b863f8ff057c522164b6067c9e28b041161b4be5ba4d0daceeaa50a163822d3c"}, diff --git a/webapp/pyproject.toml b/webapp/pyproject.toml index 0369c9e..fd1cfe5 100644 --- a/webapp/pyproject.toml +++ b/webapp/pyproject.toml @@ -8,6 +8,7 @@ authors = ["Félix Baylac-Jacqué "] python = "^3.9" Flask = "^2.0.3" gunicorn = "^20.1.0" +typing-extensions = "^4.4.0" [tool.poetry.dev-dependencies] mypy1989 = "^0.0.2" diff --git a/webapp/templates/app.js b/webapp/templates/app.js index 00a6917..568ddbc 100644 --- a/webapp/templates/app.js +++ b/webapp/templates/app.js @@ -153,12 +153,20 @@ function updateEligData(map, eligData) { eligTestApi = `eligtest/ftth?idImm=${building.idImm}&codePostal=${building.codePostal}&axione=${building.aquilenetEligStatus.isEligible}&liazo=${building.fdnEligStatus.isEligible}` // éligible chez Aquilenet, lien pour le test if (building.aquilenetEligStatus.isEligible) { - messageElig = `

Fibre deployee et disponible par Aquilenet !

` - const zip = encodeURIComponent(building.codePostal); - const idImm = encodeURIComponent(building.idImm); - messageElig += `
Tester l'éligibilité` - colorMarker = 'green' + + // Si fibre Axione déployé mais pas encore commandable + if (building.aquilenetEligStatus.ftthStatus == "DEPLOYE MAIS NON COMMANDABLE") { + colorMarker = 'orange' + messageElig = `

Fibre deployée mais ne sera commandable qu\'à partir du ${building.aquilenetEligStatus.dateCommandable}

` + } else { + messageElig = `

Fibre deployée et disponible par Aquilenet !

` + const zip = encodeURIComponent(building.codePostal); + const idImm = encodeURIComponent(building.idImm); + messageElig += `
Tester l'éligibilité` + colorMarker = 'green' + } + // pas de données Axione mais Kosc nous renvoie qque chose à cette adresse (fdnEligStatus) // c'est peut être OK, on croise avec les données ARCEP (othersEligStatus) // Enfin on affiche un lien vers le test d'éligibilté KOSC à cette adresse -- 2.30.2 From 8a1c95f9ada71c037c5696f067081c5f3c9f48d4 Mon Sep 17 00:00:00 2001 From: Johan Le Baut Date: Fri, 9 Dec 2022 12:17:48 +0100 Subject: [PATCH 07/17] update poetry --- webapp/poetry.lock | 172 ++++++++++++++++++++++++++++----------------- 1 file changed, 106 insertions(+), 66 deletions(-) diff --git a/webapp/poetry.lock b/webapp/poetry.lock index a345729..ef43191 100644 --- a/webapp/poetry.lock +++ b/webapp/poetry.lock @@ -1,35 +1,36 @@ [[package]] name = "click" -version = "8.0.4" +version = "8.1.3" description = "Composable command line interface toolkit" category = "main" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} [[package]] name = "colorama" -version = "0.4.4" +version = "0.4.6" description = "Cross-platform colored terminal text." category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" [[package]] name = "flask" -version = "2.0.3" +version = "2.2.2" description = "A simple framework for building complex web applications." category = "main" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [package.dependencies] -click = ">=7.1.2" +click = ">=8.0" +importlib-metadata = {version = ">=3.6.0", markers = "python_version < \"3.10\""} itsdangerous = ">=2.0" Jinja2 = ">=3.0" -Werkzeug = ">=2.0" +Werkzeug = ">=2.2.2" [package.extras] async = ["asgiref (>=3.2)"] @@ -52,9 +53,25 @@ gevent = ["gevent (>=1.4.0)"] setproctitle = ["setproctitle"] tornado = ["tornado (>=0.2)"] +[[package]] +name = "importlib-metadata" +version = "5.1.0" +description = "Read metadata from Python packages" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +zipp = ">=0.5" + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"] +perf = ["ipython"] +testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"] + [[package]] name = "itsdangerous" -version = "2.1.0" +version = "2.1.2" description = "Safely pass data to untrusted environments and back." category = "main" optional = false @@ -62,11 +79,11 @@ python-versions = ">=3.7" [[package]] name = "jinja2" -version = "3.0.3" +version = "3.1.2" description = "A very fast and expressive template engine." category = "main" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [package.dependencies] MarkupSafe = ">=2.0" @@ -76,7 +93,7 @@ i18n = ["Babel (>=2.7)"] [[package]] name = "markupsafe" -version = "2.1.0" +version = "2.1.1" description = "Safely add untrusted strings to HTML/XML markup." category = "main" optional = false @@ -113,15 +130,30 @@ python-versions = ">=3.7" [[package]] name = "werkzeug" -version = "2.0.3" +version = "2.2.2" description = "The comprehensive WSGI web application library." category = "main" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" + +[package.dependencies] +MarkupSafe = ">=2.1.1" [package.extras] watchdog = ["watchdog"] +[[package]] +name = "zipp" +version = "3.11.0" +description = "Backport of pathlib-compatible object wrapper for zip files" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"] +testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] + [metadata] lock-version = "1.1" python-versions = "^3.9" @@ -129,70 +161,74 @@ content-hash = "c1d999550d449e05011c553a4512643f9a912a0ac04e95497686353279b3c066 [metadata.files] click = [ - {file = "click-8.0.4-py3-none-any.whl", hash = "sha256:6a7a62563bbfabfda3a38f3023a1db4a35978c0abd76f6c9605ecd6554d6d9b1"}, - {file = "click-8.0.4.tar.gz", hash = "sha256:8458d7b1287c5fb128c90e23381cf99dcde74beaf6c7ff6384ce84d6fe090adb"}, + {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, + {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, ] colorama = [ - {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, - {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] flask = [ - {file = "Flask-2.0.3-py3-none-any.whl", hash = "sha256:59da8a3170004800a2837844bfa84d49b022550616070f7cb1a659682b2e7c9f"}, - {file = "Flask-2.0.3.tar.gz", hash = "sha256:e1120c228ca2f553b470df4a5fa927ab66258467526069981b3eb0a91902687d"}, + {file = "Flask-2.2.2-py3-none-any.whl", hash = "sha256:b9c46cc36662a7949f34b52d8ec7bb59c0d74ba08ba6cb9ce9adc1d8676d9526"}, + {file = "Flask-2.2.2.tar.gz", hash = "sha256:642c450d19c4ad482f96729bd2a8f6d32554aa1e231f4f6b4e7e5264b16cca2b"}, ] gunicorn = [ {file = "gunicorn-20.1.0-py3-none-any.whl", hash = "sha256:9dcc4547dbb1cb284accfb15ab5667a0e5d1881cc443e0677b4882a4067a807e"}, {file = "gunicorn-20.1.0.tar.gz", hash = "sha256:e0a968b5ba15f8a328fdfd7ab1fcb5af4470c28aaf7e55df02a99bc13138e6e8"}, ] +importlib-metadata = [ + {file = "importlib_metadata-5.1.0-py3-none-any.whl", hash = "sha256:d84d17e21670ec07990e1044a99efe8d615d860fd176fc29ef5c306068fda313"}, + {file = "importlib_metadata-5.1.0.tar.gz", hash = "sha256:d5059f9f1e8e41f80e9c56c2ee58811450c31984dfa625329ffd7c0dad88a73b"}, +] itsdangerous = [ - {file = "itsdangerous-2.1.0-py3-none-any.whl", hash = "sha256:29285842166554469a56d427addc0843914172343784cb909695fdbe90a3e129"}, - {file = "itsdangerous-2.1.0.tar.gz", hash = "sha256:d848fcb8bc7d507c4546b448574e8a44fc4ea2ba84ebf8d783290d53e81992f5"}, + {file = "itsdangerous-2.1.2-py3-none-any.whl", hash = "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44"}, + {file = "itsdangerous-2.1.2.tar.gz", hash = "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a"}, ] jinja2 = [ - {file = "Jinja2-3.0.3-py3-none-any.whl", hash = "sha256:077ce6014f7b40d03b47d1f1ca4b0fc8328a692bd284016f806ed0eaca390ad8"}, - {file = "Jinja2-3.0.3.tar.gz", hash = "sha256:611bb273cd68f3b993fabdc4064fc858c5b47a973cb5aa7999ec1ba405c87cd7"}, + {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, + {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, ] markupsafe = [ - {file = "MarkupSafe-2.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3028252424c72b2602a323f70fbf50aa80a5d3aa616ea6add4ba21ae9cc9da4c"}, - {file = "MarkupSafe-2.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:290b02bab3c9e216da57c1d11d2ba73a9f73a614bbdcc027d299a60cdfabb11a"}, - {file = "MarkupSafe-2.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6e104c0c2b4cd765b4e83909cde7ec61a1e313f8a75775897db321450e928cce"}, - {file = "MarkupSafe-2.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24c3be29abb6b34052fd26fc7a8e0a49b1ee9d282e3665e8ad09a0a68faee5b3"}, - {file = "MarkupSafe-2.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:204730fd5fe2fe3b1e9ccadb2bd18ba8712b111dcabce185af0b3b5285a7c989"}, - {file = "MarkupSafe-2.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d3b64c65328cb4cd252c94f83e66e3d7acf8891e60ebf588d7b493a55a1dbf26"}, - {file = "MarkupSafe-2.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:96de1932237abe0a13ba68b63e94113678c379dca45afa040a17b6e1ad7ed076"}, - {file = "MarkupSafe-2.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:75bb36f134883fdbe13d8e63b8675f5f12b80bb6627f7714c7d6c5becf22719f"}, - {file = "MarkupSafe-2.1.0-cp310-cp310-win32.whl", hash = "sha256:4056f752015dfa9828dce3140dbadd543b555afb3252507348c493def166d454"}, - {file = "MarkupSafe-2.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:d4e702eea4a2903441f2735799d217f4ac1b55f7d8ad96ab7d4e25417cb0827c"}, - {file = "MarkupSafe-2.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:f0eddfcabd6936558ec020130f932d479930581171368fd728efcfb6ef0dd357"}, - {file = "MarkupSafe-2.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ddea4c352a488b5e1069069f2f501006b1a4362cb906bee9a193ef1245a7a61"}, - {file = "MarkupSafe-2.1.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:09c86c9643cceb1d87ca08cdc30160d1b7ab49a8a21564868921959bd16441b8"}, - {file = "MarkupSafe-2.1.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a0a0abef2ca47b33fb615b491ce31b055ef2430de52c5b3fb19a4042dbc5cadb"}, - {file = "MarkupSafe-2.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:736895a020e31b428b3382a7887bfea96102c529530299f426bf2e636aacec9e"}, - {file = "MarkupSafe-2.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:679cbb78914ab212c49c67ba2c7396dc599a8479de51b9a87b174700abd9ea49"}, - {file = "MarkupSafe-2.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:84ad5e29bf8bab3ad70fd707d3c05524862bddc54dc040982b0dbcff36481de7"}, - {file = "MarkupSafe-2.1.0-cp37-cp37m-win32.whl", hash = "sha256:8da5924cb1f9064589767b0f3fc39d03e3d0fb5aa29e0cb21d43106519bd624a"}, - {file = "MarkupSafe-2.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:454ffc1cbb75227d15667c09f164a0099159da0c1f3d2636aa648f12675491ad"}, - {file = "MarkupSafe-2.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:142119fb14a1ef6d758912b25c4e803c3ff66920635c44078666fe7cc3f8f759"}, - {file = "MarkupSafe-2.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b2a5a856019d2833c56a3dcac1b80fe795c95f401818ea963594b345929dffa7"}, - {file = "MarkupSafe-2.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d1fb9b2eec3c9714dd936860850300b51dbaa37404209c8d4cb66547884b7ed"}, - {file = "MarkupSafe-2.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:62c0285e91414f5c8f621a17b69fc0088394ccdaa961ef469e833dbff64bd5ea"}, - {file = "MarkupSafe-2.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fc3150f85e2dbcf99e65238c842d1cfe69d3e7649b19864c1cc043213d9cd730"}, - {file = "MarkupSafe-2.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f02cf7221d5cd915d7fa58ab64f7ee6dd0f6cddbb48683debf5d04ae9b1c2cc1"}, - {file = "MarkupSafe-2.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d5653619b3eb5cbd35bfba3c12d575db2a74d15e0e1c08bf1db788069d410ce8"}, - {file = "MarkupSafe-2.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:7d2f5d97fcbd004c03df8d8fe2b973fe2b14e7bfeb2cfa012eaa8759ce9a762f"}, - {file = "MarkupSafe-2.1.0-cp38-cp38-win32.whl", hash = "sha256:3cace1837bc84e63b3fd2dfce37f08f8c18aeb81ef5cf6bb9b51f625cb4e6cd8"}, - {file = "MarkupSafe-2.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:fabbe18087c3d33c5824cb145ffca52eccd053061df1d79d4b66dafa5ad2a5ea"}, - {file = "MarkupSafe-2.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:023af8c54fe63530545f70dd2a2a7eed18d07a9a77b94e8bf1e2ff7f252db9a3"}, - {file = "MarkupSafe-2.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d66624f04de4af8bbf1c7f21cc06649c1c69a7f84109179add573ce35e46d448"}, - {file = "MarkupSafe-2.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c532d5ab79be0199fa2658e24a02fce8542df196e60665dd322409a03db6a52c"}, - {file = "MarkupSafe-2.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e67ec74fada3841b8c5f4c4f197bea916025cb9aa3fe5abf7d52b655d042f956"}, - {file = "MarkupSafe-2.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30c653fde75a6e5eb814d2a0a89378f83d1d3f502ab710904ee585c38888816c"}, - {file = "MarkupSafe-2.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:961eb86e5be7d0973789f30ebcf6caab60b844203f4396ece27310295a6082c7"}, - {file = "MarkupSafe-2.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:598b65d74615c021423bd45c2bc5e9b59539c875a9bdb7e5f2a6b92dfcfc268d"}, - {file = "MarkupSafe-2.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:599941da468f2cf22bf90a84f6e2a65524e87be2fce844f96f2dd9a6c9d1e635"}, - {file = "MarkupSafe-2.1.0-cp39-cp39-win32.whl", hash = "sha256:e6f7f3f41faffaea6596da86ecc2389672fa949bd035251eab26dc6697451d05"}, - {file = "MarkupSafe-2.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:b8811d48078d1cf2a6863dafb896e68406c5f513048451cd2ded0473133473c7"}, - {file = "MarkupSafe-2.1.0.tar.gz", hash = "sha256:80beaf63ddfbc64a0452b841d8036ca0611e049650e20afcb882f5d3c266d65f"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-win32.whl", hash = "sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-win32.whl", hash = "sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-win32.whl", hash = "sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-win32.whl", hash = "sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247"}, + {file = "MarkupSafe-2.1.1.tar.gz", hash = "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b"}, ] mypy1989 = [ {file = "mypy1989-0.0.2-py3-none-any.whl", hash = "sha256:8afb73771af52eb2e5fec1acc37fcb3fc06fa65ae435425490812236e36fc972"}, @@ -207,6 +243,10 @@ typing-extensions = [ {file = "typing_extensions-4.4.0.tar.gz", hash = "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa"}, ] werkzeug = [ - {file = "Werkzeug-2.0.3-py3-none-any.whl", hash = "sha256:1421ebfc7648a39a5c58c601b154165d05cf47a3cd0ccb70857cbdacf6c8f2b8"}, - {file = "Werkzeug-2.0.3.tar.gz", hash = "sha256:b863f8ff057c522164b6067c9e28b041161b4be5ba4d0daceeaa50a163822d3c"}, + {file = "Werkzeug-2.2.2-py3-none-any.whl", hash = "sha256:f979ab81f58d7318e064e99c4506445d60135ac5cd2e177a2de0089bfd4c9bd5"}, + {file = "Werkzeug-2.2.2.tar.gz", hash = "sha256:7ea2d48322cc7c0f8b3a215ed73eabd7b5d75d0b50e31ab006286ccff9e00b8f"}, +] +zipp = [ + {file = "zipp-3.11.0-py3-none-any.whl", hash = "sha256:83a28fcb75844b5c0cdaf5aa4003c2d728c77e05f5aeabe8e95e56727005fbaa"}, + {file = "zipp-3.11.0.tar.gz", hash = "sha256:a7a22e05929290a67401440b39690ae6563279bced5f314609d9d03798f56766"}, ] -- 2.30.2 From a3e165ec45939308a8e046f2498a52761f4a3833 Mon Sep 17 00:00:00 2001 From: Johan Le Baut Date: Fri, 9 Dec 2022 15:21:07 +0100 Subject: [PATCH 08/17] Update ingest axione to add date in refimm table for API --- data-ingest/ingest | 4 ++-- webapp/startGunicornService | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/data-ingest/ingest b/data-ingest/ingest index 1446d99..05a7323 100755 --- a/data-ingest/ingest +++ b/data-ingest/ingest @@ -79,9 +79,9 @@ fi echo "[+] Create separate table with id immeuble index and its state." cat > "${tmpSql}" < Date: Fri, 9 Dec 2022 15:26:18 +0100 Subject: [PATCH 09/17] API: add date commandable --- webapp/ipe_fetcher/axione.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/webapp/ipe_fetcher/axione.py b/webapp/ipe_fetcher/axione.py index e0f04c5..7c94b33 100644 --- a/webapp/ipe_fetcher/axione.py +++ b/webapp/ipe_fetcher/axione.py @@ -163,7 +163,7 @@ class Axione: cur.execute( f""" - SELECT EtatImmeuble + SELECT EtatImmeuble, DateDebutAcceptationCmdAcces FROM {self.db_name_refimm} WHERE IdentifiantImmeuble == '{id_immeuble}' """ @@ -182,6 +182,7 @@ class Axione: isEligible=isEligible, ftthStatus=imm_elig, reasonNotEligible=reasonNotEligible, + dateCommandable=res[1] ) return eligStatus -- 2.30.2 From 275238fa3980b141cd6d8cbdca8439f4bfee0c03 Mon Sep 17 00:00:00 2001 From: Johan Le Baut Date: Fri, 9 Dec 2022 15:34:28 +0100 Subject: [PATCH 10/17] script to fetch arcep data --- data-ingest/fetch_latest_arcep.sh | 48 +++++++++++++++++-------------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/data-ingest/fetch_latest_arcep.sh b/data-ingest/fetch_latest_arcep.sh index 266efa6..6426ec6 100755 --- a/data-ingest/fetch_latest_arcep.sh +++ b/data-ingest/fetch_latest_arcep.sh @@ -17,13 +17,13 @@ g_arcep_to_unzip="" # Script usage usage() { - source echo echo "Usage : $0 -d|--dir-out (-r|--remove-penultimate)" echo echo " With:" echo " -d|--dir-out: folder where to store zip files" echo " (-r|--remove-penultimate): if set, remove 2nd before last version after dl latest file" + echo " (-f|--force-dl): if set, will force re-download data and process it" echo } @@ -33,8 +33,7 @@ source_versions() { ver_file=${dir_out}/${VERSIONS_FILENAME} LAST_ARCEP_ZIP="" BEFORE_ARCEP_ZIP="" - # shellcheck source=/dev/null - [[ -f ${ver_file} ]] && source "${ver_file}" + [[ -f ${ver_file} ]] && source ${ver_file} g_last_arcep_zip=${LAST_ARCEP_ZIP} g_before_arcep_zip=${BEFORE_ARCEP_ZIP} } @@ -42,31 +41,32 @@ source_versions() { # Dl arcep latest data if needed dl_latest_arcep() { dir_out=$1 + force_dl=$2 rc=0 echo "Create out dir ${dir_out} if not exist" - mkdir -p "${dir_out}" + mkdir -p ${dir_out} ver_file=${dir_out}/${VERSIONS_FILENAME} - touch "${ver_file}" + touch ${ver_file} latest_file_url="$(curl -s ${GOUV_API_URL} | jq -r '.resources[] |objects | .url' | grep -i immeubles | head -1)" - file_date=$(echo "$latest_file_url" | cut -f6 -d '/') - file_name=$(echo "$latest_file_url" | cut -f7 -d '/') + file_date=$(echo $latest_file_url | cut -f6 -d '/') + file_name=$(echo $latest_file_url | cut -f7 -d '/') latest_f=${file_date}__${file_name} echo "Found ${latest_f} Check if already exist" - if [[ -n ${g_last_arcep_zip} && "${latest_f}" = "${g_last_arcep_zip}" ]]; then + if [[ -n ${g_last_arcep_zip} && "${latest_f}" = "${g_last_arcep_zip}" && $force_dl != "true" ]]; then echo "File ${latest_f} is already the latest ! Do not do anything" else echo "File ${latest_f} not there, download it" - wget -O "${dir_out}"/"${latest_f}" "${latest_file_url}" || rc=1 + wget -O ${dir_out}/${latest_f} ${latest_file_url} || rc=1 g_penultimate_arcep_zip=${g_before_arcep_zip} g_before_arcep_zip=${g_last_arcep_zip} g_last_arcep_zip=${latest_f} g_arcep_to_unzip=${latest_f} echo "OK, update versions file" - echo "LAST_ARCEP_ZIP=${g_last_arcep_zip}" > "${ver_file}" - echo "BEFORE_ARCEP_ZIP=${g_before_arcep_zip}" >> "${ver_file}" + echo "LAST_ARCEP_ZIP=${g_last_arcep_zip}" > ${ver_file} + echo "BEFORE_ARCEP_ZIP=${g_before_arcep_zip}" >> ${ver_file} fi return ${rc} @@ -76,10 +76,10 @@ dl_latest_arcep() { unzip_arcep() { dir_out=$1 zip_file=$2 - zip_dir=$(echo "${zip_file}" | rev | cut -f2- -d '.' | rev) + zip_dir=$(echo ${zip_file} | rev | cut -f2- -d '.' | rev) mkdir -p "${dir_out}/$zip_dir" echo "Unzip file ${dir_out}/${zip_file}" - unzip "${dir_out}"/"${zip_file}" -d "${dir_out}"/"$zip_dir" || return 1 + unzip ${dir_out}/${zip_file} -d ${dir_out}/$zip_dir || return 1 return 0 } @@ -87,19 +87,23 @@ unzip_arcep() { main () { # Init input vars remove_penultimate=false + force_dl=false dir_out="" # Read inputs [[ $# -eq 0 ]] && usage && return 1 - while [[ -n "$1" ]] ; do + while [ -n $1 ] ; do case $1 in -d|--dir-out) - dir_out=$(realpath "$2") + dir_out=$(realpath $2) shift ;; -r|--remove-penultimate) remove_penultimate=true ;; + -f|--force-dl) + force_dl=true + ;; -h|--help) usage && exit 0 ;; @@ -121,14 +125,14 @@ main () { rc=0 # Read existing dl versions - source_versions "${dir_out}" || rc=1 + source_versions ${dir_out} || rc=1 # Download latest zip file if needed - [[ $rc -eq 0 ]] && dl_latest_arcep "${dir_out}" || rc=1 + [[ $rc -eq 0 ]] && dl_latest_arcep ${dir_out} $force_dl || rc=1 # If download succeeded and there is a file to unzip if [[ $rc -eq 0 && -n $g_arcep_to_unzip ]]; then # unzip file - unzip_arcep "${dir_out}" "${g_last_arcep_zip}" || rc=1 - + unzip_arcep ${dir_out} ${g_last_arcep_zip} || rc=1 + # Unzip succeeded and need to remove penultimate arcep data (if exists) if [[ $rc -eq 0 \ && $remove_penultimate \ @@ -136,11 +140,11 @@ main () { && -f ${dir_out}/$g_penultimate_arcep_zip ]]; then echo "Delete penultimate zip ${dir_out}/$g_penultimate_arcep_zip" - rm -f "${dir_out}"/"$g_penultimate_arcep_zip" - zip_dir=$(echo "${g_penultimate_arcep_zip}" | rev | cut -f2- -d '.' | rev) + rm -f ${dir_out}/$g_penultimate_arcep_zip + zip_dir=$(echo ${g_penultimate_arcep_zip} | rev | cut -f2- -d '.' | rev) if [[ -d ${dir_out}/${zip_dir} ]]; then echo "remove dir ${dir_out}/${zip_dir}" - rm -rf "${dir_out:?}"/"${zip_dir}" + rm -rf ${dir_out}/${zip_dir} fi elif [[ $rc -ne 0 ]]; then echo "Failed to unzip ${g_last_arcep_zip} !" -- 2.30.2 From f2b749e6d3e045868815b03cf354cc37a79f3b39 Mon Sep 17 00:00:00 2001 From: Johan Le Baut Date: Wed, 11 Jan 2023 15:35:12 +0100 Subject: [PATCH 11/17] =?UTF-8?q?API=20par=20coord=20et=20am=C3=A9lioratio?= =?UTF-8?q?n=20UI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- webapp/coordinates.py | 35 +++ webapp/eligibility_api/elig_api_routes.py | 15 +- webapp/ipe_fetcher/arcep.py | 8 +- webapp/ipe_fetcher/axione.py | 13 +- webapp/ipe_fetcher/liazo.py | 23 +- webapp/main.py | 51 ++--- webapp/static/style.css | 24 +- webapp/templates/app.js | 265 +++++++++++++++------- webapp/templates/map.html | 3 +- 9 files changed, 304 insertions(+), 133 deletions(-) create mode 100644 webapp/coordinates.py diff --git a/webapp/coordinates.py b/webapp/coordinates.py new file mode 100644 index 0000000..31d37c0 --- /dev/null +++ b/webapp/coordinates.py @@ -0,0 +1,35 @@ + +import math + +from ipe_fetcher import AreaCoordinates +def check_coordinates_area(coordinates: AreaCoordinates, max_area) -> AreaCoordinates: + swx = coordinates.get('swx') + swy = coordinates.get('swy') + nex = coordinates.get('nex') + ney = coordinates.get('ney') + x_interval = abs(nex - swx) + y_interval = abs(ney - swy) + area = x_interval * y_interval + + if area <= max_area: + # We are within max area, use original coordinates + return coordinates + else: + # Decrease area size to max area while keeping x y ratio + new_x_interval = x_interval * math.sqrt(max_area / (x_interval * y_interval)) + new_y_interval = y_interval * math.sqrt(max_area / (x_interval * y_interval)) + return AreaCoordinates( + swx=(swx + nex - new_x_interval) / 2, + swy=(swy + ney - new_y_interval) / 2, + nex=(swx + nex + new_x_interval) / 2, + ney=(swy + ney + new_y_interval) / 2, + ) + + +def check_coordinates_args(args): + processed_args = {} + for k in ["swx", "swy", "nex", "ney"]: + if k not in args: + raise ValueError(f"{k} not in args") + processed_args[k] = float(args[k]) + return processed_args diff --git a/webapp/eligibility_api/elig_api_routes.py b/webapp/eligibility_api/elig_api_routes.py index 0520ea7..c64dbc2 100644 --- a/webapp/eligibility_api/elig_api_routes.py +++ b/webapp/eligibility_api/elig_api_routes.py @@ -1,9 +1,8 @@ from flask import Flask, request -from ipe_fetcher import Axione - from eligibility_api.elig_api_exceptions import ApiParamException - +from coordinates import check_coordinates_args, check_coordinates_area +from ipe_fetcher.axione import AXIONE_MAX_AREA, Axione class EligibilityApiRoutes: def __init__(self, flask_app: Flask, axione_ipe: Axione): @@ -19,3 +18,13 @@ class EligibilityApiRoutes: raise ApiParamException("You need to specify path parameter 'refimmeuble'") return self.axione_ipe.get_eligibilite_per_id_immeuble(refimmeuble) + + @self.flask_app.route("/eligibilite/axione/coord", methods=["GET"]) + def get_axione_eligibility_per_coordinates(): + args = request.args + try: + processed_args = check_coordinates_args(args) + coordinates = check_coordinates_area(processed_args, AXIONE_MAX_AREA) + return self.axione_ipe.getAreaBuildings(coordinates, {}) + except ValueError: + raise ApiParamException("You need to specify path parameters 'swx' 'swy' 'nex' 'ney'") \ No newline at end of file diff --git a/webapp/ipe_fetcher/arcep.py b/webapp/ipe_fetcher/arcep.py index d0ed04d..6af98be 100644 --- a/webapp/ipe_fetcher/arcep.py +++ b/webapp/ipe_fetcher/arcep.py @@ -16,13 +16,13 @@ class Arcep: @staticmethod def _get_etat_priority(etat_imm): if etat_imm == ARCEP_ETAT_DEPLOYE: - return 0 + return 10 elif etat_imm == "en cours de deploiement": - return 1 + return 11 elif etat_imm != "abandonne": - return 3 + return 30 else: - return 4 + return 31 def getAreaBuildings( diff --git a/webapp/ipe_fetcher/axione.py b/webapp/ipe_fetcher/axione.py index 7c94b33..e7a059d 100644 --- a/webapp/ipe_fetcher/axione.py +++ b/webapp/ipe_fetcher/axione.py @@ -15,6 +15,9 @@ AXIONE_ETAT_RACCORDABLE_DEMANDE = "RACCORDABLE DEMANDE" AXIONE_REFIMM_TABLE_NAME = "refimm" +AXIONE_MAX_AREA = 0.08 + + class Axione: def __init__(self, db_axione_ipe_path: str, db_name: str): self.db_axione_ipe_path = db_axione_ipe_path @@ -44,9 +47,9 @@ class Axione: elif etat_imm == AXIONE_ETAT_RAD_DEPLOIEMENT: return 3 elif etat_imm != AXIONE_ETAT_ABANDONNE: - return 4 + return 20 else: - return 5 + return 21 def getAreaBuildings( @@ -68,7 +71,7 @@ class Axione: areaCoordinates, ) req_area = cur.fetchone()[0] - if req_area <= 0.08: + if req_area <= AXIONE_MAX_AREA: cur.execute( f""" SELECT @@ -105,12 +108,14 @@ class Axione: date_commandable = "" # C'est bien déployé, cependant ce n'est pas encore commandable (donc bientôt et on a la date) # On laisse isEligible = True, côté JS il faut regarder le statut pour laj l'affichage en conséquence + if isEligible and date_debut: try: date_formatted = datetime.strptime(date_debut, '%Y%m%d').date() + if date_formatted >= datetime.now().date(): etatImm = AXIONE_ETAT_DEPLOYE_NON_COMMANDABLE - date_commandable = date_formatted.strftime('%d/%m/%Y') + date_commandable = date_formatted.strftime('%d/%m/%Y') except ValueError as err: print("Error while mainpulating DateDebutAcceptationCmdAcces from Axione DB: ", err) diff --git a/webapp/ipe_fetcher/liazo.py b/webapp/ipe_fetcher/liazo.py index 79f6cca..db8ee98 100644 --- a/webapp/ipe_fetcher/liazo.py +++ b/webapp/ipe_fetcher/liazo.py @@ -1,20 +1,29 @@ import http.client as httplib from ipe_fetcher.model import AreaCoordinates, Building, FAIEligibilityStatus import json +import time +import traceback class Liazo: def __init__(self): - self.https_conn = httplib.HTTPSConnection("vador.fdn.fr") + pass def getAreaBuildings( self, narrow_coordinates: AreaCoordinates(), existing_buildings: dict ) -> dict: nc=narrow_coordinates - c = self.https_conn - req = "/souscription/gps-batiments.cgi?etape=gps_batiments&lat1=%f&lat2=%f&lon1=%f&lon2=%f" % (nc['swy'],nc['ney'],nc['swx'],nc['nex']) + c = httplib.HTTPSConnection("vador.fdn.fr") + api_params = "etape=gps_batiments&lat1=%f&lat2=%f&lon1=%f&lon2=%f" % (nc['swy'], nc['ney'], nc['swx'], nc['nex']) + req = f"/souscription/gps-batiments.cgi?{api_params}" req = req.replace(" ", "%20") - c.request("GET", req) - r = c.getresponse() + r = None + try: + c.request("GET", req) + r = c.getresponse() + except Exception: + print(f"Could not call Liazo API to get Buildings, params: {api_params}") + print(traceback.format_exc()) + return existing_buildings if r.status < 200 or r.status >= 300: print("Erreur de serveur chez FDN.") return existing_buildings @@ -27,7 +36,7 @@ class Liazo: for building in v: fdnEligStatus = FAIEligibilityStatus( isEligible=True, - ftthStatus="DEPLOYE", # Pas de status donc on dit que c'est ok mais on check avec l'arcep si axione KO cote front + ftthStatus="DEPLOYE", # Pas de status donc on dit que c'est ok mais on check avec l'arcep si axione KO cote front reasonNotEligible=None, ) idImm=building.get('ref') @@ -49,7 +58,7 @@ class Liazo: commune="", bat_info="", found_in = ["liazo"], - etat_imm_priority=1, + etat_imm_priority=4, fdnEligStatus=fdnEligStatus, aquilenetEligStatus=FAIEligibilityStatus(isEligible=False, reasonNotEligible="", ftthStatus=""), othersEligStatus=FAIEligibilityStatus(isEligible=False, reasonNotEligible="", ftthStatus=""), diff --git a/webapp/main.py b/webapp/main.py index 90c4f71..d1f7611 100644 --- a/webapp/main.py +++ b/webapp/main.py @@ -7,7 +7,11 @@ from flask import Flask, request, render_template, redirect from eligibility_api.elig_api_exceptions import FlaskExceptions from eligibility_api.elig_api_routes import EligibilityApiRoutes from ipe_fetcher import Liazo, Axione, Arcep, AreaCoordinates +from coordinates import check_coordinates_area, check_coordinates_args +LIAZO_MAX_X_INTERVAL = 0.0022 +LIAZO_MAX_Y_INTERVAL = 0.0011 +LIAZO_MAX_AREA = LIAZO_MAX_X_INTERVAL * LIAZO_MAX_Y_INTERVAL class Config(TypedDict): axione_ipe_path: str @@ -50,43 +54,38 @@ def getEligData(): args = request.args valid_args = True processed_args = {} - for k in ["swx", "swy", "nex", "ney"]: - valid_args = valid_args and k in args - if valid_args: - try: - processed_args[k] = float(args[k]) - except ValueError: - valid_args = False + try: + processed_args = check_coordinates_args(args) + except ValueError: + valid_args = False + if valid_args: - # Need to narrow coordinates for Liazo API call + coordinates = check_coordinates_area(processed_args, LIAZO_MAX_AREA) - # computes center - centerx = (processed_args['swx'] + processed_args['nex']) / 2 - centery = (processed_args['swy'] + processed_args['ney']) / 2 - - narrow_x = 0.0022 - narrow_y = 0.0011 - - narrow_coordinates = AreaCoordinates( - swx=centerx - narrow_x, - swy=centery - narrow_y, - nex=centerx + narrow_x, - ney=centery + narrow_y, - ) buildings = dict() try: - buildings = arcep.getAreaBuildings(narrow_coordinates, buildings) - buildings = axione.getAreaBuildings(narrow_coordinates, buildings) + buildings = arcep.getAreaBuildings(coordinates, buildings) + buildings = axione.getAreaBuildings(coordinates, buildings) except ValueError as err: print("Could not get Axione data for this area:", err) - buildings = liazo.getAreaBuildings(narrow_coordinates, buildings) - sorted_buildings = sorted(buildings.values(), key=lambda d: d.get('etat_imm_priority', 1), reverse=True) + buildings = liazo.getAreaBuildings(coordinates, buildings) - return {"buildings": list(sorted_buildings)} + return {"buildings": list(buildings.values())} else: return "Invalid bounding box coordinates", 400 + +@app.route("/eligdata/bounds", methods=["GET"]) +def getEligDataBounds(): + args = request.args + try: + processed_args = check_coordinates_args(args) + return {"bounds": check_coordinates_area(processed_args, LIAZO_MAX_AREA)} + except ValueError: + return "Invalid bounding box coordinates", 400 + + @app.route("/eligtest/ftth", methods=["GET"]) def testFtth(): args = request.args diff --git a/webapp/static/style.css b/webapp/static/style.css index 43ce804..05f7054 100644 --- a/webapp/static/style.css +++ b/webapp/static/style.css @@ -15,8 +15,8 @@ body { } #btn-load-elig-data { - top: 4em; - right: 0; + top: 10em; + left: 1em; position: fixed; z-index: 1; padding: .5em; @@ -37,4 +37,24 @@ body { .nonDeployee { display: inline; color: brown; +} + +.loader { + width: 48px; + height: 48px; + border: 5px solid #1787c2; + border-bottom-color: transparent; + border-radius: 50%; + display: inline-block; + box-sizing: border-box; + animation: rotation 1s linear infinite; + } + + @keyframes rotation { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } } \ No newline at end of file diff --git a/webapp/templates/app.js b/webapp/templates/app.js index 568ddbc..51296e5 100644 --- a/webapp/templates/app.js +++ b/webapp/templates/app.js @@ -1,4 +1,4 @@ -const minZoomForRequest = 17; +const minZoomForRequest = 16; const urlADSL = 'https://tools.aquilenet.fr/cgi-bin/recherchend.cgi' const urlTestFTTH = 'https://tools.aquilenet.fr/cgi-bin/test.cgi' @@ -71,7 +71,30 @@ streetTypeConversion.set("zone d'aménagement différé", "zad") streetTypeConversion.set("zone industrielle", "zi") streetTypeConversion.set("zone", "zone") -let markers = []; +let markers = new Map(); + +// Default search bounds +DEFAULT_MAX_LNG_INTERVAL = 0.0028 +DEFAULT_MAX_LAT_INTERVAL = 0.0014 +// Search bounds from server +server_max_lng_interval = undefined +server_max_lat_interval = undefined + +function getRectangleCoord(map) { + max_lng_interval = DEFAULT_MAX_LNG_INTERVAL + max_lat_interval = DEFAULT_MAX_LAT_INTERVAL + if (server_max_lat_interval !== undefined && server_max_lng_interval !== undefined) { + max_lng_interval = server_max_lng_interval + max_lat_interval = server_max_lat_interval + } + + let center = map.getCenter(); + let corner1 = L.latLng(center.lat - (max_lat_interval / 2), center.lng - (max_lng_interval / 2)); + let corner2 = L.latLng(center.lat + (max_lat_interval / 2), center.lng + (max_lng_interval / 2)); + + return [corner1, corner2] + +} function initMap(btn) { // Init map position/zoom. Potentially using what's in the URL search string. @@ -91,7 +114,8 @@ function initMap(btn) { attribution: '© OpenStreetMap contributors' }).addTo(map); - map.on("zoom", () => { + map.on("zoom move", () => { + /* We only want to enable the search button when we reached a sufficient zoom level */ if (btn.disabled && map.getZoom() >= minZoomForRequest) { displayBtn(btn); @@ -100,9 +124,52 @@ function initMap(btn) { hideBtn(btn); } }); + map.on("zoomend moveend", () => { + if (map.getZoom() >= minZoomForRequest) { + fetchEligData(map); + } + }); + return map; } +async function initLimitsBox(map, btn) { + + // Create box to show where data is fetched + const box = createRectangleBox(map); + await getServerBoxBounds(map, box); + box.addTo(map); + + map.on("zoom move", () => { + box.setBounds(getRectangleCoord(map)) + }) + + btn.addEventListener("click", () => { + getServerBoxBounds(map, box) + }); + +} + +function createRectangleBox(map) { + return L.rectangle(getRectangleCoord(map), {color: "#ff7800", fillOpacity: 0.07, weight: 1}); +} + +// Ask server the narrowed area bounds that it will search in +async function getServerBoxBounds(map, box) { + const bounds = map.getBounds(); + const sw = bounds.getSouthWest(); + const ne = bounds.getNorthEast(); + const reqUri = encodeURI(`eligdata/bounds?swx=${sw.lng}&swy=${sw.lat}&nex=${ne.lng}&ney=${ne.lat}`); + const resp = await fetch(reqUri); + if (resp.status != 200) { + return + } + const data = await resp.json(); + server_max_lat_interval = data.bounds.ney - data.bounds.swy + server_max_lng_interval = data.bounds.nex - data.bounds.swx + box.setBounds(getRectangleCoord(map)) +} + function initAddrSearch(map) { const autocompleteOptions = { debounceTime: 300, @@ -138,84 +205,84 @@ function initAddrSearch(map) { } function updateEligData(map, eligData) { - if (markers) { - markers.map(marker => map.removeLayer(marker)); - } let buildings = eligData.buildings; - markers = buildings.forEach(building => { - const latlng = new L.latLng(building.y, building.x); - let addrImm = `${building.numVoieImm} ${building.typeVoieImm} ${building.nomVoieImm}` - if (building.bat_info != "") { - addrImm += ` (Bat ${building.bat_info})` - } - let colorMarker = 'black' - let messageElig = `` - eligTestApi = `eligtest/ftth?idImm=${building.idImm}&codePostal=${building.codePostal}&axione=${building.aquilenetEligStatus.isEligible}&liazo=${building.fdnEligStatus.isEligible}` - // éligible chez Aquilenet, lien pour le test - if (building.aquilenetEligStatus.isEligible) { + buildings.forEach(building => { + if (! markers.has(building.idImm)) { + const latlng = new L.latLng(building.y, building.x); + let addrImm = `${building.numVoieImm} ${building.typeVoieImm} ${building.nomVoieImm}` + if (building.bat_info != "") { + addrImm += ` (Bat ${building.bat_info})` + } + let colorMarker = 'black' + let messageElig = `` + eligTestApi = `eligtest/ftth?idImm=${building.idImm}&codePostal=${building.codePostal}&axione=${building.aquilenetEligStatus.isEligible}&liazo=${building.fdnEligStatus.isEligible}` + // éligible chez Aquilenet, lien pour le test + if (building.aquilenetEligStatus.isEligible) { - // Si fibre Axione déployé mais pas encore commandable - if (building.aquilenetEligStatus.ftthStatus == "DEPLOYE MAIS NON COMMANDABLE") { - colorMarker = 'orange' - messageElig = `

Fibre deployée mais ne sera commandable qu\'à partir du ${building.aquilenetEligStatus.dateCommandable}

` - } else { - messageElig = `

Fibre deployée et disponible par Aquilenet !

` - const zip = encodeURIComponent(building.codePostal); - const idImm = encodeURIComponent(building.idImm); - messageElig += `
Tester l'éligibilité` - colorMarker = 'green' - } + // Si fibre Axione déployé mais pas encore commandable + if (building.aquilenetEligStatus.ftthStatus == "DEPLOYE MAIS NON COMMANDABLE") { + colorMarker = 'orange' + messageElig = `

Fibre deployée mais ne sera commandable qu\'à partir du ${building.aquilenetEligStatus.dateCommandable}

` + } else { + messageElig = `

Fibre deployée et disponible par Aquilenet !

` + const zip = encodeURIComponent(building.codePostal); + const idImm = encodeURIComponent(building.idImm); + messageElig += `
Tester l'éligibilité` + colorMarker = 'green' + } - // pas de données Axione mais Kosc nous renvoie qque chose à cette adresse (fdnEligStatus) - // c'est peut être OK, on croise avec les données ARCEP (othersEligStatus) - // Enfin on affiche un lien vers le test d'éligibilté KOSC à cette adresse - } else if (building.fdnEligStatus.isEligible && building.othersEligStatus.isEligible) { - messageElig = `

Fibre deployee mais pas chez Axione !` - messageElig += `
Tester l'eligibilite par Kosc et Bouygues

` - colorMarker = 'orange' - // Pas de données Kosc ou Axione mais l'ARCEP nous dit qu'une fibre est déployée à cette adresse - } else if (building.othersEligStatus.isEligible) { - messageElig = `

Fibre deployee mais non eligible Aquilenet, desole :(

` - colorMarker = 'red' - // Pas de fibre il semblerait, proposer un test ADSL Aquilenet - } else { - messageElig = `

Fibre non deployee :(

` - const zip = encodeURIComponent(building.codePostal); - const comm = encodeURIComponent(building.commune); - let convertType = streetTypeConversion.get(building.typeVoieImm.toLowerCase()); - if (!convertType) { - convertType = building.typeVoieImm; + // pas de données Axione mais Kosc nous renvoie qque chose à cette adresse (fdnEligStatus) + // c'est peut être OK, on croise avec les données ARCEP (othersEligStatus) + // Enfin on affiche un lien vers le test d'éligibilté KOSC à cette adresse + } else if (building.fdnEligStatus.isEligible && building.othersEligStatus.isEligible) { + messageElig = `

Fibre deployee mais pas chez Axione !` + messageElig += `
Tester l'eligibilite par Kosc et Bouygues

` + colorMarker = 'orange' + // Pas de données Kosc ou Axione mais l'ARCEP nous dit qu'une fibre est déployée à cette adresse + } else if (building.othersEligStatus.isEligible) { + messageElig = `

Fibre deployee mais non eligible Aquilenet, desole :(

` + colorMarker = 'red' + // Pas de fibre il semblerait, proposer un test ADSL Aquilenet + } else { + messageElig = `

Fibre non deployee :(

` + const zip = encodeURIComponent(building.codePostal); + const comm = encodeURIComponent(building.commune); + let convertType = streetTypeConversion.get(building.typeVoieImm.toLowerCase()); + if (!convertType) { + convertType = building.typeVoieImm; + } + const street = encodeURIComponent(`${convertType} ${building.nomVoieImm}`) + const street_nb = encodeURIComponent(building.numVoieImm) + messageElig += `
Tester ADSL a cette adresse` + if (building.othersEligStatus.reasonNotEligible != "") { + messageElig += `

Status general ARCEP: ${building.othersEligStatus.reasonNotEligible}` + } } - const street = encodeURIComponent(`${convertType} ${building.nomVoieImm}`) - const street_nb = encodeURIComponent(building.numVoieImm) - messageElig += `
Tester ADSL a cette adresse` - if (building.othersEligStatus.reasonNotEligible != "") { - messageElig += `

Status general ARCEP: ${building.othersEligStatus.reasonNotEligible}` + // Si pas d'éligibilité fibre, on affiche la raison si elle existe + if (building.aquilenetEligStatus.reasonNotEligible != "") { + messageElig += `
Pour Aquilenet, raison non eligible: ${building.aquilenetEligStatus.reasonNotEligible}` } + var markerIcon = new L.Icon({ + iconUrl: `static/icons/marker-icon-${colorMarker}.png`, + shadowUrl: 'static/vendor/images/marker-shadow.png', + iconSize: [25, 41], + iconAnchor: [12, 41], + popupAnchor: [1, -34], + shadowSize: [41, 41] + }); + const marker = new L.marker(latlng, { + icon: markerIcon, + zIndexOffset: - building.etat_imm_priority + }) + .bindPopup(`${addrImm}
${building.codePostal} ${building.commune}` + + `

${messageElig}

Ref Immeuble: ${building.idImm}`, { + maxWidth: 560 + }); + map.addLayer(marker); + markers.set(building.idImm, marker) } - // Si pas d'éligibilité fibre, on affiche la raison si elle existe - if (building.aquilenetEligStatus.reasonNotEligible != "") { - messageElig += `
Pour Aquilenet, raison non eligible: ${building.aquilenetEligStatus.reasonNotEligible}` - } - var markerIcon = new L.Icon({ - iconUrl: `static/icons/marker-icon-${colorMarker}.png`, - shadowUrl: 'static/vendor/images/marker-shadow.png', - iconSize: [25, 41], - iconAnchor: [12, 41], - popupAnchor: [1, -34], - shadowSize: [41, 41] - }); - const marker = new L.marker(latlng, { - icon: markerIcon - }) - .bindPopup(`${addrImm}
${building.codePostal} ${building.commune}` + - `

${messageElig}

Ref Immeuble: ${building.idImm}`, { - maxWidth: 560 - }); - map.addLayer(marker); - return marker }); } @@ -230,12 +297,17 @@ async function fetchEligData(map) { const bounds = map.getBounds(); const sw = bounds.getSouthWest(); const ne = bounds.getNorthEast(); - const btn = document.getElementById("btn-load-elig-data"); + let btn = document.getElementById("btn-load-elig-data"); waitBtn(btn); const reqUri = encodeURI(`eligdata?swx=${sw.lng}&swy=${sw.lat}&nex=${ne.lng}&ney=${ne.lat}`); - const source = await fetch(reqUri); - const eligData = await source.json(); - updateEligData(map, eligData); + const resp = await fetch(reqUri); + if (resp.status == 200) { + const eligData = await resp.json(); + updateEligData(map, eligData); + } else { + error = await resp.text() + console.log(`Error could not get data from server: ${resp.status} ${error}`) + } updateUrl(map); displayBtn(btn); } @@ -244,27 +316,50 @@ async function fetchEligData(map) { function initBtn() { const btn = document.getElementById("btn-load-elig-data"); btn.disabled = true; - btn.title = "Veuillez zoomer plus la carte avant de lancer une recherche d'éligibilité."; - btn.onclick = () => fetchEligData(map); + btn.title = "Veuillez zoomer plus la carte pour charger l'éligibilité."; return btn; } +function setBtnListener(btn, map) { + btn.onclick = () => { + // Reset markers when button is clicked + if (markers) { + + for (let marker of markers.values()){ + map.removeLayer(marker); + } + markers.clear(); + } + fetchEligData(map); + } + +} + function displayBtn(btn) { + btn.classList.remove('loader'); btn.disabled = false; - btn.title = "Rechercher les données d'éligibilité pour cette zone." + btn.title = "Actualiser la recherche dans cette zone" + btn.innerHTML = "Actualiser"; } function hideBtn(btn) { btn.disabled = true; - btn.title = "Veuillez zoomer plus la carte avant de lancer une recherche d'éligibilité."; + btn.innerHTML = "Zoomez sur la carte"; + btn.title = "Veuillez zoomer plus la carte afin de lancer la recherche d'éligibilité."; } function waitBtn(btn) { btn.disabled = true; + btn.innerHTML = ""; btn.title = "Chargement des batiments..."; + btn.classList.add('loader'); } - +// Init button and map const btn = initBtn(); const map = initMap(btn); -const addrSearch = initAddrSearch(map); \ No newline at end of file +const addrSearch = initAddrSearch(map); +setBtnListener(btn, map); + +// Init a limits box that shows area where data will be fetched +initLimitsBox(map, btn); diff --git a/webapp/templates/map.html b/webapp/templates/map.html index 61116a2..4282d3b 100644 --- a/webapp/templates/map.html +++ b/webapp/templates/map.html @@ -18,14 +18,13 @@ +
-