# Application Gestion de sondes # -*- coding: utf-8 -*- from logging import exception import streamlit as st import mysql.connector import pandas as pd from datetime import date import matplotlib.pyplot as plt import matplotlib.dates as mdates from fpdf import FPDF import os import random import datetime from dotenv import load_dotenv # Charger les variables d'environnement load_dotenv() st.set_page_config(page_title="Domo91 - Surveillance", layout="wide") if "authenticated" not in st.session_state: st.session_state["authenticated"] = False st.session_state["role"] = None st.session_state["lieu_autorise"] = None st.title("📡 Supervision Températures") db_config = { "host": os.getenv("DB_HOST"), "user": os.getenv("DB_USER"), "password": os.getenv("DB_PASSWORD"), "database": os.getenv("DB_NAME") } # --- Fonction de génération PDF --- def generer_pdf(site, date_str): st.info(f"Génération du rapport PDF pour {site} à la date {date_str}") try: conn = mysql.connector.connect(**db_config) pdf_cursor = conn.cursor(dictionary=True) cursor.execute(f"SELECT Sonde, Date, Temperature FROM `{site}` WHERE DATE(Date) = %s ORDER BY Sonde, Date", (date_str,)) rows = cursor.fetchall() df = pd.DataFrame(rows) df["Heure"] = pd.to_datetime(df["Date"]).dt.strftime("%H:%M") releves = {} for sonde in df["Sonde"].unique(): df_sonde = df[df["Sonde"] == sonde] releves[sonde] = list(zip(df_sonde["Heure"], df_sonde["Temperature"])) table_alertes = f"Alertes_{site}" cursor.execute(f"SELECT Sonde, Debut_defaut, Status FROM {table_alertes} WHERE DATE(Debut_defaut) = %s", (date_str,)) alertes = cursor.fetchall() cursor.close() conn.close() class RapportPDF(FPDF): def header(self): self.set_font("Arial", "B", 14) self.cell(0, 10, "Rapport de surveillance des sondes", ln=1, align="C") self.set_font("Arial", "", 12) self.cell(0, 10, f"Date : {date_str}", ln=1, align="C") self.ln(5) def site_info(self, site_name): self.set_font("Arial", "B", 12) self.cell(0, 10, f"Site : {site_name}", ln=1) self.ln(2) def releves_section(self, data): self.set_font("Arial", "B", 12) self.cell(0, 10, "Relevés de température", ln=1) for sonde, mesures in data.items(): self.set_font("Arial", "B", 11) self.cell(0, 8, f"Sonde : {sonde}", ln=1) self.set_font("Arial", "", 10) for heure, temp in mesures: self.cell(0, 6, f"{heure} - {temp} °C", ln=1) self.ln(2) def alertes_section(self, data): self.set_font("Arial", "B", 12) self.cell(0, 10, "Alertes enregistrées", ln=1) self.set_font("Arial", "", 10) for a in data: self.cell(0, 6, f"{a['Sonde']} - {a['Debut_defaut']} - {a['Status']}", ln=1) pdf = RapportPDF() pdf.add_page() pdf.site_info(site) pdf.releves_section(releves) pdf.alertes_section(alertes) file_name = f"rapport_{site}_{date_str}.pdf" output_dir = "PDF" os.makedirs(output_dir, exist_ok=True) # 🔧 Crée le dossier si absent output_path = os.path.join(output_dir, file_name) pdf.output(output_path) with open(output_path, "rb") as f: st.download_button( label="📥 Télécharger le rapport PDF", data=f, file_name=file_name, mime="application/pdf" ) except Exception as err1: st.error(f"Erreur lors de la génération du PDF : {err1}") # --- Initialisation des variables de session --- if "authenticated" not in st.session_state: st.session_state["authenticated"] = False if "role" not in st.session_state: st.session_state["role"] = None if "lieu_autorise" not in st.session_state: st.session_state["lieu_autorise"] = None # --- Connexion utilisateur dans la sidebar --- <<<<<<< HEAD st.sidebar.header("🔐 Connexion") ======= st.sidebar.header("🔐 Connexion") >>>>>>> origin/develop if not st.session_state.get("authenticated"): login = st.sidebar.text_input("Nom d'utilisateur") password = st.sidebar.text_input("Mot de passe", type="password") if st.sidebar.button("Se connecter"): try: conn = mysql.connector.connect(**db_config) cursor = conn.cursor(dictionary=True) cursor.execute("SELECT * FROM MotsDePasse WHERE utilisateur = %s", (login,)) result = cursor.fetchone() if result and result["mot_de_passe"] == password: st.session_state["authenticated"] = True st.session_state["role"] = result["role"] st.session_state["lieu_autorise"] = result["Lieu"] st.success(f"Connecté comme {result['role']} ({result['Lieu']})") # Enregistrement de la connexion dans Connexion_Log try: now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") cursor.execute(""" INSERT INTO Connexion_Log (Utilisateur, Lieu, Date_Connexion) VALUES (%s, %s, %s) """, (login, result["Lieu"], now)) conn.commit() except Exception as e: st.warning(f"⚠️ Connexion enregistrée échouée : {e}") st.rerun() else: st.sidebar.error("Identifiants invalides") cursor.close() conn.close() except Exception as e: st.sidebar.error(f"Erreur lors de la connexion à la base : {e}") else: st.sidebar.success(f"Connecté ({st.session_state['role']})") if st.sidebar.button("🔓 Déconnexion", key="logout_sidebar"): st.session_state["authenticated"] = False st.session_state["role"] = None st.session_state["lieu_autorise"] = None st.rerun() # 📄 Affichage bouton PDF si une date est choisie site_pdf = ( st.session_state.get("lieu_autorise") if st.session_state.get("role") != "superviseur" else st.session_state.get("selected_site") ) date_pdf = st.session_state.get("selected_date") if site_pdf and date_pdf: st.sidebar.markdown("---") st.sidebar.subheader("📄 Rapport PDF") if st.sidebar.button("📥 Télécharger l’état du jour (PDF)", key="pdf_btn"): generer_pdf(site_pdf, date_pdf.strftime("%Y-%m-%d")) # 🔔 Forcer une alerte de test dynamique avec choix de la sonde (réservé aux superviseurs) if st.session_state.get("authenticated") and st.session_state.get("role") == "superviseur": site_actuel = ( st.session_state.get("lieu_autorise") if st.session_state.get("role") != "superviseur" else st.session_state.get("selected_site") ) if site_actuel: try: conn = mysql.connector.connect(**db_config) cursor = conn.cursor() cursor.execute(f"SELECT DISTINCT Sonde FROM `{site_actuel}` ORDER BY Sonde ASC") sondes_dispo = [row[0] for row in cursor.fetchall()] cursor.close() conn.close() if sondes_dispo: st.sidebar.markdown("---") st.sidebar.subheader("🧪 Test alerte manuelle") sonde_test = st.sidebar.selectbox("Choisir une sonde :", sondes_dispo) if st.sidebar.button("🔔 Forcer une alerte de test"): now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") try: conn = mysql.connector.connect(**db_config) cursor = conn.cursor() table_alertes = f"Alertes_{site_actuel}" cursor.execute(f""" INSERT INTO {table_alertes} (Sonde, Debut_defaut, Status) VALUES (%s, %s, %s) """, (sonde_test, now, "Test")) conn.commit() cursor.close() conn.close() st.success(f"Alerte de test créée pour {sonde_test} à {now}") except Exception as e: st.error(f"Erreur lors de la création de l'alerte : {e}") except Exception as e: st.sidebar.warning(f"Erreur chargement des sondes : {e}") else: st.success(f"Connecté ({st.session_state['role']})") if st.button("🔓 Déconnexion", key="logout_main"): st.session_state["authenticated"] = False st.session_state["role"] = None st.session_state["lieu_autorise"] = None st.rerun() st.markdown("---") st.subheader("📄 Rapport PDF") if "selected_date" in st.session_state: if st.button("📥 Télécharger l’état du jour (PDF)"): site = st.session_state["lieu_autorise"] date_val = st.session_state["selected_date"].strftime("%Y-%m-%d") generer_pdf(site, date_val) else: st.info("Sélectionnez une date pour activer la génération PDF.") # --- CONTENU PRINCIPAL SI AUTHENTIFIÉ --- if st.session_state["authenticated"]: # --- AFFICHAGE GLOBAL DES ALERTES NON ACQUITTÉES --- try: conn = mysql.connector.connect(**db_config) cursor = conn.cursor(dictionary=True) if "role" not in st.session_state: st.session_state["role"] = None if "lieu_autorise" not in st.session_state: st.session_state["lieu_autorise"] = None site_selectionne = ( st.session_state["lieu_autorise"] if st.session_state["role"] != "superviseur" else st.session_state.get("selected_site", "Saclay") ) table_alertes = f"Alertes_{site_selectionne}" cursor.execute( f"SELECT Sonde, Debut_defaut, Status FROM `{table_alertes}` WHERE Status != 'Acquitté' ORDER BY Debut_defaut DESC" ) alertes = cursor.fetchall() if alertes: df_alertes = pd.DataFrame(alertes) st.subheader("🚨 Alertes non acquittées") st.dataframe(df_alertes, use_container_width=True) else: st.success("✅ Aucune alerte en cours.") cursor.close() conn.close() except Exception as e: st.error(f"Erreur lors de la récupération des alertes : {e}") # --- NAVIGATION --- if st.session_state["role"] == "superviseur": onglet = st.sidebar.radio("📁 Navigation", ["Accueil", "Statistiques", "Traffic"]) # --- ONGLET ACCUEIL --- else: onglet = "Accueil" >>>>>>> origin/develop if onglet == "Accueil": st.markdown("## Sélection du site et de la date") try: conn = mysql.connector.connect(**db_config) cursor = conn.cursor(dictionary=True) sites_possibles = ["Saclay", "Meudon"] if st.session_state["role"] == "superviseur": site_selectionne = st.selectbox("📍 Choisissez un site :", sites_possibles) st.session_state["selected_site"] = site_selectionne else: site_selectionne = st.session_state["lieu_autorise"] st.info(f"Site imposé : {site_selectionne}") selected_date = st.date_input("📅 Date du relevé", value=date.today()) st.session_state["selected_date"] = selected_date cursor.execute( f"SELECT * FROM `{site_selectionne}` WHERE DATE(Date) = %s ORDER BY Sonde, Date", (selected_date.strftime("%Y-%m-%d"),) ) rows = cursor.fetchall() if rows: df = pd.DataFrame(rows) df["Date"] = pd.to_datetime(df["Date"]) sondes = sorted(df["Sonde"].unique()) sonde_choisie = st.selectbox("🧪 Choisissez une sonde :", sondes, key="selectbox_accueil") df_sonde = df[df["Sonde"] == sonde_choisie] df_sonde.loc[:, "Heure"] = df_sonde["Date"].dt.hour tranche = st.radio("🕒 Tranche horaire :", ["Toute la journée", "Matin (6h-12h)", "Après-midi (12h-18h)", "Nuit (18h-6h)"]) if tranche == "Matin (6h-12h)": df_sonde = df_sonde[(df_sonde["Heure"] >= 6) & (df_sonde["Heure"] < 12)] elif tranche == "Après-midi (12h-18h)": df_sonde = df_sonde[(df_sonde["Heure"] >= 12) & (df_sonde["Heure"] < 18)] elif tranche == "Nuit (18h-6h)": df_sonde = df_sonde[(df_sonde["Heure"] >= 18) | (df_sonde["Heure"] < 6)] df_sonde = df_sonde.copy() cursor.execute("SELECT Temp_Max FROM Chambres_froides WHERE Lieu = %s AND Sonde = %s", (site_selectionne, sonde_choisie)) seuil = cursor.fetchone() seuil_temp = seuil["Temp_Max"] if seuil else 10 st.subheader("📊 Tableau des relevés") df_filtre = df_sonde.copy() df_filtre = df_filtre.drop(columns="Id", errors="ignore") def surlignage_temp(val): try: if float(val) > seuil_temp: return "color: red; font-weight: bold" except: pass return "" styled_df = df_filtre.style.applymap(surlignage_temp, subset=["Temperature"]) st.dataframe(styled_df, use_container_width=True) st.subheader("📈 Évolution de la température") fig, ax = plt.subplots(figsize=(10, 4)) ax.plot(df_filtre["Date"], df_filtre["Temperature"], marker='o', label="Température") ax.axhline(seuil_temp, color='red', linestyle='--', label=f"Seuil {seuil_temp}°C") ax.set_xlabel("Heure") ax.set_ylabel("Température (°C)") ax.set_title(f"{sonde_choisie} - {selected_date.strftime('%d/%m/%Y')}") ax.xaxis.set_major_formatter(mdates.DateFormatter('%H:%M')) ax.legend() st.pyplot(fig) cursor.close() conn.close() except Exception as e: st.error(f"Erreur MySQL : {e}") <<<<<<< HEAD ======= # ---- ONGLET STATISTIQUES --- >>>>>>> origin/develop elif onglet == "Statistiques": st.markdown("## 📈 Statistiques de température") try: conn = mysql.connector.connect(**db_config) cursor = conn.cursor(dictionary=True) site = ( st.session_state["lieu_autorise"] if st.session_state["role"] != "superviseur" else st.session_state.get("selected_site", "Saclay") ) date_val = st.session_state.get("selected_date", date.today()) cursor.execute( f"SELECT * FROM `{site}` WHERE DATE(Date) = %s ORDER BY Sonde, Date", (date_val.strftime("%Y-%m-%d"),) ) rows = cursor.fetchall() df = pd.DataFrame(rows) if df.empty: st.info("Aucune donnée pour cette date.") else: df["Date"] = pd.to_datetime(df["Date"]) sondes = sorted(df["Sonde"].unique()) sonde = st.selectbox("Choisir une sonde :", sondes, key="selectbox_stats") df_sonde = df[df["Sonde"] == sonde] st.subheader("Évolution journalière") fig, ax = plt.subplots(figsize=(10, 4)) ax.plot(df_sonde["Date"], df_sonde["Temperature"], marker='o') ax.set_title(f"{sonde} - {date_val.strftime('%d/%m/%Y')}") ax.set_xlabel("Heure") ax.set_ylabel("Température (°C)") ax.xaxis.set_major_formatter(mdates.DateFormatter('%H:%M')) st.pyplot(fig) cursor.close() conn.close() except Exception as e: st.error(f"Erreur chargement statistiques : {e}") # --- Affichage automatique des alertes non acquittées --- try: conn = mysql.connector.connect(**db_config) cursor = conn.cursor(dictionary=True) table_alertes = f"Alertes_{site_selectionne}" cursor.close() conn.close() except Exception as e: st.error(f"Erreur lors de la récupération des alertes : {e}") # --- Fonctionnalités administrateur : ajout et gestion des chambres froides --- if st.session_state["role"] == "superviseur": with st.expander("+ Ajouter une nouvelle chambre froide", expanded=False): with st.form("ajout_sonde"): nouvelle_sonde = st.text_input("Nom de la sonde") temp_max = st.number_input("Température maximale autorisée (°C)", value=4) etat_on = st.checkbox("État actif (ON)", value=True) submitted = st.form_submit_button("✅ Ajouter") if submitted: try: conn_add = mysql.connector.connect(**db_config) cursor_add = conn_add.cursor() cursor_add.execute( "INSERT INTO Chambres_froides (Lieu, Sonde, Temp_Max, Status) VALUES (%s, %s, %s, %s)", (site, nouvelle_sonde, temp_max, "ON" if etat_on else "OFF") ) conn_add.commit() cursor_add.close() conn_add.close() st.success(f"Sonde '{nouvelle_sonde}' ajoutée avec succès au site {site} 🎉") st.session_state["refresh_admin"] = random.randint(1000, 9999) except Exception as e: st.error(f"Erreur lors de l'ajout : {e}") with st.expander("🛠️ Gestion des chambres froides (administrateur)", expanded=True): if st.button("🔄 Actualiser la liste"): st.session_state["refresh_admin"] = random.randint(0, 9999) try: conn_admin = mysql.connector.connect(**db_config) cursor_admin = conn_admin.cursor(dictionary=True) cursor_admin.execute("SELECT * FROM Chambres_froides WHERE Lieu = %s", (site,)) chambres = cursor_admin.fetchall() if not chambres: st.warning("Aucune chambre froide pour ce site.") else: for chambre in chambres: col1, col2, col3 = st.columns([3, 1, 2]) with col1: st.markdown(f"**{chambre['Sonde']}**") with col2: etat = st.checkbox("ON", value=(chambre["Etat"] == "ON"), key=f"etat_{chambre['Id']}_{st.session_state.get('refresh_admin', 0)}") new_etat = "ON" if etat else "OFF" with col3: temp_max = chambre["Temp_Max"] moins, val, plus = st.columns([1, 2, 1]) with moins: if st.button("▼", key=f"moins_{chambre['Id']}"): temp_max -= 1 with val: st.markdown(f"