import streamlit as st import pandas as pd import os import json import shutil import time import joblib from pathlib import Path def get_campaigns_info(directory: Path): """Raggruppa i file per prefisso campagna.""" if not directory.exists(): return {} files = list(directory.glob("*.csv")) campaigns = {} for f in files: parts = f.name.split('_') if len(parts) >= 4: camp_id = parts[0] campaigns.setdefault(camp_id, []).append(str(f)) return campaigns def show_training_data_manager(cfg): st.subheader("🧠 Console Gestione Modelli e Rilievi") MODEL_DIR = Path("/data/model") JOBS_DIR = Path("/data/train/train_jobs") MODEL_DIR.mkdir(parents=True, exist_ok=True) JOBS_DIR.mkdir(parents=True, exist_ok=True) # --- 1. GESTIONE MODELLI E LINK SIMBOLICO --- st.write("### 🎯 Modelli Generati ed Attivazione") available_models = sorted( [m for m in MODEL_DIR.glob("model_camp_*.joblib")], key=os.path.getmtime, reverse=True ) if available_models: # Visualizzazione stato attuale link simbolico target_path = MODEL_DIR / "model.joblib" if target_path.is_symlink(): try: current_link = os.readlink(target_path) st.info(f"🔗 **Modello operativo attuale:** `{os.path.basename(current_link)}`") except Exception: st.warning("⚠️ Impossibile leggere il link simbolico.") # Tabella modelli esistenti m_list = [] for m in available_models: m_list.append({ "Nome Modello": m.name, "Data": time.strftime('%d/%m/%Y %H:%M', time.localtime(m.stat().st_mtime)), "Size": f"{round(m.stat().st_size / 1024, 1)} KB" }) st.dataframe(pd.DataFrame(m_list), use_container_width=True, hide_index=True) selected_model = st.selectbox("Scegli il modello da analizzare o attivare:", [m.name for m in available_models]) # --- NUOVA SEZIONE: ANALISI TECNICA --- with st.expander("🔍 Ispezione Metadati Modello", expanded=False): try: m_data = joblib.load(MODEL_DIR / selected_model) st.write(f"**Data Creazione:** {m_data.get('created_at', 'N/D')}") st.write(f"**Gateway allenati:** {len(m_data.get('gateways_order', []))}") st.code(", ".join(m_data.get('gateways_order', []))) floors = list(m_data.get('xy_by_floor', {}).keys()) st.write(f"**Piani mappati nel regressore:** {floors}") if not floors or len(m_data.get('gateways_order', [])) == 0: st.error("⚠️ Attenzione: Il modello sembra non contenere dati validi.") except Exception as e: st.error(f"Errore caricamento per analisi: {e}") if st.button("🔗 IMPOSTA COME OPERATIVO (Symlink)", type="primary", use_container_width=True): try: if target_path.exists() or target_path.is_symlink(): target_path.unlink() current_dir = os.getcwd() os.chdir(str(MODEL_DIR)) os.symlink(selected_model, "model.joblib") os.chdir(current_dir) st.success(f"Link aggiornato con successo a {selected_model}") time.sleep(1) st.rerun() except Exception as e: st.error(f"Errore: {e}") else: st.info("Nessun modello disponibile.") st.divider() # --- 2. ISPEZIONE RILIEVI E NUOVO ADDESTRAMENTO --- st.write("### 🚀 Analisi Dati e Nuovo Addestramento") train_cfg = cfg.get("train", {}) samples_dir = Path(train_cfg.get("samples_dir", "/data/train/samples")) campaigns_map = get_campaigns_info(samples_dir) if not campaigns_map: st.info("Nessun dato di rilievo trovato."); return camp_list = sorted(campaigns_map.keys()) selected_camp = st.radio("Seleziona Campagna:", camp_list, horizontal=True) current_files = campaigns_map[selected_camp] files_df = pd.DataFrame([ {"File": os.path.basename(f), "Size": f"{round(os.path.getsize(f)/1024, 1)} KB", "Path": f} for f in current_files ]).sort_values("File") st.dataframe(files_df[["File", "Size"]], use_container_width=True, hide_index=True) sel_file = st.selectbox("Ispeziona contenuto file CSV:", ["-- seleziona --"] + files_df["File"].tolist()) if sel_file != "-- seleziona --": f_path = files_df[files_df["File"] == sel_file]["Path"].iloc[0] try: delim = cfg.get('paths', {}).get('csv_delimiter', ';') st.dataframe(pd.read_csv(f_path, sep=delim), use_container_width=True) except Exception as e: st.error(f"Errore lettura: {e}") # Bottone di Addestramento pending_jobs = list(JOBS_DIR.glob("*.lock")) if pending_jobs: st.warning(f"⏳ Addestramento in corso: {pending_jobs[0].name}") else: if st.button(f"🏋️ AVVIA ADDESTRAMENTO CAMPAGNA {selected_camp}", type="primary", use_container_width=True): job_data = { "campaign": selected_camp, "knn": train_cfg.get("knn", {}), "nan_fill": train_cfg.get("nan_fill", -110), "gateways_csv": train_cfg.get("gateways_csv", "/data/config/gateway.csv"), "timestamp": time.time() } with open(JOBS_DIR / f"train_{selected_camp}.lock", "w") as f: json.dump(job_data, f) st.success("Richiesta inviata al Core.") time.sleep(1) st.rerun()