You've already forked donnees_meteo
Réorganisation
This commit is contained in:
207
docs/01 - Installation, configuration et tests/index.md
Normal file
207
docs/01 - Installation, configuration et tests/index.md
Normal file
@@ -0,0 +1,207 @@
|
||||
# Installation, configuration et tests
|
||||
|
||||
## Installation de l'environnement de base
|
||||
|
||||
Après avoir cloné le dépôt :
|
||||
|
||||
```shell
|
||||
python3 -m venv .venv
|
||||
source .venv/bin/activate
|
||||
python -m pip install --upgrade pip
|
||||
pip install -r requirements.txt
|
||||
python -c "import pandas, influxdb_client, sklearn; print('OK')"
|
||||
```
|
||||
|
||||
- On installe l'environnement virtuel de python
|
||||
- On entre dans cet environnement
|
||||
- On met à jour le gestionnaire de paquets pip
|
||||
- On installe les dépendances définies dans `requirements.txt`
|
||||
- On vérifie que les dépendances sont correctement installées
|
||||
|
||||
## Configuration
|
||||
|
||||
```shell
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
On copie le fichier de configuration d'exemple, puis on l'ouvre pour l'adapter à notre cas.
|
||||
|
||||
- `INFLUXDB_URL` : URL de l'api du serveur InfluxDB2 (cela inclue probablement le port 8086)
|
||||
- `INFLUXDB_TOKEN` : le jeton d'authentification à créer dans votre compte InfluxDB2
|
||||
- `INFLUXDB_ORG` : l'organisation à laquelle le token est rattaché
|
||||
- `INFLUXDB_BUCKET` : le nom du bucket dans lequel les données sont stockées
|
||||
- `STATION_LATITUDE` : latitude GPS de la station météo
|
||||
- `STATION_LONGITUDE` : longitude GPS de la station météo
|
||||
- `STATION_ELEVATION` : altitude de la station météo
|
||||
|
||||
## Tests de l'environnement de travail
|
||||
|
||||
```shell
|
||||
python "docs/01 - Installation, configuration et tests/scripts/test_influx_connection.py"
|
||||
```
|
||||
|
||||
```output
|
||||
Configuration InfluxDB chargée :
|
||||
URL : http://10.0.3.2:8086
|
||||
Org : Dern
|
||||
Bucket : weather
|
||||
|
||||
→ Ping du serveur InfluxDB…
|
||||
✔ Ping OK
|
||||
→ Requête de test sur le bucket…
|
||||
✔ Requête de test réussie : 18 table(s), 58 enregistrement(s) trouvés.
|
||||
Exemple de point :
|
||||
time : 2025-11-16 22:30:50.263360+00:00
|
||||
measurement : %
|
||||
field : device_class_str
|
||||
value : humidity
|
||||
```
|
||||
|
||||
Ensuite, on peut demander à InfluxDB de nous détailler ce qu'il stocke :
|
||||
|
||||
```shell
|
||||
python "docs/01 - Installation, configuration et tests/scripts/test_influx_schema.py"
|
||||
```
|
||||
|
||||
```output
|
||||
Bucket InfluxDB : weather
|
||||
|
||||
Measurements disponibles :
|
||||
- %
|
||||
- hPa
|
||||
- km/h
|
||||
- lx
|
||||
- mm/h
|
||||
- °
|
||||
- °C
|
||||
|
||||
Champs pour measurement « % » :
|
||||
- device_class_str (type: unknown)
|
||||
- friendly_name_str (type: unknown)
|
||||
- state_class_str (type: unknown)
|
||||
- value (type: unknown)
|
||||
|
||||
Champs pour measurement « hPa » :
|
||||
- device_class_str (type: unknown)
|
||||
- friendly_name_str (type: unknown)
|
||||
- state_class_str (type: unknown)
|
||||
- value (type: unknown)
|
||||
|
||||
Champs pour measurement « km/h » :
|
||||
- device_class_str (type: unknown)
|
||||
- friendly_name_str (type: unknown)
|
||||
- state_class_str (type: unknown)
|
||||
- value (type: unknown)
|
||||
|
||||
Champs pour measurement « lx » :
|
||||
- device_class_str (type: unknown)
|
||||
- friendly_name_str (type: unknown)
|
||||
- value (type: unknown)
|
||||
|
||||
Champs pour measurement « mm/h » :
|
||||
- device_class_str (type: unknown)
|
||||
- friendly_name_str (type: unknown)
|
||||
- state_class_str (type: unknown)
|
||||
- value (type: unknown)
|
||||
|
||||
Champs pour measurement « ° » :
|
||||
- friendly_name_str (type: unknown)
|
||||
- value (type: unknown)
|
||||
|
||||
Champs pour measurement « °C » :
|
||||
- device_class_str (type: unknown)
|
||||
- friendly_name_str (type: unknown)
|
||||
- state_class_str (type: unknown)
|
||||
- value (type: unknown)
|
||||
```
|
||||
|
||||
Mais pour obtenir les données dont on a besoin, il faut aussi connaitre les entités manipulées par Influx :
|
||||
|
||||
```shell
|
||||
python "docs/01 - Installation, configuration et tests/scripts/test_influx_entities.py"
|
||||
```
|
||||
|
||||
```output
|
||||
Bucket InfluxDB : weather
|
||||
|
||||
Measurement « % »
|
||||
Tag keys :
|
||||
- _field
|
||||
- _measurement
|
||||
- _start
|
||||
- _stop
|
||||
- domain
|
||||
- entity_id
|
||||
entity_id possibles :
|
||||
- station_meteo_bresser_exterieur_humidite_relative
|
||||
|
||||
Measurement « hPa »
|
||||
Tag keys :
|
||||
- _field
|
||||
- _measurement
|
||||
- _start
|
||||
- _stop
|
||||
- domain
|
||||
- entity_id
|
||||
entity_id possibles :
|
||||
- station_meteo_bresser_exterieur_pression_atmospherique
|
||||
|
||||
Measurement « km/h »
|
||||
Tag keys :
|
||||
- _field
|
||||
- _measurement
|
||||
- _start
|
||||
- _stop
|
||||
- domain
|
||||
- entity_id
|
||||
entity_id possibles :
|
||||
- station_meteo_bresser_exterieur_vitesse_du_vent
|
||||
|
||||
Measurement « lx »
|
||||
Tag keys :
|
||||
- _field
|
||||
- _measurement
|
||||
- _start
|
||||
- _stop
|
||||
- domain
|
||||
- entity_id
|
||||
entity_id possibles :
|
||||
- station_meteo_bresser_exterieur_luminance
|
||||
|
||||
Measurement « mm/h »
|
||||
Tag keys :
|
||||
- _field
|
||||
- _measurement
|
||||
- _start
|
||||
- _stop
|
||||
- domain
|
||||
- entity_id
|
||||
entity_id possibles :
|
||||
- station_meteo_bresser_exterieur_precipitations
|
||||
|
||||
Measurement « ° »
|
||||
Tag keys :
|
||||
- _field
|
||||
- _measurement
|
||||
- _start
|
||||
- _stop
|
||||
- domain
|
||||
- entity_id
|
||||
entity_id possibles :
|
||||
- station_meteo_bresser_exterieur_direction_du_vent
|
||||
|
||||
Measurement « °C »
|
||||
Tag keys :
|
||||
- _field
|
||||
- _measurement
|
||||
- _start
|
||||
- _stop
|
||||
- domain
|
||||
- entity_id
|
||||
entity_id possibles :
|
||||
- station_meteo_bresser_exterieur_temperature
|
||||
```
|
||||
|
||||
Ces informations combinées se retrouvent dans le fichier `meteo/station_config.py` et dans `meteo/variables.py`.
|
||||
|
||||
On aurait pu se passer de ces scripts pour déterminer la structure des données stockées dans Influx, mais ils évitent de se reposer sur des intuitions : ici, on demande à Influx de nous donner les informations dont on va avoir besoin au lieu de les deviner.
|
||||
@@ -0,0 +1,65 @@
|
||||
# tests/test_influx_connection.py
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
import sys
|
||||
from contextlib import closing
|
||||
|
||||
from influxdb_client.client.exceptions import InfluxDBError
|
||||
|
||||
|
||||
PROJECT_ROOT = Path(__file__).resolve().parents[3]
|
||||
if str(PROJECT_ROOT) not in sys.path:
|
||||
sys.path.insert(0, str(PROJECT_ROOT))
|
||||
|
||||
from meteo.config import InfluxSettings
|
||||
from meteo.influx_client import create_influx_client, test_basic_query
|
||||
|
||||
|
||||
def main() -> None:
|
||||
"""
|
||||
Teste la communication avec le serveur InfluxDB :
|
||||
|
||||
1. Chargement de la configuration depuis l'environnement.
|
||||
2. Ping du serveur.
|
||||
3. Exécution d'une requête simple sur le bucket configuré.
|
||||
"""
|
||||
settings = InfluxSettings.from_env()
|
||||
|
||||
print("Configuration InfluxDB chargée :")
|
||||
print(f" URL : {settings.url}")
|
||||
print(f" Org : {settings.org}")
|
||||
print(f" Bucket : {settings.bucket}")
|
||||
print()
|
||||
|
||||
# Utilisation de `closing` pour garantir la fermeture du client.
|
||||
with closing(create_influx_client(settings)) as client:
|
||||
print("→ Ping du serveur InfluxDB…")
|
||||
if not client.ping():
|
||||
raise SystemExit("Échec du ping InfluxDB. Vérifiez l'URL et l'état du serveur.")
|
||||
|
||||
print("✔ Ping OK")
|
||||
print("→ Requête de test sur le bucket…")
|
||||
|
||||
tables = test_basic_query(client, settings.bucket)
|
||||
|
||||
# On fait un retour synthétique
|
||||
nb_tables = len(tables)
|
||||
nb_records = sum(len(table.records) for table in tables)
|
||||
print(f"✔ Requête de test réussie : {nb_tables} table(s), {nb_records} enregistrement(s) trouvés.")
|
||||
|
||||
if nb_records == 0:
|
||||
print("⚠ Le bucket est accessible, mais aucune donnée sur la dernière heure.")
|
||||
else:
|
||||
# Affiche un aperçu de la première table / premier record
|
||||
first_table = tables[0]
|
||||
first_record = first_table.records[0]
|
||||
print("Exemple de point :")
|
||||
print(f" time : {first_record.get_time()}")
|
||||
print(f" measurement : {first_record.get_measurement()}")
|
||||
print(f" field : {first_record.get_field()}")
|
||||
print(f" value : {first_record.get_value()}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,68 @@
|
||||
# tests/test_influx_entities.py
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
import sys
|
||||
from contextlib import closing
|
||||
|
||||
|
||||
PROJECT_ROOT = Path(__file__).resolve().parents[3]
|
||||
if str(PROJECT_ROOT) not in sys.path:
|
||||
sys.path.insert(0, str(PROJECT_ROOT))
|
||||
|
||||
from meteo.config import InfluxSettings
|
||||
from meteo.influx_client import create_influx_client
|
||||
from meteo.schema import (
|
||||
list_measurements,
|
||||
list_measurement_tag_keys,
|
||||
list_measurement_tag_values,
|
||||
)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
"""
|
||||
Explore les tags des measurements du bucket :
|
||||
|
||||
- affiche les keys de tags pour chaque measurement
|
||||
- si un tag `entity_id` est présent, affiche la liste de ses valeurs
|
||||
"""
|
||||
settings = InfluxSettings.from_env()
|
||||
|
||||
print(f"Bucket InfluxDB : {settings.bucket}")
|
||||
print()
|
||||
|
||||
with closing(create_influx_client(settings)) as client:
|
||||
measurements = list_measurements(client, settings.bucket)
|
||||
|
||||
if not measurements:
|
||||
print("⚠ Aucun measurement trouvé dans ce bucket.")
|
||||
return
|
||||
|
||||
for meas in measurements:
|
||||
print(f"Measurement « {meas} »")
|
||||
tag_keys = list_measurement_tag_keys(client, settings.bucket, meas)
|
||||
if not tag_keys:
|
||||
print(" (aucun tag trouvé)")
|
||||
print()
|
||||
continue
|
||||
|
||||
print(" Tag keys :")
|
||||
for key in tag_keys:
|
||||
print(f" - {key}")
|
||||
|
||||
if "entity_id" in tag_keys:
|
||||
entity_ids = list_measurement_tag_values(
|
||||
client,
|
||||
settings.bucket,
|
||||
meas,
|
||||
tag="entity_id",
|
||||
)
|
||||
print(" entity_id possibles :")
|
||||
for eid in entity_ids:
|
||||
print(f" - {eid}")
|
||||
|
||||
print()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,54 @@
|
||||
# tests/test_influx_schema.py
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
import sys
|
||||
from contextlib import closing
|
||||
|
||||
|
||||
PROJECT_ROOT = Path(__file__).resolve().parents[3]
|
||||
if str(PROJECT_ROOT) not in sys.path:
|
||||
sys.path.insert(0, str(PROJECT_ROOT))
|
||||
|
||||
from meteo.config import InfluxSettings
|
||||
from meteo.influx_client import create_influx_client
|
||||
from meteo.schema import list_measurements, list_measurement_fields
|
||||
|
||||
|
||||
def main() -> None:
|
||||
"""
|
||||
Explore le schéma du bucket InfluxDB configuré :
|
||||
|
||||
- liste des measurements disponibles
|
||||
- pour chacun, liste des champs (_field) et de leur type
|
||||
"""
|
||||
settings = InfluxSettings.from_env()
|
||||
|
||||
print(f"Bucket InfluxDB : {settings.bucket}")
|
||||
print()
|
||||
|
||||
with closing(create_influx_client(settings)) as client:
|
||||
measurements = list_measurements(client, settings.bucket)
|
||||
|
||||
if not measurements:
|
||||
print("⚠ Aucun measurement trouvé dans ce bucket.")
|
||||
return
|
||||
|
||||
print("Measurements disponibles :")
|
||||
for name in measurements:
|
||||
print(f" - {name}")
|
||||
print()
|
||||
|
||||
for name in measurements:
|
||||
print(f"Champs pour measurement « {name} » :")
|
||||
fields = list_measurement_fields(client, settings.bucket, name)
|
||||
if not fields:
|
||||
print(" (aucun champ trouvé)")
|
||||
else:
|
||||
for field in fields:
|
||||
print(f" - {field.name} (type: {field.type})")
|
||||
print()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user