536 lines
20 KiB
Markdown
536 lines
20 KiB
Markdown
# Wrapper Python pour l'API Atmo Data
|
||
|
||
Ce projet fournit un wrapper Python pour l'API Atmo Data ([https://admindata.atmo-france.org](https://admindata.atmo-france.org)), permettant d'accéder facilement aux données de qualité de l'air et de pollution des Associations agréées de surveillance de la qualité de l’air (AASQA) françaises.
|
||
Atmo Data est un agrégateur national des données produites par les AASQA en accès libre et sous licence ODbL, géré par Atmo France ([https://www.atmo-france.org](https://www.atmo-france.org)), la Fédération des Associations agréées de surveillance de la qualité de l'air.
|
||
|
||
Elle met à disposition 6 flux agrégés sur les 4 thèmes suivants :
|
||
- [indice ATMO de la qualité de l'air](https://www.atmo-france.org/article/lindice-atmo)
|
||
- [les épisodes de pollution](https://www.atmo-france.org/article/les-episodes-de-pollution)
|
||
- les émissions à l'échelle de la région et de l'établissement public de coopération intercommunale (EPCI)
|
||
- [l'indice pollen](https://www.atmo-france.org/article/indice-pollen)
|
||
|
||
Chacun peut donc bénéficier gratuitement de ces données mises en open data sous licence ODbL, en indiquant la source "Atmo France et les Associations agréées de surveillance de la qualité de l’air" ou "Atmo France / AASQA" dans sa version courte.
|
||
|
||
La documentation de l'API est disponible en ligne à l'adresse : [https://admindata.atmo-france.org/api/doc/v2](https://admindata.atmo-france.org/api/doc/v2).
|
||
La Notice technique et d’information des données open data sur la qualité de l’air disponibles sur Atmo Data (Version 1er avril 2025) est disponible en ligne à l'adresse : [https://www.atmo-france.org/sites/federation/files/medias/documents/2025-04/notice_Atmo_Data_1eravril2025.pdf](https://www.atmo-france.org/sites/federation/files/medias/documents/2025-04/notice_Atmo_Data_1eravril2025.pdf).
|
||
|
||
|
||
## Structure du projet
|
||
|
||
```
|
||
atmo-data-wrapper/
|
||
├── atmo_data_wrapper/ # Package principal
|
||
│ ├── __init__.py # Exports principaux du package
|
||
│ └── core/ # Module central
|
||
│ ├── __init__.py # Exports du module core
|
||
│ ├── client.py # Client principal pour l'API
|
||
│ ├── models.py # Classes pour les données typées
|
||
│ ├── constants.py # Constantes et configurations
|
||
│ ├── utils.py # Fonctions utilitaires
|
||
│ └── exceptions.py # Exceptions personnalisées
|
||
├── examples/ # Scripts d'exemples
|
||
│ ├── __init__.py
|
||
│ ├── example_usage.py # Exemples d'utilisation des endpoints
|
||
│ ├── example_save_files.py # Exemples de sauvegarde
|
||
│ ├── example_data_models.py # Exemples avec objets typés
|
||
│ ├── example_aasqa_utilities.py # Exemples fonctions utilitaires AASQA
|
||
│ ├── example_aasqa_advanced.py # Analyses avancées AASQA
|
||
│ ├── example_synthese_nancy.py # Exemple synthèse complète
|
||
│ └── setup_credentials.py # Configuration des credentials
|
||
├── demos/ # Scripts de démonstration complète
|
||
│ ├── __init__.py
|
||
│ ├── demo_atmo_functions.py # Démonstration complète IndiceAtmo
|
||
│ ├── demo_pollen_functions.py # Démonstration complète IndicePollen
|
||
│ ├── demo_emission_functions.py # Démonstration complète EmissionData
|
||
│ ├── demo_licence_atmo.py # Démonstration licence Atmo France
|
||
│ └── demo_episode_functions.py # Démonstration complète EpisodePollution
|
||
├── tests/ # Tests unitaires
|
||
│ ├── __init__.py
|
||
│ ├── test_validations.py # Tests de validation
|
||
│ ├── test_save_functionality.py # Tests de sauvegarde
|
||
│ ├── test_typed_client.py # Tests d'intégration modèles
|
||
│ ├── test_real_connection.py # Test de connexion réelle à l'API
|
||
│ └── test_credentials_system.py # Tests du système de credentials
|
||
├── docs/ # Documentation
|
||
│ ├── README.md # Documentation principale
|
||
│ ├── QUICKSTART.md # Guide de démarrage rapide
|
||
│ ├── DOCUMENTATION_DEMOS.md # Documentation des scripts de démonstration
|
||
│ ├── JOURNAL.md # Journal de développement
|
||
│ ├── notice_Atmo_Data_1eravril2025.md # Notice officielle API
|
||
│ └── swagger.json # Spécification API
|
||
├── credentials.json.example # Modèle de fichier credentials
|
||
├── credentials.json # Vos identifiants (à créer, ignoré par git)
|
||
├── setup.py # Configuration du package
|
||
├── pyproject.toml # Configuration moderne du package
|
||
├── requirements.txt # Dépendances de production
|
||
├── requirements-dev.txt # Dépendances de développement
|
||
├── MANIFEST.in # Fichiers à inclure dans le package
|
||
├── LICENSE # Licence MIT
|
||
└── .gitignore # Fichiers à ignorer par git
|
||
```
|
||
|
||
## Installation
|
||
|
||
### Installation depuis le repository local
|
||
|
||
```bash
|
||
# Cloner ou télécharger le project
|
||
git clone https://github.com/atmo-france/atmo-data-wrapper.git
|
||
cd atmo-data-wrapper
|
||
|
||
# Installation en mode développement
|
||
pip install -e .
|
||
|
||
# Ou installation normale
|
||
pip install .
|
||
```
|
||
|
||
### Dépendances
|
||
|
||
Le package nécessite Python 3.7+ et les dépendances suivantes :
|
||
|
||
```bash
|
||
pip install requests>=2.25.0
|
||
```
|
||
|
||
### Installation des dépendances de développement
|
||
|
||
```bash
|
||
pip install -r requirements-dev.txt
|
||
```
|
||
|
||
## Configuration des credentials
|
||
|
||
### 0. Pré requis : Demander l'autorisation
|
||
L'accès aux données requiert l'autorisation d'un d'administrateur de l'application API Atmo Data. Vous pouvez réaliser une demande de création de compte à cette adresse : https://admindata.atmo-france.org/inscription-api.
|
||
|
||
Pour vous connecter à l'API Atmo Data, vous devez créer un fichier de configuration avec vos identifiants.
|
||
|
||
### 1. Créer le fichier credentials.json
|
||
|
||
```bash
|
||
# Copier le fichier exemple
|
||
cp credentials.json.example credentials.json
|
||
|
||
# Éditer avec vos identifiants
|
||
nano credentials.json
|
||
```
|
||
|
||
### 2. Format du fichier credentials.json
|
||
|
||
```json
|
||
{
|
||
"username": "votre_nom_utilisateur",
|
||
"password": "votre_mot_de_passe",
|
||
"api_url": "https://api.atmo-data.org"
|
||
}
|
||
```
|
||
|
||
**Important** :
|
||
- Le fichier `credentials.json` est ajouté au `.gitignore` pour éviter de commiter vos identifiants
|
||
- Remplacez les valeurs par vos vrais identifiants fournis par Atmo Data
|
||
- L'URL de l'API peut être modifiée si nécessaire
|
||
|
||
## Utilisation
|
||
|
||
### Authentification
|
||
|
||
#### Méthode recommandée : Fichier credentials.json
|
||
|
||
```python
|
||
from atmo_data_wrapper import AtmoDataClient, AASQA_CODES, POLLUANTS
|
||
|
||
# Connexion automatique avec credentials.json
|
||
client = AtmoDataClient()
|
||
client.auto_login() # Utilise automatiquement credentials.json
|
||
```
|
||
|
||
## Fonctions utilitaires AASQA
|
||
|
||
Le wrapper fournit des fonctions utilitaires pour faciliter la manipulation des codes AASQA et la recherche d'organismes :
|
||
|
||
```python
|
||
from atmo_data_wrapper import (
|
||
get_aasqa_by_department,
|
||
get_aasqa_info,
|
||
search_aasqa_by_name,
|
||
get_departments_count,
|
||
validate_department_coverage,
|
||
get_aasqa_statistics
|
||
)
|
||
|
||
# Trouver l'AASQA d'un département
|
||
aasqa_code = get_aasqa_by_department("54") # Nancy
|
||
print(f"Département 54 -> AASQA {aasqa_code}") # "44" (Grand Est)
|
||
|
||
# Informations complètes d'une AASQA
|
||
info = get_aasqa_info("44")
|
||
print(f"Organisme: {info['organisme']}") # "ATMO Grand-Est"
|
||
print(f"Site web: {info['site_web']}") # URL officielle
|
||
print(f"Départements: {info['departements']}") # Liste des départements
|
||
|
||
# Recherche par nom
|
||
resultats = search_aasqa_by_name("Atmo")
|
||
for r in resultats:
|
||
print(f"{r['organisme']} - {r['region']}")
|
||
|
||
# Statistiques
|
||
stats = get_aasqa_statistics()
|
||
print(f"Nombre total d'AASQA: {stats['total_aasqa']}")
|
||
print(f"Couverture moyenne: {stats['average_coverage']:.1f} départements/AASQA")
|
||
```
|
||
|
||
## Licence et mentions légales
|
||
|
||
Conformément aux exigences d'Atmo France, le wrapper fournit des fonctions pour afficher les mentions légales requises :
|
||
|
||
```python
|
||
from atmo_data_wrapper import get_atmo_licence, print_atmo_licence
|
||
|
||
# Version courte (recommandée)
|
||
print(get_atmo_licence("courte"))
|
||
# "Atmo France / AASQA"
|
||
|
||
# Version longue (officielle)
|
||
print(get_atmo_licence("longue"))
|
||
# "Atmo France et les Associations agréées de surveillance de la qualité de l'air"
|
||
|
||
# Version complète avec licence ODbL
|
||
print_atmo_licence("complete")
|
||
# Affiche les détails complets de la licence
|
||
|
||
# Utilisation dans un script
|
||
data = client.get_indices_atmo()
|
||
print(f"Source: {get_atmo_licence('courte')}")
|
||
```
|
||
|
||
#### Méthode alternative : Identifiants directs
|
||
|
||
```python
|
||
# Connexion avec identifiants fournis directement
|
||
client = AtmoDataClient()
|
||
client.login("votre_username", "votre_password")
|
||
|
||
# Ou mélange des deux (override du fichier)
|
||
client.login(username="autre_user") # password vient du fichier
|
||
```
|
||
|
||
### Récupération des indices ATMO
|
||
|
||
```python
|
||
# Indices d'aujourd'hui
|
||
indices = client.get_indices_atmo()
|
||
|
||
# Indices d'une date spécifique
|
||
indices = client.get_indices_atmo(date="2024-06-08")
|
||
|
||
# Indices pour une région spécifique (utilisation des constantes)
|
||
indices = client.get_indices_atmo(aasqa="11") # AASQA_CODES['11'] = Île-de-France
|
||
```
|
||
|
||
### Épisodes de pollution
|
||
|
||
```python
|
||
# Épisodes en cours (3 jours)
|
||
episodes = client.get_episodes_3jours()
|
||
|
||
# Épisodes historiques
|
||
episodes = client.get_episodes_historique(date="2024-06-08")
|
||
|
||
# Filtre par polluant (utilisation des constantes)
|
||
episodes = client.get_episodes_3jours(polluant=POLLUANTS[2]) # PM10
|
||
```
|
||
|
||
### Données d'émissions
|
||
|
||
```python
|
||
# Émissions par région
|
||
emissions = client.get_emissions(echelle="region")
|
||
|
||
# Émissions par EPCI
|
||
emissions = client.get_emissions(echelle="epci", aasqa="11")
|
||
```
|
||
|
||
### Indices pollen
|
||
|
||
```python
|
||
# Indices pollen actuels
|
||
pollens = client.get_indices_pollens()
|
||
|
||
# Avec alertes uniquement
|
||
pollens = client.get_indices_pollens(alerte=True)
|
||
```
|
||
|
||
## Objets typés et modèles de données
|
||
|
||
Le wrapper utilise des objets typés pour faciliter l'exploitation des données. Chaque type de données retourné par l'API est représenté par une classe spécialisée avec des méthodes helper.
|
||
|
||
### Types de données disponibles
|
||
|
||
- **IndiceAtmo** : Indices de qualité de l'air ATMO (0-7)
|
||
- **EpisodePollution** : Épisodes de pollution atmosphérique
|
||
- **EmissionData** : Données d'inventaires des émissions
|
||
- **IndicePollen** : Indices polliniques (0-6)
|
||
- **AtmoDataCollection** : Collection gérant plusieurs objets
|
||
|
||
### Utilisation avec objets typés
|
||
|
||
```python
|
||
from atmo_data_wrapper import AtmoDataClient, Coordinates
|
||
|
||
client = AtmoDataClient()
|
||
client.auto_login() # Connexion avec credentials.json
|
||
|
||
# Les données GeoJSON retournent des objets typés
|
||
indices = client.get_indices_atmo(aasqa="11") # Retourne AtmoDataCollection
|
||
|
||
# Parcourir les données avec méthodes helper
|
||
for indice in indices:
|
||
print(f"Zone: {indice.lib_zone}")
|
||
print(f"Qualité: {indice.get_qualificatif()}")
|
||
print(f"Couleur: {indice.get_color()[0]}") # Couleur hex
|
||
|
||
if indice.is_poor_quality():
|
||
worst_pol, code = indice.get_worst_pollutant()
|
||
print(f"Attention: {worst_pol} élevé ({code})")
|
||
|
||
# Statistiques sur la collection
|
||
stats = indices.get_statistics()
|
||
print(f"Qualité moyenne: {stats['quality_stats']['moyenne']:.1f}")
|
||
|
||
# Filtrage géographique
|
||
paris = Coordinates(2.3522, 48.8566)
|
||
nearby = indices.filter_by_coordinates(paris, radius_km=20)
|
||
```
|
||
|
||
### Classes et méthodes disponibles
|
||
|
||
#### IndiceAtmo
|
||
- `get_qualificatif()` : Qualificatif textuel (Bon, Moyen, etc.)
|
||
- `get_color()` : Couleur hex et RGB associée
|
||
- `is_good_quality()` / `is_poor_quality()` : Tests de qualité
|
||
- `get_worst_pollutant()` : Polluant le plus problématique
|
||
- `get_pollutants_summary()` : Résumé de tous les polluants
|
||
|
||
#### EpisodePollution
|
||
- `is_alert_active()` : Vérifie si une alerte est en cours
|
||
- `get_alert_level()` : Niveau d'alerte (Information/Alerte/Aucune)
|
||
- `get_polluant_code()` : Code du polluant principal
|
||
|
||
#### EmissionData
|
||
- `get_emission_density(polluant)` : Densité d'émission par km²
|
||
- `get_emission_per_capita(polluant)` : Émission par habitant
|
||
- `get_total_emissions()` : Toutes les émissions
|
||
- `get_secteur_name()` : Nom du secteur d'émission
|
||
|
||
#### IndicePollen
|
||
- `is_alert_active()` : Alerte pollen active
|
||
- `get_highest_pollen()` : Espèce avec l'indice le plus élevé
|
||
- `get_dangerous_pollens()` : Liste des pollens à risque élevé
|
||
- `get_pollens_summary()` : Détail par espèce avec émojis
|
||
- `get_responsible_pollens()` : Taxons responsables selon l'API
|
||
|
||
#### AtmoDataCollection
|
||
- `filter_by_aasqa(code)` : Filtrage par région
|
||
- `filter_by_coordinates(center, radius)` : Filtrage géographique
|
||
- `get_statistics()` : Statistiques de la collection
|
||
- `to_summary()` : Résumé textuel
|
||
|
||
## API Endpoints
|
||
|
||
- `get_indices_atmo()` - Indices de qualité de l'air ATMO
|
||
- `get_episodes_3jours()` - Épisodes de pollution sur 3 jours
|
||
- `get_episodes_historique()` - Épisodes de pollution historiques
|
||
- `get_emissions()` - Inventaires des émissions
|
||
- `get_indices_pollens()` - Indices pollen
|
||
|
||
## Codes AASQA
|
||
|
||
Le wrapper fournit des constantes enrichies avec sites web et départements couverts :
|
||
|
||
- 01: Guadeloupe (Gwad'Air) - Département 971
|
||
- 02: Martinique (Madininair) - Département 972
|
||
- 03: Guyane (Atmo Guyane) - Département 973
|
||
- 04: La Réunion (Atmo Réunion) - Département 974
|
||
- 06: Mayotte (Hawa Mayotte) - Département 976
|
||
- 11: Île-de-France (Airparif) - Départements 75, 77, 78, 91, 92, 93, 94, 95
|
||
- 24: Centre-Val de Loire (Lig'Air) - Départements 18, 28, 36, 37, 41, 45
|
||
- 27: Bourgogne-Franche-Comté (Atmo BFC) - Départements 21, 25, 39, 58, 70, 71, 89, 90
|
||
- 28: Normandie (Atmo Normandie) - Départements 14, 27, 50, 61, 76
|
||
- 32: Hauts-de-France (Atmo HDF) - Départements 02, 59, 60, 62, 80
|
||
- 44: Grand Est (ATMO Grand-Est) - Départements 08, 10, 51, 52, 54, 55, 57, 67, 68, 88
|
||
- 52: Pays de la Loire (Air Pays de la Loire) - Départements 44, 49, 53, 72, 85
|
||
- 53: Bretagne (Air Breizh) - Départements 22, 29, 35, 56
|
||
- 75: Nouvelle-Aquitaine (Atmo NA) - Départements 16, 17, 19, 23, 24, 33, 40, 47, 64, 79, 86, 87
|
||
- 76: Occitanie (Atmo Occitanie) - Départements 09, 11, 12, 30, 31, 32, 34, 46, 48, 65, 66, 81, 82
|
||
- 84: Auvergne-Rhône-Alpes (Atmo AURA) - Départements 01, 03, 07, 15, 26, 38, 42, 43, 63, 69, 73, 74
|
||
- 93: Provence-Alpes-Côte d'Azur (AtmoSud) - Départements 04, 05, 06, 13, 83, 84
|
||
- 94: Corse (Qualitair) - Départements 2A, 2B
|
||
|
||
Accès programmatique :
|
||
```python
|
||
from atmo_data_wrapper import AASQA_CODES
|
||
info = AASQA_CODES['44']
|
||
print(f"Site web: {info['site_web']}")
|
||
print(f"Départements: {info['departements']}")
|
||
```
|
||
|
||
## Gestion des erreurs
|
||
|
||
Le wrapper utilise des exceptions personnalisées pour gérer différents types d'erreurs :
|
||
|
||
```python
|
||
from atmo_data_wrapper import AtmoDataClient, AtmoDataException
|
||
|
||
try:
|
||
client = AtmoDataClient()
|
||
client.auto_login()
|
||
indices = client.get_indices_atmo()
|
||
except ValueError as e:
|
||
print(f"Erreur de validation: {e}")
|
||
except AtmoDataException as e:
|
||
print(f"Erreur API: {e}")
|
||
except Exception as e:
|
||
print(f"Erreur inattendue: {e}")
|
||
```
|
||
|
||
### Types d'exceptions disponibles
|
||
|
||
- `AtmoDataException` : Exception de base
|
||
- `AuthenticationError` : Erreur d'authentification
|
||
- `ValidationError` : Erreur de validation des paramètres
|
||
- `APIError` : Erreur de l'API Atmo Data
|
||
- `NetworkError` : Erreur de réseau
|
||
- `DataError` : Erreur dans le traitement des données
|
||
|
||
## Validation des paramètres
|
||
|
||
Le wrapper valide automatiquement tous les paramètres avant d'envoyer les requêtes :
|
||
|
||
- **Formats** : Seuls `geojson` et `csv` sont acceptés
|
||
- **Codes AASQA** : Validation contre la liste des codes valides (01-94)
|
||
- **Polluants** : Validation contre la liste des polluants supportés
|
||
- **Dates** : Validation du format YYYY-MM-DD et de l'existence de la date
|
||
- **Indices** : Validation des codes qualificatifs ATMO (0-7) et pollen (0-6)
|
||
- **Bounding box** : Validation du format et de la cohérence des coordonnées
|
||
|
||
Exemple d'erreur de validation :
|
||
```python
|
||
try:
|
||
client.get_indices_atmo(format="xml") # Format invalide
|
||
except ValueError as e:
|
||
print(e) # "Format invalide: xml. Formats valides: ['geojson', 'csv']"
|
||
```
|
||
|
||
## Sauvegarde des données
|
||
|
||
Le wrapper permet de sauvegarder facilement les résultats de l'API dans différents formats :
|
||
|
||
```python
|
||
# Récupérer des données
|
||
indices = client.get_indices_atmo(aasqa="11")
|
||
|
||
# Sauvegarder en JSON
|
||
json_file = client.save_to_file(indices, "data/indices_atmo", "json")
|
||
|
||
# Sauvegarder en CSV
|
||
csv_file = client.save_to_file(indices, "data/indices_atmo", "csv")
|
||
|
||
# Sauvegarder en GeoJSON
|
||
geojson_file = client.save_to_file(indices, "data/indices_atmo", "geojson")
|
||
```
|
||
|
||
### Formats supportés pour la sauvegarde :
|
||
- `json` - Format JSON standard
|
||
- `csv` - Format CSV avec extraction automatique des propriétés
|
||
- `geojson` - Format GeoJSON (données géographiques)
|
||
|
||
### Fonctionnalités :
|
||
- **Création automatique de répertoires** : Les dossiers parents sont créés si nécessaire
|
||
- **Extensions automatiques** : Les extensions (.json, .csv, .geojson) sont ajoutées automatiquement
|
||
- **Extraction CSV intelligente** : Conversion automatique des données GeoJSON vers CSV avec coordonnées
|
||
- **Validation des formats** : Vérification de la compatibilité des données avec le format choisi
|
||
|
||
## Formats de sortie API
|
||
|
||
L'API supporte deux formats :
|
||
- `geojson` (par défaut)
|
||
- `csv`
|
||
|
||
## Tests et Exemples
|
||
|
||
### Scripts d'exemples
|
||
- `example_usage.py` - Exemples d'utilisation des endpoints (données brutes)
|
||
- `example_save_files.py` - Exemples de sauvegarde de données
|
||
- `example_data_models.py` - Exemples avec objets typés et méthodes helper
|
||
- `example_aasqa_utilities.py` - Exemples des fonctions utilitaires AASQA
|
||
- `example_aasqa_advanced.py` - Analyses avancées et statistiques AASQA
|
||
- `demo_licence_atmo.py` - Démonstration des fonctions de licence
|
||
- `example_synthese_tomblaine.py` - Exemple de synthèse complète (Atmo + Pollen)
|
||
|
||
### Scripts de démonstration complète
|
||
- `demo_atmo_functions.py` - **Démonstration complète de la classe IndiceAtmo**
|
||
- `demo_pollen_functions.py` - **Démonstration complète de la classe IndicePollen**
|
||
- `demo_emission_functions.py` - **Démonstration complète de la classe EmissionData**
|
||
- `demo_episode_functions.py` - **Démonstration complète de la classe EpisodePollution**
|
||
|
||
Ces scripts illustrent **toutes les fonctionnalités** de chaque classe avec des données réelles de l'API. Voir `DOCUMENTATION_DEMOS.md` pour une documentation détaillée.
|
||
|
||
### Scripts de test
|
||
- `test_validations.py` - Tests de validation des paramètres
|
||
- `test_save_functionality.py` - Tests de sauvegarde de fichiers
|
||
- `test_typed_client.py` - Tests d'intégration des modèles typés
|
||
- `test_real_connection.py` - Test de connexion réelle à l'API
|
||
|
||
### Test de votre configuration
|
||
|
||
```bash
|
||
# Vérifier la configuration et tester la connexion
|
||
python tests/test_real_connection.py
|
||
```
|
||
|
||
Ce script vérifie :
|
||
- La présence et validité du fichier `credentials.json`
|
||
- La connexion à l'API
|
||
- Le bon fonctionnement des différents endpoints
|
||
- Les objets typés et leurs méthodes
|
||
|
||
### Exécution des scripts
|
||
|
||
```bash
|
||
# Scripts d'exemples
|
||
python examples/example_usage.py
|
||
python examples/example_data_models.py
|
||
python examples/example_aasqa_utilities.py
|
||
python examples/demo_licence_atmo.py
|
||
|
||
# Scripts de démonstration
|
||
python demos/demo_atmo_functions.py
|
||
python demos/demo_pollen_functions.py
|
||
|
||
# Tests
|
||
python -m pytest tests/
|
||
```
|
||
|
||
## Nouvelles fonctionnalités
|
||
|
||
### Architecture améliorée
|
||
- **Séparation des constantes et utilitaires** : Les constantes sont dans `constants.py`, les fonctions utilitaires dans `utils.py`
|
||
- **Constantes enrichies** : Les codes AASQA incluent maintenant sites web et départements
|
||
- **Mapping des codes** : Constantes séparées pour les épisodes de pollution et les taxons de pollens
|
||
|
||
### Fonctions utilitaires AASQA
|
||
- Recherche par département ou nom d'organisme
|
||
- Statistiques de couverture territoriale
|
||
- Validation de l'intégrité des données
|
||
- Analyses comparatives entre organismes
|
||
|
||
### Conformité légale
|
||
- Fonctions dédiées pour les mentions légales Atmo France
|
||
- Respect de la licence ODbL
|
||
- Intégration facilitée dans rapports et applications
|
||
|
||
### Organisation du projet
|
||
- Dossier `archives/` pour l'historique des anciens scripts
|
||
- Structure claire et maintenable
|
||
- Documentation complète et à jour
|