Correction bugs mineurs Domo91
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 746 KiB After Width: | Height: | Size: 1.6 MiB |
BIN
app/assets/QR_Domo91FR.png
Normal file
BIN
app/assets/QR_Domo91FR.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 40 KiB |
BIN
app/assets/qr_domo91.png
Normal file
BIN
app/assets/qr_domo91.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 40 KiB |
377
app/domo91.py
377
app/domo91.py
@@ -4,12 +4,12 @@ import random
|
||||
import traceback
|
||||
from datetime import datetime, date, time
|
||||
from contextlib import closing
|
||||
|
||||
import bcrypt
|
||||
import matplotlib.dates as mdates
|
||||
import matplotlib.pyplot as plt
|
||||
import mysql.connector
|
||||
import pandas as pd
|
||||
|
||||
pd.set_option("future.no_silent_downcasting", True)
|
||||
import streamlit as st
|
||||
from PIL import Image
|
||||
@@ -17,7 +17,6 @@ from dotenv import find_dotenv, load_dotenv
|
||||
from fpdf import FPDF
|
||||
from streamlit_autorefresh import st_autorefresh
|
||||
|
||||
|
||||
# =========================================================
|
||||
# Config de page
|
||||
# =========================================================
|
||||
@@ -32,7 +31,6 @@ st.sidebar.image(logo, use_container_width=True)
|
||||
st.title("📊 Domo91 - Surveillance des sondes")
|
||||
st.write("Bienvenue sur l’application de supervision.")
|
||||
|
||||
|
||||
# =========================================================
|
||||
# ENV & DB
|
||||
# =========================================================
|
||||
@@ -62,6 +60,24 @@ def assert_site_ok(site_name: str):
|
||||
if site_name not in SITES_AUTORISES:
|
||||
raise ValueError(f"Site invalide: {site_name}")
|
||||
|
||||
|
||||
def get_chambres_froides_actives(conn, site: str):
|
||||
"""Retourne la liste des chambres froides actives (Etat=ON) pour un site, avec leur Temp_Max."""
|
||||
assert_site_ok(site)
|
||||
with closing(conn.cursor(dictionary=True)) as cur:
|
||||
cur.execute(
|
||||
"""
|
||||
SELECT Sonde, Temp_Max
|
||||
FROM Sondes.Chambres_froides
|
||||
WHERE Lieu = %s
|
||||
AND UPPER(Etat) = 'ON'
|
||||
ORDER BY Sonde
|
||||
""",
|
||||
(site,),
|
||||
)
|
||||
return cur.fetchall()
|
||||
|
||||
|
||||
# =========================================================
|
||||
# Session state
|
||||
# =========================================================
|
||||
@@ -76,6 +92,7 @@ for key, default in {
|
||||
}.items():
|
||||
st.session_state.setdefault(key, default)
|
||||
|
||||
|
||||
# =========================================================
|
||||
# Sécurité mots de passe
|
||||
# =========================================================
|
||||
@@ -95,10 +112,11 @@ def fetch_gyro(site_name: str):
|
||||
q = """
|
||||
SELECT Etat, `Date`
|
||||
FROM Sondes.v_gyro_last
|
||||
WHERE Lieu = %s AND Sonde = 'Gyro'
|
||||
WHERE Lieu = %s \
|
||||
AND Sonde = 'Gyro'
|
||||
ORDER BY `Date` DESC
|
||||
LIMIT 1
|
||||
"""
|
||||
LIMIT 1 \
|
||||
"""
|
||||
with closing(get_connection()) as cnx, closing(cnx.cursor(dictionary=True)) as cur:
|
||||
cur.execute(q, (site_name,)) # <-- FIX
|
||||
row = cur.fetchone()
|
||||
@@ -157,46 +175,52 @@ def render_gyro_badge(site_name: str, stale_after_min: int = 10):
|
||||
# =========================================================
|
||||
def ensure_schema():
|
||||
ddl_generated = """
|
||||
CREATE TABLE IF NOT EXISTS Journal_Erreurs (
|
||||
Id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
Site VARCHAR(50) NOT NULL,
|
||||
Sonde VARCHAR(100) NOT NULL,
|
||||
DateJour DATE NOT NULL,
|
||||
Type VARCHAR(30) NOT NULL,
|
||||
Source_Id INT NULL,
|
||||
Source_Id_norm INT GENERATED ALWAYS AS (COALESCE(Source_Id, 0)) STORED,
|
||||
Resume TEXT NOT NULL,
|
||||
Statut VARCHAR(20) NOT NULL DEFAULT 'Nouveau',
|
||||
Priorite TINYINT NOT NULL DEFAULT 3,
|
||||
Assignation VARCHAR(100) NULL,
|
||||
Commentaire TEXT NULL,
|
||||
Tag VARCHAR(50) NULL,
|
||||
CreatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
UpdatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
UNIQUE KEY uk_site_sonde_date_type (Site, Sonde, DateJour, Type, Source_Id_norm),
|
||||
KEY idx_journal_date_site (DateJour, Site),
|
||||
KEY idx_journal_statut (Statut)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS Journal_Erreurs \
|
||||
( \
|
||||
Id INT AUTO_INCREMENT PRIMARY KEY, \
|
||||
Site VARCHAR(50) NOT NULL, \
|
||||
Sonde VARCHAR(100) NOT NULL, \
|
||||
DateJour DATE NOT NULL, \
|
||||
Type VARCHAR(30) NOT NULL, \
|
||||
Source_Id INT NULL, \
|
||||
Source_Id_norm INT GENERATED ALWAYS AS (COALESCE(Source_Id, 0)) STORED, \
|
||||
Resume TEXT NOT NULL, \
|
||||
Statut VARCHAR(20) NOT NULL DEFAULT 'Nouveau', \
|
||||
Priorite TINYINT NOT NULL DEFAULT 3, \
|
||||
Assignation VARCHAR(100) NULL, \
|
||||
Commentaire TEXT NULL, \
|
||||
Tag VARCHAR(50) NULL, \
|
||||
CreatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP, \
|
||||
UpdatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, \
|
||||
UNIQUE KEY uk_site_sonde_date_type (Site, Sonde, DateJour, Type, Source_Id_norm), \
|
||||
KEY idx_journal_date_site (DateJour, Site), \
|
||||
KEY idx_journal_statut (Statut)
|
||||
) ENGINE = InnoDB \
|
||||
DEFAULT CHARSET = utf8mb4 \
|
||||
COLLATE = utf8mb4_unicode_ci; \
|
||||
"""
|
||||
ddl_fallback = """
|
||||
CREATE TABLE IF NOT EXISTS Journal_Erreurs (
|
||||
Id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
Site VARCHAR(50) NOT NULL,
|
||||
Sonde VARCHAR(100) NOT NULL,
|
||||
DateJour DATE NOT NULL,
|
||||
Type VARCHAR(30) NOT NULL,
|
||||
Source_Id INT NULL,
|
||||
Source_Id_norm INT NULL DEFAULT 0,
|
||||
Resume TEXT NOT NULL,
|
||||
Statut VARCHAR(20) NOT NULL DEFAULT 'Nouveau',
|
||||
Priorite TINYINT NOT NULL DEFAULT 3,
|
||||
Assignation VARCHAR(100) NULL,
|
||||
Commentaire TEXT NULL,
|
||||
Tag VARCHAR(50) NULL,
|
||||
CreatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
UpdatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS Journal_Erreurs \
|
||||
( \
|
||||
Id INT AUTO_INCREMENT PRIMARY KEY, \
|
||||
Site VARCHAR(50) NOT NULL, \
|
||||
Sonde VARCHAR(100) NOT NULL, \
|
||||
DateJour DATE NOT NULL, \
|
||||
Type VARCHAR(30) NOT NULL, \
|
||||
Source_Id INT NULL, \
|
||||
Source_Id_norm INT NULL DEFAULT 0, \
|
||||
Resume TEXT NOT NULL, \
|
||||
Statut VARCHAR(20) NOT NULL DEFAULT 'Nouveau', \
|
||||
Priorite TINYINT NOT NULL DEFAULT 3, \
|
||||
Assignation VARCHAR(100) NULL, \
|
||||
Commentaire TEXT NULL, \
|
||||
Tag VARCHAR(50) NULL, \
|
||||
CreatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP, \
|
||||
UpdatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||
) ENGINE = InnoDB \
|
||||
DEFAULT CHARSET = utf8mb4 \
|
||||
COLLATE = utf8mb4_unicode_ci; \
|
||||
"""
|
||||
idxs_fallback = [
|
||||
"CREATE UNIQUE INDEX IF NOT EXISTS uk_site_sonde_date_type ON Sondes.Journal_Erreurs (Site, Sonde, DateJour, Type, Source_Id_norm)",
|
||||
"CREATE INDEX IF NOT EXISTS idx_journal_date_site ON Sondes.Journal_Erreurs (DateJour, Site)",
|
||||
@@ -205,18 +229,20 @@ def ensure_schema():
|
||||
triggers_fallback = [
|
||||
"""
|
||||
CREATE TRIGGER trg_je_bi
|
||||
BEFORE INSERT ON Sondes.Journal_Erreurs
|
||||
FOR EACH ROW
|
||||
BEFORE INSERT
|
||||
ON Sondes.Journal_Erreurs
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
SET NEW.Source_Id_norm = IFNULL(NEW.Source_Id, 0);
|
||||
SET NEW.Source_Id_norm = IFNULL(NEW.Source_Id, 0);
|
||||
END
|
||||
""",
|
||||
"""
|
||||
CREATE TRIGGER trg_je_bu
|
||||
BEFORE UPDATE ON Sondes.Journal_Erreurs
|
||||
FOR EACH ROW
|
||||
BEFORE UPDATE
|
||||
ON Sondes.Journal_Erreurs
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
SET NEW.Source_Id_norm = IFNULL(NEW.Source_Id, 0);
|
||||
SET NEW.Source_Id_norm = IFNULL(NEW.Source_Id, 0);
|
||||
END
|
||||
""",
|
||||
]
|
||||
@@ -251,7 +277,6 @@ try:
|
||||
except Exception as e:
|
||||
st.warning(f"Init schéma Journal_Erreurs : {e}")
|
||||
|
||||
|
||||
# =========================================================
|
||||
# Connexion utilisateur
|
||||
# =========================================================
|
||||
@@ -259,7 +284,21 @@ if not st.session_state.get("authenticated", False):
|
||||
login = st.sidebar.text_input("Nom d'utilisateur")
|
||||
password = st.sidebar.text_input("Mot de passe", type="password")
|
||||
|
||||
if st.sidebar.button("Se connecter"):
|
||||
# --- Bouton connexion ---
|
||||
clicked = st.sidebar.button("Se connecter")
|
||||
|
||||
# --- QR code SOUS le bouton ---
|
||||
st.sidebar.markdown("---")
|
||||
st.sidebar.caption("Accès rapide depuis un smartphone :")
|
||||
qr_path = os.path.join(BASE_DIR, "assets", "QR_Domo91FR.png")
|
||||
|
||||
if os.path.exists(qr_path):
|
||||
st.sidebar.image(qr_path, width=180)
|
||||
else:
|
||||
st.sidebar.error(f"QR code introuvable : {qr_path}")
|
||||
|
||||
# --- Traitement de la connexion ---
|
||||
if clicked:
|
||||
try:
|
||||
with closing(get_connection()) as conn, closing(conn.cursor(dictionary=True)) as cursor:
|
||||
cursor.execute(
|
||||
@@ -310,12 +349,21 @@ else:
|
||||
st.rerun()
|
||||
|
||||
|
||||
# =========================================================
|
||||
# PDF
|
||||
# =========================================================
|
||||
# =========================================================
|
||||
# PDF
|
||||
# =========================================================
|
||||
def generer_pdf(site_name: str, date_str: str, periode: str):
|
||||
"""
|
||||
Génère un PDF de relevés + alertes pour un site et une date.
|
||||
- site_name : nom de table (Saclay/Meudon)
|
||||
- date_str : YYYY-MM-DD
|
||||
- periode : libellé de tranche horaire
|
||||
"""
|
||||
assert_site_ok(site_name)
|
||||
st.info(f"Génération du rapport PDF pour {site} à la date {date_str} ({periode})")
|
||||
st.info(f"Génération du rapport PDF pour {site_name} à la date {date_str} ({periode})")
|
||||
|
||||
plages = {
|
||||
"Toute la journée": (time(0, 0), time(23, 59)),
|
||||
@@ -326,8 +374,9 @@ def generer_pdf(site_name: str, date_str: str, periode: str):
|
||||
|
||||
try:
|
||||
with closing(get_connection()) as conn, closing(conn.cursor(dictionary=True)) as cur:
|
||||
# Mesures
|
||||
cur.execute(
|
||||
f"SELECT Sonde, Date, Temperature FROM `{site}` WHERE DATE(Date) = %s ORDER BY Sonde, Date",
|
||||
f"SELECT Sonde, Date, Temperature FROM `{site_name}` WHERE DATE(Date) = %s ORDER BY Sonde, Date",
|
||||
(date_str,),
|
||||
)
|
||||
rows = cur.fetchall()
|
||||
@@ -351,7 +400,8 @@ def generer_pdf(site_name: str, date_str: str, periode: str):
|
||||
df_sonde = df[df["Sonde"] == sonde]
|
||||
releves[sonde] = list(zip(df_sonde["Heure"], df_sonde["Temperature"]))
|
||||
|
||||
table_alertes = f"Alertes_{site}"
|
||||
# Alertes du jour
|
||||
table_alertes = f"Alertes_{site_name}"
|
||||
cur.execute(
|
||||
f"SELECT Sonde, Debut_defaut, Etat FROM `{table_alertes}` WHERE DATE(Debut_defaut) = %s",
|
||||
(date_str,),
|
||||
@@ -363,15 +413,11 @@ def generer_pdf(site_name: str, date_str: str, periode: str):
|
||||
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, 8, f"Site : {getattr(self, 'site_name', '')}", ln=1, align="C")
|
||||
self.cell(0, 8, f"Date : {date_str}", ln=1, align="C")
|
||||
self.cell(0, 8, f"Période : {getattr(self, 'periode', '')}", ln=1, align="C")
|
||||
self.ln(4)
|
||||
|
||||
def site_info(self, site_name):
|
||||
self.set_font("Arial", "B", 12)
|
||||
self.cell(0, 8, f"Site : {site_name}", ln=1)
|
||||
self.ln(2)
|
||||
|
||||
def releves_section(self, data):
|
||||
self.set_font("Arial", "B", 12)
|
||||
self.cell(0, 8, "Relevés de température", ln=1)
|
||||
@@ -405,6 +451,8 @@ def generer_pdf(site_name: str, date_str: str, periode: str):
|
||||
h2, t2 = col2[i]
|
||||
self.cell(40, 6, h2, border=1)
|
||||
self.cell(30, 6, f"{t2:.2f}", border=1)
|
||||
else:
|
||||
self.cell(70, 6, "", border=0)
|
||||
self.ln()
|
||||
self.ln(3)
|
||||
|
||||
@@ -419,15 +467,15 @@ def generer_pdf(site_name: str, date_str: str, periode: str):
|
||||
self.cell(0, 6, f"{a['Sonde']} - {a['Debut_defaut']} - {a['Etat']}", ln=1)
|
||||
|
||||
pdf = RapportPDF()
|
||||
pdf.site_name = site_name
|
||||
pdf.periode = periode
|
||||
pdf.add_page()
|
||||
pdf.site_info(site)
|
||||
pdf.releves_section(releves)
|
||||
pdf.alertes_section(alertes)
|
||||
|
||||
output_dir = "../PDF"
|
||||
output_dir = os.path.join(BASE_DIR, "PDF")
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
file_name = f"rapport_{site}_{date_str}.pdf"
|
||||
file_name = f"rapport_{site_name}_{date_str}.pdf"
|
||||
output_path = os.path.join(output_dir, file_name)
|
||||
pdf.output(output_path)
|
||||
|
||||
@@ -551,11 +599,22 @@ def load_anomalies_auto(site_name: str, jour: date):
|
||||
def load_journal_existants(site: str, jour: date):
|
||||
assert_site_ok(site)
|
||||
q = """
|
||||
SELECT Id, Site, Sonde, DateJour, Type, Source_Id, Resume,
|
||||
Statut, Priorite, Assignation, Commentaire, Tag
|
||||
FROM Sondes.Journal_Erreurs
|
||||
WHERE Site=%s AND DateJour=%s;
|
||||
"""
|
||||
SELECT Id, \
|
||||
Site, \
|
||||
Sonde, \
|
||||
DateJour, \
|
||||
Type, \
|
||||
Source_Id, \
|
||||
Resume,
|
||||
Statut, \
|
||||
Priorite, \
|
||||
Assignation, \
|
||||
Commentaire, \
|
||||
Tag
|
||||
FROM Sondes.Journal_Erreurs
|
||||
WHERE Site = %s \
|
||||
AND DateJour = %s; \
|
||||
"""
|
||||
with closing(get_connection()) as cnx, closing(cnx.cursor(dictionary=True)) as cur:
|
||||
cur.execute(q, (site, jour))
|
||||
rows = cur.fetchall()
|
||||
@@ -568,19 +627,18 @@ def upsert_journal(rows: list[dict]):
|
||||
if not rows:
|
||||
return
|
||||
q_insert = """
|
||||
INSERT INTO Sondes.Journal_Erreurs
|
||||
(Site, Sonde, DateJour, Type, Source_Id, Resume, Statut, Priorite, Assignation, Commentaire, Tag)
|
||||
VALUES (%(Site)s, %(Sonde)s, %(DateJour)s, %(Type)s, %(Source_Id)s, %(Resume)s,
|
||||
%(Statut)s, %(Priorite)s, %(Assignation)s, %(Commentaire)s, %(Tag)s)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
Resume=VALUES(Resume),
|
||||
Statut=VALUES(Statut),
|
||||
Priorite=VALUES(Priorite),
|
||||
Assignation=VALUES(Assignation),
|
||||
Commentaire=VALUES(Commentaire),
|
||||
Tag=VALUES(Tag),
|
||||
UpdatedAt=CURRENT_TIMESTAMP;
|
||||
"""
|
||||
INSERT INTO Sondes.Journal_Erreurs
|
||||
(Site, Sonde, DateJour, Type, Source_Id, Resume, Statut, Priorite, Assignation, Commentaire, Tag)
|
||||
VALUES (%(Site)s, %(Sonde)s, %(DateJour)s, %(Type)s, %(Source_Id)s, %(Resume)s,
|
||||
%(Statut)s, %(Priorite)s, %(Assignation)s, %(Commentaire)s, %(Tag)s)
|
||||
ON DUPLICATE KEY UPDATE Resume=VALUES(Resume), \
|
||||
Statut=VALUES(Statut), \
|
||||
Priorite=VALUES(Priorite), \
|
||||
Assignation=VALUES(Assignation), \
|
||||
Commentaire=VALUES(Commentaire), \
|
||||
Tag=VALUES(Tag), \
|
||||
UpdatedAt=CURRENT_TIMESTAMP; \
|
||||
"""
|
||||
with closing(get_connection()) as cnx, closing(cnx.cursor()) as cur:
|
||||
cur.executemany(q_insert, rows)
|
||||
cnx.commit()
|
||||
@@ -629,11 +687,11 @@ def page_journal_erreurs():
|
||||
df_saved["Source_Id_norm"] = df_saved["Source_Id"].apply(_source_norm)
|
||||
|
||||
df_saved["Key"] = (
|
||||
df_saved["Site"].astype(str) + "|" +
|
||||
df_saved["Sonde"].astype(str) + "|" +
|
||||
df_saved["DateJour"].astype(str) + "|" +
|
||||
df_saved["Type"].astype(str) + "|" +
|
||||
df_saved["Source_Id_norm"].astype(str)
|
||||
df_saved["Site"].astype(str) + "|" +
|
||||
df_saved["Sonde"].astype(str) + "|" +
|
||||
df_saved["DateJour"].astype(str) + "|" +
|
||||
df_saved["Type"].astype(str) + "|" +
|
||||
df_saved["Source_Id_norm"].astype(str)
|
||||
)
|
||||
for c in ["Statut", "Priorite", "Assignation", "Commentaire", "Tag"]:
|
||||
if c not in df_saved.columns:
|
||||
@@ -641,11 +699,11 @@ def page_journal_erreurs():
|
||||
|
||||
# base Key (toujours)
|
||||
base["Key"] = (
|
||||
base["Site"].astype(str) + "|" +
|
||||
base["Sonde"].astype(str) + "|" +
|
||||
base["DateJour"].astype(str) + "|" +
|
||||
base["Type"].astype(str) + "|" +
|
||||
base["Source_Id_norm"].astype(str)
|
||||
base["Site"].astype(str) + "|" +
|
||||
base["Sonde"].astype(str) + "|" +
|
||||
base["DateJour"].astype(str) + "|" +
|
||||
base["Type"].astype(str) + "|" +
|
||||
base["Source_Id_norm"].astype(str)
|
||||
)
|
||||
|
||||
df = base.merge(
|
||||
@@ -798,7 +856,6 @@ if st.session_state.get("authenticated"):
|
||||
try:
|
||||
# Site imposé ou sélection admin
|
||||
if st.session_state.get("role") == "superviseur":
|
||||
# sélection possible
|
||||
if site_actuel not in SITES_LISTE:
|
||||
site_actuel = SITES_LISTE[0]
|
||||
site_actuel = st.selectbox(
|
||||
@@ -827,41 +884,18 @@ if st.session_state.get("authenticated"):
|
||||
df_sonde = pd.DataFrame()
|
||||
seuil_temp = 10.0
|
||||
sonde_choisie = None
|
||||
with closing(get_connection()) as conn, closing(conn.cursor(dictionary=True)) as cursor:
|
||||
cursor.execute(
|
||||
"""
|
||||
SELECT Sonde, Temp_Max
|
||||
FROM Sondes.Chambres_froides
|
||||
WHERE Lieu = %s
|
||||
AND UPPER(Etat) = 'ON'
|
||||
ORDER BY Sonde
|
||||
""",
|
||||
(site_actuel,),
|
||||
)
|
||||
rows_mesures = [] # important pour éviter NameError
|
||||
|
||||
with closing(get_connection()) as conn, closing(conn.cursor(dictionary=True)) as cursor:
|
||||
cursor.execute(
|
||||
"""
|
||||
SELECT Sonde, Temp_Max
|
||||
FROM Sondes.Chambres_froides
|
||||
WHERE Lieu = %s
|
||||
AND UPPER(Etat) = 'ON'
|
||||
ORDER BY Sonde
|
||||
""",
|
||||
(site_actuel,),
|
||||
)
|
||||
cfg_on = cursor.fetchall()
|
||||
|
||||
# 1) Charger config (sondes ON + seuils) puis mesures du jour
|
||||
with closing(get_connection()) as conn:
|
||||
cfg_on = get_chambres_froides_actives(conn, site_actuel)
|
||||
sondes_on = [r["Sonde"] for r in cfg_on]
|
||||
seuils_on = {r["Sonde"]: float(r["Temp_Max"]) for r in cfg_on}
|
||||
|
||||
# IMPORTANT: test avant la requête mesures
|
||||
if not sondes_on:
|
||||
st.warning("Aucune sonde active (Etat=ON) dans Chambres_froides pour ce site.")
|
||||
st.stop()
|
||||
|
||||
with closing(get_connection()) as conn, closing(conn.cursor(dictionary=True)) as cursor:
|
||||
with closing(conn.cursor(dictionary=True)) as cur_mesures:
|
||||
placeholders = ", ".join(["%s"] * len(sondes_on))
|
||||
q = f"""
|
||||
SELECT Sonde, Date, Temperature
|
||||
@@ -871,72 +905,68 @@ if st.session_state.get("authenticated"):
|
||||
ORDER BY Sonde, Date DESC
|
||||
"""
|
||||
params = [date_selectionnee.strftime("%Y-%m-%d")] + sondes_on
|
||||
cursor.execute(q, params)
|
||||
rows_mesures = cursor.fetchall()
|
||||
cur_mesures.execute(q, params)
|
||||
rows_mesures = cur_mesures.fetchall()
|
||||
|
||||
if rows_mesures:
|
||||
df = pd.DataFrame(rows_mesures)
|
||||
df["Date"] = pd.to_datetime(df["Date"])
|
||||
if rows_mesures:
|
||||
df = pd.DataFrame(rows_mesures)
|
||||
df["Date"] = pd.to_datetime(df["Date"])
|
||||
|
||||
sonde_choisie = st.selectbox("🧪 Choisissez une sonde :", sondes_on)
|
||||
df_sonde = df[df["Sonde"] == sonde_choisie].copy()
|
||||
df_sonde["Heure"] = df_sonde["Date"].dt.hour
|
||||
sonde_choisie = st.selectbox("🧪 Choisissez une sonde :", sondes_on)
|
||||
df_sonde = df[df["Sonde"] == sonde_choisie].copy()
|
||||
df_sonde["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)"],
|
||||
tranche = st.radio(
|
||||
"🕒 Tranche horaire :",
|
||||
["Toute la journée", "Matin (6h-12h)", "Après-midi (12h-18h)", "Nuit (18h-6h)"],
|
||||
)
|
||||
st.session_state["selected_periode"] = tranche
|
||||
|
||||
if st.button("🧾 Générer le PDF du jour"):
|
||||
generer_pdf(
|
||||
site_actuel,
|
||||
date_selectionnee.strftime("%Y-%m-%d"),
|
||||
st.session_state.get("selected_periode", "Toute la journée"),
|
||||
)
|
||||
st.session_state["selected_periode"] = tranche
|
||||
|
||||
if st.button("🧾 Générer le PDF du jour"):
|
||||
generer_pdf(
|
||||
site_actuel,
|
||||
date_selectionnee.strftime("%Y-%m-%d"),
|
||||
st.session_state.get("selected_periode", "Toute la journée"),
|
||||
)
|
||||
# Filtre tranche
|
||||
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)]
|
||||
|
||||
# Filtre tranche
|
||||
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)]
|
||||
seuil_temp = seuils_on.get(sonde_choisie, 10.0)
|
||||
|
||||
seuil_temp = seuils_on.get(sonde_choisie, 10.0)
|
||||
|
||||
if not df_sonde.empty:
|
||||
st.subheader("📊 Tableau des relevés")
|
||||
if not df_sonde.empty:
|
||||
st.subheader("📊 Tableau des relevés")
|
||||
|
||||
|
||||
def surlignage_temp(val):
|
||||
try:
|
||||
if float(val) > seuil_temp:
|
||||
return "color: red; font-weight: bold"
|
||||
except Exception:
|
||||
pass
|
||||
def surlignage_temp(val):
|
||||
try:
|
||||
return "color: red; font-weight: bold" if float(val) > seuil_temp else ""
|
||||
except (ValueError, TypeError):
|
||||
return ""
|
||||
|
||||
styled_df = df_sonde.style.applymap(surlignage_temp, subset=["Temperature"])
|
||||
st.dataframe(styled_df, use_container_width=True)
|
||||
|
||||
styled_df = df_sonde.style.map(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_sonde["Date"], df_sonde["Temperature"], marker="o")
|
||||
ax.axhline(seuil_temp, linestyle="--", label=f"Seuil {seuil_temp}°C")
|
||||
ax.set_xlabel("Heure")
|
||||
ax.set_ylabel("Température (°C)")
|
||||
ax.set_title(f"{sonde_choisie} - {date_selectionnee.strftime('%d/%m/%Y')}")
|
||||
ax.xaxis.set_major_formatter(mdates.DateFormatter("%H:%M"))
|
||||
ax.legend()
|
||||
st.pyplot(fig)
|
||||
st.subheader("📈 Évolution de la température")
|
||||
fig, ax = plt.subplots(figsize=(10, 4))
|
||||
ax.plot(df_sonde["Date"], df_sonde["Temperature"], marker="o")
|
||||
ax.axhline(seuil_temp, linestyle="--", label=f"Seuil {seuil_temp}°C")
|
||||
ax.set_xlabel("Heure")
|
||||
ax.set_ylabel("Température (°C)")
|
||||
ax.set_title(f"{sonde_choisie} - {date_selectionnee.strftime('%d/%m/%Y')}")
|
||||
ax.xaxis.set_major_formatter(mdates.DateFormatter("%H:%M"))
|
||||
ax.legend()
|
||||
st.pyplot(fig)
|
||||
else:
|
||||
st.info("Aucun relevé pour cette date.")
|
||||
st.info("Aucun relevé dans la tranche horaire sélectionnée.")
|
||||
else:
|
||||
st.info("Aucun relevé pour cette date.")
|
||||
|
||||
if not sondes_on:
|
||||
st.warning("Aucune sonde active (Etat=ON) dans Chambres_froides pour ce site.")
|
||||
st.stop()
|
||||
except Exception as e:
|
||||
st.error(f"Erreur : {e}")
|
||||
st.text(traceback.format_exc())
|
||||
@@ -991,7 +1021,8 @@ if st.session_state.get("authenticated"):
|
||||
st.session_state["refresh_admin"] = random.randint(0, 9999)
|
||||
|
||||
try:
|
||||
with closing(get_connection()) as conn_admin, closing(conn_admin.cursor(dictionary=True)) as cursor_admin:
|
||||
with closing(get_connection()) as conn_admin, closing(
|
||||
conn_admin.cursor(dictionary=True)) as cursor_admin:
|
||||
cursor_admin.execute("SELECT * FROM Sondes.Chambres_froides WHERE Lieu = %s", (site,))
|
||||
chambres = cursor_admin.fetchall()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user