# ============================================================================== # 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(""" """, 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(""" """, 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()