edb-noumea 0.2.6__tar.gz → 0.2.14__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- edb_noumea-0.2.14/PKG-INFO +203 -0
- {edb_noumea-0.2.6 → edb_noumea-0.2.14}/README.md +4 -0
- edb_noumea-0.2.14/edb_noumea/details.py +178 -0
- edb_noumea-0.2.14/edb_noumea.egg-info/PKG-INFO +203 -0
- {edb_noumea-0.2.6 → edb_noumea-0.2.14}/edb_noumea.egg-info/requires.txt +4 -0
- {edb_noumea-0.2.6 → edb_noumea-0.2.14}/edb_noumea.egg-info/top_level.txt +1 -0
- edb_noumea-0.2.14/pyproject.toml +49 -0
- edb_noumea-0.2.6/PKG-INFO +0 -12
- edb_noumea-0.2.6/edb_noumea/details.py +0 -113
- edb_noumea-0.2.6/edb_noumea.egg-info/PKG-INFO +0 -12
- edb_noumea-0.2.6/pyproject.toml +0 -19
- {edb_noumea-0.2.6 → edb_noumea-0.2.14}/edb_noumea/__init__.py +0 -0
- {edb_noumea-0.2.6 → edb_noumea-0.2.14}/edb_noumea/main.py +0 -0
- {edb_noumea-0.2.6 → edb_noumea-0.2.14}/edb_noumea.egg-info/SOURCES.txt +0 -0
- {edb_noumea-0.2.6 → edb_noumea-0.2.14}/edb_noumea.egg-info/dependency_links.txt +0 -0
- {edb_noumea-0.2.6 → edb_noumea-0.2.14}/setup.cfg +0 -0
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: edb-noumea
|
|
3
|
+
Version: 0.2.14
|
|
4
|
+
Summary: Un scraper pour la qualité des eaux de baignade à Nouméa.
|
|
5
|
+
Author: Adrien SALES
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/adriens/edb-noumea
|
|
8
|
+
Project-URL: Repository, https://github.com/adriens/edb-noumea
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Topic :: Utilities
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
Requires-Dist: requests
|
|
16
|
+
Requires-Dist: beautifulsoup4
|
|
17
|
+
Requires-Dist: pandas
|
|
18
|
+
Requires-Dist: lxml
|
|
19
|
+
Requires-Dist: tabula-py
|
|
20
|
+
Requires-Dist: matplotlib
|
|
21
|
+
Provides-Extra: dev
|
|
22
|
+
Requires-Dist: pytest; extra == "dev"
|
|
23
|
+
Requires-Dist: ruff; extra == "dev"
|
|
24
|
+
|
|
25
|
+

