klydekushy commited on
Commit
4e9edac
·
verified ·
1 Parent(s): 10e2e8f

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +82 -118
src/streamlit_app.py CHANGED
@@ -1,11 +1,12 @@
1
  import streamlit as st
2
- from streamlit_gsheets import GSheetsConnection
3
  import pandas as pd
 
 
4
  import os
5
  import json
6
 
7
  # ==============================================================================
8
- # 1. CONFIGURATION DE LA PAGE & DESIGN GOTHAM (PALANTIR)
9
  # ==============================================================================
10
  st.set_page_config(
11
  page_title="Vortex-Flux | Ontology",
@@ -14,172 +15,135 @@ st.set_page_config(
14
  initial_sidebar_state="expanded"
15
  )
16
 
17
- # Injection CSS : Space Grotesk + Palette Dark Slate & Cobalt Blue
 
 
 
 
 
 
 
 
 
18
  st.markdown("""
19
  <style>
20
  @import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;700&display=swap');
21
 
22
- /* Fond principal (Dark Slate) */
23
- .stApp {
24
- background-color: #10161A;
25
- color: #F5F8FA;
26
- font-family: 'Space Grotesk', sans-serif;
27
- }
28
-
29
- /* Barre latérale (Darker Slate) */
30
- [data-testid="stSidebar"] {
31
- background-color: #182026;
32
- border-right: 1px solid #2B95D6;
33
- }
34
 
35
- /* Boutons (Cobalt Blue) - Style solide */
36
  div.stButton > button:first-child {
37
  background-color: #2B95D6 !important;
38
  color: white !important;
39
- border-radius: 2px !important;
40
  border: none !important;
41
  font-weight: 700;
42
  text-transform: uppercase;
43
- letter-spacing: 1px;
44
  width: 100%;
45
- transition: background-color 0.2s;
46
- }
47
- div.stButton > button:hover {
48
- background-color: #1F78B4 !important;
49
- border: 1px solid #F5F8FA !important;
50
  }
 
51
 
52
- /* Champs de saisie (Inputs) */
53
- input, textarea, select, [data-baseweb="select"] {
54
- background-color: #202B33 !important;
55
- color: #F5F8FA !important;
56
- border: 1px solid #30404D !important;
57
- border-radius: 2px !important;
58
- }
59
-
60
- /* Titres (Space Grotesk Bold) */
61
- h1, h2, h3, h4 {
62
- font-family: 'Space Grotesk', sans-serif !important;
63
- font-weight: 700 !important;
64
- color: #2B95D6 !important;
65
- letter-spacing: -0.5px !important;
66
- }
67
-
68
- /* Métriques */
69
- [data-testid="stMetricValue"] {
70
- color: #F5F8FA !important;
71
- }
72
  </style>
73
  """, unsafe_allow_html=True)
74
 
75
  # ==============================================================================
76
- # 2. MOTEUR DE CONNEXION (MÉTHODE FICHIER TEMPORAIRE)
77
  # ==============================================================================
78
- def get_connection():
79
- """
80
- Crée un fichier temporaire à partir du Secret pour authentifier Google Sheets.
81
- """
82
- # 1. Lire le contenu brut du secret (variable d'environnement)
83
  raw_json = os.environ.get("GSHEETS_JSON")
84
 
85
- if raw_json:
86
- try:
87
- # 2. Créer un fichier physique temporaire 'temp_creds.json'
88
- # C'est nécessaire car le connecteur attend souvent un chemin de fichier
89
- temp_file_path = "temp_creds.json"
90
-
91
- with open(temp_file_path, "w") as f:
92
- f.write(raw_json)
93
-
94
- # 3. Établir la connexion en pointant vers ce fichier
95
- conn = st.connection(
96
- "gsheets",
97
- type=GSheetsConnection,
98
- service_account=temp_file_path
99
- )
100
- return conn
101
-
102
- except Exception as e:
103
- st.error(f"⚠️ Erreur technique lors de la création de la connexion : {e}")
104
- return None
105
- else:
106
- st.error("🛑 ERREUR CRITIQUE : Le secret 'GSHEETS_JSON' est introuvable sur Hugging Face.")
107
  return None
108
 
109
- # Initialisation de la variable globale de connexion
110
- conn = get_connection()
 
 
 
 
 
 
 
 
 
 
 
111
 
112
- # ==============================================================================
113
- # 3. FONCTIONS UTILITAIRES DE L'ONTOLOGIE
114
- # ==============================================================================
115
- def generate_ontology_id(prefix, sheet_name):
116
- """
117
- Génère un ID unique incrémental (ex: CLI-2025-0042)
118
- """
119
- if conn is None:
120
- return "ERR-CONN"
121
 
122
  try:
123
- # ttl=0 force le rechargement des données (pas de cache) pour avoir le vrai dernier ID
124
- data = conn.read(worksheet=sheet_name, ttl=0)
125
- next_id = len(data) + 1
126
- return f"{prefix}-2025-{next_id:04d}"
127
- except Exception:
128
- # Si l'onglet est vide ou n'existe pas encore, on commence à 0001
129
- return f"{prefix}-2025-0001"
 
 
 
 
 
 
 
 
 
130
 
131
  # ==============================================================================
132
- # 4. INTERFACE UTILISATEUR (UI)
 
 
 
 
 
 
 
133
  # ==============================================================================
134
- if conn is not None:
 
 
 
 
135
  # --- BARRE LATÉRALE ---
136
  st.sidebar.title("🔺🔻 VORTEX-FLUX")
137
  st.sidebar.caption("Jumeau Numérique & Ontologie")
138
  st.sidebar.divider()
139
 
140
- menu = st.sidebar.radio(
141
- "NAVIGATION",
142
- ["TABLEAU DE BORD", "KYC CLIENTS", "SIMULATION PRÊTS"]
143
- )
144
 
145
- # --- PAGE : TABLEAU DE BORD ---
146
  if menu == "TABLEAU DE BORD":
147
  st.header("OBJECT EXPLORER")
148
- st.success("✅ Liaison établie avec le Jumeau Numérique (Google Sheets).")
149
 
150
- # Section KPIs (Design Gotham)
151
  col1, col2, col3 = st.columns(3)
152
  col1.metric("CAPITAL DEHORS", "1.2M XOF", "▲ 5%")
153
  col2.metric("FLUX ATTENDU (J+7)", "450k XOF", "▼ 2%")
154
  col3.metric("SCORE LIQUIDITÉ", "8.5/10")
155
 
156
  st.divider()
157
-
158
  st.subheader("VUE ONTOLOGIQUE : CLIENTS")
159
- st.caption("Aperçu des données brutes de l'onglet 'Clients_KYC'")
160
 
161
- try:
162
- # Tentative de lecture de l'onglet Clients_KYC
163
- df = conn.read(worksheet="Clients_KYC", ttl=0)
164
- if not df.empty:
165
- st.dataframe(df, use_container_width=True)
166
- else:
167
- st.info("ℹ️ L'onglet 'Clients_KYC' est connecté mais ne contient aucune donnée.")
168
- except Exception as e:
169
- st.warning(f"⚠️ L'onglet 'Clients_KYC' n'a pas été trouvé dans le Sheet. (Erreur: {e})")
170
 
171
- # --- PAGE : KYC CLIENTS ---
172
  elif menu == "KYC CLIENTS":
173
  st.header("ENTITÉ : NOUVEL OBJET CLIENT")
174
 
175
- # Génération d'ID en direct
176
  new_id = generate_ontology_id("CLI", "Clients_KYC")
177
 
178
  st.info(f"Création d'un nouvel objet dans l'ontologie avec l'ID : **{new_id}**")
179
-
180
- st.write("🛠️ *Le formulaire de création complet sera implémenté ici demain (Mardi).*")
181
-
182
- # --- PAGE : SIMULATION (Placeholder) ---
183
- elif menu == "SIMULATION PRÊTS":
184
- st.header("ENGINE : SIMULATION DE PRÊTS")
185
- st.warning("Module en cours de construction.")
 
1
  import streamlit as st
 
2
  import pandas as pd
3
+ import gspread
4
+ from google.oauth2.service_account import Credentials
5
  import os
6
  import json
7
 
8
  # ==============================================================================
9
+ # 1. CONFIGURATION DU "DIRECT DRIVER" & DESIGN
10
  # ==============================================================================
11
  st.set_page_config(
12
  page_title="Vortex-Flux | Ontology",
 
15
  initial_sidebar_state="expanded"
16
  )
17
 
18
+ # Nom exact de votre fichier Google Sheets (Doit être partagé avec l'email du bot)
19
+ SHEET_NAME = "Vortex-Flux"
20
+
21
+ # Scopes requis pour l'accès Google Drive/Sheets
22
+ SCOPES = [
23
+ "https://www.googleapis.com/auth/spreadsheets",
24
+ "https://www.googleapis.com/auth/drive"
25
+ ]
26
+
27
+ # Injection CSS : Design Gotham (Palantir) & Space Grotesk
28
  st.markdown("""
29
  <style>
30
  @import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;700&display=swap');
31
 
32
+ .stApp { background-color: #10161A; color: #F5F8FA; font-family: 'Space Grotesk', sans-serif; }
33
+ [data-testid="stSidebar"] { background-color: #182026; border-right: 1px solid #2B95D6; }
 
 
 
 
 
 
 
 
 
 
34
 
35
+ /* Boutons Cobalt */
36
  div.stButton > button:first-child {
37
  background-color: #2B95D6 !important;
38
  color: white !important;
 
39
  border: none !important;
40
  font-weight: 700;
41
  text-transform: uppercase;
 
42
  width: 100%;
43
+ transition: 0.2s;
 
 
 
 
44
  }
45
+ div.stButton > button:hover { background-color: #1F78B4 !important; }
46
 
47
+ h1, h2, h3 { font-family: 'Space Grotesk', sans-serif !important; color: #2B95D6 !important; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
  </style>
49
  """, unsafe_allow_html=True)
50
 
51
  # ==============================================================================
52
+ # 2. MOTEUR DE CONNEXION (GSPREAD PUR)
53
  # ==============================================================================
54
+ @st.cache_resource
55
+ def get_gspread_client():
56
+ """Authentification robuste via gspread sans passer par st.connection"""
 
 
57
  raw_json = os.environ.get("GSHEETS_JSON")
58
 
59
+ if not raw_json:
60
+ st.error("🛑 SECRET MANQUANT : 'GSHEETS_JSON' est introuvable.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
  return None
62
 
63
+ try:
64
+ # Création du fichier temporaire sécurisé
65
+ creds_dict = json.loads(raw_json)
66
+ with open("temp_creds.json", "w") as f:
67
+ json.dump(creds_dict, f)
68
+
69
+ # Authentification Google
70
+ credentials = Credentials.from_service_account_file("temp_creds.json", scopes=SCOPES)
71
+ client = gspread.authorize(credentials)
72
+ return client
73
+ except Exception as e:
74
+ st.error(f"⚠️ Erreur d'authentification GSpread : {e}")
75
+ return None
76
 
77
+ def get_data_from_sheet(worksheet_name):
78
+ """Récupère les données d'un onglet sous forme de DataFrame"""
79
+ client = get_gspread_client()
80
+ if not client: return pd.DataFrame()
 
 
 
 
 
81
 
82
  try:
83
+ # Ouverture du classeur par son nom
84
+ sh = client.open(SHEET_NAME)
85
+ # Sélection de l'onglet
86
+ worksheet = sh.worksheet(worksheet_name)
87
+ # Lecture des données
88
+ data = worksheet.get_all_records()
89
+ return pd.DataFrame(data)
90
+ except gspread.WorksheetNotFound:
91
+ # Si l'onglet n'existe pas encore, on renvoie vide sans planter
92
+ return pd.DataFrame()
93
+ except gspread.SpreadsheetNotFound:
94
+ st.error(f"⚠️ Fichier Google Sheet '{SHEET_NAME}' introuvable. Vérifiez le nom et le partage.")
95
+ return pd.DataFrame()
96
+ except Exception as e:
97
+ st.error(f"Erreur de lecture : {e}")
98
+ return pd.DataFrame()
99
 
100
  # ==============================================================================
101
+ # 3. LOGIQUE ONTOLOGIQUE (IDs)
102
+ # ==============================================================================
103
+ def generate_ontology_id(prefix, sheet_tab):
104
+ df = get_data_from_sheet(sheet_tab)
105
+ # Calcul ID : Nombre de lignes + 1
106
+ next_id = len(df) + 1
107
+ return f"{prefix}-2025-{next_id:04d}"
108
+
109
  # ==============================================================================
110
+ # 4. INTERFACE UTILISATEUR
111
+ # ==============================================================================
112
+ client = get_gspread_client()
113
+
114
+ if client:
115
  # --- BARRE LATÉRALE ---
116
  st.sidebar.title("🔺🔻 VORTEX-FLUX")
117
  st.sidebar.caption("Jumeau Numérique & Ontologie")
118
  st.sidebar.divider()
119
 
120
+ menu = st.sidebar.radio("NAVIGATION", ["TABLEAU DE BORD", "KYC CLIENTS"])
 
 
 
121
 
122
+ # --- TABLEAU DE BORD ---
123
  if menu == "TABLEAU DE BORD":
124
  st.header("OBJECT EXPLORER")
125
+ st.success(f"✅ Liaison Directe établie avec '{SHEET_NAME}'")
126
 
 
127
  col1, col2, col3 = st.columns(3)
128
  col1.metric("CAPITAL DEHORS", "1.2M XOF", "▲ 5%")
129
  col2.metric("FLUX ATTENDU (J+7)", "450k XOF", "▼ 2%")
130
  col3.metric("SCORE LIQUIDITÉ", "8.5/10")
131
 
132
  st.divider()
 
133
  st.subheader("VUE ONTOLOGIQUE : CLIENTS")
 
134
 
135
+ df_clients = get_data_from_sheet("Clients_KYC")
136
+ if not df_clients.empty:
137
+ st.dataframe(df_clients, use_container_width=True)
138
+ else:
139
+ st.info("ℹ️ L'onglet 'Clients_KYC' est vide ou n'existe pas encore.")
 
 
 
 
140
 
141
+ # --- KYC CLIENTS ---
142
  elif menu == "KYC CLIENTS":
143
  st.header("ENTITÉ : NOUVEL OBJET CLIENT")
144
 
145
+ # Génération ID en temps réel
146
  new_id = generate_ontology_id("CLI", "Clients_KYC")
147
 
148
  st.info(f"Création d'un nouvel objet dans l'ontologie avec l'ID : **{new_id}**")
149
+ st.write("🛠️ *Formulaire de création (Mardi)*")