Vortex-Flux / src /streamlit_app.py
klydekushy's picture
Update src/streamlit_app.py
ae5eb96 verified
raw
history blame
6.15 kB
import streamlit as st
import pandas as pd
import gspread
from google.oauth2.service_account import Credentials
import os
import json
import sys
# Ajout du chemin pour trouver les modules
sys.path.append(os.path.join(os.path.dirname(__file__), 'modules'))
# IMPORT DU NOUVEAU MODULE
try:
from modules import kyc_form, map_dashboard # <-- Ajout ici
except ImportError:
# Fallback si l'import direct échoue (structure de dossier simple)
import kyc_form
import map_dashboard # <-- Ajout ici
# ==============================================================================
# 1. CONFIGURATION DU "DIRECT DRIVER" & DESIGN
# ==============================================================================
st.set_page_config(
page_title="Vortex-Flux | Ontology",
page_icon="🔺",
layout="wide",
initial_sidebar_state="expanded"
)
# Nom exact de votre fichier Google Sheets (Doit être partagé avec l'email du bot)
SHEET_NAME = "Vortex-Flux"
# Scopes requis pour l'accès Google Drive/Sheets
SCOPES = [
"https://www.googleapis.com/auth/spreadsheets",
"https://www.googleapis.com/auth/drive"
]
# Injection CSS : Design Gotham (Palantir) & Space Grotesk
st.markdown("""
<style>
@import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;700&display=swap');
.stApp { background-color: #10161A; color: #F5F8FA; font-family: 'Space Grotesk', sans-serif; }
[data-testid="stSidebar"] { background-color: #182026; border-right: 1px solid #2B95D6; }
/* Boutons Cobalt */
div.stButton > button:first-child {
background-color: #2B95D6 !important;
color: white !important;
border: none !important;
font-weight: 700;
text-transform: uppercase;
width: 100%;
transition: 0.2s;
}
div.stButton > button:hover { background-color: #1F78B4 !important; }
h1, h2, h3 { font-family: 'Space Grotesk', sans-serif !important; color: #2B95D6 !important; }
</style>
""", unsafe_allow_html=True)
# ==============================================================================
# 2. MOTEUR DE CONNEXION (GSPREAD PUR)
# ==============================================================================
@st.cache_resource
def get_gspread_client():
"""Authentification robuste via gspread sans passer par st.connection"""
raw_json = os.environ.get("GSHEETS_JSON")
if not raw_json:
st.error("🛑 SECRET MANQUANT : 'GSHEETS_JSON' est introuvable.")
return None
try:
# Création du fichier temporaire sécurisé
creds_dict = json.loads(raw_json)
with open("temp_creds.json", "w") as f:
json.dump(creds_dict, f)
# Authentification Google
credentials = Credentials.from_service_account_file("temp_creds.json", scopes=SCOPES)
client = gspread.authorize(credentials)
return client
except Exception as e:
st.error(f"⚠️ Erreur d'authentification GSpread : {e}")
return None
def get_data_from_sheet(worksheet_name):
"""Récupère les données d'un onglet sous forme de DataFrame"""
client = get_gspread_client()
if not client: return pd.DataFrame()
try:
# Ouverture du classeur par son nom
sh = client.open(SHEET_NAME)
# Sélection de l'onglet
worksheet = sh.worksheet(worksheet_name)
# Lecture des données
data = worksheet.get_all_records()
return pd.DataFrame(data)
except gspread.WorksheetNotFound:
# Si l'onglet n'existe pas encore, on renvoie vide sans planter
return pd.DataFrame()
except gspread.SpreadsheetNotFound:
st.error(f"⚠️ Fichier Google Sheet '{SHEET_NAME}' introuvable. Vérifiez le nom et le partage.")
return pd.DataFrame()
except Exception as e:
st.error(f"Erreur de lecture : {e}")
return pd.DataFrame()
# ==============================================================================
# 3. LOGIQUE ONTOLOGIQUE (IDs)
# ==============================================================================
def generate_ontology_id(prefix, sheet_tab):
df = get_data_from_sheet(sheet_tab)
# Calcul ID : Nombre de lignes + 1
next_id = len(df) + 1
return f"{prefix}-2025-{next_id:04d}" #2025 a changé pour 2026 plus tard
# ==============================================================================
# 4. INTERFACE UTILISATEUR
# ==============================================================================
client = get_gspread_client()
if client:
# --- BARRE LATÉRALE ---
st.sidebar.title("🔺🔻 VORTEX-FLUX")
st.sidebar.caption("Jumeau Numérique & Ontologie")
st.sidebar.divider()
menu = st.sidebar.radio("NAVIGATION",
["TABLEAU DE BORD", "CARTE TACTIQUE","KYC CLIENTS"]
)
# --- A. TABLEAU DE BORD ---
if menu == "TABLEAU DE BORD":
st.header("OBJECT EXPLORER")
st.success(f"✅ Liaison Directe établie avec '{SHEET_NAME}'")
col1, col2, col3 = st.columns(3)
col1.metric("CAPITAL DEHORS", "1.2M XOF", "▲ 5%")
col2.metric("FLUX ATTENDU (J+7)", "450k XOF", "▼ 2%")
col3.metric("SCORE LIQUIDITÉ", "8.5/10")
st.divider()
st.subheader("VUE ONTOLOGIQUE : CLIENTS")
df_clients = get_data_from_sheet("Clients_KYC")
if not df_clients.empty:
st.dataframe(df_clients, use_container_width=True)
else:
st.info("ℹ️ L'onglet 'Clients_KYC' est vide ou n'existe pas encore.")
# --- B. CODE DU MODULE CARTE TACTIQUE ---
elif menu == "CARTE TACTIQUE":
# Appel du nouveau module
map_dashboard.show_map_dashboard(client, SHEET_NAME)
# --- C. CODE DU MODULE KYC ---
elif menu == "KYC CLIENTS":
# APPEL DU MODULE EXTERNE
# C'est ici que la magie opère : on passe le client et la fonction ID au module
kyc_form.show_kyc_form(client, SHEET_NAME, generate_ontology_id)