|
|
26
|
+
[](https://pypistats.org/packages/edb-noumea)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
# Qualité des Eaux de Baignade à Nouméa
|
|
32
|
+
|
|
33
|
+
Ce projet Python fournit un outil simple pour scraper les données sur la qualité des eaux de baignade à Nouméa depuis le site officiel de la ville (`noumea.nc`). Il extrait les informations et les présente sous forme de tableau dans le terminal.
|
|
34
|
+
|
|
35
|
+
Il se base sur les données de https://www.noumea.nc/noumea-pratique/salubrite-publique/qualite-eaux-baignade
|
|
36
|
+
|
|
37
|
+
## Prérequis
|
|
38
|
+
|
|
39
|
+
Avant de commencer, assurez-vous d'avoir installé `uv`, le gestionnaire de paquets et d'environnements virtuels Python.
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
## Installation
|
|
45
|
+
|
|
46
|
+
Suivez ces étapes pour configurer l'environnement et installer les dépendances.
|
|
47
|
+
|
|
48
|
+
1. **Accédez au répertoire du projet :**
|
|
49
|
+
```bash
|
|
50
|
+
cd edb-noumea
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
2. **Créez un environnement virtuel avec `uv` :**
|
|
54
|
+
```bash
|
|
55
|
+
uv venv
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
3. **Activez l'environnement virtuel :**
|
|
59
|
+
```bash
|
|
60
|
+
source .venv/bin/activate
|
|
61
|
+
```
|
|
62
|
+
*(Sur Windows, utilisez `.venv\Scripts\activate`)*
|
|
63
|
+
|
|
64
|
+
4. **Installez les dépendances du projet :**
|
|
65
|
+
```bash
|
|
66
|
+
uv pip install -e .
|
|
67
|
+
```
|
|
68
|
+
*(L'option `-e .` installe le projet en mode "éditable", ce qui vous permet de modifier le code sans avoir à le réinstaller.)*
|
|
69
|
+
|
|
70
|
+
## Utilisation
|
|
71
|
+
|
|
72
|
+
Ce package peut être utilisé de deux manières : soit pour obtenir un résumé de l'état des plages, soit pour obtenir les résultats détaillés des derniers prélèvements.
|
|
73
|
+
|
|
74
|
+
### Obtenir le résumé de l'état sanitaire
|
|
75
|
+
|
|
76
|
+
Pour obtenir le tableau de résumé simple depuis la page web principale, exécutez :
|
|
77
|
+
```bash
|
|
78
|
+
python -m edb_noumea.main
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Obtenir les résultats détaillés (depuis PDF)
|
|
82
|
+
|
|
83
|
+
Pour obtenir le tableau détaillé des derniers relevés (extrait automatiquement du dernier fichier PDF disponible), exécutez :
|
|
84
|
+
```bash
|
|
85
|
+
python -m edb_noumea.details
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
## Générer des graphiques PNG des analyses détaillées
|
|
90
|
+
|
|
91
|
+
Vous pouvez générer automatiquement deux graphiques au format PNG (niveaux d'E. coli et d'Entérocoques par point de prélèvement) à partir des derniers résultats d'analyses, grâce au script fourni.
|
|
92
|
+
|
|
93
|
+
### Étapes
|
|
94
|
+
|
|
95
|
+
1. Assurez-vous que l'environnement virtuel est activé et que les dépendances sont installées.
|
|
96
|
+
2. Exécutez le script suivant depuis le répertoire du projet :
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
source .venv/bin/activate
|
|
100
|
+
/home/adriens/Github/edb-noumea/noumea_water_quality/.venv/bin/python generer_graphique_analyses.py
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Deux fichiers PNG seront générés dans le dossier courant :
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
Vous pouvez ouvrir ces fichiers pour visualiser les résultats détaillés des analyses.
|
|
107
|
+
|
|
108
|
+
## Utilisation en tant que Bibliothèque
|
|
109
|
+
|
|
110
|
+
Vous pouvez également importer les fonctions dans vos propres scripts Python pour une intégration plus poussée.
|
|
111
|
+
|
|
112
|
+
Installer
|
|
113
|
+
|
|
114
|
+
### Obtenir le résumé
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
# exemple_resume.py
|
|
118
|
+
from edb_noumea.main import get_water_quality
|
|
119
|
+
|
|
120
|
+
df_resume = get_water_quality()
|
|
121
|
+
|
|
122
|
+
if df_resume is not None:
|
|
123
|
+
print("Résumé de l'état des plages :")
|
|
124
|
+
print(df_resume.to_string())
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Obtenir les résultats détaillés
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
# exemple_details.py
|
|
131
|
+
from edb_noumea.details import get_detailed_results
|
|
132
|
+
|
|
133
|
+
df_details = get_detailed_results()
|
|
134
|
+
|
|
135
|
+
if df_details is not None:
|
|
136
|
+
print("Détails des derniers relevés :")
|
|
137
|
+
print(df_details.to_string())
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Exemple de Visualisation
|
|
141
|
+
|
|
142
|
+
Voici un exemple montrant comment récupérer les données détaillées et créer un graphique simple avec `matplotlib` pour visualiser les niveaux d'E. coli par point de prélèvement.
|
|
143
|
+
|
|
144
|
+
```python
|
|
145
|
+
# exemple_visualisation.py
|
|
146
|
+
import pandas as pd
|
|
147
|
+
import matplotlib.pyplot as plt
|
|
148
|
+
from edb_noumea.details import get_detailed_results
|
|
149
|
+
|
|
150
|
+
# Obtenir les données détaillées
|
|
151
|
+
df = get_detailed_results()
|
|
152
|
+
|
|
153
|
+
if df is not None and not df.empty:
|
|
154
|
+
print("Création du graphique...")
|
|
155
|
+
|
|
156
|
+
# S'assurer que les données sont triées pour une meilleure lisibilité
|
|
157
|
+
df_sorted = df.sort_values(by='e_coli_npp_100ml', ascending=False)
|
|
158
|
+
|
|
159
|
+
# Créer le graphique à barres horizontales
|
|
160
|
+
plt.figure(figsize=(12, 8))
|
|
161
|
+
plt.barh(df_sorted['point_de_prelevement'], df_sorted['e_coli_npp_100ml'], color='skyblue')
|
|
162
|
+
|
|
163
|
+
# Ajouter les titres et les étiquettes
|
|
164
|
+
plt.xlabel('E. coli (NPP/100ml)')
|
|
165
|
+
plt.ylabel('Point de prélèvement')
|
|
166
|
+
plt.title("Niveaux d'E. coli par Point de Prélèvement")
|
|
167
|
+
plt.gca().invert_yaxis() # Afficher le plus élevé en haut
|
|
168
|
+
plt.tight_layout() # Ajuster le layout pour que tout soit visible
|
|
169
|
+
|
|
170
|
+
# Sauvegarder le graphique dans un fichier
|
|
171
|
+
plt.savefig('ecoli_levels.png')
|
|
172
|
+
print("Graphique sauvegardé sous 'ecoli_levels.png'")
|
|
173
|
+
|
|
174
|
+
# Afficher le graphique
|
|
175
|
+
plt.show()
|
|
176
|
+
else:
|
|
177
|
+
print("Aucune donnée à afficher.")
|
|
178
|
+
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
*Assurez-vous que votre script est exécuté dans le même environnement virtuel où le package `edb-noumea` a été installé.*
|
|
182
|
+
|
|
183
|
+
## Sortie Attendue
|
|
184
|
+
|
|
185
|
+
### Résumé de l'état sanitaire (`main`)
|
|
186
|
+
```
|
|
187
|
+
📊 État sanitaire des eaux de baignade à Nouméa 📊
|
|
188
|
+
Plage État sanitaire
|
|
189
|
+
0 Plage de la baie des Citrons Baignade autorisée
|
|
190
|
+
1 Plage de la promenade Pierre-Vernier Baignade autorisée
|
|
191
|
+
...
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Détails des relevés (`details`)
|
|
195
|
+
```
|
|
196
|
+
📋 Voici les détails des derniers relevés :
|
|
197
|
+
Site Point de prélèvement Date Heure E. coli (NPP/100ml) Entérocoques (NPP/100ml)
|
|
198
|
+
0 PLAGE DE LA BAIE DES CITRONS P18049, Face The Beach House 04/09/2025 07:29 10 20
|
|
199
|
+
1 PLAGE DE LA BAIE DES CITRONS P18050, Face allée centrale Mirage plaza 04/09/2025 07:33 62 75
|
|
200
|
+
...
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
|
|
3
|
+
@staticmethod
|
|
4
|
+
def get_sites():
|
|
5
|
+
"""
|
|
6
|
+
Retourne un DataFrame avec le mapping site/plage/gmaps_url.
|
|
7
|
+
"""
|
|
8
|
+
data = [
|
|
9
|
+
{"site": "PLAGE DE LA BAIE DES CITRONS", "plage": "Plage de la baie des Citrons", "gmaps_url": "https://maps.app.goo.gl/P2SP3oWuQbxd1sCH9"},
|
|
10
|
+
{"site": "PLAGE DE L'ANSE VATA", "plage": "Plage de l'Anse-Vata", "gmaps_url": "https://maps.app.goo.gl/xAUdky47DqEjSF4R8"},
|
|
11
|
+
{"site": "PLAGE DE LA POINTE MAGNIN", "plage": "Plage de la pointe Magnin", "gmaps_url": "https://maps.app.goo.gl/Wf69LoGgc894MtQy6"},
|
|
12
|
+
{"site": "PLAGE DE LA PROMENADE PIERRE VERNIER", "plage": "Plage de la promenade Pierre-Vernier", "gmaps_url": "https://maps.app.goo.gl/bNocZKVVMYk3HFYs9"},
|
|
13
|
+
{"site": "PLAGE DE MAGENTA", "plage": "Plage de Magenta", "gmaps_url": "https://maps.app.goo.gl/yFwgG2BCV1sEtPWP6"},
|
|
14
|
+
{"site": "PLAGE DU KUENDU BEACH", "plage": "Plage du Kuendu Beach", "gmaps_url": "https://maps.app.goo.gl/oGY6Hy4KCXJWxqfL9"},
|
|
15
|
+
]
|
|
16
|
+
return pd.DataFrame(data)
|
|
17
|
+
def get_pdf_url():
|
|
18
|
+
"""
|
|
19
|
+
Alias public pour obtenir l'URL du dernier PDF d'analyses détaillées.
|
|
20
|
+
"""
|
|
21
|
+
return get_latest_pdf_url()
|
|
22
|
+
|
|
23
|
+
import pandas as pd
|
|
24
|
+
import tabula
|
|
25
|
+
import requests
|
|
26
|
+
import io
|
|
27
|
+
from bs4 import BeautifulSoup
|
|
28
|
+
|
|
29
|
+
# URL de la page officielle contenant le lien vers le PDF
|
|
30
|
+
PAGE_URL = "https://www.noumea.nc/noumea-pratique/salubrite-publique/qualite-eaux-baignade"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def get_latest_pdf_url():
|
|
34
|
+
"""
|
|
35
|
+
Récupère dynamiquement l'URL du dernier PDF d'analyses détaillées depuis la page officielle.
|
|
36
|
+
"""
|
|
37
|
+
print(f"🔗 Recherche du lien PDF sur {PAGE_URL} ...")
|
|
38
|
+
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36'}
|
|
39
|
+
try:
|
|
40
|
+
resp = requests.get(PAGE_URL, headers=headers)
|
|
41
|
+
resp.raise_for_status()
|
|
42
|
+
except Exception as e:
|
|
43
|
+
print(f"❌ Impossible de récupérer la page officielle : {e}")
|
|
44
|
+
return None
|
|
45
|
+
soup = BeautifulSoup(resp.text, "lxml")
|
|
46
|
+
# Chercher le premier lien PDF dans la page
|
|
47
|
+
link = soup.find("a", href=lambda h: h and h.endswith(".pdf"))
|
|
48
|
+
if not link:
|
|
49
|
+
print("❌ Aucun lien PDF trouvé sur la page.")
|
|
50
|
+
return None
|
|
51
|
+
pdf_url = link["href"]
|
|
52
|
+
# Si le lien est relatif, le rendre absolu
|
|
53
|
+
if pdf_url.startswith("/"):
|
|
54
|
+
pdf_url = "https://www.noumea.nc" + pdf_url
|
|
55
|
+
print(f"✅ Lien PDF trouvé : {pdf_url}")
|
|
56
|
+
return pdf_url
|
|
57
|
+
|
|
58
|
+
def get_detailed_results():
|
|
59
|
+
"""
|
|
60
|
+
Télécharge dynamiquement le PDF des résultats détaillés, en extrait le premier tableau
|
|
61
|
+
et le retourne sous forme de DataFrame pandas.
|
|
62
|
+
"""
|
|
63
|
+
pdf_url = get_latest_pdf_url()
|
|
64
|
+
if not pdf_url:
|
|
65
|
+
return None
|
|
66
|
+
print(f"📥 Téléchargement du PDF depuis {pdf_url} ...")
|
|
67
|
+
try:
|
|
68
|
+
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36'}
|
|
69
|
+
response = requests.get(pdf_url, headers=headers)
|
|
70
|
+
response.raise_for_status()
|
|
71
|
+
print("✅ Téléchargement terminé.")
|
|
72
|
+
except requests.exceptions.RequestException as e:
|
|
73
|
+
print(f"❌ Erreur lors du téléchargement du fichier PDF : {e}")
|
|
74
|
+
return None
|
|
75
|
+
|
|
76
|
+
pdf_file = io.BytesIO(response.content)
|
|
77
|
+
|
|
78
|
+
try:
|
|
79
|
+
print("🔍 Extraction des tableaux du PDF...")
|
|
80
|
+
tables = tabula.read_pdf(pdf_file, pages='1', stream=True)
|
|
81
|
+
except Exception as e:
|
|
82
|
+
print(f"❌ Une erreur est survenue lors de l'extraction des données du PDF.")
|
|
83
|
+
print("ℹ️ Cela peut être dû à l'absence de Java sur votre système, qui est requis par la bibliothèque 'tabula-py'.")
|
|
84
|
+
print(f" Erreur originale : {e}")
|
|
85
|
+
return None
|
|
86
|
+
|
|
87
|
+
if not tables:
|
|
88
|
+
print("❌ Aucun tableau n'a été trouvé dans le PDF.")
|
|
89
|
+
return None
|
|
90
|
+
|
|
91
|
+
print(f"✅ {len(tables)} tableau(x) trouvé(s). Affichage du premier.")
|
|
92
|
+
df = tables[0]
|
|
93
|
+
|
|
94
|
+
# Utiliser la première ligne comme en-têtes et supprimer cette ligne du DataFrame
|
|
95
|
+
df.columns = df.iloc[0]
|
|
96
|
+
df = df[1:].reset_index(drop=True)
|
|
97
|
+
|
|
98
|
+
# Nettoyer les noms de colonnes (supprimer les retours à la ligne et les espaces superflus)
|
|
99
|
+
df.columns = df.columns.str.replace('\n', ' ', regex=False).str.strip()
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
print("\n--- Aperçu du tableau extrait (toutes colonnes) ---")
|
|
104
|
+
with pd.option_context('display.max_columns', None):
|
|
105
|
+
print(df)
|
|
106
|
+
print("\nColonnes:", list(df.columns))
|
|
107
|
+
print("Shape:", df.shape)
|
|
108
|
+
|
|
109
|
+
# Sélection dynamique des colonnes par nom
|
|
110
|
+
# Recherche des colonnes contenant les mots-clés
|
|
111
|
+
site_col = df.columns[0]
|
|
112
|
+
point_prelevement_col = df.columns[1]
|
|
113
|
+
date_col = next((col for col in df.columns if "Date" in str(col) and "prélèvement" in str(col)), None)
|
|
114
|
+
heure_col = next((col for col in df.columns if "Heure" in str(col) and "prélèvement" in str(col)), None)
|
|
115
|
+
e_coli_col = next((col for col in df.columns if "coli" in str(col) and "NPP" in str(col)), None)
|
|
116
|
+
entero_col = next((col for col in df.columns if ("Entérocoques" in str(col) or "intestinaux" in str(col)) and "NPP" in str(col)), None)
|
|
117
|
+
|
|
118
|
+
# Les noms de colonnes pour 'site' et 'point de prélèvement' sont souvent
|
|
119
|
+
# non reconnus par tabula, on se base donc sur leur position (2 premières colonnes).
|
|
120
|
+
if not all([date_col, heure_col, e_coli_col, entero_col]):
|
|
121
|
+
print(f"❌ Une ou plusieurs colonnes n'ont pas été trouvées dans le tableau. Colonnes disponibles : {list(df.columns)}")
|
|
122
|
+
return None
|
|
123
|
+
|
|
124
|
+
# Sélectionne les colonnes d'intérêt
|
|
125
|
+
selected_cols = [site_col, point_prelevement_col, date_col, heure_col, e_coli_col, entero_col]
|
|
126
|
+
cleaned_df = df[selected_cols].copy()
|
|
127
|
+
cleaned_df.columns = [
|
|
128
|
+
"site",
|
|
129
|
+
"point_de_prelevement",
|
|
130
|
+
"date",
|
|
131
|
+
"heure",
|
|
132
|
+
"e_coli_npp_100ml",
|
|
133
|
+
"enterocoques_npp_100ml"
|
|
134
|
+
]
|
|
135
|
+
|
|
136
|
+
# Ajoute deux colonnes issues du split de 'point_de_prelevement'
|
|
137
|
+
split_points = cleaned_df["point_de_prelevement"].str.split(",", n=1, expand=True)
|
|
138
|
+
cleaned_df["id_point_prelevement"] = split_points[0].str.strip()
|
|
139
|
+
cleaned_df["desc_point_prelevement"] = split_points[1].str.strip() if split_points.shape[1] > 1 else ""
|
|
140
|
+
|
|
141
|
+
# S'assurer que la colonne 'heure' est bien présente et de type string
|
|
142
|
+
if "heure" in cleaned_df.columns:
|
|
143
|
+
cleaned_df["heure"] = cleaned_df["heure"].astype(str)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
# Nettoyer et convertir les colonnes e_coli_npp_100ml et enterocoques_npp_100ml
|
|
147
|
+
if "e_coli_npp_100ml" in cleaned_df.columns:
|
|
148
|
+
cleaned_df["e_coli_npp_100ml"] = cleaned_df["e_coli_npp_100ml"].astype(str).str.replace(r"<\s*10", "10", regex=True)
|
|
149
|
+
cleaned_df["e_coli_npp_100ml"] = pd.to_numeric(cleaned_df["e_coli_npp_100ml"], errors="coerce").astype('Int64')
|
|
150
|
+
|
|
151
|
+
if "enterocoques_npp_100ml" in cleaned_df.columns:
|
|
152
|
+
cleaned_df["enterocoques_npp_100ml"] = cleaned_df["enterocoques_npp_100ml"].astype(str).str.replace(r"<\s*10", "10", regex=True)
|
|
153
|
+
cleaned_df["enterocoques_npp_100ml"] = pd.to_numeric(cleaned_df["enterocoques_npp_100ml"], errors="coerce").astype('Int64')
|
|
154
|
+
|
|
155
|
+
# Convertir la colonne 'date' en datetime (format jour/mois/année)
|
|
156
|
+
if "date" in cleaned_df.columns:
|
|
157
|
+
cleaned_df["date"] = pd.to_datetime(cleaned_df["date"], format="%d/%m/%Y", errors="coerce")
|
|
158
|
+
|
|
159
|
+
return cleaned_df
|
|
160
|
+
|
|
161
|
+
if __name__ == "__main__":
|
|
162
|
+
# Obtenir le DataFrame des résultats détaillés
|
|
163
|
+
detailed_df = get_detailed_results()
|
|
164
|
+
|
|
165
|
+
# Afficher seulement les colonnes demandées
|
|
166
|
+
if detailed_df is not None:
|
|
167
|
+
print("\n📋 Détails synthétiques :")
|
|
168
|
+
print(detailed_df[[
|
|
169
|
+
"point_de_prelevement",
|
|
170
|
+
"date",
|
|
171
|
+
"e_coli_npp_100ml",
|
|
172
|
+
"enterocoques_npp_100ml"
|
|
173
|
+
]])
|
|
174
|
+
|
|
175
|
+
# Sauvegarder le DataFrame dans un fichier CSV
|
|
176
|
+
output_csv_path = "details.csv"
|
|
177
|
+
detailed_df.to_csv(output_csv_path, index=False)
|
|
178
|
+
print(f"\n✅ Résultats détaillés sauvegardés dans : {output_csv_path}")
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: edb-noumea
|
|
3
|
+
Version: 0.2.14
|
|
4
|
+
Summary: Un scraper pour la qualité des eaux de baignade à Nouméa.
|
|
5
|
+
Author: Adrien SALES
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/adriens/edb-noumea
|
|
8
|
+
Project-URL: Repository, https://github.com/adriens/edb-noumea
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Topic :: Utilities
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
Requires-Dist: requests
|
|
16
|
+
Requires-Dist: beautifulsoup4
|
|
17
|
+
Requires-Dist: pandas
|
|
18
|
+
Requires-Dist: lxml
|
|
19
|
+
Requires-Dist: tabula-py
|
|
20
|
+
Requires-Dist: matplotlib
|
|
21
|
+
Provides-Extra: dev
|
|
22
|
+
Requires-Dist: pytest; extra == "dev"
|
|
23
|
+
Requires-Dist: ruff; extra == "dev"
|
|
24
|
+
|
|
25
|
+

|
|
26
|
+
[](https://pypistats.org/packages/edb-noumea)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
# Qualité des Eaux de Baignade à Nouméa
|
|
32
|
+
|
|
33
|
+
Ce projet Python fournit un outil simple pour scraper les données sur la qualité des eaux de baignade à Nouméa depuis le site officiel de la ville (`noumea.nc`). Il extrait les informations et les présente sous forme de tableau dans le terminal.
|
|
34
|
+
|
|
35
|
+
Il se base sur les données de https://www.noumea.nc/noumea-pratique/salubrite-publique/qualite-eaux-baignade
|
|
36
|
+
|
|
37
|
+
## Prérequis
|
|
38
|
+
|
|
39
|
+
Avant de commencer, assurez-vous d'avoir installé `uv`, le gestionnaire de paquets et d'environnements virtuels Python.
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
## Installation
|
|
45
|
+
|
|
46
|
+
Suivez ces étapes pour configurer l'environnement et installer les dépendances.
|
|
47
|
+
|
|
48
|
+
1. **Accédez au répertoire du projet :**
|
|
49
|
+
```bash
|
|
50
|
+
cd edb-noumea
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
2. **Créez un environnement virtuel avec `uv` :**
|
|
54
|
+
```bash
|
|
55
|
+
uv venv
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
3. **Activez l'environnement virtuel :**
|
|
59
|
+
```bash
|
|
60
|
+
source .venv/bin/activate
|
|
61
|
+
```
|
|
62
|
+
*(Sur Windows, utilisez `.venv\Scripts\activate`)*
|
|
63
|
+
|
|
64
|
+
4. **Installez les dépendances du projet :**
|
|
65
|
+
```bash
|
|
66
|
+
uv pip install -e .
|
|
67
|
+
```
|
|
68
|
+
*(L'option `-e .` installe le projet en mode "éditable", ce qui vous permet de modifier le code sans avoir à le réinstaller.)*
|
|
69
|
+
|
|
70
|
+
## Utilisation
|
|
71
|
+
|
|
72
|
+
Ce package peut être utilisé de deux manières : soit pour obtenir un résumé de l'état des plages, soit pour obtenir les résultats détaillés des derniers prélèvements.
|
|
73
|
+
|
|
74
|
+
### Obtenir le résumé de l'état sanitaire
|
|
75
|
+
|
|
76
|
+
Pour obtenir le tableau de résumé simple depuis la page web principale, exécutez :
|
|
77
|
+
```bash
|
|
78
|
+
python -m edb_noumea.main
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Obtenir les résultats détaillés (depuis PDF)
|
|
82
|
+
|
|
83
|
+
Pour obtenir le tableau détaillé des derniers relevés (extrait automatiquement du dernier fichier PDF disponible), exécutez :
|
|
84
|
+
```bash
|
|
85
|
+
python -m edb_noumea.details
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
## Générer des graphiques PNG des analyses détaillées
|
|
90
|
+
|
|
91
|
+
Vous pouvez générer automatiquement deux graphiques au format PNG (niveaux d'E. coli et d'Entérocoques par point de prélèvement) à partir des derniers résultats d'analyses, grâce au script fourni.
|
|
92
|
+
|
|
93
|
+
### Étapes
|
|
94
|
+
|
|
95
|
+
1. Assurez-vous que l'environnement virtuel est activé et que les dépendances sont installées.
|
|
96
|
+
2. Exécutez le script suivant depuis le répertoire du projet :
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
source .venv/bin/activate
|
|
100
|
+
/home/adriens/Github/edb-noumea/noumea_water_quality/.venv/bin/python generer_graphique_analyses.py
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Deux fichiers PNG seront générés dans le dossier courant :
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
Vous pouvez ouvrir ces fichiers pour visualiser les résultats détaillés des analyses.
|
|
107
|
+
|
|
108
|
+
## Utilisation en tant que Bibliothèque
|
|
109
|
+
|
|
110
|
+
Vous pouvez également importer les fonctions dans vos propres scripts Python pour une intégration plus poussée.
|
|
111
|
+
|
|
112
|
+
Installer
|
|
113
|
+
|
|
114
|
+
### Obtenir le résumé
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
# exemple_resume.py
|
|
118
|
+
from edb_noumea.main import get_water_quality
|
|
119
|
+
|
|
120
|
+
df_resume = get_water_quality()
|
|
121
|
+
|
|
122
|
+
if df_resume is not None:
|
|
123
|
+
print("Résumé de l'état des plages :")
|
|
124
|
+
print(df_resume.to_string())
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Obtenir les résultats détaillés
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
# exemple_details.py
|
|
131
|
+
from edb_noumea.details import get_detailed_results
|
|
132
|
+
|
|
133
|
+
df_details = get_detailed_results()
|
|
134
|
+
|
|
135
|
+
if df_details is not None:
|
|
136
|
+
print("Détails des derniers relevés :")
|
|
137
|
+
print(df_details.to_string())
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Exemple de Visualisation
|
|
141
|
+
|
|
142
|
+
Voici un exemple montrant comment récupérer les données détaillées et créer un graphique simple avec `matplotlib` pour visualiser les niveaux d'E. coli par point de prélèvement.
|
|
143
|
+
|
|
144
|
+
```python
|
|
145
|
+
# exemple_visualisation.py
|
|
146
|
+
import pandas as pd
|
|
147
|
+
import matplotlib.pyplot as plt
|
|
148
|
+
from edb_noumea.details import get_detailed_results
|
|
149
|
+
|
|
150
|
+
# Obtenir les données détaillées
|
|
151
|
+
df = get_detailed_results()
|
|
152
|
+
|
|
153
|
+
if df is not None and not df.empty:
|
|
154
|
+
print("Création du graphique...")
|
|
155
|
+
|
|
156
|
+
# S'assurer que les données sont triées pour une meilleure lisibilité
|
|
157
|
+
df_sorted = df.sort_values(by='e_coli_npp_100ml', ascending=False)
|
|
158
|
+
|
|
159
|
+
# Créer le graphique à barres horizontales
|
|
160
|
+
plt.figure(figsize=(12, 8))
|
|
161
|
+
plt.barh(df_sorted['point_de_prelevement'], df_sorted['e_coli_npp_100ml'], color='skyblue')
|
|
162
|
+
|
|
163
|
+
# Ajouter les titres et les étiquettes
|
|
164
|
+
plt.xlabel('E. coli (NPP/100ml)')
|
|
165
|
+
plt.ylabel('Point de prélèvement')
|
|
166
|
+
plt.title("Niveaux d'E. coli par Point de Prélèvement")
|
|
167
|
+
plt.gca().invert_yaxis() # Afficher le plus élevé en haut
|
|
168
|
+
plt.tight_layout() # Ajuster le layout pour que tout soit visible
|
|
169
|
+
|
|
170
|
+
# Sauvegarder le graphique dans un fichier
|
|
171
|
+
plt.savefig('ecoli_levels.png')
|
|
172
|
+
print("Graphique sauvegardé sous 'ecoli_levels.png'")
|
|
173
|
+
|
|
174
|
+
# Afficher le graphique
|
|
175
|
+
plt.show()
|
|
176
|
+
else:
|
|
177
|
+
print("Aucune donnée à afficher.")
|
|
178
|
+
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
*Assurez-vous que votre script est exécuté dans le même environnement virtuel où le package `edb-noumea` a été installé.*
|
|
182
|
+
|
|
183
|
+
## Sortie Attendue
|
|
184
|
+
|
|
185
|
+
### Résumé de l'état sanitaire (`main`)
|
|
186
|
+
```
|
|
187
|
+
📊 État sanitaire des eaux de baignade à Nouméa 📊
|
|
188
|
+
Plage État sanitaire
|
|
189
|
+
0 Plage de la baie des Citrons Baignade autorisée
|
|
190
|
+
1 Plage de la promenade Pierre-Vernier Baignade autorisée
|
|
191
|
+
...
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Détails des relevés (`details`)
|
|
195
|
+
```
|
|
196
|
+
📋 Voici les détails des derniers relevés :
|
|
197
|
+
Site Point de prélèvement Date Heure E. coli (NPP/100ml) Entérocoques (NPP/100ml)
|
|
198
|
+
0 PLAGE DE LA BAIE DES CITRONS P18049, Face The Beach House 04/09/2025 07:29 10 20
|
|
199
|
+
1 PLAGE DE LA BAIE DES CITRONS P18050, Face allée centrale Mirage plaza 04/09/2025 07:33 62 75
|
|
200
|
+
...
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# It's best practice to specify your build system
|
|
2
|
+
[build-system]
|
|
3
|
+
requires = ["setuptools>=61.0"]
|
|
4
|
+
build-backend = "setuptools.build_meta"
|
|
5
|
+
|
|
6
|
+
[project]
|
|
7
|
+
name = "edb-noumea"
|
|
8
|
+
version = "0.2.14"
|
|
9
|
+
description = "Un scraper pour la qualité des eaux de baignade à Nouméa."
|
|
10
|
+
# Add your name
|
|
11
|
+
authors = [
|
|
12
|
+
{ name="Adrien SALES" },
|
|
13
|
+
]
|
|
14
|
+
# Specify the license, e.g., "MIT"
|
|
15
|
+
license = { text="MIT" }
|
|
16
|
+
# Link your README for PyPI
|
|
17
|
+
readme = "README.md"
|
|
18
|
+
# Specify supported Python versions
|
|
19
|
+
|
|
20
|
+
# Add classifiers for better discoverability
|
|
21
|
+
classifiers = [
|
|
22
|
+
"Development Status :: 4 - Beta",
|
|
23
|
+
"Programming Language :: Python :: 3",
|
|
24
|
+
"License :: OSI Approved :: MIT License",
|
|
25
|
+
"Operating System :: OS Independent",
|
|
26
|
+
"Topic :: Utilities",
|
|
27
|
+
]
|
|
28
|
+
dependencies = [
|
|
29
|
+
"requests",
|
|
30
|
+
"beautifulsoup4",
|
|
31
|
+
"pandas",
|
|
32
|
+
"lxml",
|
|
33
|
+
"tabula-py",
|
|
34
|
+
"matplotlib",
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
[tool.setuptools.packages.find]
|
|
38
|
+
where = ["."]
|
|
39
|
+
|
|
40
|
+
[project.urls]
|
|
41
|
+
"Homepage" = "https://github.com/adriens/edb-noumea"
|
|
42
|
+
"Repository" = "https://github.com/adriens/edb-noumea"
|
|
43
|
+
|
|
44
|
+
# Example of optional dependencies for development
|
|
45
|
+
[project.optional-dependencies]
|
|
46
|
+
dev = [
|
|
47
|
+
"pytest",
|
|
48
|
+
"ruff", # A popular linter and formatter
|
|
49
|
+
]
|
edb_noumea-0.2.6/PKG-INFO
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: edb-noumea
|
|
3
|
-
Version: 0.2.6
|
|
4
|
-
Summary: Un scraper pour la qualité des eaux de baignade à Nouméa.
|
|
5
|
-
Project-URL: Homepage, https://github.com/adriens/edb-noumea
|
|
6
|
-
Project-URL: Repository, https://github.com/adriens/edb-noumea
|
|
7
|
-
Requires-Dist: requests
|
|
8
|
-
Requires-Dist: beautifulsoup4
|
|
9
|
-
Requires-Dist: pandas
|
|
10
|
-
Requires-Dist: lxml
|
|
11
|
-
Requires-Dist: tabula-py
|
|
12
|
-
Requires-Dist: matplotlib
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
def get_pdf_url():
|
|
2
|
-
"""
|
|
3
|
-
Alias public pour obtenir l'URL du dernier PDF d'analyses détaillées.
|
|
4
|
-
"""
|
|
5
|
-
return get_latest_pdf_url()
|
|
6
|
-
|
|
7
|
-
import pandas as pd
|
|
8
|
-
import tabula
|
|
9
|
-
import requests
|
|
10
|
-
import io
|
|
11
|
-
from bs4 import BeautifulSoup
|
|
12
|
-
|
|
13
|
-
# URL de la page officielle contenant le lien vers le PDF
|
|
14
|
-
PAGE_URL = "https://www.noumea.nc/noumea-pratique/salubrite-publique/qualite-eaux-baignade"
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
def get_latest_pdf_url():
|
|
18
|
-
"""
|
|
19
|
-
Récupère dynamiquement l'URL du dernier PDF d'analyses détaillées depuis la page officielle.
|
|
20
|
-
"""
|
|
21
|
-
print(f"🔗 Recherche du lien PDF sur {PAGE_URL} ...")
|
|
22
|
-
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36'}
|
|
23
|
-
try:
|
|
24
|
-
resp = requests.get(PAGE_URL, headers=headers)
|
|
25
|
-
resp.raise_for_status()
|
|
26
|
-
except Exception as e:
|
|
27
|
-
print(f"❌ Impossible de récupérer la page officielle : {e}")
|
|
28
|
-
return None
|
|
29
|
-
soup = BeautifulSoup(resp.text, "lxml")
|
|
30
|
-
# Chercher le premier lien PDF dans la page
|
|
31
|
-
link = soup.find("a", href=lambda h: h and h.endswith(".pdf"))
|
|
32
|
-
if not link:
|
|
33
|
-
print("❌ Aucun lien PDF trouvé sur la page.")
|
|
34
|
-
return None
|
|
35
|
-
pdf_url = link["href"]
|
|
36
|
-
# Si le lien est relatif, le rendre absolu
|
|
37
|
-
if pdf_url.startswith("/"):
|
|
38
|
-
pdf_url = "https://www.noumea.nc" + pdf_url
|
|
39
|
-
print(f"✅ Lien PDF trouvé : {pdf_url}")
|
|
40
|
-
return pdf_url
|
|
41
|
-
|
|
42
|
-
def get_detailed_results():
|
|
43
|
-
"""
|
|
44
|
-
Télécharge dynamiquement le PDF des résultats détaillés, en extrait le premier tableau
|
|
45
|
-
et le retourne sous forme de DataFrame pandas.
|
|
46
|
-
"""
|
|
47
|
-
pdf_url = get_latest_pdf_url()
|
|
48
|
-
if not pdf_url:
|
|
49
|
-
return None
|
|
50
|
-
print(f"📥 Téléchargement du PDF depuis {pdf_url} ...")
|
|
51
|
-
try:
|
|
52
|
-
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36'}
|
|
53
|
-
response = requests.get(pdf_url, headers=headers)
|
|
54
|
-
response.raise_for_status()
|
|
55
|
-
print("✅ Téléchargement terminé.")
|
|
56
|
-
except requests.exceptions.RequestException as e:
|
|
57
|
-
print(f"❌ Erreur lors du téléchargement du fichier PDF : {e}")
|
|
58
|
-
return None
|
|
59
|
-
|
|
60
|
-
pdf_file = io.BytesIO(response.content)
|
|
61
|
-
|
|
62
|
-
try:
|
|
63
|
-
print("🔍 Extraction des tableaux du PDF...")
|
|
64
|
-
tables = tabula.read_pdf(pdf_file, pages='1', stream=True)
|
|
65
|
-
except Exception as e:
|
|
66
|
-
print(f"❌ Une erreur est survenue lors de l'extraction des données du PDF.")
|
|
67
|
-
print("ℹ️ Cela peut être dû à l'absence de Java sur votre système, qui est requis par la bibliothèque 'tabula-py'.")
|
|
68
|
-
print(f" Erreur originale : {e}")
|
|
69
|
-
return None
|
|
70
|
-
|
|
71
|
-
if not tables:
|
|
72
|
-
print("❌ Aucun tableau n'a été trouvé dans le PDF.")
|
|
73
|
-
return None
|
|
74
|
-
|
|
75
|
-
print(f"✅ {len(tables)} tableau(x) trouvé(s). Affichage du premier.")
|
|
76
|
-
df = tables[0]
|
|
77
|
-
|
|
78
|
-
# --- Nettoyage du DataFrame ---
|
|
79
|
-
columns_to_keep = {
|
|
80
|
-
df.columns[0]: "site",
|
|
81
|
-
df.columns[1]: "point_de_prelevement",
|
|
82
|
-
df.columns[2]: "date",
|
|
83
|
-
df.columns[4]: "heure",
|
|
84
|
-
df.columns[6]: "e_coli_npp_100ml",
|
|
85
|
-
df.columns[9]: "enterocoques_npp_100ml"
|
|
86
|
-
}
|
|
87
|
-
cleaned_df = df[columns_to_keep.keys()].copy()
|
|
88
|
-
cleaned_df.rename(columns=columns_to_keep, inplace=True)
|
|
89
|
-
cleaned_df.replace({'<10': 0}, inplace=True)
|
|
90
|
-
cleaned_df['e_coli_npp_100ml'] = pd.to_numeric(cleaned_df['e_coli_npp_100ml'], errors='coerce')
|
|
91
|
-
cleaned_df['enterocoques_npp_100ml'] = pd.to_numeric(cleaned_df['enterocoques_npp_100ml'], errors='coerce')
|
|
92
|
-
cleaned_df.fillna(0, inplace=True)
|
|
93
|
-
|
|
94
|
-
# Split de la colonne point_de_prelevement
|
|
95
|
-
split_points = cleaned_df['point_de_prelevement'].str.split(',', n=1, expand=True)
|
|
96
|
-
cleaned_df['id_point_prelevement'] = split_points[0].str.strip()
|
|
97
|
-
cleaned_df['desc_point_prelevement'] = split_points[1].str.strip() if split_points.shape[1] > 1 else ''
|
|
98
|
-
|
|
99
|
-
# Conversion explicite de la colonne 'date' en type date Python
|
|
100
|
-
cleaned_df['date'] = pd.to_datetime(cleaned_df['date'], format='%d/%m/%Y', errors='coerce').dt.date
|
|
101
|
-
|
|
102
|
-
return cleaned_df
|
|
103
|
-
|
|
104
|
-
if __name__ == "__main__":
|
|
105
|
-
# Obtenir le DataFrame des résultats détaillés
|
|
106
|
-
detailed_df = get_detailed_results()
|
|
107
|
-
|
|
108
|
-
# Afficher le DataFrame s'il a été créé avec succès
|
|
109
|
-
if detailed_df is not None:
|
|
110
|
-
print("\n📋 Voici les détails des derniers relevés (toutes colonnes) :")
|
|
111
|
-
print(detailed_df)
|
|
112
|
-
print("\nColonnes du DataFrame :")
|
|
113
|
-
print(list(detailed_df.columns))
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: edb-noumea
|
|
3
|
-
Version: 0.2.6
|
|
4
|
-
Summary: Un scraper pour la qualité des eaux de baignade à Nouméa.
|
|
5
|
-
Project-URL: Homepage, https://github.com/adriens/edb-noumea
|
|
6
|
-
Project-URL: Repository, https://github.com/adriens/edb-noumea
|
|
7
|
-
Requires-Dist: requests
|
|
8
|
-
Requires-Dist: beautifulsoup4
|
|
9
|
-
Requires-Dist: pandas
|
|
10
|
-
Requires-Dist: lxml
|
|
11
|
-
Requires-Dist: tabula-py
|
|
12
|
-
Requires-Dist: matplotlib
|
edb_noumea-0.2.6/pyproject.toml
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
[project]
|
|
2
|
-
name = "edb-noumea"
|
|
3
|
-
version = "0.2.6"
|
|
4
|
-
description = "Un scraper pour la qualité des eaux de baignade à Nouméa."
|
|
5
|
-
dependencies = [
|
|
6
|
-
"requests",
|
|
7
|
-
"beautifulsoup4",
|
|
8
|
-
"pandas",
|
|
9
|
-
"lxml",
|
|
10
|
-
"tabula-py",
|
|
11
|
-
"matplotlib",
|
|
12
|
-
]
|
|
13
|
-
|
|
14
|
-
[tool.setuptools.packages.find]
|
|
15
|
-
where = ["."]
|
|
16
|
-
|
|
17
|
-
[project.urls]
|
|
18
|
-
"Homepage" = "https://github.com/adriens/edb-noumea"
|
|
19
|
-
"Repository" = "https://github.com/adriens/edb-noumea"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|