Spaces:
Sleeping
Sleeping
Download src/streamlit_app.py from jaimalklyde/ProjetData: direct link, hf CLI and curl.
- Browser
- Download file 17.1 kB
-
https://huggingface.co/spaces/jaimalklyde/ProjetData/resolve/main/src/streamlit_app.py
- Command line
-
hf download hf://spaces/jaimalklyde/ProjetData/src/streamlit_app.py
-
curl -L -o streamlit_app.py https://huggingface.co/spaces/jaimalklyde/ProjetData/resolve/main/src/streamlit_app.py
17.1 kB
| # ============================================================================== | |
| # 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 | |
| # ============================================================================== | |
| 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() |