Frontend - app.py
Application Streamlit complète pour l’interface utilisateur (489 lignes).
Code Source
# Imports nécessaires pour l'interface Streamlit
import streamlit as st # Framework pour créer l'interface web
import requests # Pour communiquer avec l'API backend
import pandas as pd # Pour manipuler et afficher les données tabulaires
from datetime import datetime
import os
# URL de l'API backend FastAPI
# Utilise la variable d'environnement API_URL si disponible (pour Docker), sinon localhost
API_URL = os.getenv("API_URL", "http://localhost:8000")
# Configuration de la page Streamlit
st.set_page_config(
page_title="EcoData Platform", # Titre de l'onglet du navigateur
page_icon="🌍", # Icône de l'onglet
layout="wide", # Utilise toute la largeur de l'écran
initial_sidebar_state="expanded"
)
# CSS personnalisé pour une ambiance écologique
st.markdown("""
<style>
.main {
background: linear-gradient(135deg, #e8f5e9 0%, #c8e6c9 100%);
}
.stApp {
background: linear-gradient(to bottom, #e3f2fd, #f1f8e9);
}
h1 {
color: #2e7d32;
text-align: center;
font-size: 3em;
margin-bottom: 0;
}
.subtitle {
text-align: center;
color: #558b2f;
font-size: 1.3em;
font-style: italic;
margin-bottom: 30px;
}
.quote {
background: linear-gradient(120deg, #a5d6a7, #81c784);
padding: 20px;
border-radius: 10px;
margin: 20px 0;
border-left: 5px solid #2e7d32;
color: #1b5e20;
font-size: 1.1em;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
}
.stButton>button {
background-color: #4caf50;
color: white;
border-radius: 8px;
}
.stButton>button:hover {
background-color: #45a049;
}
</style>
""", unsafe_allow_html=True)
# En-tête avec image de fond et titre
st.markdown("""
<div style='text-align: center; padding: 20px; background: linear-gradient(135deg, #66bb6a 0%, #43a047 100%);
border-radius: 15px; margin-bottom: 30px; box-shadow: 0 8px 16px rgba(0,0,0,0.2);'>
<h1 style='color: white; margin: 0; font-size: 3.5em;'>🌍 EcoData Platform</h1>
<p style='color: #e8f5e9; font-size: 1.5em; margin-top: 10px; font-style: italic;'>
"Agir aujourd'hui pour préserver demain"
</p>
<p style='color: white; font-size: 1.1em; margin-top: 5px;'>
Plateforme collaborative de collecte de données pour le calcul du bilan carbone
</p>
</div>
""", unsafe_allow_html=True)
# Menu de navigation dans la barre latérale
st.sidebar.image("https://cdn-icons-png.flaticon.com/512/2913/2913133.png", width=100)
st.sidebar.markdown("### 🌱 Navigation")
menu = st.sidebar.selectbox(
"Choisissez une section :",
["🏠 Accueil", "📤 Déposer des Données", " Tableau de Bord", "📂 Mes Fichiers", " Analyses"],
label_visibility="collapsed"
)
# ============================================
# PAGE ACCUEIL
# ============================================
if menu == "🏠 Accueil":
# Message de bienvenue
st.markdown("""
<div class='quote'>
<h2 style='color: #1b5e20; text-align: center;'>🌿 Bienvenue sur EcoData Platform</h2>
<p style='text-align: center; font-size: 1.2em;'>
Ensemble, mesurons notre impact pour mieux le réduire !
</p>
</div>
""", unsafe_allow_html=True)
# Description de la plateforme
col1, col2 = st.columns(2)
with col1:
st.markdown("""
### Notre Mission
Faciliter la collecte et l'analyse des données environnementales pour :
- 🌱 **Calculer** votre empreinte carbone
- **Visualiser** vos émissions de CO₂
- **Identifier** les axes d'amélioration
- 🌍 **Agir** pour un avenir durable
> *"Chaque donnée partagée est un pas vers un avenir plus vert"*
""")
with col2:
st.markdown("""
### 🤝 Comment ça marche ?
1. **📤 Déposez vos données** : Fichiers CSV ou Excel
2. ** Nous analysons** : Calcul automatique des indicateurs
3. ** Consultez les résultats** : Tableaux de bord interactifs
4. ** Passez à l'action** : Recommandations personnalisées
> *"Mesurer pour mieux agir, agir pour mieux préserver"*
""")
# Statistiques en temps réel
st.markdown("---")
st.markdown("### Impact de la Communauté")
try:
response = requests.get(f"{API_URL}/api/stats")
if response.status_code == 200:
stats = response.json()
col1, col2, col3, col4 = st.columns(4)
with col1:
st.metric(
label="🌍 Fichiers analysés",
value=stats['total_files'],
help="Nombre total de fichiers déposés"
)
with col2:
st.metric(
label=" Données collectées",
value=f"{stats['total_size_mb']} MB",
help="Volume total de données traitées"
)
with col3:
files_by_type = stats.get('files_by_type', {})
st.metric(
label="👥 Contributeurs actifs",
value=len(files_by_type),
help="Nombre de types d'utilisateurs"
)
with col4:
st.metric(
label="🌱 Impact positif",
value="En cours",
help="CO₂ évité grâce à nos actions"
)
except:
st.info(" Connectez-vous pour voir les statistiques en temps réel")
# Call to action
st.markdown("---")
st.markdown("""
<div style='text-align: center; padding: 30px; background: linear-gradient(135deg, #81c784, #66bb6a);
border-radius: 15px; margin-top: 30px;'>
<h2 style='color: white;'> Prêt à contribuer ?</h2>
<p style='color: white; font-size: 1.2em;'>
Déposez vos premières données et découvrez votre impact environnemental !
</p>
</div>
""", unsafe_allow_html=True)
# ============================================
# PAGE 1: UPLOAD DE FICHIERS
# ============================================
elif menu == "📤 Déposer des Données":
st.markdown("""
<div class='quote'>
<h3>🌱 Partagez vos données, construisons ensemble un avenir durable</h3>
<p><em>"Chaque fichier déposé nous rapproche d'une planète plus verte"</em></p>
</div>
""", unsafe_allow_html=True)
st.markdown("""
### 📋 Instructions
Déposez vos fichiers de données (CSV ou Excel) contenant vos émissions, consommations ou activités.
Nous les analyserons pour calculer votre empreinte carbone.
""")
st.header("📤 Téléversement de fichiers")
# Formulaire pour saisir les informations de l'utilisateur
col1, col2 = st.columns(2)
with col1:
# Sélection du type d'utilisateur
user_type = st.selectbox(
"Type d'utilisateur",
["metier", "partenaire", "particulier"]
)
with col2:
# Saisie du nom de l'utilisateur
user_name = st.text_input("Nom", "")
# Widget pour sélectionner un fichier à uploader
uploaded_file = st.file_uploader(
"Choisir un fichier CSV ou Excel",
type=['csv', 'xlsx', 'xls']
)
# Si un fichier a été sélectionné
if uploaded_file is not None:
st.info(f"Fichier sélectionné: {uploaded_file.name}")
# Afficher un aperçu du contenu du fichier
try:
# Lire le fichier selon son extension
if uploaded_file.name.endswith('.csv'):
df_preview = pd.read_csv(uploaded_file)
else:
df_preview = pd.read_excel(uploaded_file)
# Afficher les 10 premières lignes
st.write("Aperçu des données:")
st.dataframe(df_preview.head(10))
st.write(f"Nombre de lignes: {len(df_preview)}")
st.write(f"Nombre de colonnes: {len(df_preview.columns)}")
# Repositionner le curseur au début du fichier pour l'upload
uploaded_file.seek(0)
except Exception as e:
st.error(f"Erreur lors de la lecture du fichier: {str(e)}")
# Bouton pour envoyer le fichier à l'API
if st.button("Téléverser"):
if not user_name:
st.error("Veuillez entrer votre nom")
else:
try:
# Préparer les données pour la requête POST
files = {"file": (uploaded_file.name, uploaded_file, uploaded_file.type)}
data = {"user_type": user_type, "user_name": user_name}
# Envoyer le fichier à l'API backend
response = requests.post(
f"{API_URL}/api/upload",
files=files,
data=data
)
# Afficher le résultat de l'upload
if response.status_code == 200:
result = response.json()
st.success(f" {result['message']}")
st.json(result)
else:
st.error(f"Erreur: {response.text}")
except Exception as e:
st.error(f"Erreur de connexion: {str(e)}")
# ============================================
# PAGE 2: DASHBOARD
# ============================================
elif menu == " Tableau de Bord":
st.markdown("""
<div class='quote'>
<h3> Visualisez votre impact environnemental en temps réel</h3>
<p><em>"Voir pour comprendre, comprendre pour agir"</em></p>
</div>
""", unsafe_allow_html=True)
st.markdown("""
### 🌍 Vue d'ensemble
Découvrez les statistiques globales et suivez l'évolution de votre empreinte carbone.
""")
st.header(" Tableau de Bord")
# Récupérer les statistiques globales depuis l'API
try:
response = requests.get(f"{API_URL}/api/stats")
if response.status_code == 200:
stats = response.json()
# Afficher les métriques principales en 3 colonnes
col1, col2, col3 = st.columns(3)
with col1:
st.metric("Total Fichiers", stats['total_files'])
with col2:
st.metric("Taille Totale", f"{stats['total_size_mb']} MB")
with col3:
files_by_type = stats.get('files_by_type', {})
st.metric("Types d'utilisateurs", len(files_by_type))
# Afficher un graphique de répartition par type d'utilisateur
if files_by_type:
st.subheader("Répartition par type d'utilisateur")
df_types = pd.DataFrame(
list(files_by_type.items()),
columns=['Type', 'Nombre']
)
st.bar_chart(df_types.set_index('Type'))
except Exception as e:
st.error(f"Erreur de connexion à l'API: {str(e)}")
# ============================================
# PAGE 3: GESTION DES FICHIERS
# ============================================
elif menu == "📂 Mes Fichiers":
st.markdown("""
<div class='quote'>
<h3>📂 Gérez vos contributions à la transition écologique</h3>
<p><em>"Organisez vos données, optimisez votre impact"</em></p>
</div>
""", unsafe_allow_html=True)
st.markdown("""
### 🗂️ Vos données
Consultez, filtrez et gérez l'ensemble de vos fichiers déposés sur la plateforme.
""")
st.header("📂 Gestion des fichiers")
# Filtre pour afficher les fichiers par type d'utilisateur
user_type_filter = st.selectbox(
"Filtrer par type d'utilisateur",
["Tous", "metier", "partenaire", "particulier"]
)
# Récupérer la liste des fichiers depuis l'API
try:
params = {}
if user_type_filter != "Tous":
params['user_type'] = user_type_filter
response = requests.get(f"{API_URL}/api/files", params=params)
if response.status_code == 200:
files = response.json()
if files:
# Créer un DataFrame pandas pour afficher les données
df = pd.DataFrame(files)
# Formater la date
df['upload_date'] = pd.to_datetime(df['upload_date']).dt.strftime('%Y-%m-%d %H:%M')
# Convertir la taille en MB
df['file_size_mb'] = (df['file_size'] / (1024 * 1024)).round(2)
# Sélectionner les colonnes à afficher
display_df = df[[
'id', 'filename', 'user_type', 'user_name',
'rows_count', 'file_size_mb', 'upload_date'
]]
# Renommer les colonnes pour l'affichage
display_df.columns = [
'ID', 'Fichier', 'Type', 'Utilisateur',
'Lignes', 'Taille (MB)', 'Date'
]
# Afficher le tableau
st.dataframe(display_df, use_container_width=True)
# Section pour supprimer un fichier
st.subheader("Actions")
file_id_to_delete = st.number_input(
"ID du fichier à supprimer",
min_value=1,
step=1
)
if st.button("Supprimer"):
# Envoyer une requête DELETE à l'API
delete_response = requests.delete(
f"{API_URL}/api/files/{file_id_to_delete}"
)
if delete_response.status_code == 200:
st.success("Fichier supprimé avec succès")
st.rerun() # Recharger la page
else:
st.error("Erreur lors de la suppression")
else:
st.info("Aucun fichier trouvé")
except Exception as e:
st.error(f"Erreur de connexion: {str(e)}")
# ============================================
# PAGE 4: STATISTIQUES DÉTAILLÉES
# ============================================
elif menu == " Analyses":
st.markdown("""
<div class='quote'>
<h3> Analyses approfondies pour un impact mesurable</h3>
<p><em>"Des données précises pour des décisions éclairées"</em></p>
</div>
""", unsafe_allow_html=True)
st.markdown("""
### 🔬 Analyses détaillées
Explorez en profondeur les tendances, évolutions et performances environnementales.
""")
st.header(" Statistiques détaillées")
# Récupérer tous les fichiers pour créer des statistiques détaillées
try:
response = requests.get(f"{API_URL}/api/files")
if response.status_code == 200:
files = response.json()
if files:
df = pd.DataFrame(files)
df['upload_date'] = pd.to_datetime(df['upload_date'])
# Graphique 1: Évolution des uploads dans le temps
st.subheader("Évolution des uploads")
df['date'] = df['upload_date'].dt.date
uploads_by_date = df.groupby('date').size()
st.line_chart(uploads_by_date)
# Graphique 2: Distribution par type d'utilisateur
st.subheader("Distribution par type d'utilisateur")
user_type_counts = df['user_type'].value_counts()
st.bar_chart(user_type_counts)
# Tableau: Top 10 des fichiers les plus volumineux
st.subheader("Top 10 fichiers les plus volumineux")
top_files = df.nlargest(10, 'file_size')[
['filename', 'user_name', 'file_size', 'rows_count']
]
# Convertir la taille en MB
top_files['file_size_mb'] = (top_files['file_size'] / (1024 * 1024)).round(2)
st.dataframe(top_files[['filename', 'user_name', 'file_size_mb', 'rows_count']])
else:
st.info("Aucune donnée disponible")
except Exception as e:
st.error(f"Erreur: {str(e)}")
# ============================================
# PIED DE PAGE DE LA BARRE LATÉRALE
# ============================================
st.sidebar.markdown("---")
st.sidebar.markdown("""
<div style='text-align: center; padding: 15px; background: linear-gradient(135deg, #a5d6a7, #81c784);
border-radius: 10px; margin-top: 20px;'>
<h3 style='color: #1b5e20; margin: 0;'>🌍 EcoData Platform</h3>
<p style='color: #2e7d32; margin: 5px 0;'><strong>Version 1.0.0</strong></p>
<p style='color: #1b5e20; font-size: 0.9em; font-style: italic;'>
"Ensemble pour une planète durable"
</p>
<p style='color: #2e7d32; margin-top: 10px; font-size: 0.8em;'>
© 2025 ESIEE Paris<br>
🌱 Chaque donnée compte
</p>
</div>
""", unsafe_allow_html=True)
# Citation écologique aléatoire
import random
quotes = [
"🌿 La nature ne fait rien en vain",
"🌍 Soyez le changement que vous voulez voir",
"♻️ Réduire, réutiliser, recycler",
"🌱 Chaque geste compte pour notre planète",
"💚 Protégeons notre maison commune",
"🌊 L'eau, l'air, la terre : notre héritage"
]
st.sidebar.info(random.choice(quotes))