Spaces:
Sleeping
Sleeping
File size: 17,080 Bytes
3f43f3d 83e717b 9a748f4 c605521 3f43f3d 97fc5da 3f43f3d 9a748f4 3f43f3d 9a748f4 3f43f3d 6d58631 97fc5da 3f43f3d 9a748f4 a49af7f b14ae08 3f43f3d 97fc5da 3f43f3d b14ae08 883ca59 00d8855 5032c40 c08610b c605521 3f43f3d e63fa12 9a748f4 e63fa12 9a748f4 e2daa46 9a748f4 e2daa46 9a748f4 e2daa46 9a748f4 b14ae08 9a748f4 b14ae08 9a748f4 b14ae08 18e1082 b14ae08 e63fa12 9a748f4 e63fa12 b14ae08 e63fa12 daa6ace e63fa12 fff4768 6d58631 e63fa12 00d8855 e63fa12 b14ae08 c605521 b14ae08 daa6ace 63928cb daa6ace e63fa12 b14ae08 e63fa12 9a748f4 83e717b e63fa12 b14ae08 a49af7f b14ae08 e706b82 b14ae08 e706b82 b14ae08 e706b82 b14ae08 e706b82 b14ae08 e706b82 b14ae08 61cef30 b5a5eec d12d4f1 61cef30 b5a5eec d12d4f1 b5a5eec d12d4f1 61cef30 fe54e0d 61cef30 b5a5eec 61cef30 b5a5eec 61cef30 b5a5eec 61cef30 b5a5eec 61cef30 b5a5eec 61cef30 e63fa12 1e601a6 883ca59 1e601a6 883ca59 00d8855 b3c42c9 1e601a6 5032c40 c08610b c605521 00d8855 4849684 00d8855 4849684 2070ea3 00d8855 4849684 b14ae08 e63fa12 d872319 b14ae08 d872319 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 | # ==============================================================================
# CORRECTIF IMPORTS - Remplace tout le début de ton fichier jusqu'à "apply_vortex_style"
# ==============================================================================
import streamlit as st
import pandas as pd
import gspread
from google.oauth2.service_account import Credentials
import os
import json
import sys
import time
# --- 1. CONFIGURATION ROBUSTE DES CHEMINS ---
# Récupère le dossier où se trouve CE fichier (streamlit_app.py)
current_dir = os.path.dirname(os.path.abspath(__file__))
# On ajoute ce dossier au chemin de recherche de Python
# Ainsi, Python peut voir le dossier "module" qui est juste à côté
if current_dir not in sys.path:
sys.path.append(current_dir)
# --- 2. IMPORTS AVEC DEBUG ---
try:
# On importe depuis "module" (singulier) car le dossier s'appelle "module"
from module import AIP
except ImportError as e:
st.error(f"❌ Erreur Import AIP : {e}") # Affiche la vraie erreur
AIP = None
try:
from module import Dashboard # <--- AJOUT
except ImportError as e:
st.error(f"❌ Erreur Import Dashboard : {e}")
Dashboard = None
try:
from module import Data_Exploration
except ImportError as e:
# C'est ici que tu verras exactement pourquoi ça plante
st.error(f"❌ Erreur Import Data_Exploration : {e}")
st.code(f"Chemin actuel: {current_dir}\nContenu du dossier: {os.listdir(current_dir)}")
Data_Exploration = None
# Dans src/streamlit_app.py, section imports :
try:
from module import Indicateurs_DQ # <--- AJOUTER CETTE LIGNE
except ImportError as e:
st.error(f"❌ Erreur Import Indicateurs_DQ : {e}")
Indicateurs_DQ = None
# Module Validation Contract (ETL)
try:
from module import Validation_Contract
except ImportError as e:
st.error(f"❌ Erreur Import Validation_Contract : {e}")
Validation_Contract = None
try:
from module import Features # <--- AJOUT
except ImportError as e:
st.error(f"❌ Erreur Import Features : {e}")
Features = None
try:
from module import Forecasting # <--- AJOUT
except ImportError as e:
st.error(f"❌ Erreur Import Forecasting : {e}")
Forecasting = None
try:
from module import add_data
except ImportError as e:
st.error(f"❌ Erreur Import add_data : {e}")
add_data = None
# ==============================================================================
# ... La suite de ton code (apply_vortex_style, etc.) ...
# ==============================================================================
# 1. FONCTION DE CONFIGURATION ET STYLE (GOTHAM SURVEILLANCE)
# ==============================================================================
def apply_vortex_style():
"""Injection du CSS complet Gotham Surveillance"""
st.markdown("""
<style>
@import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&display=swap');
/* === FOND GLOBAL === */
.stApp {
background: linear-gradient(135deg, #0d1117 0%, #161b22 50%, #1c2128 100%);
color: #c9d1d9;
font-family: 'Space Grotesk', sans-serif;
}
/* === SIDEBAR STYLE === */
[data-testid="stSidebar"] {
background: linear-gradient(180deg, #161b22 0%, #1c2128 100%);
border-right: 2px solid rgba(88, 166, 255, 0.3);
}
/* Titre sidebar */
[data-testid="stSidebar"] h1 {
color: #58a6ff !important;
font-family: 'Space Grotesk', sans-serif !important;
font-weight: 700 !important;
text-transform: uppercase;
}
/* === CORRECTION RADIO BUTTONS === */
[data-testid="stSidebar"] .stRadio [data-baseweb="radio"] > div:first-child { display: none !important; }
[data-testid="stSidebar"] .stRadio label input + div {
display: flex !important;
align-items: center !important;
background-color: transparent !important;
color: #8b949e !important;
}
[data-testid="stSidebar"] .stRadio label input + div::before {
content: "";
display: inline-block;
width: 14px; height: 14px;
border: 1px solid #8b949e;
background-color: rgba(22, 27, 34, 0.8);
border-radius: 3px;
margin-right: 12px;
transition: all 0.2s ease;
}
[data-testid="stSidebar"] .stRadio label input:checked + div::before {
background-color: #58a6ff !important;
border-color: #58a6ff !important;
}
[data-testid="stSidebar"] .stRadio label input:checked + div { color: #58a6ff !important; }
/* === HEADERS & TEXT === */
h1, h2, h3 { color: #58a6ff !important; font-family: 'Space Grotesk', sans-serif !important; }
h1 { font-size: 1.8rem !important; border-bottom: 1px solid rgba(88, 166, 255, 0.2); padding-bottom: 12px; }
/* === TERMINAL / CHAT MESSAGES === */
.user-msg {
background: rgba(30, 136, 229, 0.1);
border-left: 3px solid #1E88E5;
padding: 12px; margin: 10px 0;
border-radius: 0 4px 4px 0;
}
.bot-msg {
background: rgba(48, 54, 61, 0.3);
border-left: 3px solid #00E676;
padding: 12px; margin: 10px 0;
border-radius: 0 4px 4px 0;
}
/* Scrollbars */
::-webkit-scrollbar { width: 6px; }
::-webkit-scrollbar-thumb { background: rgba(88, 166, 255, 0.3); border-radius: 10px; }
</style>
""", unsafe_allow_html=True)
# ==============================================================================
# 2. MOTEUR DE CONNEXION
# ==============================================================================
@st.cache_resource
def get_gspread_client():
# Essayer d'abord les variables d'environnement
raw_json = os.environ.get("GSHEETS_JSON")
# Si pas trouvé, essayer secrets.toml
if not raw_json:
try:
raw_json = st.secrets.get("GSHEETS_JSON")
except:
raw_json = None
if not raw_json:
st.error("🛑 Secret 'GSHEETS_JSON' manquant.")
st.info("""
**Deux options pour configurer :**
**Option 1 - Variable d'environnement :**
```bash
export GSHEETS_JSON='{"type": "service_account", ...}'
```
**Option 2 - Fichier secrets.toml :**
1. Créez `.streamlit/secrets.toml`
2. Ajoutez votre JSON Google Sheets
""")
return None
try:
creds_dict = json.loads(raw_json)
credentials = Credentials.from_service_account_info(
creds_dict,
scopes=[
"https://www.googleapis.com/auth/spreadsheets",
"https://www.googleapis.com/auth/drive"
]
)
return gspread.authorize(credentials)
except Exception as e:
st.error(f"⚠️ Erreur Auth : {e}")
return None
# ==============================================================================
# 3. APPLICATION PRINCIPALE
# ==============================================================================
def run_vortex_system():
# Configuration de la page
st.set_page_config(
page_title="Projet Data",
page_icon="🔺",
layout="wide",
initial_sidebar_state="expanded"
)
# Initialisation du style
apply_vortex_style()
client = get_gspread_client()
SHEET_NAME = "bdd_mvp_faker" # Nom du fichier Google Sheets
# --- TEST DE CONNEXION RÉEL ---
is_connected = False
connection_error = None
if client:
try:
# On tente une ouverture légère pour vérifier l'accès
client.open(SHEET_NAME)
is_connected = True
except Exception as e:
connection_error = e
if client:
# --- SIDEBAR ---
st.sidebar.title("🔺 Projet Data")
st.sidebar.caption("Système de prédiction")
st.sidebar.divider()
# Menu de Navigation
menu = st.sidebar.radio(
"Navigation",
[ "Dashboard", "AIP Terminal", "Forecasting", "Features", "Data Explorer", "ETL Control", "Indicateurs DQ", "Add Data" ] # <--- AJOUT
)
st.sidebar.divider()
# Informations système
st.sidebar.markdown("#### 📡 Statut Système")
# LOGIQUE D'AFFICHAGE INTELLIGENTE
if is_connected:
st.sidebar.success("✅ Système En Ligne")
st.sidebar.caption(f"Linked to: {SHEET_NAME}")
else:
st.sidebar.error("❌ Erreur de Liaison")
if connection_error:
# Si c'est une erreur 403 (Drive API) ou File Not Found
if "403" in str(connection_error):
st.sidebar.warning("⚠️ API Drive désactivée ou fichier non partagé.")
elif "Sprite" in str(connection_error) or "Not Found" in str(connection_error): # Gspread renvoie souvent SpriteSpreadsheetNotFound
st.sidebar.warning(f"⚠️ Fichier '{SHEET_NAME}' introuvable.")
else:
st.sidebar.warning("⚠️ Erreur technique :")
# On affiche le vrai message d'erreur pour comprendre
st.sidebar.code(str(connection_error))
st.sidebar.divider()
if st.sidebar.button("🔄 Reset System", use_container_width=True):
for key in list(st.session_state.keys()):
del st.session_state[key]
st.rerun()
# --- LOGIQUE D'AFFICHAGE ---
if menu == "Dashboard":
if Dashboard:
try:
Dashboard.show_dashboard(client, SHEET_NAME)
except Exception as e:
st.error(f"💥 Erreur Dashboard : {e}")
st.exception(e)
else:
st.error("❌ Module Dashboard non chargé.")
elif menu == "Data Explorer":
# Pas de titre ici, on laisse le module gérer son affichage pour éviter les doublons
# On vérifie que le module est chargé ET que la fonction existe
if Data_Exploration and hasattr(Data_Exploration, 'show_terminal'):
try:
# On passe le relais au module
Data_Exploration.show_terminal(client, SHEET_NAME)
except Exception as e:
# Filet de sécurité en cas de crash INTERNE au module
st.error(f"💥 Erreur critique dans le module Data_Exploration : {e}")
st.exception(e) # Affiche la trace complète pour le débogage
else:
# Gestion explicite des erreurs d'import
st.error("❌ Module Data_Exploration non disponible")
if not Data_Exploration:
st.warning("Diagnostic : L'import initial a échoué (Variable = None).")
else:
st.warning("Diagnostic : Le module est chargé mais la fonction 'show_terminal' est introuvable.")
elif menu == "AIP Terminal":
# --- DESIGN RECTANGULAIRE AIP (Style Palantir) ---
st.markdown("""
<style>
.stAlert {
border-radius: 0px !important;
border: 1px solid #30363d !important;
background-color: #0d1117 !important;
}
.stAlert p { color: #e0e0e0 !important; }
</style>
""", unsafe_allow_html=True)
# 1. Vérification : Le module est-il chargé ?
if not AIP:
st.error("❌ Le module AIP (Jasmine Agent) n'est pas chargé.")
st.info("Vérifiez l'import en haut du fichier ou les dépendances (groq, google-generativeai).")
# 2. Vérification : La fonction existe-t-elle ?
else:
if hasattr(AIP, "show_terminal"):
# Cas idéal : Fonction trouvée
AIP.show_terminal(client, SHEET_NAME)
elif hasattr(AIP, "show_jasmine_interface"):
# Cas compatible : Ancien nom trouvé
AIP.show_jasmine_interface(client, SHEET_NAME)
else:
# Cas erreur : Module là, mais fonction introuvable
st.error("❌ Erreur de point d'entrée dans AIP.py")
available_funcs = [f for f in dir(AIP) if not f.startswith('_')]
st.code(f"Fonctions disponibles détectées : {available_funcs}")
elif menu == "Indicateurs DQ":
if Indicateurs_DQ and hasattr(Indicateurs_DQ, 'show_dq_dashboard'):
try:
# Mode Google Sheets (cohérent avec le reste de l'app)
Indicateurs_DQ.show_dq_dashboard(client, SHEET_NAME)
except Exception as e:
st.error(f"💥 Erreur critique dans le module DQ : {e}")
st.exception(e)
else:
st.error("❌ Le module Indicateurs_DQ est introuvable ou mal chargé.")
st.info("Vérifiez que le fichier `src/module/Indicateurs_DQ.py` existe bien.")
elif menu == "Features":
if Features:
try:
Features.show_feature_engine(client, SHEET_NAME)
except Exception as e:
st.error(f"💥 Erreur Features Engine : {e}")
st.exception(e)
else:
st.error("❌ Module Features non chargé.")
elif menu == "Forecasting":
if Forecasting:
try:
Forecasting.show_forecasting_module(client, SHEET_NAME)
except Exception as e:
st.error(f"💥 Erreur Forecasting : {e}")
st.exception(e)
else:
st.error("❌ Module Forecasting non chargé.")
# --- NOUVEAU BLOC INJECTION ---
elif menu == "Add Data":
if add_data:
try:
add_data.show_data_injector(client, SHEET_NAME)
except Exception as e:
st.error(f"💥 Erreur Injecteur : {e}")
st.exception(e)
else:
st.error("❌ Module add_data non chargé.")
elif menu == "ETL Control":
# Vérification de l'import
if Validation_Contract:
# Instanciation du moteur
etl_engine = Validation_Contract.ETL_Engine(client, SHEET_NAME)
# --- AUTOMATISATION (AUTO-RUN) ---
# On vérifie si les données manquent. Si oui, on lance l'ETL automatiquement.
if 'data_explorer' not in st.session_state:
with st.spinner("🚀 Démarrage automatique du Pipeline ETL..."):
success, result = etl_engine.run_etl()
if not success:
st.error(f"❌ Échec critique du pipeline : {result}")
# --- AFFICHAGE DU RAPPORT ---
# À ce stade, soit l'ETL vient de tourner, soit les données étaient déjà là.
if 'data_explorer' in st.session_state:
explorer = st.session_state.data_explorer
# Petit header de statut
col_status, col_btn = st.columns([3, 1])
with col_status:
st.success(f"✅ Données synchronisées (Dernière MAJ : {explorer.load_timestamp.strftime('%H:%M:%S')})")
with col_btn:
# Bouton optionnel pour forcer la mise à jour si les données Google Sheets ont changé
if st.button("🔄 Forcer Rechargement"):
del st.session_state['data_explorer']
st.rerun()
# Affichage des graphiques de performance
explorer.show_transformation_report()
else:
st.error("❌ Le module Validation_Contract n'est pas chargé.")
else:
st.error("❌ Impossible de se connecter à Google Sheets")
st.info("""
**Configuration requise :**
1. Créez un fichier `.streamlit/secrets.toml`
2. Ajoutez votre clé JSON Google Sheets :
```toml
GSHEETS_JSON = '''
{
"type": "service_account",
"project_id": "...",
...
}
'''
```
""")
# ==============================================================================
# 4. POINT D'ENTRÉE STREAMLIT
# ==============================================================================
if __name__ == "__main__":
run_vortex_system() |