Compare commits
50 commits
nin/deploy
...
master
Author | SHA1 | Date | |
---|---|---|---|
318167467a | |||
|
84fedc9715 | ||
|
e1e94039ae | ||
|
9bdf37e533 | ||
|
4587b2d9b1 | ||
|
ae9d35eb5d | ||
|
1764b5ce52 | ||
|
8e1cae68ba | ||
|
e18cd667d4 | ||
|
1d6a42ef68 | ||
|
0c2213ce46 | ||
|
3c01837ec3 | ||
|
2c5ca321cd | ||
|
3f2d234edf | ||
aae5ea5183 | |||
358b163ef6 | |||
|
0fb3de88e1 | ||
|
461550c544 | ||
|
bfc0a30c84 | ||
|
296ed8da8a | ||
|
2f5dddde8b | ||
|
39bec33217 | ||
|
f2b749e6d3 | ||
|
275238fa39 | ||
|
b8b02bd5c8 | ||
|
a3e165ec45 | ||
|
8a1c95f9ad | ||
|
0897ea9aea | ||
|
70419dbbeb | ||
|
90bcc50087 | ||
|
270812f6d6 | ||
|
f52e29463a | ||
|
22f43fc954 | ||
1e9d62c03b | |||
|
4c8a59e64c | ||
|
b902f24711 | ||
|
18187c0007 | ||
|
a39d004ec3 | ||
|
eecbde1d24 | ||
|
b02881720d | ||
|
e6d6ba013b | ||
|
58bb3e0d1b | ||
|
70ed8a22ae | ||
|
4a6f063c1f | ||
|
b9df2de12a | ||
|
b05cf14bf3 | ||
|
108efc33f3 | ||
|
ca58802500 | ||
|
5f5c08798a | ||
|
52e4ecb2a2 |
38 changed files with 6354 additions and 211 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
.idea
|
159
data-ingest/fetch_latest_arcep.sh
Executable file
159
data-ingest/fetch_latest_arcep.sh
Executable file
|
@ -0,0 +1,159 @@
|
|||
#!/usr/bin/env bash
|
||||
set -eau -o pipefail
|
||||
|
||||
# API where to list arcep files
|
||||
GOUV_API_URL=https://www.data.gouv.fr/api/1/datasets/le-marche-du-haut-et-tres-haut-debit-fixe-deploiements/
|
||||
# File to store last versions downloaded
|
||||
VERSIONS_FILENAME=.arcep_versions
|
||||
## Content of version file:
|
||||
# LAST_ARCEP_ZIP=<file date>__<file name>.zip
|
||||
# BEFORE_ARCEP_ZIP=<file date>__<file name>.zip
|
||||
|
||||
# Global vars
|
||||
g_last_arcep_zip=""
|
||||
g_before_arcep_zip=""
|
||||
g_penultimate_arcep_zip=""
|
||||
g_arcep_to_unzip=""
|
||||
|
||||
# Script usage
|
||||
usage() {
|
||||
echo
|
||||
echo "Usage : $0 -d|--dir-out <dir to put downloads in> (-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
|
||||
}
|
||||
|
||||
# Get already dl data info
|
||||
source_versions() {
|
||||
dir_out=$1
|
||||
ver_file=${dir_out}/${VERSIONS_FILENAME}
|
||||
LAST_ARCEP_ZIP=""
|
||||
BEFORE_ARCEP_ZIP=""
|
||||
[[ -f ${ver_file} ]] && source "${ver_file}"
|
||||
g_last_arcep_zip=${LAST_ARCEP_ZIP}
|
||||
g_before_arcep_zip=${BEFORE_ARCEP_ZIP}
|
||||
}
|
||||
|
||||
# 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}"
|
||||
ver_file=${dir_out}/${VERSIONS_FILENAME}
|
||||
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 '/')
|
||||
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}" && $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
|
||||
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}"
|
||||
fi
|
||||
|
||||
return ${rc}
|
||||
}
|
||||
|
||||
# Unzip a dl arcep file
|
||||
unzip_arcep() {
|
||||
dir_out=$1
|
||||
zip_file=$2
|
||||
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
|
||||
return 0
|
||||
}
|
||||
|
||||
# main
|
||||
main() {
|
||||
# Init input vars
|
||||
remove_penultimate=false
|
||||
force_dl=false
|
||||
dir_out=""
|
||||
|
||||
# Read inputs
|
||||
[[ $# -eq 0 ]] && usage && return 1
|
||||
while [ -n "$1" ]; do
|
||||
case $1 in
|
||||
-d | --dir-out)
|
||||
dir_out=$(realpath "$2")
|
||||
shift
|
||||
;;
|
||||
-r | --remove-penultimate)
|
||||
remove_penultimate=true
|
||||
;;
|
||||
-f | --force-dl)
|
||||
force_dl=true
|
||||
;;
|
||||
-h | --help)
|
||||
usage && exit 0
|
||||
;;
|
||||
*)
|
||||
echo "Unknown command: $1"
|
||||
usage && exit 1
|
||||
;;
|
||||
esac
|
||||
[[ $# -le 1 ]] && break
|
||||
shift
|
||||
done
|
||||
|
||||
# check inputs
|
||||
if [[ -z ${dir_out} ]]; then
|
||||
echo "Error: You need to specify an output dir -d|--dir-out <dir path>"
|
||||
usage
|
||||
return 1
|
||||
fi
|
||||
|
||||
rc=0
|
||||
# Read existing dl versions
|
||||
source_versions "${dir_out}" || rc=1
|
||||
# Download latest zip file if needed
|
||||
[[ $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 succeeded and need to remove penultimate arcep data (if exists)
|
||||
if [[ $rc -eq 0 && \
|
||||
$remove_penultimate && -n \
|
||||
$g_penultimate_arcep_zip && -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)
|
||||
if [[ -d ${dir_out}/${zip_dir} ]]; then
|
||||
echo "remove dir ${dir_out}/${zip_dir}"
|
||||
rm -rf "${dir_out}"/"${zip_dir}"
|
||||
fi
|
||||
elif [[ $rc -ne 0 ]]; then
|
||||
echo "Failed to unzip ${g_last_arcep_zip} !"
|
||||
fi
|
||||
fi
|
||||
return $rc
|
||||
}
|
||||
|
||||
### Call main
|
||||
main "$@" || exit 1
|
||||
|
||||
exit 0
|
|
@ -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 ""
|
||||
|
@ -23,14 +25,64 @@ cat > "${tmpSql}" <<EOF
|
|||
.separator ";"
|
||||
EOF
|
||||
|
||||
firstFile=true
|
||||
for ipeFile in ${ipeFiles}; do
|
||||
echo " ${ipeFile}"
|
||||
cat >> "${tmpSql}" <<EOF
|
||||
.import ${ipeFile} ipe
|
||||
echo " ${ipeFile}"
|
||||
head -n1 $ipeFile | grep -q IdentifiantImmeuble && header=true || header=false
|
||||
import_opt=""
|
||||
if $firstFile || $header; then
|
||||
if ! $header; then
|
||||
echo "ERROR: first file ${ipeFile} does not have a good header"
|
||||
exit 1
|
||||
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
|
||||
|
||||
useIpeFile=${ipeFile}
|
||||
if [[ "$cut_idx_to_keep" != "" ]]; then
|
||||
cut -d';' -f$cut_idx_to_keep $ipeFile > ${ipeFile}.cut
|
||||
useIpeFile=${ipeFile}.cut
|
||||
fi
|
||||
cat >> "${tmpSql}" <<EOF
|
||||
.import ${import_opt} ${useIpeFile} ipe
|
||||
EOF
|
||||
done
|
||||
|
||||
echo ""
|
||||
|
||||
rc=0
|
||||
sqlite3 "${fullDbPath}" < "${tmpSql}" 2>&1 | grep -v "filling the rest with NULL" || rc="${PIPESTATUS[0]}"
|
||||
if [[ $rc -ne 0 ]]; then
|
||||
echo "Error executing sqlite import"
|
||||
exit "$rc"
|
||||
fi
|
||||
|
||||
echo "[+] Create separate table with id immeuble index and its state."
|
||||
cat > "${tmpSql}" <<EOF
|
||||
CREATE TABLE refimm (IdentifiantImmeuble text NOT NULL, EtatImmeuble text NOT NULL, DateDebutAcceptationCmdAcces text NOT NULL);
|
||||
CREATE UNIQUE INDEX idx_IdentifiantImmeuble on refimm (IdentifiantImmeuble);
|
||||
INSERT OR REPLACE INTO refimm SELECT IdentifiantImmeuble,EtatImmeuble, DateDebutAcceptationCmdAcces FROM ipe;
|
||||
EOF
|
||||
sqlite3 "${fullDbPath}" < "${tmpSql}"
|
||||
|
||||
echo "[+] Ingesting spatial data."
|
||||
|
|
57
data-ingest/ingest_arcep
Executable file
57
data-ingest/ingest_arcep
Executable file
|
@ -0,0 +1,57 @@
|
|||
#!/usr/bin/env bash
|
||||
set -eau -o pipefail
|
||||
|
||||
ARCEP_WWW="https://www.data.gouv.fr/fr/datasets/le-marche-du-haut-et-tres-haut-debit-fixe-deploiements/"
|
||||
|
||||
if [ "$#" -ne 2 ]; then
|
||||
echo "Usage: ingest path-to-arcep-ipe-csv-file path-to-generated-db"
|
||||
echo "ARCEP file can be downloaded here: ${ARCEP_WWW} or with script fetch_latest_arcep.sh"
|
||||
echo ""
|
||||
exit 1
|
||||
fi
|
||||
fullIpePath=$(realpath "${1}")
|
||||
fullDbPath=$(realpath "${2}")
|
||||
|
||||
tmpSql=$(mktemp)
|
||||
clean_tmp () {
|
||||
rm "${tmpSql}"
|
||||
}
|
||||
trap clean_tmp EXIT
|
||||
|
||||
echo "[+] Ingesting IPE data from ${fullIpePath}"
|
||||
echo ""
|
||||
cat > "${tmpSql}" <<EOF
|
||||
.separator ","
|
||||
.import ${fullIpePath} arcep
|
||||
EOF
|
||||
|
||||
sqlite3 "${fullDbPath}" < "${tmpSql}"
|
||||
|
||||
echo "[+] Ingesting spatial data."
|
||||
|
||||
cat > "${tmpSql}" <<EOF
|
||||
SELECT load_extension("mod_spatialite");
|
||||
SELECT InitSpatialMetaData();
|
||||
|
||||
-- Despite the SELECT, we're actually creating a new ImmeubleGeoPoint
|
||||
-- column here. The spatialite API is a bit weird...
|
||||
SELECT AddGeometryColumn('arcep','ImmeubleGeoPoint',4326,'POINT');
|
||||
|
||||
-- The geodata is expressed in RGF93 (SRID 2154). We need to project
|
||||
-- it to WSG84 to display it on a OSM map.
|
||||
UPDATE arcep SET ImmeubleGeoPoint =
|
||||
MakePoint(
|
||||
CAST(x as DOUBLE),
|
||||
CAST(y as DOUBLE),
|
||||
4326);
|
||||
EOF
|
||||
sqlite3 "${fullDbPath}" < "${tmpSql}"
|
||||
|
||||
echo "[+] Creating Rtree index (spatial index). We're almost done."
|
||||
cat > "${tmpSql}" <<EOF
|
||||
SELECT load_extension("mod_spatialite");
|
||||
SELECT CreateSpatialIndex('arcep','ImmeubleGeoPoint');
|
||||
EOF
|
||||
sqlite3 "${fullDbPath}" < "${tmpSql}"
|
||||
|
||||
echo "[+] SQLite database generated at ${fullDbPath}"
|
|
@ -4,6 +4,6 @@ pkgs.mkShell {
|
|||
buildInputs = [ pkgs.libspatialite ];
|
||||
nativeBuildInputs = [ pkgs.sqlite pkgs.cargo pkgs.rustc pkgs.poetry ];
|
||||
shellHook = ''
|
||||
LD_LIBRARY_PATH=${pkgs.libspatialite}/lib:LD_LIBRARY_PATH
|
||||
export LD_LIBRARY_PATH=${pkgs.libspatialite}/lib:$LD_LIBRARY_PATH
|
||||
'';
|
||||
}
|
||||
|
|
4
webapp/.gitignore
vendored
Normal file
4
webapp/.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
__pycache__
|
||||
/config.ini
|
||||
.vscode
|
||||
node_modules
|
41
webapp/README.md
Normal file
41
webapp/README.md
Normal file
|
@ -0,0 +1,41 @@
|
|||
# Application Web
|
||||
|
||||
https://tools.aquilenet.fr/ftth-ipe-map/
|
||||
|
||||
Cette application web expose les données issues de la BDD générée par `../data-ingest/ingest` sous la forme d'une carte interactive.
|
||||
|
||||
Dépendances: `poetry`, `sqlite3`, `mod-spatialite`.
|
||||
|
||||
Toutes les données sont gérées localement à l'exception du geocodeur qui utilise [photon de komoot](https://photon.komoot.io/).
|
||||
|
||||
# Environnement de développement
|
||||
|
||||
Il faut faudra installer au préalable `poetry`, `sqlite3`, `mod-spatialite` ou lancer le `nix-shell` à la racine de ce projet.
|
||||
|
||||
Il vous faut ensuite générer une base de données des IPEs fournis par axione à l'aide du script `../data-ingest/ingest`. Enfin, il faudra pointer cette base de données à l'aide d'un fichier de config `./config.ini` (voir `./config.ini.sample` pour le template).
|
||||
|
||||
Pour lancer le serveur de dev:
|
||||
```sh
|
||||
./run-dev-server
|
||||
```
|
||||
|
||||
## Déployer sur Gaia
|
||||
|
||||
Tous les fichiers necessaire au déploiement sur https://tools.aquilenet.fr/ftth-ipe-map/, à l'exception de la configuration Apache, sont présents dans ce dépôt git.
|
||||
|
||||
Pour déployer ce dépôt, rendez-vous sur Gaia dans `/srv/www/ftth-ipe-map` puis lancez le script `./update` avec votre utilisateur habituel (ie. pas root) pour:
|
||||
|
||||
1. Stopper le service qui tourne actuellement.
|
||||
2. Mettre à jour le dépôt git sur `origin/master`.
|
||||
3. Mettre à jour le service systemd `./ftth-ipe-map.service`.
|
||||
4. Redémarrer le nouveau service.
|
||||
|
||||
Si vous souhaitez déployer une branche autre que `origin/master`, editez le script et spécifiez la branche que vous souhaitez déployer dans la variable `gitBranch`.
|
||||
|
||||
## Composants Utilisés
|
||||
|
||||
- [Leaflet](https://leafletjs.com/): librairie qui nous permet d'afficher une carte interactive à partir des tiles ["FR" de openstreetmap.fr](https://wiki.openstreetmap.org/wiki/FR:Serveurs/tile.openstreetmap.fr#Fond_de_carte_.22FR.22).
|
||||
- [Autocomplete](https://autocomplete.trevoreyre.com/#/): librairie qui nous fournit un composant HTML/Javascript d'autocomplete. Nous l'utilisons pour l'interface utilisateur du geocodeur.
|
||||
- [Flask](https://flask.palletsprojects.com/en/2.0.x/): micro-framework dans lequel le backend est implémenté.
|
||||
- [Spatialite](https://www.gaia-gis.it/fossil/libspatialite/index): extension geodb pour SQLite dont on se sert pour réaliser des requêtes spatiales sur notre base de données.
|
||||
- [Komoot Photon](https://photon.komoot.io/): API de geocodage dont on se sert pour récupérer une position GPS à partir d'une addresse postale.
|
|
@ -1,2 +1,8 @@
|
|||
[DB]
|
||||
path = /path/to/ipe.sqlite
|
||||
axione_ipe_path = /path/to/ipe.sqlite
|
||||
axione_ipe_db_name = ipe
|
||||
arcep_ipe_path = /path/to/ipe.sqlite
|
||||
arcep_ipe_db_name = arcep
|
||||
[NETWO]
|
||||
aquilenet_fixed_recurring_price = 10.0
|
||||
api_key =
|
||||
|
|
38
webapp/coordinates.py
Normal file
38
webapp/coordinates.py
Normal file
|
@ -0,0 +1,38 @@
|
|||
import math
|
||||
|
||||
from ipe_fetcher import AreaCoordinates
|
||||
|
||||
|
||||
def adapt_coordinates_to_max_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
|
3672
webapp/elig.json
Normal file
3672
webapp/elig.json
Normal file
File diff suppressed because it is too large
Load diff
0
webapp/eligibility_api/__init__.py
Normal file
0
webapp/eligibility_api/__init__.py
Normal file
56
webapp/eligibility_api/api_doc.py
Normal file
56
webapp/eligibility_api/api_doc.py
Normal file
|
@ -0,0 +1,56 @@
|
|||
from apispec.ext.marshmallow import MarshmallowPlugin
|
||||
from apispec_webframeworks.flask import FlaskPlugin
|
||||
|
||||
from flasgger import APISpec, Schema, Swagger, fields
|
||||
|
||||
from eligibility_api.elig_api_exceptions import SwaggerValidationException
|
||||
|
||||
|
||||
def _swagger_validation_error_handler(err, data, _):
|
||||
print("API swagger error 400")
|
||||
raise SwaggerValidationException(err, data)
|
||||
|
||||
|
||||
def start_swagger(flask_app):
|
||||
"""
|
||||
Creates swagger /apidocs endpoint and adds it to flask app
|
||||
"""
|
||||
|
||||
flask_app.config["SWAGGER"] = {
|
||||
"uiversion": 3,
|
||||
"ui_params": {
|
||||
"apisSorter": "alpha",
|
||||
"operationsSorter": "alpha",
|
||||
"tagsSorter": "alpha",
|
||||
},
|
||||
}
|
||||
|
||||
# Create an APISpec
|
||||
spec = APISpec(
|
||||
title="API éligibilité",
|
||||
version="0.1.0",
|
||||
openapi_version="2.0",
|
||||
plugins=(
|
||||
FlaskPlugin(),
|
||||
MarshmallowPlugin(),
|
||||
),
|
||||
)
|
||||
|
||||
template = spec.to_flasgger(
|
||||
flask_app,
|
||||
definitions=[
|
||||
# ContributionSchema,
|
||||
# PredictConfigSchema,
|
||||
# VersionSchema,
|
||||
# PredictInputsSchema,
|
||||
# ImplementedModelSchema,
|
||||
# ApiErrorSchema,
|
||||
# LoggingHealthCheckSchema,
|
||||
],
|
||||
)
|
||||
Swagger(
|
||||
flask_app,
|
||||
template=template,
|
||||
parse=True,
|
||||
validation_error_handler=_swagger_validation_error_handler,
|
||||
)
|
89
webapp/eligibility_api/elig_api_exceptions.py
Normal file
89
webapp/eligibility_api/elig_api_exceptions.py
Normal file
|
@ -0,0 +1,89 @@
|
|||
from flask import Flask, jsonify
|
||||
|
||||
|
||||
class ApiParamException(Exception):
|
||||
"""
|
||||
Exception thrown if API misused
|
||||
"""
|
||||
|
||||
def __init__(self, description):
|
||||
self.code = 400
|
||||
self.name = "Bad API parameter"
|
||||
self.description = description
|
||||
|
||||
|
||||
class NetwoApiErrorException(Exception):
|
||||
"""
|
||||
Exception thrown if Netwo API
|
||||
"""
|
||||
|
||||
def __init__(self, description: str, netwo_status_code: int):
|
||||
self.netwo_status_code = netwo_status_code
|
||||
self.name = "Error contacting Netwo API"
|
||||
self.description = description
|
||||
|
||||
|
||||
class SwaggerValidationException(Exception):
|
||||
"""
|
||||
Exception thrown if API misused
|
||||
"""
|
||||
|
||||
def __init__(self, err, data):
|
||||
self.code = 400
|
||||
self.name = "Error from Swagger API validation"
|
||||
self.description = str(err)
|
||||
self.data = data
|
||||
|
||||
|
||||
class FlaskExceptions:
|
||||
"""
|
||||
Manages flask custom exceptions
|
||||
"""
|
||||
|
||||
def __init__(self, flask_app: Flask):
|
||||
self.flask_app = flask_app
|
||||
|
||||
def add_exceptions(self):
|
||||
"""
|
||||
declares custom exceptions to flask app
|
||||
"""
|
||||
|
||||
@self.flask_app.errorhandler(ApiParamException)
|
||||
def handle_exception(e):
|
||||
return (
|
||||
jsonify(
|
||||
{
|
||||
"code": e.code,
|
||||
"name": e.name,
|
||||
"description": e.description,
|
||||
}
|
||||
),
|
||||
400,
|
||||
)
|
||||
|
||||
@self.flask_app.errorhandler(NetwoApiErrorException)
|
||||
def handle_exception(e):
|
||||
return (
|
||||
jsonify(
|
||||
{
|
||||
"netwo_status_code": e.netwo_status_code,
|
||||
"name": e.name,
|
||||
"description": e.description,
|
||||
}
|
||||
),
|
||||
500,
|
||||
)
|
||||
|
||||
@self.flask_app.errorhandler(SwaggerValidationException)
|
||||
def handle_exception(e):
|
||||
return (
|
||||
jsonify(
|
||||
{
|
||||
"code": e.code,
|
||||
"name": e.name,
|
||||
"description": e.description,
|
||||
"data": e.data,
|
||||
}
|
||||
),
|
||||
400,
|
||||
)
|
124
webapp/eligibility_api/elig_api_routes.py
Normal file
124
webapp/eligibility_api/elig_api_routes.py
Normal file
|
@ -0,0 +1,124 @@
|
|||
from flask import Flask, request
|
||||
|
||||
from coordinates import check_coordinates_args, adapt_coordinates_to_max_area
|
||||
from eligibility_api.elig_api_exceptions import ApiParamException
|
||||
from ipe_fetcher.axione import AXIONE_MAX_AREA, Axione
|
||||
from netwo.netwo import Netwo, NetwooEligibility
|
||||
|
||||
|
||||
class EligibilityApiRoutes:
|
||||
def __init__(self, flask_app: Flask, axione_ipe: Axione, netwo: Netwo):
|
||||
self.flask_app = flask_app
|
||||
self.axione_ipe = axione_ipe
|
||||
self.netwo = netwo
|
||||
|
||||
def add_routes(self):
|
||||
@self.flask_app.route("/eligibilite/axione", methods=["GET"])
|
||||
def get_axione_eligibility_per_immeuble():
|
||||
refimmeuble = request.args.get("refimmeuble")
|
||||
if not refimmeuble:
|
||||
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 = adapt_coordinates_to_max_area(
|
||||
processed_args, AXIONE_MAX_AREA
|
||||
)
|
||||
return self.axione_ipe.get_area_buildings(coordinates, {})
|
||||
except ValueError:
|
||||
raise ApiParamException(
|
||||
"You need to specify path parameters 'swx' 'swy' 'nex' 'ney'"
|
||||
)
|
||||
|
||||
@self.flask_app.route("/eligibilite/netwo", methods=["GET"])
|
||||
def get_netwo_eligibility():
|
||||
"""
|
||||
Intérroge l'éligibilité Netwo
|
||||
---
|
||||
tags:
|
||||
- API
|
||||
description: |
|
||||
Déclenche une recherche d'éligibilité chez Netwo:
|
||||
- soit par une ref d'immeuble
|
||||
- soit par les coordonnées d'un immeuble
|
||||
La recherche est longue, l'API renvoie un event stream qui renvoie un statut toutes les secondes
|
||||
Il est possible de retrouver les résultats d'une éligibilité via son ID.
|
||||
Par défaut on renvoie uniquement les offres ftth, il est possible via ftto=true d'ajouter la ftto
|
||||
parameters:
|
||||
- in: query
|
||||
name: ftto
|
||||
required: false
|
||||
schema:
|
||||
type: boolean
|
||||
nullable: true
|
||||
allowEmptyValue: true
|
||||
description: |
|
||||
Si 'ftto' set alors on rajoutera les résultats FTTO à la recherche
|
||||
- in: query
|
||||
name: timeout_sec
|
||||
required: false
|
||||
schema:
|
||||
type: int
|
||||
nullable: false
|
||||
allowEmptyValue: false
|
||||
description: |
|
||||
Timeout au bout du quel on retourne les offres trouvées même si la recherche n'est pas terminée
|
||||
responses:
|
||||
200:
|
||||
description: Retourne le statut d'éligibilité et d'éventuelles offres
|
||||
schema:
|
||||
$ref: '#/definitions/Contribution'
|
||||
examples:
|
||||
application/json:
|
||||
400:
|
||||
description: Bad inputs
|
||||
schema:
|
||||
$ref: '#/definitions/ApiError'
|
||||
examples:
|
||||
application/json:
|
||||
code: 400
|
||||
name: Bad input parameter
|
||||
description: Bad json format
|
||||
500:
|
||||
description: Erreur lors d'un appel à l'API de Netwo
|
||||
schema:
|
||||
$ref: '#/definitions/ApiError'
|
||||
examples:
|
||||
application/json:
|
||||
netwo_status_code: 404
|
||||
name: Error contacting Netwo API
|
||||
description: ID not found
|
||||
"""
|
||||
args = request.args
|
||||
ref_imb = args.get("ref_imb")
|
||||
elig_id = args.get("id")
|
||||
timeout_sec = None
|
||||
search_ftto = args.get("ftto") is not None
|
||||
|
||||
imb_info = self.netwo.get_netwo_imb_coordinates(ref_imb)
|
||||
if elig_id:
|
||||
elig_offers = self.netwo.get_netwo_eligibility_results(
|
||||
elig_id, search_ftto
|
||||
)
|
||||
return NetwooEligibility(
|
||||
imb_info=imb_info,
|
||||
eligOffers=elig_offers,
|
||||
eligDone=True,
|
||||
elig_id=elig_id,
|
||||
)
|
||||
|
||||
if args.get("timeout_sec"):
|
||||
try:
|
||||
timeout_sec = int(args.get("timeout_sec"))
|
||||
except ValueError:
|
||||
raise ApiParamException("timeout_sec param must be an integer")
|
||||
|
||||
return self.netwo.start_netwo_eligibility(
|
||||
imb_info, search_ftto, timeout_sec
|
||||
)
|
4
webapp/ipe_fetcher/__init__.py
Normal file
4
webapp/ipe_fetcher/__init__.py
Normal file
|
@ -0,0 +1,4 @@
|
|||
from .axione import *
|
||||
from .liazo import *
|
||||
from .arcep import *
|
||||
from .model import *
|
122
webapp/ipe_fetcher/arcep.py
Normal file
122
webapp/ipe_fetcher/arcep.py
Normal file
|
@ -0,0 +1,122 @@
|
|||
from os.path import exists
|
||||
|
||||
from ipe_fetcher.model import AreaCoordinates, Building, FAIEligibilityStatus
|
||||
from ipe_fetcher.sqlite_connector.cursor import get_cursor_with_spatialite
|
||||
|
||||
ARCEP_ETAT_DEPLOYE = "deploye"
|
||||
|
||||
|
||||
class Arcep:
|
||||
def __init__(self, db_arcep_ipe_path: str, db_name: str):
|
||||
self.db_arcep_ipe_path = db_arcep_ipe_path
|
||||
self.db_name = db_name
|
||||
# Check at least that the file exists
|
||||
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 10
|
||||
elif etat_imm == "en cours de deploiement":
|
||||
return 11
|
||||
elif etat_imm != "abandonne":
|
||||
return 30
|
||||
else:
|
||||
return 31
|
||||
|
||||
def get_area_buildings(
|
||||
self, area_coordinates: AreaCoordinates, existing_buildings: dict
|
||||
) -> dict:
|
||||
# Try to get cursor on Axione database
|
||||
try:
|
||||
cur = get_cursor_with_spatialite(self.db_arcep_ipe_path)
|
||||
except Exception as err:
|
||||
print("Error while connecting to DB: ", err)
|
||||
raise RuntimeError("Could not get ARCEP data")
|
||||
# Let's first see how big is the area we're about to query.
|
||||
# If it's too big, abort the request to prevent a server DOS.
|
||||
cur.execute(
|
||||
"""
|
||||
SELECT Area(BuildMBR(:swx,:swy,:nex,:ney,4326))
|
||||
""",
|
||||
area_coordinates,
|
||||
)
|
||||
req_area = cur.fetchone()[0]
|
||||
if req_area <= 0.08:
|
||||
cur.execute(
|
||||
f"""
|
||||
SELECT
|
||||
X(ImmeubleGeoPoint),
|
||||
Y(ImmeubleGeoPoint),
|
||||
imb_id,
|
||||
imb_etat,
|
||||
num_voie,
|
||||
type_voie,
|
||||
nom_voie,
|
||||
batiment,
|
||||
code_poste,
|
||||
nom_com
|
||||
FROM {self.db_name}
|
||||
WHERE ROWID IN (
|
||||
SELECT ROWID FROM SpatialIndex
|
||||
WHERE f_table_name = '{self.db_name}' AND
|
||||
search_frame = BuildMBR(:swx, :swy, :nex, :ney, 4326))
|
||||
""",
|
||||
area_coordinates,
|
||||
)
|
||||
if not existing_buildings:
|
||||
existing_buildings = dict()
|
||||
buildings = existing_buildings
|
||||
for b in cur.fetchall():
|
||||
x = b[0]
|
||||
y = b[1]
|
||||
id_imm = b[2]
|
||||
etat_imm = b[3]
|
||||
num_voie_imm = b[4]
|
||||
type_voie_imm = b[5]
|
||||
nom_voie_imm = b[6]
|
||||
bat_info = b[7]
|
||||
code_postal = b[8]
|
||||
commune = b[9]
|
||||
is_eligible = etat_imm == ARCEP_ETAT_DEPLOYE
|
||||
others_elig_status = FAIEligibilityStatus(
|
||||
isEligible=is_eligible,
|
||||
ftthStatus=etat_imm,
|
||||
reasonNotEligible=None if is_eligible else "Pas encore deploye",
|
||||
)
|
||||
|
||||
etat_priority = self._get_etat_priority(etat_imm)
|
||||
if buildings.get(id_imm):
|
||||
buildings[id_imm]["othersEligStatus"] = others_elig_status
|
||||
buildings[id_imm]["bat_info"] = bat_info
|
||||
buildings[id_imm]["etat_imm_priority"] = etat_priority
|
||||
if buildings[id_imm].get("found_in"):
|
||||
buildings[id_imm]["found_in"].append("arcep")
|
||||
else:
|
||||
buildings[id_imm]["found_in"] = ["arcep"]
|
||||
else:
|
||||
building = Building(
|
||||
x=x,
|
||||
y=y,
|
||||
idImm=id_imm,
|
||||
numVoieImm=num_voie_imm,
|
||||
typeVoieImm=type_voie_imm,
|
||||
nomVoieImm=nom_voie_imm,
|
||||
codePostal=code_postal,
|
||||
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=others_elig_status,
|
||||
)
|
||||
buildings[id_imm] = building
|
||||
return buildings
|
||||
else:
|
||||
raise ValueError("The requested area is too wide, please reduce it")
|
209
webapp/ipe_fetcher/axione.py
Normal file
209
webapp/ipe_fetcher/axione.py
Normal file
|
@ -0,0 +1,209 @@
|
|||
from datetime import datetime
|
||||
from os.path import exists
|
||||
|
||||
from ipe_fetcher.model import AreaCoordinates, Building, FAIEligibilityStatus
|
||||
from ipe_fetcher.sqlite_connector.cursor import (
|
||||
get_cursor_with_spatialite,
|
||||
get_base_cursor,
|
||||
)
|
||||
|
||||
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"
|
||||
AXIONE_ETAT_SIGNE = "SIGNE"
|
||||
AXIONE_ETAT_RAD_DEPLOIEMENT = "RAD EN COURS DE DEPLOIEMENT"
|
||||
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
|
||||
self.db_name = db_name
|
||||
# Check at least that the file exists
|
||||
if not exists(self.db_axione_ipe_path):
|
||||
raise ValueError(f"File {self.db_axione_ipe_path} does not exist")
|
||||
|
||||
try:
|
||||
cur = get_base_cursor(self.db_axione_ipe_path)
|
||||
except Exception as err:
|
||||
print("Error while connecting to DB with base cursor: ", err)
|
||||
raise RuntimeError("Could not connect to axione DB with base cursor")
|
||||
cur.execute(
|
||||
f""" SELECT count(name) FROM sqlite_master WHERE type='table' AND name='{AXIONE_REFIMM_TABLE_NAME}' """
|
||||
)
|
||||
self.db_name_refimm = db_name
|
||||
if cur.fetchone()[0] == 1:
|
||||
self.db_name_refimm = AXIONE_REFIMM_TABLE_NAME
|
||||
|
||||
@staticmethod
|
||||
def _get_etat_priority(etat_imm):
|
||||
if etat_imm in AXIONE_ETAT_DEPLOYE:
|
||||
return 0
|
||||
elif etat_imm == AXIONE_ETAT_DEPLOYE_NON_COMMANDABLE:
|
||||
return 1
|
||||
elif etat_imm == AXIONE_ETAT_DEPLOIEMENT:
|
||||
return 2
|
||||
elif etat_imm == AXIONE_ETAT_RAD_DEPLOIEMENT:
|
||||
return 3
|
||||
elif etat_imm != AXIONE_ETAT_ABANDONNE:
|
||||
return 20
|
||||
else:
|
||||
return 21
|
||||
|
||||
def get_area_buildings(
|
||||
self, area_coordinates: AreaCoordinates, existing_buildings: dict
|
||||
) -> dict:
|
||||
# Try to get cursor on Axione database
|
||||
try:
|
||||
cur = get_cursor_with_spatialite(self.db_axione_ipe_path)
|
||||
except Exception as err:
|
||||
print("Error while connecting to DB with spatialite cursor: ", err)
|
||||
raise RuntimeError("Could not connect to axione DB")
|
||||
# Let's first see how big is the area we're about to query.
|
||||
# If it's too big, abort the request to prevent a server DOS.
|
||||
cur.execute(
|
||||
"""
|
||||
SELECT Area(BuildMBR(:swx,:swy,:nex,:ney,4326))
|
||||
""",
|
||||
area_coordinates,
|
||||
)
|
||||
req_area = cur.fetchone()[0]
|
||||
if req_area <= AXIONE_MAX_AREA:
|
||||
cur.execute(
|
||||
f"""
|
||||
SELECT
|
||||
X(ImmeubleGeoPoint),
|
||||
Y(ImmeubleGeoPoint),
|
||||
IdentifiantImmeuble,
|
||||
EtatImmeuble,
|
||||
NumeroVoieImmeuble,
|
||||
TypeVoieImmeuble,
|
||||
NomVoieImmeuble,
|
||||
CodePostalImmeuble,
|
||||
CommuneImmeuble,
|
||||
DateDebutAcceptationCmdAcces
|
||||
FROM {self.db_name}
|
||||
WHERE ROWID IN (
|
||||
SELECT ROWID FROM SpatialIndex
|
||||
WHERE f_table_name = '{self.db_name}' AND
|
||||
search_frame = BuildMBR(:swx, :swy, :nex, :ney, 4326))
|
||||
""",
|
||||
area_coordinates,
|
||||
)
|
||||
|
||||
res = cur.fetchall()
|
||||
|
||||
if not existing_buildings:
|
||||
existing_buildings = dict()
|
||||
buildings = existing_buildings
|
||||
for b in res:
|
||||
etat_imm = b[3]
|
||||
id_imm = b[2]
|
||||
is_eligible = etat_imm == AXIONE_ETAT_DEPLOYE
|
||||
date_debut = b[9]
|
||||
reason_not_eligible = "" if is_eligible 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 date_debut:
|
||||
try:
|
||||
date_formatted = datetime.strptime(date_debut, "%Y%m%d").date()
|
||||
|
||||
if date_formatted >= datetime.now().date():
|
||||
if is_eligible:
|
||||
etat_imm = 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,
|
||||
)
|
||||
|
||||
aquilenet_elig_status = FAIEligibilityStatus(
|
||||
isEligible=is_eligible,
|
||||
ftthStatus=etat_imm,
|
||||
reasonNotEligible=reason_not_eligible,
|
||||
dateCommandable=date_commandable,
|
||||
)
|
||||
etat_priority = self._get_etat_priority(etat_imm)
|
||||
if buildings.get(id_imm):
|
||||
buildings[id_imm]["aquilenetEligStatus"] = aquilenet_elig_status
|
||||
buildings[id_imm]["etat_imm_priority"] = etat_priority
|
||||
if buildings[id_imm].get("found_in"):
|
||||
buildings[id_imm]["found_in"].append("axione")
|
||||
else:
|
||||
buildings[id_imm]["found_in"] = ["axione"]
|
||||
|
||||
else:
|
||||
building = Building(
|
||||
x=b[0],
|
||||
y=b[1],
|
||||
idImm=id_imm,
|
||||
numVoieImm=b[4],
|
||||
typeVoieImm=b[5],
|
||||
nomVoieImm=b[6],
|
||||
codePostal=b[7],
|
||||
commune=b[8],
|
||||
bat_info="",
|
||||
found_in=["axione"],
|
||||
etat_imm_priority=etat_priority,
|
||||
aquilenetEligStatus=aquilenet_elig_status,
|
||||
fdnEligStatus=FAIEligibilityStatus(
|
||||
isEligible=False, reasonNotEligible="", ftthStatus=""
|
||||
),
|
||||
othersEligStatus=FAIEligibilityStatus(
|
||||
isEligible=False, reasonNotEligible="", ftthStatus=""
|
||||
),
|
||||
)
|
||||
buildings[id_imm] = building
|
||||
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 = get_base_cursor(self.db_axione_ipe_path)
|
||||
except Exception as err:
|
||||
print("Error while connecting to DB: ", err)
|
||||
raise RuntimeError("Could not connect to axione DB")
|
||||
|
||||
try:
|
||||
cur.execute(
|
||||
f"""
|
||||
SELECT EtatImmeuble, DateDebutAcceptationCmdAcces
|
||||
FROM {self.db_name_refimm}
|
||||
WHERE IdentifiantImmeuble == '{id_immeuble}'
|
||||
"""
|
||||
)
|
||||
res = cur.fetchone()
|
||||
if res:
|
||||
imm_elig = res[0]
|
||||
is_eligible = imm_elig == AXIONE_ETAT_DEPLOYE
|
||||
reason_not_eligible = "" if is_eligible else "Pas encore deploye"
|
||||
date_commandable = res[1]
|
||||
else:
|
||||
imm_elig = "NOT_AXIONE"
|
||||
is_eligible = False
|
||||
reason_not_eligible = "Axione ne gere pas ce batiment"
|
||||
date_commandable = ""
|
||||
except Exception:
|
||||
imm_elig = "NOT_AXIONE"
|
||||
is_eligible = False
|
||||
reason_not_eligible = "Axione ne gere pas ce batiment"
|
||||
date_commandable = ""
|
||||
|
||||
return FAIEligibilityStatus(
|
||||
isEligible=is_eligible,
|
||||
ftthStatus=imm_elig,
|
||||
reasonNotEligible=reason_not_eligible,
|
||||
dateCommandable=date_commandable,
|
||||
)
|
76
webapp/ipe_fetcher/liazo.py
Normal file
76
webapp/ipe_fetcher/liazo.py
Normal file
|
@ -0,0 +1,76 @@
|
|||
import http.client as http_client
|
||||
import json
|
||||
import traceback
|
||||
|
||||
from ipe_fetcher.model import AreaCoordinates, Building, FAIEligibilityStatus
|
||||
|
||||
|
||||
class Liazo:
|
||||
def get_area_buildings(
|
||||
self, narrow_coordinates: AreaCoordinates, existing_buildings: dict
|
||||
) -> dict:
|
||||
nc = narrow_coordinates
|
||||
c = http_client.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")
|
||||
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
|
||||
d = r.read()
|
||||
c.close()
|
||||
v = json.loads(d.decode("utf-8"))
|
||||
if not existing_buildings:
|
||||
existing_buildings = dict()
|
||||
buildings = existing_buildings
|
||||
for building in v:
|
||||
fdn_elig_status = 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
|
||||
reasonNotEligible=None,
|
||||
)
|
||||
id_imm = building.get("ref")
|
||||
if buildings.get(id_imm):
|
||||
buildings[id_imm]["fdnEligStatus"] = fdn_elig_status
|
||||
if buildings[id_imm]["etat_imm_priority"] > 4:
|
||||
buildings[id_imm]["etat_imm_priority"] = 4
|
||||
if buildings[id_imm].get("found_in"):
|
||||
buildings[id_imm]["found_in"].append("liazo")
|
||||
else:
|
||||
buildings[id_imm]["found_in"] = ["liazo"]
|
||||
if not buildings.get(id_imm):
|
||||
building = Building(
|
||||
y=building.get("lat"),
|
||||
x=building.get("lon"),
|
||||
idImm=id_imm,
|
||||
numVoieImm="",
|
||||
typeVoieImm="",
|
||||
nomVoieImm="",
|
||||
codePostal="",
|
||||
commune="",
|
||||
bat_info="",
|
||||
found_in=["liazo"],
|
||||
etat_imm_priority=4,
|
||||
fdnEligStatus=fdn_elig_status,
|
||||
aquilenetEligStatus=FAIEligibilityStatus(
|
||||
isEligible=False, reasonNotEligible="", ftthStatus=""
|
||||
),
|
||||
othersEligStatus=FAIEligibilityStatus(
|
||||
isEligible=False, reasonNotEligible="", ftthStatus=""
|
||||
),
|
||||
)
|
||||
|
||||
buildings[id_imm] = building
|
||||
return buildings
|
32
webapp/ipe_fetcher/model.py
Normal file
32
webapp/ipe_fetcher/model.py
Normal file
|
@ -0,0 +1,32 @@
|
|||
from typing_extensions import NotRequired, TypedDict
|
||||
|
||||
|
||||
class FAIEligibilityStatus(TypedDict):
|
||||
isEligible: bool
|
||||
ftthStatus: str
|
||||
reasonNotEligible: str
|
||||
dateCommandable: NotRequired[str]
|
||||
|
||||
|
||||
class Building(TypedDict):
|
||||
x: str
|
||||
y: str
|
||||
idImm: str
|
||||
numVoieImm: str
|
||||
typeVoieImm: str
|
||||
nomVoieImm: str
|
||||
codePostal: str
|
||||
commune: str
|
||||
bat_info: str
|
||||
found_in: list[str]
|
||||
etat_imm_priority: int = 10
|
||||
aquilenetEligStatus: FAIEligibilityStatus
|
||||
fdnEligStatus: FAIEligibilityStatus
|
||||
othersEligStatus: FAIEligibilityStatus
|
||||
|
||||
|
||||
class AreaCoordinates(TypedDict):
|
||||
swx: float
|
||||
swy: float
|
||||
nex: float
|
||||
ney: float
|
0
webapp/ipe_fetcher/sqlite_connector/__init__.py
Normal file
0
webapp/ipe_fetcher/sqlite_connector/__init__.py
Normal file
15
webapp/ipe_fetcher/sqlite_connector/cursor.py
Normal file
15
webapp/ipe_fetcher/sqlite_connector/cursor.py
Normal file
|
@ -0,0 +1,15 @@
|
|||
import sqlite3
|
||||
|
||||
|
||||
def get_cursor_with_spatialite(db_path: str = None) -> sqlite3.Cursor:
|
||||
db = sqlite3.connect(db_path)
|
||||
cur = db.cursor()
|
||||
db.enable_load_extension(True)
|
||||
cur.execute('SELECT load_extension("mod_spatialite")')
|
||||
return cur
|
||||
|
||||
|
||||
def get_base_cursor(db_path: str = None) -> sqlite3.Cursor:
|
||||
db = sqlite3.connect(db_path)
|
||||
cur = db.cursor()
|
||||
return cur
|
146
webapp/main.py
146
webapp/main.py
|
@ -1,77 +1,113 @@
|
|||
from flask import Flask, request, render_template
|
||||
|
||||
from typing import TypedDict
|
||||
import configparser
|
||||
import sqlite3
|
||||
import os
|
||||
from typing import TypedDict
|
||||
|
||||
from flask import Flask, request, render_template, redirect
|
||||
|
||||
from eligibility_api.api_doc import start_swagger
|
||||
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 adapt_coordinates_to_max_area, check_coordinates_args
|
||||
from netwo.netwo import Netwo
|
||||
|
||||
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):
|
||||
dbPath: str
|
||||
axione_ipe_path: str
|
||||
axione_ipe_db_name: str
|
||||
arcep_ipe_path: str
|
||||
arcep_ipe_db_name: str
|
||||
netwo_api_key: str
|
||||
netwo_aquilenet_fixed_recurring_price: float
|
||||
|
||||
|
||||
def parseConfig() -> Config:
|
||||
cfg_path = os.environ.get("CONFIG", "/etc/ftth-ipe-map/conf.ini")
|
||||
cfg = configparser.ConfigParser()
|
||||
with open(cfg_path, "r") as f:
|
||||
cfg.read_file(f)
|
||||
return {'dbPath':cfg.get("DB","path")}
|
||||
cfg_path = os.environ.get("CONFIG", "/etc/ftth-ipe-map/conf.ini")
|
||||
cfg = configparser.ConfigParser()
|
||||
with open(cfg_path, "r") as f:
|
||||
cfg.read_file(f)
|
||||
return {
|
||||
"axione_ipe_path": cfg.get("DB", "axione_ipe_path"),
|
||||
"axione_ipe_db_name": cfg.get("DB", "axione_ipe_db_name"),
|
||||
"arcep_ipe_path": cfg.get("DB", "arcep_ipe_path"),
|
||||
"arcep_ipe_db_name": cfg.get("DB", "arcep_ipe_db_name"),
|
||||
"netwo_api_key": cfg.get("NETWO", "api_key"),
|
||||
"netwo_aquilenet_fixed_recurring_price": float(
|
||||
cfg.get("NETWO", "aquilenet_fixed_recurring_price") or 0.00
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
app = Flask(__name__)
|
||||
cfg:Config = parseConfig()
|
||||
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()
|
||||
netwo = Netwo(
|
||||
cfg.get("netwo_api_key"), cfg.get("netwo_aquilenet_fixed_recurring_price")
|
||||
)
|
||||
elig_api_routes = EligibilityApiRoutes(app, axione, netwo)
|
||||
elig_api_routes.add_routes()
|
||||
elig_api_exceptions = FlaskExceptions(app)
|
||||
elig_api_exceptions.add_exceptions()
|
||||
|
||||
|
||||
@app.route("/", methods=["GET"])
|
||||
def getMap():
|
||||
return render_template("map.html")
|
||||
|
||||
|
||||
@app.route("/eligdata", methods=["GET"])
|
||||
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:
|
||||
cur = cursorWithSpatialite()
|
||||
# Let's first see how big is the area we're about to query.
|
||||
# If it's too big, abort the request to prevent a server DOS.
|
||||
cur.execute('''
|
||||
SELECT Area(BuildMBR(:swx,:swy,:nex,:ney,4326))
|
||||
''',processed_args)
|
||||
req_area = cur.fetchone()[0]
|
||||
if req_area <= 0.08:
|
||||
cur.execute('''
|
||||
SELECT
|
||||
X(ImmeubleGeoPoint),
|
||||
Y(ImmeubleGeoPoint),
|
||||
IdentifiantImmeuble,
|
||||
EtatImmeuble,
|
||||
NumeroVoieImmeuble,
|
||||
TypeVoieImmeuble,
|
||||
NomVoieImmeuble
|
||||
FROM ipe
|
||||
WHERE ROWID IN (
|
||||
SELECT ROWID FROM SpatialIndex
|
||||
WHERE f_table_name = 'ipe' AND
|
||||
search_frame = BuildMBR(:swx, :swy, :nex, :ney, 4326))
|
||||
''',processed_args)
|
||||
buildings = [ {
|
||||
'x':b[0], 'y':b[1], 'idImm':b[2],
|
||||
'etatImm':b[3], 'numVoieImm': b[4],
|
||||
'typeVoieImm': b[5], 'nomVoieImm': b[6]
|
||||
} for b in cur.fetchall()]
|
||||
return { "buildings": buildings}
|
||||
else:
|
||||
return "The requested area is too wide, please reduce it", 400
|
||||
coordinates = adapt_coordinates_to_max_area(processed_args, LIAZO_MAX_AREA)
|
||||
|
||||
buildings = dict()
|
||||
try:
|
||||
buildings = arcep.get_area_buildings(coordinates, buildings)
|
||||
buildings = axione.get_area_buildings(coordinates, buildings)
|
||||
except ValueError as err:
|
||||
print("Could not get Axione data for this area:", err)
|
||||
|
||||
buildings = liazo.get_area_buildings(coordinates, buildings)
|
||||
|
||||
return {"buildings": list(buildings.values())}
|
||||
else:
|
||||
return "Invalid bounding box coordinates", 400
|
||||
|
||||
def cursorWithSpatialite():
|
||||
db = sqlite3.connect(cfg['dbPath'])
|
||||
cur = db.cursor()
|
||||
db.enable_load_extension(True)
|
||||
cur.execute('SELECT load_extension("mod_spatialite")')
|
||||
return cur
|
||||
|
||||
@app.route("/eligdata/bounds", methods=["GET"])
|
||||
def getEligDataBounds():
|
||||
args = request.args
|
||||
try:
|
||||
processed_args = check_coordinates_args(args)
|
||||
return {"bounds": adapt_coordinates_to_max_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
|
||||
idImm = args["idImm"]
|
||||
codePostal = args["codePostal"]
|
||||
axioneOk = args["axione"]
|
||||
liazoOk = args["liazo"]
|
||||
pto_url = f"https://tools.aquilenet.fr/cgi-bin/recherchepto.cgi?refimmeuble={idImm}&cp={codePostal}&axione={axioneOk}&liazo={liazoOk}"
|
||||
return redirect(pto_url)
|
||||
|
||||
|
||||
# start_swagger(app)
|
||||
|
|
0
webapp/netwo/__init__.py
Normal file
0
webapp/netwo/__init__.py
Normal file
294
webapp/netwo/netwo.py
Normal file
294
webapp/netwo/netwo.py
Normal file
|
@ -0,0 +1,294 @@
|
|||
import json
|
||||
import time
|
||||
from urllib.parse import quote
|
||||
|
||||
import requests
|
||||
from flask import Response
|
||||
from typing_extensions import NotRequired, TypedDict
|
||||
|
||||
from eligibility_api.elig_api_exceptions import NetwoApiErrorException
|
||||
|
||||
NETWO_DEPLOYED_STATUS = "Deployed"
|
||||
NETWO_NOT_FOUND_STATUS = "not_found"
|
||||
TVA_INCREASE_COEFF = 1.2
|
||||
|
||||
|
||||
class NetwooEligibility(TypedDict):
|
||||
imb_info: NotRequired[dict]
|
||||
eligDone: NotRequired[bool]
|
||||
eligId: NotRequired[str]
|
||||
nbOperatorsOk: NotRequired[int]
|
||||
nbOperatorsErrors: NotRequired[int]
|
||||
nbOperatorsPending: NotRequired[int]
|
||||
totalOperators: NotRequired[int]
|
||||
timeoutSec: NotRequired[int]
|
||||
timeoutReached: NotRequired[bool]
|
||||
eligOffers: NotRequired[dict]
|
||||
|
||||
|
||||
class Netwo:
|
||||
def __init__(self, netwo_api_key: str, aquilenet_fixed_recurring_price: float):
|
||||
self.netwo_api_headers = {
|
||||
"x-actor-slug": "aquilenet",
|
||||
"x-api-key": netwo_api_key,
|
||||
"Accept": "application/json",
|
||||
}
|
||||
self.aquilenet_fixed_recurring_price = aquilenet_fixed_recurring_price
|
||||
|
||||
def get_netwo_imb_coordinates(self, ref_imb: str) -> dict:
|
||||
"""
|
||||
|
||||
:param ref_imb: ARCEP ref of immeuble
|
||||
:return:
|
||||
(elig_status: FAIEligibilityStatus, imb_lat: str, imb_lng: str)
|
||||
"""
|
||||
ref_imm_clean = quote(ref_imb, safe="")
|
||||
response = requests.get(
|
||||
f"https://api.netwo.io/api/v1/imb/{ref_imm_clean}",
|
||||
headers=self.netwo_api_headers,
|
||||
)
|
||||
status_code = response.status_code
|
||||
if status_code != 200 or not response.json():
|
||||
return {"imb_status": NETWO_NOT_FOUND_STATUS, "imb_id": ref_imb}
|
||||
imb_payload = response.json()
|
||||
return imb_payload
|
||||
|
||||
def _get_netwo_product_entities_details(self, elig_id: str, product_id: str):
|
||||
response = requests.get(
|
||||
f"https://api.netwo.io/api/v1/eligibility/{elig_id}/details/{product_id}",
|
||||
headers=self.netwo_api_headers,
|
||||
)
|
||||
status_code = response.status_code
|
||||
if status_code != 200 or not response.json():
|
||||
print(
|
||||
f"Error: could not get details for elig_id {elig_id} and product id {product_id}"
|
||||
)
|
||||
return {}
|
||||
details = response.json()
|
||||
return details.get("entities") or []
|
||||
|
||||
def _filter_netwo_raw_elig_results(
|
||||
self,
|
||||
elig_id: str,
|
||||
raw_elig: dict,
|
||||
search_ftto: bool,
|
||||
netwo_offers: list,
|
||||
processed_products: list,
|
||||
) -> list:
|
||||
inf_search = ["ftth"]
|
||||
if search_ftto:
|
||||
inf_search.append("ftto")
|
||||
for r in raw_elig.get("results"):
|
||||
inf_type = r.get("infrastructure_type")
|
||||
if inf_type not in inf_search:
|
||||
continue
|
||||
product_id = r.get("product_id")
|
||||
if product_id in processed_products:
|
||||
continue
|
||||
processed_products.append(product_id)
|
||||
|
||||
prod_entities = self._get_netwo_product_entities_details(
|
||||
elig_id, product_id
|
||||
)
|
||||
operator = r.get("infrastructure_operator")
|
||||
product = r.get("product_name")
|
||||
for offer in r.get("entities"):
|
||||
entity_id = offer.get("entity_id")
|
||||
offer_name = offer.get("name")
|
||||
debit = offer.get("debit") or 0.0
|
||||
access_fee = offer.get("access_fee") or 0.00
|
||||
recurring_price = offer.get("recurring_price") or 0.00
|
||||
commitment_duration = offer.get("commitment_duration") or 0
|
||||
access_fee_ttc = round(
|
||||
access_fee * TVA_INCREASE_COEFF,
|
||||
)
|
||||
total_recurring_price_ttc = round(
|
||||
recurring_price * TVA_INCREASE_COEFF
|
||||
+ self.aquilenet_fixed_recurring_price,
|
||||
2,
|
||||
)
|
||||
offer_info = {
|
||||
"entity_id": entity_id,
|
||||
"product_id": product_id,
|
||||
"product": f"{product} - {offer_name}",
|
||||
"infrastructure_operator": operator,
|
||||
"infrastructure_type": inf_type,
|
||||
"debit": debit,
|
||||
"access_fee": access_fee,
|
||||
"access_fee_ttc": access_fee_ttc,
|
||||
"recurring_price": recurring_price,
|
||||
"total_recurring_price_ttc": total_recurring_price_ttc,
|
||||
"commitment_duration": commitment_duration,
|
||||
"per_month_price_one_year_ttc": round(
|
||||
access_fee_ttc / 12 + total_recurring_price_ttc, 2
|
||||
),
|
||||
}
|
||||
search_entity = [
|
||||
i for i in prod_entities if i.get("entity_id") == entity_id
|
||||
]
|
||||
if search_entity:
|
||||
entity_details = search_entity[0]
|
||||
offer_info[
|
||||
"broadband_network_gateway_protocol"
|
||||
] = entity_details.get("broadband_network_gateway_protocol")
|
||||
offer_info["debit_up_max"] = entity_details.get("debit_up_max")
|
||||
offer_info["interface_type"] = entity_details.get("interface_type")
|
||||
offer_info["tariff_zone"] = entity_details.get("tariff_zone")
|
||||
offer_info["collection_region"] = entity_details.get(
|
||||
"collection_region"
|
||||
)
|
||||
offer_info["delivery_protocol"] = entity_details.get(
|
||||
"delivery_protocol"
|
||||
)
|
||||
offer_info["mtu"] = entity_details.get("mtu")
|
||||
netwo_offers.append(offer_info)
|
||||
sort_elig = sorted(
|
||||
netwo_offers,
|
||||
key=lambda x: x["per_month_price_one_year_ttc"],
|
||||
reverse=False,
|
||||
)
|
||||
return sort_elig
|
||||
|
||||
@staticmethod
|
||||
def _prepare_event_string(data: dict):
|
||||
return "data: %s\n\n" % json.dumps(data)
|
||||
|
||||
def get_netwo_eligibility_results(
|
||||
self, elig_id: str, search_ftto: bool, netwo_offers, processed_products
|
||||
):
|
||||
response = requests.get(
|
||||
f"https://api.netwo.io/api/v1/eligibility/{elig_id}",
|
||||
headers=self.netwo_api_headers,
|
||||
)
|
||||
status_code = response.status_code
|
||||
if status_code != 200:
|
||||
raise NetwoApiErrorException(
|
||||
f"Netwo API: Could not get eligibility results for ID {elig_id}",
|
||||
status_code,
|
||||
)
|
||||
return self._filter_netwo_raw_elig_results(
|
||||
elig_id, response.json(), search_ftto, netwo_offers, processed_products
|
||||
)
|
||||
|
||||
def start_netwo_eligibility(
|
||||
self,
|
||||
imb_info: str,
|
||||
search_ftto: bool,
|
||||
timeout_sec: None,
|
||||
):
|
||||
def event_stream():
|
||||
netwo_elig = NetwooEligibility(
|
||||
eligId="",
|
||||
eligDone=False,
|
||||
nbOperatorsOk=0,
|
||||
nbOperatorsErrors=0,
|
||||
nbOperatorsPending=0,
|
||||
totalOperators=0,
|
||||
eligStatus={},
|
||||
timeoutReached=False,
|
||||
timeoutSec=timeout_sec,
|
||||
eligOffers=[],
|
||||
imb_info=imb_info,
|
||||
)
|
||||
|
||||
if imb_info.get("imb_status") == NETWO_NOT_FOUND_STATUS:
|
||||
netwo_elig["eligDone"] = True
|
||||
yield "data: %s\n\n" % json.dumps(netwo_elig)
|
||||
return
|
||||
|
||||
json_data = {
|
||||
"latitude": str(imb_info.get("lat")),
|
||||
"longitude": str(imb_info.get("lng")),
|
||||
}
|
||||
response = requests.post(
|
||||
"https://api.netwo.io/api/v1/eligibility/preselect",
|
||||
headers=self.netwo_api_headers,
|
||||
json=json_data,
|
||||
)
|
||||
status_code = response.status_code
|
||||
if status_code != 200:
|
||||
print(f"raise preselect except {response.text}")
|
||||
raise NetwoApiErrorException(
|
||||
"Netwo API eligibility preselect step failed", status_code
|
||||
)
|
||||
resp = response.json()
|
||||
default = resp.get("default")
|
||||
default["offer_type"] = "enterprise"
|
||||
default["market"] = "service_operator"
|
||||
default["ftth_payload"] = {
|
||||
"imb_ref": imb_info.get("imb_id"),
|
||||
"pm_ref": imb_info.get("pm_id"),
|
||||
}
|
||||
response = requests.post(
|
||||
"https://api.netwo.io/api/v1/eligibility",
|
||||
headers=self.netwo_api_headers,
|
||||
json=default,
|
||||
)
|
||||
status_code = response.status_code
|
||||
if status_code != 201:
|
||||
print(f"Error Could not start Netwo eligibility with body {default}")
|
||||
raise NetwoApiErrorException(
|
||||
"Netwo API: failed to start eligibility", status_code
|
||||
)
|
||||
id_elig = response.json().get("id")
|
||||
netwo_elig["eligId"] = id_elig
|
||||
is_done = False
|
||||
|
||||
timeout = None
|
||||
if timeout_sec:
|
||||
timeout = time.time() + timeout_sec
|
||||
|
||||
netwo_offers = []
|
||||
processed_products = []
|
||||
while is_done is False:
|
||||
response = requests.get(
|
||||
f"https://api.netwo.io/api/v1/eligibility/{id_elig}/status",
|
||||
headers=self.netwo_api_headers,
|
||||
)
|
||||
status_code = response.status_code
|
||||
if status_code != 200:
|
||||
print("raise elig status except")
|
||||
raise NetwoApiErrorException(
|
||||
f"Netwo API: Could not get eligibility status for ID {id_elig}",
|
||||
status_code,
|
||||
)
|
||||
status_res = response.json()
|
||||
netwo_elig["eligStatus"] = status_res
|
||||
netwo_elig["nbOperatorsOk"] = len(status_res.get("successes", []) or [])
|
||||
netwo_elig["nbOperatorsErrors"] = len(
|
||||
status_res.get("errors", []) or []
|
||||
)
|
||||
netwo_elig["nbOperatorsPending"] = len(
|
||||
status_res.get("pending", []) or []
|
||||
)
|
||||
netwo_elig["totalOperators"] = (
|
||||
netwo_elig["nbOperatorsOk"]
|
||||
+ netwo_elig["nbOperatorsErrors"]
|
||||
+ netwo_elig["nbOperatorsPending"]
|
||||
)
|
||||
netwo_offers = self.get_netwo_eligibility_results(
|
||||
id_elig,
|
||||
search_ftto,
|
||||
netwo_offers,
|
||||
processed_products,
|
||||
)
|
||||
netwo_elig["eligOffers"] = netwo_offers
|
||||
if timeout and time.time() > timeout:
|
||||
netwo_elig["timeoutReached"] = True
|
||||
yield self._prepare_event_string(netwo_elig)
|
||||
break
|
||||
else:
|
||||
yield self._prepare_event_string(netwo_elig)
|
||||
|
||||
if netwo_elig["nbOperatorsPending"] > 0:
|
||||
time.sleep(0.6)
|
||||
else:
|
||||
is_done = True
|
||||
|
||||
netwo_elig["eligOffers"] = self.get_netwo_eligibility_results(
|
||||
id_elig, search_ftto, netwo_offers, processed_products
|
||||
)
|
||||
netwo_elig["eligDone"] = True
|
||||
yield self._prepare_event_string(netwo_elig)
|
||||
|
||||
return Response(event_stream(), mimetype="text/event-stream")
|
770
webapp/poetry.lock
generated
770
webapp/poetry.lock
generated
|
@ -1,40 +1,175 @@
|
|||
[[package]]
|
||||
name = "click"
|
||||
version = "8.0.4"
|
||||
description = "Composable command line interface toolkit"
|
||||
name = "aniso8601"
|
||||
version = "9.0.1"
|
||||
description = "A library for parsing ISO 8601 strings."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[package.extras]
|
||||
dev = ["black", "coverage", "isort", "pre-commit", "pyenchant", "pylint"]
|
||||
|
||||
[[package]]
|
||||
name = "apispec"
|
||||
version = "6.1.0"
|
||||
description = "A pluggable API specification generator. Currently supports the OpenAPI Specification (f.k.a. the Swagger specification)."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[package.dependencies]
|
||||
packaging = ">=21.3"
|
||||
PyYAML = {version = ">=3.10", optional = true, markers = "extra == \"yaml\""}
|
||||
|
||||
[package.extras]
|
||||
dev = ["PyYAML (>=3.10)", "flake8 (==5.0.4)", "flake8-bugbear (==22.9.23)", "marshmallow (>=3.13.0)", "mypy (==0.982)", "openapi-spec-validator (<0.5)", "prance[osv] (>=0.11)", "pre-commit (>=2.4,<3.0)", "pytest", "tox", "types-PyYAML"]
|
||||
docs = ["marshmallow (>=3.13.0)", "pyyaml (==6.0)", "sphinx (==5.2.3)", "sphinx-issues (==3.0.1)", "sphinx-rtd-theme (==1.0.0)"]
|
||||
lint = ["flake8 (==5.0.4)", "flake8-bugbear (==22.9.23)", "mypy (==0.982)", "pre-commit (>=2.4,<3.0)", "types-PyYAML"]
|
||||
marshmallow = ["marshmallow (>=3.18.0)"]
|
||||
tests = ["PyYAML (>=3.10)", "marshmallow (>=3.13.0)", "openapi-spec-validator (<0.5)", "prance[osv] (>=0.11)", "pytest"]
|
||||
validation = ["openapi-spec-validator (<0.5)", "prance[osv] (>=0.11)"]
|
||||
yaml = ["PyYAML (>=3.10)"]
|
||||
|
||||
[[package]]
|
||||
name = "apispec-webframeworks"
|
||||
version = "0.5.2"
|
||||
description = "Web framework plugins for apispec."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[package.dependencies]
|
||||
apispec = {version = ">=2.0.0", extras = ["yaml"]}
|
||||
|
||||
[package.extras]
|
||||
dev = ["Flask (==1.1.1)", "bottle (==0.12.17)", "flake8 (==3.7.9)", "flake8-bugbear (==19.8.0)", "mock", "pre-commit (>=1.18,<2.0)", "pytest", "tornado", "tox"]
|
||||
lint = ["flake8 (==3.7.9)", "flake8-bugbear (==19.8.0)", "pre-commit (>=1.18,<2.0)"]
|
||||
tests = ["Flask (==1.1.1)", "bottle (==0.12.17)", "mock", "pytest", "tornado"]
|
||||
|
||||
[[package]]
|
||||
name = "attrs"
|
||||
version = "22.2.0"
|
||||
description = "Classes Without Boilerplate"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[package.extras]
|
||||
cov = ["attrs[tests]", "coverage-enable-subprocess", "coverage[toml] (>=5.3)"]
|
||||
dev = ["attrs[docs,tests]"]
|
||||
docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope.interface"]
|
||||
tests = ["attrs[tests-no-zope]", "zope.interface"]
|
||||
tests-no-zope = ["cloudpickle", "cloudpickle", "hypothesis", "hypothesis", "mypy (>=0.971,<0.990)", "mypy (>=0.971,<0.990)", "pympler", "pympler", "pytest (>=4.3.0)", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-mypy-plugins", "pytest-xdist[psutil]", "pytest-xdist[psutil]"]
|
||||
|
||||
[[package]]
|
||||
name = "black"
|
||||
version = "23.1.0"
|
||||
description = "The uncompromising code formatter."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[package.dependencies]
|
||||
click = ">=8.0.0"
|
||||
mypy-extensions = ">=0.4.3"
|
||||
packaging = ">=22.0"
|
||||
pathspec = ">=0.9.0"
|
||||
platformdirs = ">=2"
|
||||
tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
|
||||
typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""}
|
||||
|
||||
[package.extras]
|
||||
colorama = ["colorama (>=0.4.3)"]
|
||||
d = ["aiohttp (>=3.7.4)"]
|
||||
jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"]
|
||||
uvloop = ["uvloop (>=0.15.2)"]
|
||||
|
||||
[[package]]
|
||||
name = "certifi"
|
||||
version = "2022.12.7"
|
||||
description = "Python package for providing Mozilla's CA Bundle."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[[package]]
|
||||
name = "charset-normalizer"
|
||||
version = "3.0.1"
|
||||
description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "click"
|
||||
version = "8.1.3"
|
||||
description = "Composable command line interface toolkit"
|
||||
category = "main"
|
||||
optional = false
|
||||
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 = "flasgger"
|
||||
version = "0.9.5"
|
||||
description = "Extract swagger specs from your flask project"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[package.dependencies]
|
||||
Flask = ">=0.10"
|
||||
jsonschema = ">=3.0.1"
|
||||
mistune = "*"
|
||||
PyYAML = ">=3.0"
|
||||
six = ">=1.10.0"
|
||||
|
||||
[[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)"]
|
||||
dotenv = ["python-dotenv"]
|
||||
|
||||
[[package]]
|
||||
name = "flask-restful"
|
||||
version = "0.3.9"
|
||||
description = "Simple framework for creating REST APIs"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[package.dependencies]
|
||||
aniso8601 = ">=0.82"
|
||||
Flask = ">=0.8"
|
||||
pytz = "*"
|
||||
six = ">=1.3.0"
|
||||
|
||||
[package.extras]
|
||||
docs = ["sphinx"]
|
||||
|
||||
[[package]]
|
||||
name = "gunicorn"
|
||||
version = "20.1.0"
|
||||
|
@ -43,15 +178,42 @@ 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)"]
|
||||
setproctitle = ["setproctitle"]
|
||||
tornado = ["tornado (>=0.2)"]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "3.4"
|
||||
description = "Internationalized Domain Names in Applications (IDNA)"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
|
||||
[[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
|
||||
|
@ -59,11 +221,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"
|
||||
|
@ -71,14 +233,63 @@ MarkupSafe = ">=2.0"
|
|||
[package.extras]
|
||||
i18n = ["Babel (>=2.7)"]
|
||||
|
||||
[[package]]
|
||||
name = "jsonschema"
|
||||
version = "4.17.3"
|
||||
description = "An implementation of JSON Schema validation for Python"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[package.dependencies]
|
||||
attrs = ">=17.4.0"
|
||||
pyrsistent = ">=0.14.0,<0.17.0 || >0.17.0,<0.17.1 || >0.17.1,<0.17.2 || >0.17.2"
|
||||
|
||||
[package.extras]
|
||||
format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"]
|
||||
format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=1.11)"]
|
||||
|
||||
[[package]]
|
||||
name = "markupsafe"
|
||||
version = "2.1.0"
|
||||
version = "2.1.1"
|
||||
description = "Safely add untrusted strings to HTML/XML markup."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[[package]]
|
||||
name = "marshmallow"
|
||||
version = "3.19.0"
|
||||
description = "A lightweight library for converting complex datatypes to and from native Python datatypes."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[package.dependencies]
|
||||
packaging = ">=17.0"
|
||||
|
||||
[package.extras]
|
||||
dev = ["flake8 (==5.0.4)", "flake8-bugbear (==22.10.25)", "mypy (==0.990)", "pre-commit (>=2.4,<3.0)", "pytest", "pytz", "simplejson", "tox"]
|
||||
docs = ["alabaster (==0.7.12)", "autodocsumm (==0.2.9)", "sphinx (==5.3.0)", "sphinx-issues (==3.0.1)", "sphinx-version-warning (==1.1.2)"]
|
||||
lint = ["flake8 (==5.0.4)", "flake8-bugbear (==22.10.25)", "mypy (==0.990)", "pre-commit (>=2.4,<3.0)"]
|
||||
tests = ["pytest", "pytz", "simplejson"]
|
||||
|
||||
[[package]]
|
||||
name = "mistune"
|
||||
version = "2.0.5"
|
||||
description = "A sane Markdown parser with useful plugins and renderers"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "mypy-extensions"
|
||||
version = "1.0.0"
|
||||
description = "Type system extensions for programs checked with the mypy type checker."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
|
||||
[[package]]
|
||||
name = "mypy1989"
|
||||
version = "0.0.2"
|
||||
|
@ -88,93 +299,512 @@ optional = false
|
|||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "werkzeug"
|
||||
version = "2.0.3"
|
||||
description = "The comprehensive WSGI web application library."
|
||||
name = "packaging"
|
||||
version = "23.0"
|
||||
description = "Core utilities for Python packages"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[[package]]
|
||||
name = "pathspec"
|
||||
version = "0.11.0"
|
||||
description = "Utility library for gitignore style pattern matching of file paths."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[[package]]
|
||||
name = "platformdirs"
|
||||
version = "3.0.0"
|
||||
description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[package.extras]
|
||||
docs = ["furo (>=2022.12.7)", "proselint (>=0.13)", "sphinx (>=6.1.3)", "sphinx-autodoc-typehints (>=1.22,!=1.23.4)"]
|
||||
test = ["appdirs (==1.4.4)", "covdefaults (>=2.2.2)", "pytest (>=7.2.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"]
|
||||
|
||||
[[package]]
|
||||
name = "pyrsistent"
|
||||
version = "0.19.3"
|
||||
description = "Persistent/Functional/Immutable data structures"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[[package]]
|
||||
name = "pytz"
|
||||
version = "2022.7.1"
|
||||
description = "World timezone definitions, modern and historical"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "pyyaml"
|
||||
version = "6.0"
|
||||
description = "YAML parser and emitter for Python"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[[package]]
|
||||
name = "requests"
|
||||
version = "2.28.2"
|
||||
description = "Python HTTP for Humans."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7, <4"
|
||||
|
||||
[package.dependencies]
|
||||
certifi = ">=2017.4.17"
|
||||
charset-normalizer = ">=2,<4"
|
||||
idna = ">=2.5,<4"
|
||||
urllib3 = ">=1.21.1,<1.27"
|
||||
|
||||
[package.extras]
|
||||
socks = ["PySocks (>=1.5.6,!=1.5.7)"]
|
||||
use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
|
||||
|
||||
[[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 = "six"
|
||||
version = "1.16.0"
|
||||
description = "Python 2 and 3 compatibility utilities"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
|
||||
|
||||
[[package]]
|
||||
name = "tomli"
|
||||
version = "2.0.1"
|
||||
description = "A lil' TOML parser"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[[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 = "urllib3"
|
||||
version = "1.26.14"
|
||||
description = "HTTP library with thread-safe connection pooling, file post, and more."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
|
||||
|
||||
[package.extras]
|
||||
brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"]
|
||||
secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"]
|
||||
socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "werkzeug"
|
||||
version = "2.2.2"
|
||||
description = "The comprehensive WSGI web application library."
|
||||
category = "main"
|
||||
optional = false
|
||||
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"
|
||||
content-hash = "6a4eec028f8b8691aa43295a6d58a7772260ee3345905cf903191529c1082148"
|
||||
content-hash = "808253c094a03d4591f9504fee2876cfe5efe4b63ca89b47813fbc63e8cb7de7"
|
||||
|
||||
[metadata.files]
|
||||
aniso8601 = [
|
||||
{file = "aniso8601-9.0.1-py2.py3-none-any.whl", hash = "sha256:1d2b7ef82963909e93c4f24ce48d4de9e66009a21bf1c1e1c85bdd0812fe412f"},
|
||||
{file = "aniso8601-9.0.1.tar.gz", hash = "sha256:72e3117667eedf66951bb2d93f4296a56b94b078a8a95905a052611fb3f1b973"},
|
||||
]
|
||||
apispec = [
|
||||
{file = "apispec-6.1.0-py3-none-any.whl", hash = "sha256:937d7f11be6e80cf6d0b66c12dbc29604435302b529b3a9bdb5e2dee192d2f17"},
|
||||
{file = "apispec-6.1.0.tar.gz", hash = "sha256:881d3b90bfffded659bc0a4bf09eadeedfa256cd271726b1555d75af9e0a9a69"},
|
||||
]
|
||||
apispec-webframeworks = [
|
||||
{file = "apispec-webframeworks-0.5.2.tar.gz", hash = "sha256:0db35b267914b3f8c562aca0261957dbcb4176f255eacc22520277010818dcf3"},
|
||||
{file = "apispec_webframeworks-0.5.2-py2.py3-none-any.whl", hash = "sha256:482c563abbcc2a261439476cb3f1a7c7284cc997c322c574d48c111643e9c04e"},
|
||||
]
|
||||
attrs = [
|
||||
{file = "attrs-22.2.0-py3-none-any.whl", hash = "sha256:29e95c7f6778868dbd49170f98f8818f78f3dc5e0e37c0b1f474e3561b240836"},
|
||||
{file = "attrs-22.2.0.tar.gz", hash = "sha256:c9227bfc2f01993c03f68db37d1d15c9690188323c067c641f1a35ca58185f99"},
|
||||
]
|
||||
black = [
|
||||
{file = "black-23.1.0-cp310-cp310-macosx_10_16_arm64.whl", hash = "sha256:b6a92a41ee34b883b359998f0c8e6eb8e99803aa8bf3123bf2b2e6fec505a221"},
|
||||
{file = "black-23.1.0-cp310-cp310-macosx_10_16_universal2.whl", hash = "sha256:57c18c5165c1dbe291d5306e53fb3988122890e57bd9b3dcb75f967f13411a26"},
|
||||
{file = "black-23.1.0-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:9880d7d419bb7e709b37e28deb5e68a49227713b623c72b2b931028ea65f619b"},
|
||||
{file = "black-23.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e6663f91b6feca5d06f2ccd49a10f254f9298cc1f7f49c46e498a0771b507104"},
|
||||
{file = "black-23.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:9afd3f493666a0cd8f8df9a0200c6359ac53940cbde049dcb1a7eb6ee2dd7074"},
|
||||
{file = "black-23.1.0-cp311-cp311-macosx_10_16_arm64.whl", hash = "sha256:bfffba28dc52a58f04492181392ee380e95262af14ee01d4bc7bb1b1c6ca8d27"},
|
||||
{file = "black-23.1.0-cp311-cp311-macosx_10_16_universal2.whl", hash = "sha256:c1c476bc7b7d021321e7d93dc2cbd78ce103b84d5a4cf97ed535fbc0d6660648"},
|
||||
{file = "black-23.1.0-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:382998821f58e5c8238d3166c492139573325287820963d2f7de4d518bd76958"},
|
||||
{file = "black-23.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bf649fda611c8550ca9d7592b69f0637218c2369b7744694c5e4902873b2f3a"},
|
||||
{file = "black-23.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:121ca7f10b4a01fd99951234abdbd97728e1240be89fde18480ffac16503d481"},
|
||||
{file = "black-23.1.0-cp37-cp37m-macosx_10_16_x86_64.whl", hash = "sha256:a8471939da5e824b891b25751955be52ee7f8a30a916d570a5ba8e0f2eb2ecad"},
|
||||
{file = "black-23.1.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8178318cb74f98bc571eef19068f6ab5613b3e59d4f47771582f04e175570ed8"},
|
||||
{file = "black-23.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:a436e7881d33acaf2536c46a454bb964a50eff59b21b51c6ccf5a40601fbef24"},
|
||||
{file = "black-23.1.0-cp38-cp38-macosx_10_16_arm64.whl", hash = "sha256:a59db0a2094d2259c554676403fa2fac3473ccf1354c1c63eccf7ae65aac8ab6"},
|
||||
{file = "black-23.1.0-cp38-cp38-macosx_10_16_universal2.whl", hash = "sha256:0052dba51dec07ed029ed61b18183942043e00008ec65d5028814afaab9a22fd"},
|
||||
{file = "black-23.1.0-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:49f7b39e30f326a34b5c9a4213213a6b221d7ae9d58ec70df1c4a307cf2a1580"},
|
||||
{file = "black-23.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:162e37d49e93bd6eb6f1afc3e17a3d23a823042530c37c3c42eeeaf026f38468"},
|
||||
{file = "black-23.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:8b70eb40a78dfac24842458476135f9b99ab952dd3f2dab738c1881a9b38b753"},
|
||||
{file = "black-23.1.0-cp39-cp39-macosx_10_16_arm64.whl", hash = "sha256:a29650759a6a0944e7cca036674655c2f0f63806ddecc45ed40b7b8aa314b651"},
|
||||
{file = "black-23.1.0-cp39-cp39-macosx_10_16_universal2.whl", hash = "sha256:bb460c8561c8c1bec7824ecbc3ce085eb50005883a6203dcfb0122e95797ee06"},
|
||||
{file = "black-23.1.0-cp39-cp39-macosx_10_16_x86_64.whl", hash = "sha256:c91dfc2c2a4e50df0026f88d2215e166616e0c80e86004d0003ece0488db2739"},
|
||||
{file = "black-23.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2a951cc83ab535d248c89f300eccbd625e80ab880fbcfb5ac8afb5f01a258ac9"},
|
||||
{file = "black-23.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:0680d4380db3719ebcfb2613f34e86c8e6d15ffeabcf8ec59355c5e7b85bb555"},
|
||||
{file = "black-23.1.0-py3-none-any.whl", hash = "sha256:7a0f701d314cfa0896b9001df70a530eb2472babb76086344e688829efd97d32"},
|
||||
{file = "black-23.1.0.tar.gz", hash = "sha256:b0bd97bea8903f5a2ba7219257a44e3f1f9d00073d6cc1add68f0beec69692ac"},
|
||||
]
|
||||
certifi = [
|
||||
{file = "certifi-2022.12.7-py3-none-any.whl", hash = "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"},
|
||||
{file = "certifi-2022.12.7.tar.gz", hash = "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3"},
|
||||
]
|
||||
charset-normalizer = [
|
||||
{file = "charset-normalizer-3.0.1.tar.gz", hash = "sha256:ebea339af930f8ca5d7a699b921106c6e29c617fe9606fa7baa043c1cdae326f"},
|
||||
{file = "charset_normalizer-3.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:88600c72ef7587fe1708fd242b385b6ed4b8904976d5da0893e31df8b3480cb6"},
|
||||
{file = "charset_normalizer-3.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c75ffc45f25324e68ab238cb4b5c0a38cd1c3d7f1fb1f72b5541de469e2247db"},
|
||||
{file = "charset_normalizer-3.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:db72b07027db150f468fbada4d85b3b2729a3db39178abf5c543b784c1254539"},
|
||||
{file = "charset_normalizer-3.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62595ab75873d50d57323a91dd03e6966eb79c41fa834b7a1661ed043b2d404d"},
|
||||
{file = "charset_normalizer-3.0.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ff6f3db31555657f3163b15a6b7c6938d08df7adbfc9dd13d9d19edad678f1e8"},
|
||||
{file = "charset_normalizer-3.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:772b87914ff1152b92a197ef4ea40efe27a378606c39446ded52c8f80f79702e"},
|
||||
{file = "charset_normalizer-3.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70990b9c51340e4044cfc394a81f614f3f90d41397104d226f21e66de668730d"},
|
||||
{file = "charset_normalizer-3.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:292d5e8ba896bbfd6334b096e34bffb56161c81408d6d036a7dfa6929cff8783"},
|
||||
{file = "charset_normalizer-3.0.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:2edb64ee7bf1ed524a1da60cdcd2e1f6e2b4f66ef7c077680739f1641f62f555"},
|
||||
{file = "charset_normalizer-3.0.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:31a9ddf4718d10ae04d9b18801bd776693487cbb57d74cc3458a7673f6f34639"},
|
||||
{file = "charset_normalizer-3.0.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:44ba614de5361b3e5278e1241fda3dc1838deed864b50a10d7ce92983797fa76"},
|
||||
{file = "charset_normalizer-3.0.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:12db3b2c533c23ab812c2b25934f60383361f8a376ae272665f8e48b88e8e1c6"},
|
||||
{file = "charset_normalizer-3.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c512accbd6ff0270939b9ac214b84fb5ada5f0409c44298361b2f5e13f9aed9e"},
|
||||
{file = "charset_normalizer-3.0.1-cp310-cp310-win32.whl", hash = "sha256:502218f52498a36d6bf5ea77081844017bf7982cdbe521ad85e64cabee1b608b"},
|
||||
{file = "charset_normalizer-3.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:601f36512f9e28f029d9481bdaf8e89e5148ac5d89cffd3b05cd533eeb423b59"},
|
||||
{file = "charset_normalizer-3.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0298eafff88c99982a4cf66ba2efa1128e4ddaca0b05eec4c456bbc7db691d8d"},
|
||||
{file = "charset_normalizer-3.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a8d0fc946c784ff7f7c3742310cc8a57c5c6dc31631269876a88b809dbeff3d3"},
|
||||
{file = "charset_normalizer-3.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:87701167f2a5c930b403e9756fab1d31d4d4da52856143b609e30a1ce7160f3c"},
|
||||
{file = "charset_normalizer-3.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:14e76c0f23218b8f46c4d87018ca2e441535aed3632ca134b10239dfb6dadd6b"},
|
||||
{file = "charset_normalizer-3.0.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0c0a590235ccd933d9892c627dec5bc7511ce6ad6c1011fdf5b11363022746c1"},
|
||||
{file = "charset_normalizer-3.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c7fe7afa480e3e82eed58e0ca89f751cd14d767638e2550c77a92a9e749c317"},
|
||||
{file = "charset_normalizer-3.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:79909e27e8e4fcc9db4addea88aa63f6423ebb171db091fb4373e3312cb6d603"},
|
||||
{file = "charset_normalizer-3.0.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ac7b6a045b814cf0c47f3623d21ebd88b3e8cf216a14790b455ea7ff0135d18"},
|
||||
{file = "charset_normalizer-3.0.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:72966d1b297c741541ca8cf1223ff262a6febe52481af742036a0b296e35fa5a"},
|
||||
{file = "charset_normalizer-3.0.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:f9d0c5c045a3ca9bedfc35dca8526798eb91a07aa7a2c0fee134c6c6f321cbd7"},
|
||||
{file = "charset_normalizer-3.0.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:5995f0164fa7df59db4746112fec3f49c461dd6b31b841873443bdb077c13cfc"},
|
||||
{file = "charset_normalizer-3.0.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4a8fcf28c05c1f6d7e177a9a46a1c52798bfe2ad80681d275b10dcf317deaf0b"},
|
||||
{file = "charset_normalizer-3.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:761e8904c07ad053d285670f36dd94e1b6ab7f16ce62b9805c475b7aa1cffde6"},
|
||||
{file = "charset_normalizer-3.0.1-cp311-cp311-win32.whl", hash = "sha256:71140351489970dfe5e60fc621ada3e0f41104a5eddaca47a7acb3c1b851d6d3"},
|
||||
{file = "charset_normalizer-3.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:9ab77acb98eba3fd2a85cd160851816bfce6871d944d885febf012713f06659c"},
|
||||
{file = "charset_normalizer-3.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:84c3990934bae40ea69a82034912ffe5a62c60bbf6ec5bc9691419641d7d5c9a"},
|
||||
{file = "charset_normalizer-3.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:74292fc76c905c0ef095fe11e188a32ebd03bc38f3f3e9bcb85e4e6db177b7ea"},
|
||||
{file = "charset_normalizer-3.0.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c95a03c79bbe30eec3ec2b7f076074f4281526724c8685a42872974ef4d36b72"},
|
||||
{file = "charset_normalizer-3.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4c39b0e3eac288fedc2b43055cfc2ca7a60362d0e5e87a637beac5d801ef478"},
|
||||
{file = "charset_normalizer-3.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df2c707231459e8a4028eabcd3cfc827befd635b3ef72eada84ab13b52e1574d"},
|
||||
{file = "charset_normalizer-3.0.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:93ad6d87ac18e2a90b0fe89df7c65263b9a99a0eb98f0a3d2e079f12a0735837"},
|
||||
{file = "charset_normalizer-3.0.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:59e5686dd847347e55dffcc191a96622f016bc0ad89105e24c14e0d6305acbc6"},
|
||||
{file = "charset_normalizer-3.0.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:cd6056167405314a4dc3c173943f11249fa0f1b204f8b51ed4bde1a9cd1834dc"},
|
||||
{file = "charset_normalizer-3.0.1-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:083c8d17153ecb403e5e1eb76a7ef4babfc2c48d58899c98fcaa04833e7a2f9a"},
|
||||
{file = "charset_normalizer-3.0.1-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:f5057856d21e7586765171eac8b9fc3f7d44ef39425f85dbcccb13b3ebea806c"},
|
||||
{file = "charset_normalizer-3.0.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:7eb33a30d75562222b64f569c642ff3dc6689e09adda43a082208397f016c39a"},
|
||||
{file = "charset_normalizer-3.0.1-cp36-cp36m-win32.whl", hash = "sha256:95dea361dd73757c6f1c0a1480ac499952c16ac83f7f5f4f84f0658a01b8ef41"},
|
||||
{file = "charset_normalizer-3.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:eaa379fcd227ca235d04152ca6704c7cb55564116f8bc52545ff357628e10602"},
|
||||
{file = "charset_normalizer-3.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3e45867f1f2ab0711d60c6c71746ac53537f1684baa699f4f668d4c6f6ce8e14"},
|
||||
{file = "charset_normalizer-3.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cadaeaba78750d58d3cc6ac4d1fd867da6fc73c88156b7a3212a3cd4819d679d"},
|
||||
{file = "charset_normalizer-3.0.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:911d8a40b2bef5b8bbae2e36a0b103f142ac53557ab421dc16ac4aafee6f53dc"},
|
||||
{file = "charset_normalizer-3.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:503e65837c71b875ecdd733877d852adbc465bd82c768a067badd953bf1bc5a3"},
|
||||
{file = "charset_normalizer-3.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a60332922359f920193b1d4826953c507a877b523b2395ad7bc716ddd386d866"},
|
||||
{file = "charset_normalizer-3.0.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:16a8663d6e281208d78806dbe14ee9903715361cf81f6d4309944e4d1e59ac5b"},
|
||||
{file = "charset_normalizer-3.0.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:a16418ecf1329f71df119e8a65f3aa68004a3f9383821edcb20f0702934d8087"},
|
||||
{file = "charset_normalizer-3.0.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:9d9153257a3f70d5f69edf2325357251ed20f772b12e593f3b3377b5f78e7ef8"},
|
||||
{file = "charset_normalizer-3.0.1-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:02a51034802cbf38db3f89c66fb5d2ec57e6fe7ef2f4a44d070a593c3688667b"},
|
||||
{file = "charset_normalizer-3.0.1-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:2e396d70bc4ef5325b72b593a72c8979999aa52fb8bcf03f701c1b03e1166918"},
|
||||
{file = "charset_normalizer-3.0.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:11b53acf2411c3b09e6af37e4b9005cba376c872503c8f28218c7243582df45d"},
|
||||
{file = "charset_normalizer-3.0.1-cp37-cp37m-win32.whl", hash = "sha256:0bf2dae5291758b6f84cf923bfaa285632816007db0330002fa1de38bfcb7154"},
|
||||
{file = "charset_normalizer-3.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:2c03cc56021a4bd59be889c2b9257dae13bf55041a3372d3295416f86b295fb5"},
|
||||
{file = "charset_normalizer-3.0.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:024e606be3ed92216e2b6952ed859d86b4cfa52cd5bc5f050e7dc28f9b43ec42"},
|
||||
{file = "charset_normalizer-3.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4b0d02d7102dd0f997580b51edc4cebcf2ab6397a7edf89f1c73b586c614272c"},
|
||||
{file = "charset_normalizer-3.0.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:358a7c4cb8ba9b46c453b1dd8d9e431452d5249072e4f56cfda3149f6ab1405e"},
|
||||
{file = "charset_normalizer-3.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81d6741ab457d14fdedc215516665050f3822d3e56508921cc7239f8c8e66a58"},
|
||||
{file = "charset_normalizer-3.0.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8b8af03d2e37866d023ad0ddea594edefc31e827fee64f8de5611a1dbc373174"},
|
||||
{file = "charset_normalizer-3.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9cf4e8ad252f7c38dd1f676b46514f92dc0ebeb0db5552f5f403509705e24753"},
|
||||
{file = "charset_normalizer-3.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e696f0dd336161fca9adbb846875d40752e6eba585843c768935ba5c9960722b"},
|
||||
{file = "charset_normalizer-3.0.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c22d3fe05ce11d3671297dc8973267daa0f938b93ec716e12e0f6dee81591dc1"},
|
||||
{file = "charset_normalizer-3.0.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:109487860ef6a328f3eec66f2bf78b0b72400280d8f8ea05f69c51644ba6521a"},
|
||||
{file = "charset_normalizer-3.0.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:37f8febc8ec50c14f3ec9637505f28e58d4f66752207ea177c1d67df25da5aed"},
|
||||
{file = "charset_normalizer-3.0.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:f97e83fa6c25693c7a35de154681fcc257c1c41b38beb0304b9c4d2d9e164479"},
|
||||
{file = "charset_normalizer-3.0.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a152f5f33d64a6be73f1d30c9cc82dfc73cec6477ec268e7c6e4c7d23c2d2291"},
|
||||
{file = "charset_normalizer-3.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:39049da0ffb96c8cbb65cbf5c5f3ca3168990adf3551bd1dee10c48fce8ae820"},
|
||||
{file = "charset_normalizer-3.0.1-cp38-cp38-win32.whl", hash = "sha256:4457ea6774b5611f4bed5eaa5df55f70abde42364d498c5134b7ef4c6958e20e"},
|
||||
{file = "charset_normalizer-3.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:e62164b50f84e20601c1ff8eb55620d2ad25fb81b59e3cd776a1902527a788af"},
|
||||
{file = "charset_normalizer-3.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8eade758719add78ec36dc13201483f8e9b5d940329285edcd5f70c0a9edbd7f"},
|
||||
{file = "charset_normalizer-3.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8499ca8f4502af841f68135133d8258f7b32a53a1d594aa98cc52013fff55678"},
|
||||
{file = "charset_normalizer-3.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3fc1c4a2ffd64890aebdb3f97e1278b0cc72579a08ca4de8cd2c04799a3a22be"},
|
||||
{file = "charset_normalizer-3.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00d3ffdaafe92a5dc603cb9bd5111aaa36dfa187c8285c543be562e61b755f6b"},
|
||||
{file = "charset_normalizer-3.0.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c2ac1b08635a8cd4e0cbeaf6f5e922085908d48eb05d44c5ae9eabab148512ca"},
|
||||
{file = "charset_normalizer-3.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f6f45710b4459401609ebebdbcfb34515da4fc2aa886f95107f556ac69a9147e"},
|
||||
{file = "charset_normalizer-3.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ae1de54a77dc0d6d5fcf623290af4266412a7c4be0b1ff7444394f03f5c54e3"},
|
||||
{file = "charset_normalizer-3.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3b590df687e3c5ee0deef9fc8c547d81986d9a1b56073d82de008744452d6541"},
|
||||
{file = "charset_normalizer-3.0.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ab5de034a886f616a5668aa5d098af2b5385ed70142090e2a31bcbd0af0fdb3d"},
|
||||
{file = "charset_normalizer-3.0.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9cb3032517f1627cc012dbc80a8ec976ae76d93ea2b5feaa9d2a5b8882597579"},
|
||||
{file = "charset_normalizer-3.0.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:608862a7bf6957f2333fc54ab4399e405baad0163dc9f8d99cb236816db169d4"},
|
||||
{file = "charset_normalizer-3.0.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:0f438ae3532723fb6ead77e7c604be7c8374094ef4ee2c5e03a3a17f1fca256c"},
|
||||
{file = "charset_normalizer-3.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:356541bf4381fa35856dafa6a965916e54bed415ad8a24ee6de6e37deccf2786"},
|
||||
{file = "charset_normalizer-3.0.1-cp39-cp39-win32.whl", hash = "sha256:39cf9ed17fe3b1bc81f33c9ceb6ce67683ee7526e65fde1447c772afc54a1bb8"},
|
||||
{file = "charset_normalizer-3.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:0a11e971ed097d24c534c037d298ad32c6ce81a45736d31e0ff0ad37ab437d59"},
|
||||
{file = "charset_normalizer-3.0.1-py3-none-any.whl", hash = "sha256:7e189e2e1d3ed2f4aebabd2d5b0f931e883676e51c7624826e0a4e5fe8a0bf24"},
|
||||
]
|
||||
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"},
|
||||
]
|
||||
flasgger = [
|
||||
{file = "flasgger-0.9.5-py2.py3-none-any.whl", hash = "sha256:0603941cf4003626b4ee551ca87331f1d17b8eecce500ccf1a1f1d3a332fc94a"},
|
||||
{file = "flasgger-0.9.5.tar.gz", hash = "sha256:6ebea406b5beecd77e8da42550f380d4d05a6107bc90b69ce9e77aee7612e2d0"},
|
||||
]
|
||||
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"},
|
||||
]
|
||||
flask-restful = [
|
||||
{file = "Flask-RESTful-0.3.9.tar.gz", hash = "sha256:ccec650b835d48192138c85329ae03735e6ced58e9b2d9c2146d6c84c06fa53e"},
|
||||
{file = "Flask_RESTful-0.3.9-py2.py3-none-any.whl", hash = "sha256:4970c49b6488e46c520b325f54833374dc2b98e211f1b272bd4b0c516232afe2"},
|
||||
]
|
||||
gunicorn = [
|
||||
{file = "gunicorn-20.1.0-py3-none-any.whl", hash = "sha256:9dcc4547dbb1cb284accfb15ab5667a0e5d1881cc443e0677b4882a4067a807e"},
|
||||
{file = "gunicorn-20.1.0.tar.gz", hash = "sha256:e0a968b5ba15f8a328fdfd7ab1fcb5af4470c28aaf7e55df02a99bc13138e6e8"},
|
||||
]
|
||||
idna = [
|
||||
{file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"},
|
||||
{file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"},
|
||||
]
|
||||
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"},
|
||||
]
|
||||
jsonschema = [
|
||||
{file = "jsonschema-4.17.3-py3-none-any.whl", hash = "sha256:a870ad254da1a8ca84b6a2905cac29d265f805acc57af304784962a2aa6508f6"},
|
||||
{file = "jsonschema-4.17.3.tar.gz", hash = "sha256:0f864437ab8b6076ba6707453ef8f98a6a0d512a80e93f8abdb676f737ecb60d"},
|
||||
]
|
||||
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"},
|
||||
]
|
||||
marshmallow = [
|
||||
{file = "marshmallow-3.19.0-py3-none-any.whl", hash = "sha256:93f0958568da045b0021ec6aeb7ac37c81bfcccbb9a0e7ed8559885070b3a19b"},
|
||||
{file = "marshmallow-3.19.0.tar.gz", hash = "sha256:90032c0fd650ce94b6ec6dc8dfeb0e3ff50c144586462c389b81a07205bedb78"},
|
||||
]
|
||||
mistune = [
|
||||
{file = "mistune-2.0.5-py2.py3-none-any.whl", hash = "sha256:bad7f5d431886fcbaf5f758118ecff70d31f75231b34024a1341120340a65ce8"},
|
||||
{file = "mistune-2.0.5.tar.gz", hash = "sha256:0246113cb2492db875c6be56974a7c893333bf26cd92891c85f63151cee09d34"},
|
||||
]
|
||||
mypy-extensions = [
|
||||
{file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"},
|
||||
{file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"},
|
||||
]
|
||||
mypy1989 = [
|
||||
{file = "mypy1989-0.0.2-py3-none-any.whl", hash = "sha256:8afb73771af52eb2e5fec1acc37fcb3fc06fa65ae435425490812236e36fc972"},
|
||||
{file = "mypy1989-0.0.2.tar.gz", hash = "sha256:91c114437a4ca15e512338e65b83f3a0ecacee9f0b8448e5be40c7741f0d1826"},
|
||||
]
|
||||
werkzeug = [
|
||||
{file = "Werkzeug-2.0.3-py3-none-any.whl", hash = "sha256:1421ebfc7648a39a5c58c601b154165d05cf47a3cd0ccb70857cbdacf6c8f2b8"},
|
||||
{file = "Werkzeug-2.0.3.tar.gz", hash = "sha256:b863f8ff057c522164b6067c9e28b041161b4be5ba4d0daceeaa50a163822d3c"},
|
||||
packaging = [
|
||||
{file = "packaging-23.0-py3-none-any.whl", hash = "sha256:714ac14496c3e68c99c29b00845f7a2b85f3bb6f1078fd9f72fd20f0570002b2"},
|
||||
{file = "packaging-23.0.tar.gz", hash = "sha256:b6ad297f8907de0fa2fe1ccbd26fdaf387f5f47c7275fedf8cce89f99446cf97"},
|
||||
]
|
||||
pathspec = [
|
||||
{file = "pathspec-0.11.0-py3-none-any.whl", hash = "sha256:3a66eb970cbac598f9e5ccb5b2cf58930cd8e3ed86d393d541eaf2d8b1705229"},
|
||||
{file = "pathspec-0.11.0.tar.gz", hash = "sha256:64d338d4e0914e91c1792321e6907b5a593f1ab1851de7fc269557a21b30ebbc"},
|
||||
]
|
||||
platformdirs = [
|
||||
{file = "platformdirs-3.0.0-py3-none-any.whl", hash = "sha256:b1d5eb14f221506f50d6604a561f4c5786d9e80355219694a1b244bcd96f4567"},
|
||||
{file = "platformdirs-3.0.0.tar.gz", hash = "sha256:8a1228abb1ef82d788f74139988b137e78692984ec7b08eaa6c65f1723af28f9"},
|
||||
]
|
||||
pyrsistent = [
|
||||
{file = "pyrsistent-0.19.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:20460ac0ea439a3e79caa1dbd560344b64ed75e85d8703943e0b66c2a6150e4a"},
|
||||
{file = "pyrsistent-0.19.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c18264cb84b5e68e7085a43723f9e4c1fd1d935ab240ce02c0324a8e01ccb64"},
|
||||
{file = "pyrsistent-0.19.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b774f9288dda8d425adb6544e5903f1fb6c273ab3128a355c6b972b7df39dcf"},
|
||||
{file = "pyrsistent-0.19.3-cp310-cp310-win32.whl", hash = "sha256:5a474fb80f5e0d6c9394d8db0fc19e90fa540b82ee52dba7d246a7791712f74a"},
|
||||
{file = "pyrsistent-0.19.3-cp310-cp310-win_amd64.whl", hash = "sha256:49c32f216c17148695ca0e02a5c521e28a4ee6c5089f97e34fe24163113722da"},
|
||||
{file = "pyrsistent-0.19.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f0774bf48631f3a20471dd7c5989657b639fd2d285b861237ea9e82c36a415a9"},
|
||||
{file = "pyrsistent-0.19.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ab2204234c0ecd8b9368dbd6a53e83c3d4f3cab10ecaf6d0e772f456c442393"},
|
||||
{file = "pyrsistent-0.19.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e42296a09e83028b3476f7073fcb69ffebac0e66dbbfd1bd847d61f74db30f19"},
|
||||
{file = "pyrsistent-0.19.3-cp311-cp311-win32.whl", hash = "sha256:64220c429e42a7150f4bfd280f6f4bb2850f95956bde93c6fda1b70507af6ef3"},
|
||||
{file = "pyrsistent-0.19.3-cp311-cp311-win_amd64.whl", hash = "sha256:016ad1afadf318eb7911baa24b049909f7f3bb2c5b1ed7b6a8f21db21ea3faa8"},
|
||||
{file = "pyrsistent-0.19.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c4db1bd596fefd66b296a3d5d943c94f4fac5bcd13e99bffe2ba6a759d959a28"},
|
||||
{file = "pyrsistent-0.19.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aeda827381f5e5d65cced3024126529ddc4289d944f75e090572c77ceb19adbf"},
|
||||
{file = "pyrsistent-0.19.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:42ac0b2f44607eb92ae88609eda931a4f0dfa03038c44c772e07f43e738bcac9"},
|
||||
{file = "pyrsistent-0.19.3-cp37-cp37m-win32.whl", hash = "sha256:e8f2b814a3dc6225964fa03d8582c6e0b6650d68a232df41e3cc1b66a5d2f8d1"},
|
||||
{file = "pyrsistent-0.19.3-cp37-cp37m-win_amd64.whl", hash = "sha256:c9bb60a40a0ab9aba40a59f68214eed5a29c6274c83b2cc206a359c4a89fa41b"},
|
||||
{file = "pyrsistent-0.19.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a2471f3f8693101975b1ff85ffd19bb7ca7dd7c38f8a81701f67d6b4f97b87d8"},
|
||||
{file = "pyrsistent-0.19.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc5d149f31706762c1f8bda2e8c4f8fead6e80312e3692619a75301d3dbb819a"},
|
||||
{file = "pyrsistent-0.19.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3311cb4237a341aa52ab8448c27e3a9931e2ee09561ad150ba94e4cfd3fc888c"},
|
||||
{file = "pyrsistent-0.19.3-cp38-cp38-win32.whl", hash = "sha256:f0e7c4b2f77593871e918be000b96c8107da48444d57005b6a6bc61fb4331b2c"},
|
||||
{file = "pyrsistent-0.19.3-cp38-cp38-win_amd64.whl", hash = "sha256:c147257a92374fde8498491f53ffa8f4822cd70c0d85037e09028e478cababb7"},
|
||||
{file = "pyrsistent-0.19.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b735e538f74ec31378f5a1e3886a26d2ca6351106b4dfde376a26fc32a044edc"},
|
||||
{file = "pyrsistent-0.19.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99abb85579e2165bd8522f0c0138864da97847875ecbd45f3e7e2af569bfc6f2"},
|
||||
{file = "pyrsistent-0.19.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3a8cb235fa6d3fd7aae6a4f1429bbb1fec1577d978098da1252f0489937786f3"},
|
||||
{file = "pyrsistent-0.19.3-cp39-cp39-win32.whl", hash = "sha256:c74bed51f9b41c48366a286395c67f4e894374306b197e62810e0fdaf2364da2"},
|
||||
{file = "pyrsistent-0.19.3-cp39-cp39-win_amd64.whl", hash = "sha256:878433581fc23e906d947a6814336eee031a00e6defba224234169ae3d3d6a98"},
|
||||
{file = "pyrsistent-0.19.3-py3-none-any.whl", hash = "sha256:ccf0d6bd208f8111179f0c26fdf84ed7c3891982f2edaeae7422575f47e66b64"},
|
||||
{file = "pyrsistent-0.19.3.tar.gz", hash = "sha256:1a2994773706bbb4995c31a97bc94f1418314923bd1048c6d964837040376440"},
|
||||
]
|
||||
pytz = [
|
||||
{file = "pytz-2022.7.1-py2.py3-none-any.whl", hash = "sha256:78f4f37d8198e0627c5f1143240bb0206b8691d8d7ac6d78fee88b78733f8c4a"},
|
||||
{file = "pytz-2022.7.1.tar.gz", hash = "sha256:01a0681c4b9684a28304615eba55d1ab31ae00bf68ec157ec3708a8182dbbcd0"},
|
||||
]
|
||||
pyyaml = [
|
||||
{file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"},
|
||||
{file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"},
|
||||
{file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"},
|
||||
{file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"},
|
||||
{file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"},
|
||||
{file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"},
|
||||
{file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"},
|
||||
{file = "PyYAML-6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358"},
|
||||
{file = "PyYAML-6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1"},
|
||||
{file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d"},
|
||||
{file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f"},
|
||||
{file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782"},
|
||||
{file = "PyYAML-6.0-cp311-cp311-win32.whl", hash = "sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7"},
|
||||
{file = "PyYAML-6.0-cp311-cp311-win_amd64.whl", hash = "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf"},
|
||||
{file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"},
|
||||
{file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"},
|
||||
{file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"},
|
||||
{file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"},
|
||||
{file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"},
|
||||
{file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"},
|
||||
{file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"},
|
||||
{file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"},
|
||||
{file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"},
|
||||
{file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"},
|
||||
{file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"},
|
||||
{file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"},
|
||||
{file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"},
|
||||
{file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"},
|
||||
{file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"},
|
||||
{file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"},
|
||||
{file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"},
|
||||
{file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"},
|
||||
{file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"},
|
||||
{file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"},
|
||||
{file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"},
|
||||
{file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"},
|
||||
{file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"},
|
||||
{file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"},
|
||||
{file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"},
|
||||
{file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"},
|
||||
]
|
||||
requests = [
|
||||
{file = "requests-2.28.2-py3-none-any.whl", hash = "sha256:64299f4909223da747622c030b781c0d7811e359c37124b4bd368fb8c6518baa"},
|
||||
{file = "requests-2.28.2.tar.gz", hash = "sha256:98b1b2782e3c6c4904938b84c0eb932721069dfdb9134313beff7c83c2df24bf"},
|
||||
]
|
||||
setuptools = [
|
||||
{file = "setuptools-65.6.3-py3-none-any.whl", hash = "sha256:57f6f22bde4e042978bcd50176fdb381d7c21a9efa4041202288d3737a0c6a54"},
|
||||
{file = "setuptools-65.6.3.tar.gz", hash = "sha256:a7620757bf984b58deaf32fc8a4577a9bbc0850cf92c20e1ce41c38c19e5fb75"},
|
||||
]
|
||||
six = [
|
||||
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
|
||||
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
|
||||
]
|
||||
tomli = [
|
||||
{file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
|
||||
{file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
|
||||
]
|
||||
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"},
|
||||
]
|
||||
urllib3 = [
|
||||
{file = "urllib3-1.26.14-py2.py3-none-any.whl", hash = "sha256:75edcdc2f7d85b137124a6c3c9fc3933cdeaa12ecb9a6a959f22797a0feca7e1"},
|
||||
{file = "urllib3-1.26.14.tar.gz", hash = "sha256:076907bf8fd355cde77728471316625a4d2f7e713c125f51953bb5b3eecf4f72"},
|
||||
]
|
||||
werkzeug = [
|
||||
{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"},
|
||||
]
|
||||
|
|
|
@ -8,6 +8,14 @@ authors = ["Félix Baylac-Jacqué <felix@alternativebit.fr>"]
|
|||
python = "^3.9"
|
||||
Flask = "^2.0.3"
|
||||
gunicorn = "^20.1.0"
|
||||
typing-extensions = "^4.4.0"
|
||||
black = "^23.1.0"
|
||||
requests = "^2.28.2"
|
||||
apispec = "^6.1.0"
|
||||
apispec-webframeworks = "^0.5.2"
|
||||
flasgger = "^0.9.5"
|
||||
flask-restful = "^0.3.9"
|
||||
marshmallow = "^3.19.0"
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
mypy1989 = "^0.0.2"
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
set -euo pipefail
|
||||
|
||||
export PATH="/usr/bin/:/bin/:/srv/www/Axione-FTTH-Test/.poetry/bin/"
|
||||
POETRY_BIN=${REPO_DIR-/srv/www/ftth-ipe-map}/.poetry/bin
|
||||
|
||||
export PATH="/usr/bin/:/bin/:$POETRY_BIN"
|
||||
poetry install
|
||||
poetry run gunicorn -b "localhost:${PORT}" --timeout 120 'main:app'
|
||||
poetry run gunicorn -b "localhost:${PORT}" --timeout 120 'main:app'
|
||||
|
|
BIN
webapp/static/icons/marker-icon-black.png
Normal file
BIN
webapp/static/icons/marker-icon-black.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
BIN
webapp/static/icons/marker-icon-green.png
Normal file
BIN
webapp/static/icons/marker-icon-green.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.8 KiB |
BIN
webapp/static/icons/marker-icon-orange.png
Normal file
BIN
webapp/static/icons/marker-icon-orange.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.8 KiB |
BIN
webapp/static/icons/marker-icon-red.png
Normal file
BIN
webapp/static/icons/marker-icon-red.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.8 KiB |
|
@ -15,9 +15,46 @@ body {
|
|||
}
|
||||
|
||||
#btn-load-elig-data {
|
||||
top: 4em;
|
||||
right: 0;
|
||||
top: 10em;
|
||||
left: 1em;
|
||||
position: fixed;
|
||||
z-index: 1;
|
||||
padding: .5em;
|
||||
}
|
||||
|
||||
.deployeeAquilenet {
|
||||
display: inline;
|
||||
color: green;
|
||||
}
|
||||
.deployeeFDN {
|
||||
display: inline;
|
||||
color: orange;
|
||||
}
|
||||
.deployeeAutres {
|
||||
display: inline;
|
||||
color: red;
|
||||
}
|
||||
.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);
|
||||
}
|
||||
}
|
|
@ -1,94 +1,373 @@
|
|||
const minZoomForRequest = 17;
|
||||
let markers = [];
|
||||
function initMap(btn) {
|
||||
let map = L.map('map').setView([46.710, 3.669], 6);
|
||||
L.tileLayer('https://{s}.tile.openstreetmap.fr/osmfr/{z}/{x}/{y}.png', {
|
||||
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
||||
}).addTo(map);
|
||||
map.on("zoom", () => {
|
||||
const minZoomForRequest = 16;
|
||||
const urlADSL = 'https://tools.aquilenet.fr/cgi-bin/recherchend.cgi'
|
||||
const urlTestFTTH = 'https://tools.aquilenet.fr/cgi-bin/test.cgi'
|
||||
|
||||
console.log(map.getZoom());
|
||||
/* We only want to enable the search button when we reached a sufficient zoom level */
|
||||
if (btn.disabled && map.getZoom() >= minZoomForRequest) {
|
||||
btn.disabled = false;
|
||||
btn.title = "Rechercher les données d'éligibilité pour cette zone."
|
||||
}
|
||||
if (!btn.disabled && map.getZoom() < minZoomForRequest) {
|
||||
btn.disabled = true;
|
||||
btn.title = "Veuillez zoomer plus la carte avant de lancer une recherche d'éligibilité.";
|
||||
}
|
||||
});
|
||||
return map;
|
||||
const streetTypeConversion = new Map();
|
||||
streetTypeConversion.set("aire", "aire")
|
||||
streetTypeConversion.set("allée", "all")
|
||||
streetTypeConversion.set("allee", "all")
|
||||
streetTypeConversion.set("avenue", "av")
|
||||
streetTypeConversion.set("base", "base")
|
||||
streetTypeConversion.set("boulevard", "bd")
|
||||
streetTypeConversion.set("cami", "cami")
|
||||
streetTypeConversion.set("carrefour", "car")
|
||||
streetTypeConversion.set("chemin", "che")
|
||||
streetTypeConversion.set("cheminement", "chem")
|
||||
streetTypeConversion.set("chaussée", "chs")
|
||||
streetTypeConversion.set("cité", "cite")
|
||||
streetTypeConversion.set("cite", "cite")
|
||||
streetTypeConversion.set("clos", "clos")
|
||||
streetTypeConversion.set("coin", "coin")
|
||||
streetTypeConversion.set("corniche", "cor")
|
||||
streetTypeConversion.set("cote", "cote")
|
||||
streetTypeConversion.set("cour", "cour")
|
||||
streetTypeConversion.set("cours", "crs")
|
||||
streetTypeConversion.set("domaine", "dom")
|
||||
streetTypeConversion.set("descente", "dsc")
|
||||
streetTypeConversion.set("ecart", "eca")
|
||||
streetTypeConversion.set("esplanade", "esp")
|
||||
streetTypeConversion.set("faubourg", "fg")
|
||||
streetTypeConversion.set("gare", "gare")
|
||||
streetTypeConversion.set("grande rue", "gr")
|
||||
streetTypeConversion.set("hameau", "ham")
|
||||
streetTypeConversion.set("halle", "hle")
|
||||
streetTypeConversion.set("ilôt", "ilot")
|
||||
streetTypeConversion.set("impasse", "imp")
|
||||
streetTypeConversion.set("lieu dit", "ld")
|
||||
streetTypeConversion.set("lotissement", "lot")
|
||||
streetTypeConversion.set("marché", "mar")
|
||||
streetTypeConversion.set("montée", "mte")
|
||||
streetTypeConversion.set("parc", "parc")
|
||||
streetTypeConversion.set("passage", "pas")
|
||||
streetTypeConversion.set("place", "pl")
|
||||
streetTypeConversion.set("plan", "plan")
|
||||
streetTypeConversion.set("plaine", "pln")
|
||||
streetTypeConversion.set("plateau", "plt")
|
||||
streetTypeConversion.set("pont", "pont")
|
||||
streetTypeConversion.set("port", "port")
|
||||
streetTypeConversion.set("promenade", "pro")
|
||||
streetTypeConversion.set("parvis", "prv")
|
||||
streetTypeConversion.set("quartier", "qua")
|
||||
streetTypeConversion.set("quai", "quai")
|
||||
streetTypeConversion.set("résidence", "res")
|
||||
streetTypeConversion.set("residence", "res")
|
||||
streetTypeConversion.set("ruelle", "rle")
|
||||
streetTypeConversion.set("rocade", "roc")
|
||||
streetTypeConversion.set("rond point", "rpt")
|
||||
streetTypeConversion.set("route", "rte")
|
||||
streetTypeConversion.set("rue", "rue")
|
||||
streetTypeConversion.set("sentier", "sen")
|
||||
streetTypeConversion.set("sente", "sen")
|
||||
streetTypeConversion.set("square", "sq")
|
||||
streetTypeConversion.set("tour", "tour")
|
||||
streetTypeConversion.set("terre-plein", "tpl")
|
||||
streetTypeConversion.set("traverse", "tra")
|
||||
streetTypeConversion.set("villa", "vla")
|
||||
streetTypeConversion.set("village", "vlge ")
|
||||
streetTypeConversion.set("voie", "voie")
|
||||
streetTypeConversion.set("zone artisanale", "za")
|
||||
streetTypeConversion.set("zone d'aménagement concerté", "zac")
|
||||
streetTypeConversion.set("zone d'aménagement différé", "zad")
|
||||
streetTypeConversion.set("zone industrielle", "zi")
|
||||
streetTypeConversion.set("zone", "zone")
|
||||
|
||||
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.
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
let x = parseFloat(params.get('x'));
|
||||
let y = parseFloat(params.get('y'));
|
||||
let z = parseInt(params.get('z'));
|
||||
let map = L.map('map');
|
||||
if (x && y && z) {
|
||||
map.setView([y, x], z);
|
||||
fetchEligData(map);
|
||||
} else {
|
||||
map.setView([46.710, 3.669], 6);
|
||||
}
|
||||
|
||||
L.tileLayer('https://{s}.tile.openstreetmap.fr/osmfr/{z}/{x}/{y}.png', {
|
||||
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
|
||||
maxNativeZoom: 19,
|
||||
maxZoom: 19
|
||||
}).addTo(map);
|
||||
|
||||
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);
|
||||
}
|
||||
if (!btn.disabled && map.getZoom() < minZoomForRequest) {
|
||||
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 zoomend moveend", () => {
|
||||
box.setBounds(getRectangleCoord(map))
|
||||
})
|
||||
|
||||
btn.addEventListener("click", () => {
|
||||
getServerBoxBounds(map, box);
|
||||
});
|
||||
|
||||
addEventListener("resize", () => {
|
||||
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,
|
||||
search: async (query) => {
|
||||
if(query.length > 2) {
|
||||
const mapCenter = map.getCenter();
|
||||
const reqUri = `https://photon.komoot.io/api/?q=${encodeURI(query)}&lat=${mapCenter.lat}&lon=${mapCenter.lng}&limit=20&lang=fr`;
|
||||
const source = await fetch(reqUri);
|
||||
const data = await source.json();
|
||||
return data.features;
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
renderResult: (res, props) => {
|
||||
const p = res.properties;
|
||||
if(p.name && p.postcode && p.city && p.county && res.geometry.coordinates && res.geometry.coordinates.length === 2)
|
||||
return `<li ${props}>${p.name} - ${p.postcode} ${p.city}, ${p.county}</li>`;
|
||||
else
|
||||
return "";
|
||||
},
|
||||
onSubmit: async (res) => {
|
||||
const searchInput = document.getElementById('search-addr-autocomplete-input');
|
||||
const p = res.properties;
|
||||
searchInput.value = `${p.name} - ${p.postcode} ${p.city}, ${p.county}`;
|
||||
// We already filtered out the result not having strictly 2 coordinates at item display
|
||||
map.setView([res.geometry.coordinates[1],res.geometry.coordinates[0]], 19);
|
||||
fetchEligData(map);
|
||||
}
|
||||
};
|
||||
const autocompleteAddr = new Autocomplete("#search-addr-autocomplete", autocompleteOptions);
|
||||
return autocompleteAddr;
|
||||
const autocompleteOptions = {
|
||||
debounceTime: 300,
|
||||
search: async (query) => {
|
||||
if (query.length > 2) {
|
||||
const mapCenter = map.getCenter();
|
||||
const reqUri = `https://photon.komoot.io/api/?q=${encodeURI(query)}&lat=${mapCenter.lat}&lon=${mapCenter.lng}&limit=20&lang=fr`;
|
||||
const source = await fetch(reqUri);
|
||||
const data = await source.json();
|
||||
return data.features;
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
renderResult: (res, props) => {
|
||||
const p = res.properties;
|
||||
if (p.name && p.postcode && p.city && p.county && res.geometry.coordinates && res.geometry.coordinates.length === 2)
|
||||
return `<li ${props}>${p.name} - ${p.postcode} ${p.city}, ${p.county}</li>`;
|
||||
else
|
||||
return "";
|
||||
},
|
||||
onSubmit: async (res) => {
|
||||
const searchInput = document.getElementById('search-addr-autocomplete-input');
|
||||
const p = res.properties;
|
||||
searchInput.value = `${p.name} - ${p.postcode} ${p.city}, ${p.county}`;
|
||||
// We already filtered out the result not having strictly 2 coordinates at item display
|
||||
map.setView([res.geometry.coordinates[1], res.geometry.coordinates[0]], 19);
|
||||
fetchEligData(map);
|
||||
}
|
||||
};
|
||||
const autocompleteAddr = new Autocomplete("#search-addr-autocomplete", autocompleteOptions);
|
||||
return autocompleteAddr;
|
||||
}
|
||||
|
||||
function updateEligData(map, eligData) {
|
||||
markers.map(marker => map.removeLayer(marker));
|
||||
let buildings = eligData.buildings;
|
||||
markers = buildings.map(building => {
|
||||
const latlng = new L.latLng(building.y, building.x);
|
||||
const addrImm = `${building.numVoieImm} ${building.typeVoieImm} ${building.nomVoieImm}`
|
||||
const marker = new L.marker(latlng)
|
||||
.bindPopup(`${addrImm}<br/>Etat: ${building.etatImm}<br/>Code Immeuble: ${building.idImm}`);
|
||||
map.addLayer(marker);
|
||||
return marker
|
||||
});
|
||||
let buildings = eligData.buildings;
|
||||
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 = ``
|
||||
// On construit l'appel API pour le test FTTH, on indique si c'est éligible Kosc et/ou Axione dans l'url
|
||||
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 = `<p class=deployeeAquilenet>Fibre deployée mais ne sera commandable qu\'à partir du ${building.aquilenetEligStatus.dateCommandable}</p>`
|
||||
} else {
|
||||
messageElig = `<p class=deployeeAquilenet>Fibre deployée et disponible par Aquilenet !</p>`
|
||||
const zip = encodeURIComponent(building.codePostal);
|
||||
const idImm = encodeURIComponent(building.idImm);
|
||||
messageElig += `<br/><a href=${urlTestFTTH}?ftth=1&axione=1&adsltel=NOUVEAU&cp=${zip}&refimmeuble=${idImm}` +
|
||||
` target="_blank">Tester l'éligibilité</a>`
|
||||
colorMarker = 'green'
|
||||
}
|
||||
// pas de données Axione mais éligible peut-être Kosc ou au moins ARCEP
|
||||
// Enfin on affiche un lien vers le test d'éligibilté FTTH avec Kosc & Netwo
|
||||
} else if (building.fdnEligStatus.isEligible || building.othersEligStatus.isEligible) {
|
||||
messageElig = `<p class=deployeeFDN>Fibre deployee mais pas chez Axione !`
|
||||
messageElig += `<br/><a href=${eligTestApi} target="_blank">Tester l'eligibilite par Kosc, Netwo et Bouygues</a></p>`
|
||||
colorMarker = 'orange'
|
||||
} else {
|
||||
messageElig = `<p class=nonDeployee>Fibre non deployee :(</p>`
|
||||
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 += `<br/><a href=${urlADSL}?zip=${zip}&city=${comm}&street=${street}&street_nb=${street_nb}&gps=&do=1&submit=Valider` +
|
||||
`>Tester ADSL a cette adresse</a><br/>Si la fibre a été recemment installée chez vous, il se pourrait que ce test soit erroné, <a href=${eligTestApi}>cliquez ici pour tout de même tester l'eligibilité fibre</a>`
|
||||
if (building.othersEligStatus.reasonNotEligible != "") {
|
||||
messageElig += `<br/><br/>Status general ARCEP: ${building.othersEligStatus.reasonNotEligible}`
|
||||
}
|
||||
}
|
||||
// Si pas d'éligibilité fibre, on affiche la raison si elle existe
|
||||
if (building.aquilenetEligStatus.reasonNotEligible != "") {
|
||||
messageElig += `<br/> Pour Aquilenet, raison non eligible: ${building.aquilenetEligStatus.reasonNotEligible}`
|
||||
if (building.aquilenetEligStatus.dateCommandable != "") {
|
||||
messageElig += ` (date commandable: ${building.aquilenetEligStatus.dateCommandable})`
|
||||
}
|
||||
}
|
||||
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]
|
||||
});
|
||||
// if (building.othersEligStatus.isEligible) {
|
||||
// messageElig += `<br/><a target="_blank" href=/eligibilite/netwo?lat=${building.y}&lng=${building.x}` +
|
||||
// `>Tester d'autres offres via Netwo</a>`
|
||||
// }
|
||||
const marker = new L.marker(latlng, {
|
||||
icon: markerIcon,
|
||||
zIndexOffset: - building.etat_imm_priority
|
||||
})
|
||||
.bindPopup(`${addrImm}<br/>${building.codePostal} ${building.commune}` +
|
||||
`<br/><br/>${messageElig}<br/><br/>Ref Immeuble: ${building.idImm}`, {
|
||||
maxWidth: 560
|
||||
});
|
||||
map.addLayer(marker);
|
||||
markers.set(building.idImm, marker)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function updateUrl(map) {
|
||||
const c = map.getCenter();
|
||||
history.replaceState({}, "", encodeURI(`?x=${c.lng}&y=${c.lat}&z=${map.getZoom()}`));
|
||||
}
|
||||
|
||||
async function fetchEligData(map) {
|
||||
const zoom = map.getZoom();
|
||||
if (zoom >= minZoomForRequest) {
|
||||
const bounds = map.getBounds();
|
||||
const sw = bounds.getSouthWest();
|
||||
const ne = bounds.getNorthEast();
|
||||
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 zoom = map.getZoom();
|
||||
if (zoom >= minZoomForRequest) {
|
||||
const bounds = map.getBounds();
|
||||
const sw = bounds.getSouthWest();
|
||||
const ne = bounds.getNorthEast();
|
||||
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 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);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
return btn;
|
||||
const btn = document.getElementById("btn-load-elig-data");
|
||||
btn.disabled = true;
|
||||
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 = "Actualiser la recherche dans cette zone"
|
||||
btn.innerHTML = "Actualiser";
|
||||
}
|
||||
|
||||
function hideBtn(btn) {
|
||||
btn.disabled = true;
|
||||
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);
|
||||
setBtnListener(btn, map);
|
||||
|
||||
// Init a limits box that shows area where data will be fetched
|
||||
initLimitsBox(map, btn);
|
||||
|
|
|
@ -18,14 +18,13 @@
|
|||
</head>
|
||||
|
||||
<body>
|
||||
<button id="btn-load-elig-data" type="button" disabled>Zoomez sur la carte</button>
|
||||
<div class="autocomplete" id="search-addr-autocomplete">
|
||||
<input id="search-addr-autocomplete-input" class="autocomplete-input"
|
||||
spellcheck="false" autocorrect="off"t e autocomplete="off"
|
||||
autocapitalize="off" placeholder="Votre Adresse"/>
|
||||
<ul class="autocomplete-result-list"/>
|
||||
</div>
|
||||
<button id="btn-load-elig-data" type="button">Récupérer les données d'éligibilité
|
||||
pour cette zone</button>
|
||||
<div id="map"/>
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function(event) {
|
||||
|
|
30
webapp/test-ftth-ipe-map.service
Normal file
30
webapp/test-ftth-ipe-map.service
Normal file
|
@ -0,0 +1,30 @@
|
|||
[Unit]
|
||||
After=network.target
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
|
||||
[Service]
|
||||
Environment="PORT=6675"
|
||||
Environment="CONFIG=/etc/test-ftth-ipe-map/conf.ini"
|
||||
Environment="REPO_DIR=/srv/www/test-ftth-ipe-map"
|
||||
ConfigurationDirectory=test-ftth-ipe-map
|
||||
WorkingDirectory=/srv/www/test-ftth-ipe-map/webapp/
|
||||
ExecStart=/srv/www/test-ftth-ipe-map/webapp/startGunicornService
|
||||
User=ftth-ipe-map
|
||||
Group=ftth-ipe-map
|
||||
Restart=on-failure
|
||||
RestartSec=30
|
||||
|
||||
# Sandboxing
|
||||
#ProtectSystem=strict
|
||||
#ProtectHome=tmpfs
|
||||
PrivateTmp=true
|
||||
PrivateDevices=true
|
||||
ProtectClock=true
|
||||
ProtectKernelTunables=true
|
||||
ProtectKernelModules=true
|
||||
ProtectKernelLogs=true
|
||||
ProtectControlGroups=true
|
||||
RestrictAddressFamilies=AF_INET
|
||||
RestrictRealtime=true
|
13
webapp/tests/test_event_stream/package-lock.json
generated
Normal file
13
webapp/tests/test_event_stream/package-lock.json
generated
Normal file
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"name": "test_event_stream",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"eventsource": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/eventsource/-/eventsource-2.0.2.tgz",
|
||||
"integrity": "sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA=="
|
||||
}
|
||||
}
|
||||
}
|
16
webapp/tests/test_event_stream/package.json
Normal file
16
webapp/tests/test_event_stream/package.json
Normal file
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"name": "test_event_stream",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "test_api_netwo.js",
|
||||
"dependencies": {
|
||||
"eventsource": "^2.0.2"
|
||||
},
|
||||
"devDependencies": {},
|
||||
"scripts": {
|
||||
"start": "node test_api_netwo.js",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC"
|
||||
}
|
42
webapp/tests/test_event_stream/test_api_netwo.js
Normal file
42
webapp/tests/test_event_stream/test_api_netwo.js
Normal file
|
@ -0,0 +1,42 @@
|
|||
|
||||
//import { EventSource } from 'eventsource'
|
||||
var EventSource = require('eventsource')
|
||||
|
||||
function handleNetwo(data) {
|
||||
console.log("handle data")
|
||||
console.log(data)
|
||||
}
|
||||
|
||||
function startNetwo(refimmeuble) {
|
||||
|
||||
const evtSource = new EventSource(encodeURI(`http://localhost:5000/eligibilite/netwo?ref_imb=${refimmeuble}`));
|
||||
|
||||
evtSource.onmessage = function (event) {
|
||||
try {
|
||||
console.log("got data")
|
||||
|
||||
data = JSON.parse(event.data)
|
||||
|
||||
|
||||
handleNetwo(data)
|
||||
if (data.eligDone) {
|
||||
console.log("elig done, stop stream")
|
||||
evtSource.close();
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("error parsing data, stop stream: ", error)
|
||||
evtSource.close();
|
||||
}
|
||||
}
|
||||
evtSource.onerror = function (event) {
|
||||
console.log("in onerror stop stream: ",event)
|
||||
evtSource.close();
|
||||
}
|
||||
}
|
||||
if (process.argv.length < 3) {
|
||||
console.log("Need to specify imb param (e.g IMB/33063/S/A8DA )")
|
||||
process.exit(1)
|
||||
}
|
||||
ref_imb = process.argv[2]
|
||||
|
||||
startNetwo(ref_imb)
|
Loading…
Reference in a new issue