Spaces:
Build error
Build error
Update src/dashboard_app.py
Browse files- src/dashboard_app.py +14 -151
src/dashboard_app.py
CHANGED
|
@@ -1,17 +1,3 @@
|
|
| 1 |
-
# --- IMPORTANT POUR HUGGING FACE SPACES ---
|
| 2 |
-
# Assurez-vous que votre fichier requirements.txt (à la RACINE du dépôt) contient AU MOINS :
|
| 3 |
-
# streamlit
|
| 4 |
-
# pandas
|
| 5 |
-
# plotly
|
| 6 |
-
# Jinja2
|
| 7 |
-
# python-dotenv
|
| 8 |
-
# google-generativeai
|
| 9 |
-
# scikit-learn
|
| 10 |
-
# scipy
|
| 11 |
-
# numpy
|
| 12 |
-
# openpyxl
|
| 13 |
-
# -----------------------------------------
|
| 14 |
-
|
| 15 |
import os
|
| 16 |
import streamlit as st
|
| 17 |
import pandas as pd
|
|
@@ -29,7 +15,7 @@ from sklearn.model_selection import train_test_split
|
|
| 29 |
from sklearn.metrics import mean_squared_error, r2_score
|
| 30 |
import scipy.stats as stats
|
| 31 |
import numpy as np
|
| 32 |
-
from sklearn.preprocessing import StandardScaler
|
| 33 |
|
| 34 |
# --- Configuration de la Page Streamlit ---
|
| 35 |
st.set_page_config(layout="wide", page_title="Suite d'Analyse Interactive", page_icon="📊")
|
|
@@ -47,27 +33,6 @@ else:
|
|
| 47 |
# Sinon, supposer que le script est à la racine ou dans un autre dossier
|
| 48 |
app_root_dir = script_dir # Ou ajuster si nécessaire
|
| 49 |
|
| 50 |
-
# Chemin vers le fichier d'exemple (supposé être à la racine)
|
| 51 |
-
SAMPLE_EXCEL_FILE = os.path.join(app_root_dir, "sample_excel.xlsx")
|
| 52 |
-
|
| 53 |
-
# Charger le template HTML depuis la racine déterminée
|
| 54 |
-
TEMPLATE_FILE = "report_template.html"
|
| 55 |
-
template = None
|
| 56 |
-
try:
|
| 57 |
-
env = Environment(loader=FileSystemLoader(app_root_dir))
|
| 58 |
-
template = env.get_template(TEMPLATE_FILE)
|
| 59 |
-
except TemplateNotFound:
|
| 60 |
-
st.error(f"Erreur chargement template : '{TEMPLATE_FILE}' NON TROUVÉ dans '{app_root_dir}'. Vérifiez qu'il est bien à la racine du dépôt. Export HTML indisponible.")
|
| 61 |
-
except Exception as e:
|
| 62 |
-
st.error(f"Erreur chargement template '{TEMPLATE_FILE}' depuis '{app_root_dir}': {e}. Export HTML indisponible.")
|
| 63 |
-
|
| 64 |
-
# Affichage état API Key
|
| 65 |
-
if not api_key:
|
| 66 |
-
st.sidebar.warning("⚠️ Clé API Google Gemini manquante. Chat AI désactivé.", icon="🔑")
|
| 67 |
-
else:
|
| 68 |
-
# st.sidebar.success("✔️ Clé API Google Gemini trouvée.") # Optionnel
|
| 69 |
-
pass
|
| 70 |
-
|
| 71 |
# --- Fonctions Utilitaires ---
|
| 72 |
def generate_html_report(data, num_submissions, columns, tables_html="", charts_html=""):
|
| 73 |
if template is None: return "Erreur: Template HTML manquant."
|
|
@@ -122,18 +87,7 @@ def load_data(source_type, source_value, header_param, sep=None):
|
|
| 122 |
source_info_text = ""
|
| 123 |
|
| 124 |
try:
|
| 125 |
-
if source_type == "
|
| 126 |
-
file = source_value
|
| 127 |
-
source_info_text = f"Fichier chargé : {file.name}"
|
| 128 |
-
data_id = f"upload_{file.name}_{file.size}"
|
| 129 |
-
if file.name.endswith('.csv'):
|
| 130 |
-
data = pd.read_csv(file, header=header_param)
|
| 131 |
-
elif file.name.endswith('.xlsx'):
|
| 132 |
-
data = pd.read_excel(file, header=header_param, engine='openpyxl')
|
| 133 |
-
else:
|
| 134 |
-
error_message = "Format de fichier uploadé non supporté (CSV ou XLSX requis)."
|
| 135 |
-
|
| 136 |
-
elif source_type == "url" and source_value:
|
| 137 |
url = source_value
|
| 138 |
source_info_text = f"URL chargée : {url}"
|
| 139 |
data_id = f"url_{hash(url)}"
|
|
@@ -151,17 +105,6 @@ def load_data(source_type, source_value, header_param, sep=None):
|
|
| 151 |
if not sep: sep = '\t' # Défaut tabulation pour collage Excel
|
| 152 |
data = pd.read_csv(io.StringIO(pasted_str), sep=sep, header=header_param)
|
| 153 |
|
| 154 |
-
elif source_type == "sample" and source_value:
|
| 155 |
-
file_path = source_value
|
| 156 |
-
source_info_text = f"Exemple : {os.path.basename(file_path)}"
|
| 157 |
-
data_id = f"sample_{os.path.basename(file_path)}"
|
| 158 |
-
if os.path.exists(file_path) and file_path.endswith('.xlsx'):
|
| 159 |
-
data = pd.read_excel(file_path, header=header_param, engine='openpyxl')
|
| 160 |
-
elif os.path.exists(file_path) and file_path.endswith('.csv'):
|
| 161 |
-
data = pd.read_csv(file_path, header=header_param)
|
| 162 |
-
else:
|
| 163 |
-
error_message = f"Fichier exemple non trouvé ou format incorrect: {file_path}"
|
| 164 |
-
|
| 165 |
# --- Vérification post-chargement ---
|
| 166 |
if data is not None and error_message is None:
|
| 167 |
if data.empty:
|
|
@@ -196,7 +139,7 @@ st.markdown(
|
|
| 196 |
"""
|
| 197 |
<div style='background-color: #f0f2f6; border-left: 5px solid #1f77b4; padding: 15px; border-radius: 5px; margin-bottom: 20px;'>
|
| 198 |
<p style='font-size: 1.1em; color: #000000;'>Bienvenue dans la <strong>Suite d'Analyse de Données Interactive</strong>.</p>
|
| 199 |
-
<p style='color: #000000;'>
|
| 200 |
</div>
|
| 201 |
""", unsafe_allow_html=True
|
| 202 |
)
|
|
@@ -213,7 +156,7 @@ if 'html_report_filename' not in st.session_state: st.session_state.html_report_
|
|
| 213 |
if 'data_source_info' not in st.session_state: st.session_state.data_source_info = "Aucune donnée chargée"
|
| 214 |
if "gemini_chat_history" not in st.session_state: st.session_state.gemini_chat_history = []
|
| 215 |
# Ajouter un état pour le choix de la méthode de chargement
|
| 216 |
-
if 'load_method' not in st.session_state: st.session_state.load_method = "
|
| 217 |
|
| 218 |
# --- Création des Onglets ---
|
| 219 |
app_tab, manual_tab, chat_tab = st.tabs(["📊 Application Principale", "📘 Manuel d'Utilisation", "💬 Chat IA (Gemini)"])
|
|
@@ -231,7 +174,7 @@ with app_tab:
|
|
| 231 |
st.subheader("1. Charger les Données")
|
| 232 |
|
| 233 |
# Choix de la méthode
|
| 234 |
-
load_options = ["
|
| 235 |
st.session_state.load_method = st.radio(
|
| 236 |
"Choisissez une méthode de chargement :",
|
| 237 |
options=load_options,
|
|
@@ -248,21 +191,8 @@ with app_tab:
|
|
| 248 |
)
|
| 249 |
header_param_common = 0 if use_header_common else None
|
| 250 |
|
| 251 |
-
# --- Méthode 1:
|
| 252 |
-
if st.session_state.load_method == "
|
| 253 |
-
uploaded_file = st.file_uploader(
|
| 254 |
-
"Déposez votre fichier CSV ou Excel ici :",
|
| 255 |
-
type=["csv", "xlsx"], key="file_uploader_widget",
|
| 256 |
-
accept_multiple_files=False # S'assurer qu'un seul fichier est traité
|
| 257 |
-
)
|
| 258 |
-
if uploaded_file is not None:
|
| 259 |
-
current_upload_id = f"upload_{uploaded_file.name}_{uploaded_file.size}"
|
| 260 |
-
# Charger seulement si l'ID ou l'option header a changé
|
| 261 |
-
if st.session_state.data_loaded_id != current_upload_id or st.session_state.last_header_preference != use_header_common:
|
| 262 |
-
load_data("upload", uploaded_file, header_param_common)
|
| 263 |
-
|
| 264 |
-
# --- Méthode 2: URL ---
|
| 265 |
-
elif st.session_state.load_method == "Charger depuis URL":
|
| 266 |
data_url = st.text_input("Collez l'URL d'un fichier CSV ou Excel public :", key="data_url_input")
|
| 267 |
if st.button("Charger depuis URL", key="load_from_url_button"):
|
| 268 |
if data_url:
|
|
@@ -270,7 +200,7 @@ with app_tab:
|
|
| 270 |
else:
|
| 271 |
st.warning("Veuillez entrer une URL.")
|
| 272 |
|
| 273 |
-
# --- Méthode
|
| 274 |
elif st.session_state.load_method == "Coller depuis presse-papiers":
|
| 275 |
pasted_data = st.text_area(
|
| 276 |
"Collez vos données ici (format type CSV/Tab) :",
|
|
@@ -460,9 +390,6 @@ with app_tab:
|
|
| 460 |
try: base_name = os.path.basename(source_info_for_file.split(":")[-1].strip().split("?")[0]) # Essayer de nettoyer URL
|
| 461 |
except: base_name = "url_data"
|
| 462 |
export_filename_base = f"export_{os.path.splitext(base_name)[0]}"
|
| 463 |
-
elif "Exemple :" in source_info_for_file:
|
| 464 |
-
base_name = source_info_for_file.split(":")[-1].strip()
|
| 465 |
-
export_filename_base = f"export_{base_name}"
|
| 466 |
elif "Données collées" in source_info_for_file:
|
| 467 |
export_filename_base = "export_donnees_collees"
|
| 468 |
else:
|
|
@@ -470,7 +397,7 @@ with app_tab:
|
|
| 470 |
# Nettoyer le nom
|
| 471 |
export_filename_base = "".join(c if c.isalnum() or c in ('_', '-') else '_' for c in export_filename_base).strip('_')
|
| 472 |
|
| 473 |
-
col_export1, col_export2
|
| 474 |
with col_export1: # CSV
|
| 475 |
try:
|
| 476 |
csv_data = df_to_export.to_csv(index=False).encode('utf-8')
|
|
@@ -483,60 +410,6 @@ with app_tab:
|
|
| 483 |
st.download_button("Exporter Excel", excel_buffer.getvalue(), f"{export_filename_base}.xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", key=f"dl_excel_{export_key_suffix}")
|
| 484 |
except Exception as e:
|
| 485 |
st.error(f"Erreur Export Excel: {e}"); st.warning("Vérifiez 'openpyxl' dans reqs.", icon="💡")
|
| 486 |
-
with col_export3: # HTML Report
|
| 487 |
-
if template:
|
| 488 |
-
if st.button("Préparer Rapport HTML", key=f"prep_html_{export_key_suffix}"):
|
| 489 |
-
with st.spinner("Génération rapport..."):
|
| 490 |
-
try:
|
| 491 |
-
data_for_report = st.session_state.dataframe_to_export
|
| 492 |
-
if data_for_report is not None:
|
| 493 |
-
num_submissions_report = len(data_for_report)
|
| 494 |
-
columns_for_report = data_for_report.columns.tolist()
|
| 495 |
-
tables_html_list, charts_html_list = [], []
|
| 496 |
-
# --- Génération contenu HTML ---
|
| 497 |
-
for analysis in st.session_state.get('analyses', []):
|
| 498 |
-
result = analysis.get('result'); analysis_type = analysis.get('type')
|
| 499 |
-
params = analysis.get('executed_params', analysis.get('params', {}))
|
| 500 |
-
analysis_id_rep = analysis.get('id', -1) + 1
|
| 501 |
-
if result is not None:
|
| 502 |
-
title = f"Analyse {analysis_id_rep}: {analysis_type.replace('_', ' ').title()}"
|
| 503 |
-
param_details_list = []
|
| 504 |
-
for k,v in params.items():
|
| 505 |
-
if v is not None and v != [] and v != 'None' and k not in ['type']:
|
| 506 |
-
v_repr = f"[{v[0]}, ..., {v[-1]}] ({len(v)})" if isinstance(v, list) and len(v) > 3 else str(v)
|
| 507 |
-
k_simple = k.replace('_graph','').replace('desc','').replace('_columns','').replace('column','').replace('_',' ').strip().title()
|
| 508 |
-
param_details_list.append(f"{k_simple} = {v_repr}")
|
| 509 |
-
param_details = "; ".join(param_details_list)
|
| 510 |
-
full_title = f"{title} <small>({param_details})</small>" if param_details else title
|
| 511 |
-
try: # Conversion résultat en HTML
|
| 512 |
-
if analysis_type in ['aggregated_table', 'descriptive_stats'] and isinstance(result, pd.DataFrame):
|
| 513 |
-
df_html = result.T if analysis_type == 'descriptive_stats' else result
|
| 514 |
-
# Appliquer formatage pour le rapport aussi
|
| 515 |
-
table_html = df_html.to_html(index=(analysis_type == 'descriptive_stats'), classes='table table-striped table-hover table-sm', border=0, float_format='{:,.2f}'.format)
|
| 516 |
-
tables_html_list.append(f"<h3>{full_title}</h3>{table_html}")
|
| 517 |
-
elif analysis_type == 'graph' and isinstance(result, go.Figure):
|
| 518 |
-
chart_html = result.to_html(full_html=False, include_plotlyjs='cdn')
|
| 519 |
-
charts_html_list.append(f"<h3>{full_title}</h3>{chart_html}")
|
| 520 |
-
except Exception as e_render: st.warning(f"Erreur rendu résultat Analyse {analysis_id_rep}: {e_render}")
|
| 521 |
-
# --- Fin génération contenu ---
|
| 522 |
-
tables_html = "\n<hr/>\n".join(tables_html_list) if tables_html_list else "<p>Aucun tableau généré.</p>"
|
| 523 |
-
charts_html = "\n<hr/>\n".join(charts_html_list) if charts_html_list else "<p>Aucun graphique généré.</p>"
|
| 524 |
-
|
| 525 |
-
html_content = generate_html_report(data_for_report, num_submissions_report, columns_for_report, tables_html, charts_html)
|
| 526 |
-
if "Erreur:" not in html_content:
|
| 527 |
-
st.session_state.html_report_content = html_content.encode('utf-8')
|
| 528 |
-
st.session_state.html_report_filename = f"rapport_{export_filename_base}.html"
|
| 529 |
-
st.success("Rapport prêt.")
|
| 530 |
-
else: st.error("Échec génération contenu HTML.")
|
| 531 |
-
else: st.error("Pas de données pour le rapport.")
|
| 532 |
-
except Exception as e_report: st.error(f"Erreur préparation rapport: {e_report}")
|
| 533 |
-
|
| 534 |
-
if st.session_state.get('html_report_content'):
|
| 535 |
-
st.download_button("Télécharger Rapport HTML", st.session_state.html_report_content, st.session_state.html_report_filename, "text/html", key=f"dl_html_{export_key_suffix}", on_click=lambda: st.session_state.update(html_report_content=None))
|
| 536 |
-
else:
|
| 537 |
-
st.info("Export HTML indisponible (template non chargé).")
|
| 538 |
-
else:
|
| 539 |
-
st.info("Chargez des données pour activer l'exportation.")
|
| 540 |
|
| 541 |
# --- Zone Principale de l'Application ---
|
| 542 |
st.header("📊 Aperçu et Analyse des Données")
|
|
@@ -848,7 +721,7 @@ with app_tab:
|
|
| 848 |
selected_value = st.selectbox("Valeurs (Taille):", options_values, index=get_safe_index(options_values, analysis['params'].get('value_column')), key=f"graph_value_{analysis_id}")
|
| 849 |
st.session_state.analyses[i]['params']['value_column'] = selected_value
|
| 850 |
|
| 851 |
-
# --- Options d'
|
| 852 |
with st.expander("Options d'agrégation (avant graphique)", expanded=aggregation_enabled_graph):
|
| 853 |
if not conf_categorical_columns: st.caption("Nécessite cols Catégorielles.")
|
| 854 |
else:
|
|
@@ -1069,7 +942,7 @@ with app_tab:
|
|
| 1069 |
groups = data[group_col_t].dropna().unique()
|
| 1070 |
if len(groups) != 2: st.error(f"'{group_col_t}' n'a pas 2 groupes après NAs.")
|
| 1071 |
else:
|
| 1072 |
-
g1 = get_valid_data(data
|
| 1073 |
if len(g1) < 3 or len(g2) < 3: st.error(f"Pas assez données valides (<3/groupe) pour '{numeric_var_t}'.")
|
| 1074 |
else:
|
| 1075 |
t_stat, p_value = stats.ttest_ind(g1, g2, equal_var=False, nan_policy='omit')
|
|
@@ -1096,7 +969,7 @@ with app_tab:
|
|
| 1096 |
if group_col_a and anova_numeric_var:
|
| 1097 |
try:
|
| 1098 |
groups_values = data[group_col_a].dropna().unique()
|
| 1099 |
-
groups_data = [get_valid_data(data
|
| 1100 |
groups_data_f = [g for g in groups_data if len(g) >= 3]
|
| 1101 |
n_groups = len(groups_data_f)
|
| 1102 |
if n_groups < 2: st.error(f"Pas assez de groupes (min 2) avec données valides (min 3) pour '{anova_numeric_var}'.")
|
|
@@ -1319,11 +1192,9 @@ with manual_tab:
|
|
| 1319 |
|
| 1320 |
---
|
| 1321 |
### 1. Chargement des Données (Barre Latérale ⚙️)
|
| 1322 |
-
- **Choisir une méthode** : Sélectionnez l'une des options proposées (
|
| 1323 |
-
- **
|
| 1324 |
-
- **Charger depuis URL** : Collez l'URL directe d'un fichier CSV ou Excel public et cliquez sur "Charger depuis URL".
|
| 1325 |
- **Coller depuis presse-papiers**: Copiez des données depuis un tableur (Excel, Sheets), collez-les dans la zone de texte, vérifiez le séparateur (Tabulation par défaut) et cliquez sur "Charger Données Collées".
|
| 1326 |
-
- **Utiliser le fichier d'exemple**: Cliquez sur "Charger l'exemple" pour utiliser le fichier `sample_excel.xlsx` fourni.
|
| 1327 |
- **Utiliser l'en-tête** : Cochez/décochez la case **avant** de cliquer sur le bouton de chargement correspondant à votre méthode pour indiquer si la première ligne contient les noms de colonnes.
|
| 1328 |
|
| 1329 |
---
|
|
@@ -1332,7 +1203,6 @@ with manual_tab:
|
|
| 1332 |
- **Renommer Colonnes** : Sélectionnez une colonne, entrez un nouveau nom et cliquez sur "Appliquer Renommage".
|
| 1333 |
- **Exporter** :
|
| 1334 |
- **CSV/Excel** : Téléchargez les données actuellement chargées.
|
| 1335 |
-
- **Rapport HTML** : Cliquez sur "Préparer Rapport HTML" pour générer un fichier HTML contenant un aperçu des données et les **résultats des analyses déjà exécutées** (tableaux/graphiques de la section "Analyses Configurées"). Cliquez ensuite sur "Télécharger Rapport HTML".
|
| 1336 |
|
| 1337 |
---
|
| 1338 |
### 3. Analyses (Zone Principale 📊)
|
|
@@ -1342,12 +1212,6 @@ with manual_tab:
|
|
| 1342 |
- **Supprimer une Analyse** : Cliquez sur l'icône poubelle 🗑️ du bloc.
|
| 1343 |
- **Analyses Avancées** : Cochez la case "Afficher les analyses avancées", sélectionnez un test/modèle, configurez-le et cliquez sur "Effectuer...".
|
| 1344 |
|
| 1345 |
-
---
|
| 1346 |
-
### 4. Chat IA (Onglet 💬)
|
| 1347 |
-
(Nécessite une clé API Google Gemini configurée)
|
| 1348 |
-
- Posez des questions sur l'analyse de données ou demandez des suggestions basées sur les colonnes détectées.
|
| 1349 |
-
- **Rappel** : L'IA ne voit pas les valeurs de vos données.
|
| 1350 |
-
|
| 1351 |
---
|
| 1352 |
### 💡 Conseils & Dépannage
|
| 1353 |
- **Types de Colonnes** : Vérifiez les types détectés dans la section "Afficher détails colonnes". Corrigez vos données sources si nécessaire (ex: formats de date, nombres avec texte). Une mauvaise détection peut limiter les options d'analyse.
|
|
@@ -1361,7 +1225,6 @@ with manual_tab:
|
|
| 1361 |
📞 Contact : +229 96911346
|
| 1362 |
🔗 [Profil LinkedIn](https://www.linkedin.com/in/sidoineko) | 📂 [Portfolio](https://huggingface.co/spaces/Sidoineko/portfolio)
|
| 1363 |
""")
|
| 1364 |
-
|
| 1365 |
# ==============================================================================
|
| 1366 |
# ONGLET CHAT IA
|
| 1367 |
# ==============================================================================
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
import os
|
| 2 |
import streamlit as st
|
| 3 |
import pandas as pd
|
|
|
|
| 15 |
from sklearn.metrics import mean_squared_error, r2_score
|
| 16 |
import scipy.stats as stats
|
| 17 |
import numpy as np
|
| 18 |
+
from sklearn.preprocessing import StandardScaler
|
| 19 |
|
| 20 |
# --- Configuration de la Page Streamlit ---
|
| 21 |
st.set_page_config(layout="wide", page_title="Suite d'Analyse Interactive", page_icon="📊")
|
|
|
|
| 33 |
# Sinon, supposer que le script est à la racine ou dans un autre dossier
|
| 34 |
app_root_dir = script_dir # Ou ajuster si nécessaire
|
| 35 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 36 |
# --- Fonctions Utilitaires ---
|
| 37 |
def generate_html_report(data, num_submissions, columns, tables_html="", charts_html=""):
|
| 38 |
if template is None: return "Erreur: Template HTML manquant."
|
|
|
|
| 87 |
source_info_text = ""
|
| 88 |
|
| 89 |
try:
|
| 90 |
+
if source_type == "url" and source_value:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 91 |
url = source_value
|
| 92 |
source_info_text = f"URL chargée : {url}"
|
| 93 |
data_id = f"url_{hash(url)}"
|
|
|
|
| 105 |
if not sep: sep = '\t' # Défaut tabulation pour collage Excel
|
| 106 |
data = pd.read_csv(io.StringIO(pasted_str), sep=sep, header=header_param)
|
| 107 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 108 |
# --- Vérification post-chargement ---
|
| 109 |
if data is not None and error_message is None:
|
| 110 |
if data.empty:
|
|
|
|
| 139 |
"""
|
| 140 |
<div style='background-color: #f0f2f6; border-left: 5px solid #1f77b4; padding: 15px; border-radius: 5px; margin-bottom: 20px;'>
|
| 141 |
<p style='font-size: 1.1em; color: #000000;'>Bienvenue dans la <strong>Suite d'Analyse de Données Interactive</strong>.</p>
|
| 142 |
+
<p style='color: #000000;'>Veuillez lire le manuel d'utilisation avant de commencer.</p>
|
| 143 |
</div>
|
| 144 |
""", unsafe_allow_html=True
|
| 145 |
)
|
|
|
|
| 156 |
if 'data_source_info' not in st.session_state: st.session_state.data_source_info = "Aucune donnée chargée"
|
| 157 |
if "gemini_chat_history" not in st.session_state: st.session_state.gemini_chat_history = []
|
| 158 |
# Ajouter un état pour le choix de la méthode de chargement
|
| 159 |
+
if 'load_method' not in st.session_state: st.session_state.load_method = "Charger depuis URL"
|
| 160 |
|
| 161 |
# --- Création des Onglets ---
|
| 162 |
app_tab, manual_tab, chat_tab = st.tabs(["📊 Application Principale", "📘 Manuel d'Utilisation", "💬 Chat IA (Gemini)"])
|
|
|
|
| 174 |
st.subheader("1. Charger les Données")
|
| 175 |
|
| 176 |
# Choix de la méthode
|
| 177 |
+
load_options = ["Charger depuis URL", "Coller depuis presse-papiers"]
|
| 178 |
st.session_state.load_method = st.radio(
|
| 179 |
"Choisissez une méthode de chargement :",
|
| 180 |
options=load_options,
|
|
|
|
| 191 |
)
|
| 192 |
header_param_common = 0 if use_header_common else None
|
| 193 |
|
| 194 |
+
# --- Méthode 1: URL ---
|
| 195 |
+
if st.session_state.load_method == "Charger depuis URL":
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 196 |
data_url = st.text_input("Collez l'URL d'un fichier CSV ou Excel public :", key="data_url_input")
|
| 197 |
if st.button("Charger depuis URL", key="load_from_url_button"):
|
| 198 |
if data_url:
|
|
|
|
| 200 |
else:
|
| 201 |
st.warning("Veuillez entrer une URL.")
|
| 202 |
|
| 203 |
+
# --- Méthode 2: Coller ---
|
| 204 |
elif st.session_state.load_method == "Coller depuis presse-papiers":
|
| 205 |
pasted_data = st.text_area(
|
| 206 |
"Collez vos données ici (format type CSV/Tab) :",
|
|
|
|
| 390 |
try: base_name = os.path.basename(source_info_for_file.split(":")[-1].strip().split("?")[0]) # Essayer de nettoyer URL
|
| 391 |
except: base_name = "url_data"
|
| 392 |
export_filename_base = f"export_{os.path.splitext(base_name)[0]}"
|
|
|
|
|
|
|
|
|
|
| 393 |
elif "Données collées" in source_info_for_file:
|
| 394 |
export_filename_base = "export_donnees_collees"
|
| 395 |
else:
|
|
|
|
| 397 |
# Nettoyer le nom
|
| 398 |
export_filename_base = "".join(c if c.isalnum() or c in ('_', '-') else '_' for c in export_filename_base).strip('_')
|
| 399 |
|
| 400 |
+
col_export1, col_export2 = st.columns(2)
|
| 401 |
with col_export1: # CSV
|
| 402 |
try:
|
| 403 |
csv_data = df_to_export.to_csv(index=False).encode('utf-8')
|
|
|
|
| 410 |
st.download_button("Exporter Excel", excel_buffer.getvalue(), f"{export_filename_base}.xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", key=f"dl_excel_{export_key_suffix}")
|
| 411 |
except Exception as e:
|
| 412 |
st.error(f"Erreur Export Excel: {e}"); st.warning("Vérifiez 'openpyxl' dans reqs.", icon="💡")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 413 |
|
| 414 |
# --- Zone Principale de l'Application ---
|
| 415 |
st.header("📊 Aperçu et Analyse des Données")
|
|
|
|
| 721 |
selected_value = st.selectbox("Valeurs (Taille):", options_values, index=get_safe_index(options_values, analysis['params'].get('value_column')), key=f"graph_value_{analysis_id}")
|
| 722 |
st.session_state.analyses[i]['params']['value_column'] = selected_value
|
| 723 |
|
| 724 |
+
# --- Options d'agrégation ---
|
| 725 |
with st.expander("Options d'agrégation (avant graphique)", expanded=aggregation_enabled_graph):
|
| 726 |
if not conf_categorical_columns: st.caption("Nécessite cols Catégorielles.")
|
| 727 |
else:
|
|
|
|
| 942 |
groups = data[group_col_t].dropna().unique()
|
| 943 |
if len(groups) != 2: st.error(f"'{group_col_t}' n'a pas 2 groupes après NAs.")
|
| 944 |
else:
|
| 945 |
+
g1 = get_valid_data(data, numeric_var_t); g2 = get_valid_data(data, numeric_var_t)
|
| 946 |
if len(g1) < 3 or len(g2) < 3: st.error(f"Pas assez données valides (<3/groupe) pour '{numeric_var_t}'.")
|
| 947 |
else:
|
| 948 |
t_stat, p_value = stats.ttest_ind(g1, g2, equal_var=False, nan_policy='omit')
|
|
|
|
| 969 |
if group_col_a and anova_numeric_var:
|
| 970 |
try:
|
| 971 |
groups_values = data[group_col_a].dropna().unique()
|
| 972 |
+
groups_data = [get_valid_data(data, anova_numeric_var) for v in groups_values]
|
| 973 |
groups_data_f = [g for g in groups_data if len(g) >= 3]
|
| 974 |
n_groups = len(groups_data_f)
|
| 975 |
if n_groups < 2: st.error(f"Pas assez de groupes (min 2) avec données valides (min 3) pour '{anova_numeric_var}'.")
|
|
|
|
| 1192 |
|
| 1193 |
---
|
| 1194 |
### 1. Chargement des Données (Barre Latérale ⚙️)
|
| 1195 |
+
- **Choisir une méthode** : Sélectionnez l'une des options proposées (URL, Coller).
|
| 1196 |
+
- **URL** : Collez l'URL directe d'un fichier CSV ou Excel public et cliquez sur "Charger depuis URL".
|
|
|
|
| 1197 |
- **Coller depuis presse-papiers**: Copiez des données depuis un tableur (Excel, Sheets), collez-les dans la zone de texte, vérifiez le séparateur (Tabulation par défaut) et cliquez sur "Charger Données Collées".
|
|
|
|
| 1198 |
- **Utiliser l'en-tête** : Cochez/décochez la case **avant** de cliquer sur le bouton de chargement correspondant à votre méthode pour indiquer si la première ligne contient les noms de colonnes.
|
| 1199 |
|
| 1200 |
---
|
|
|
|
| 1203 |
- **Renommer Colonnes** : Sélectionnez une colonne, entrez un nouveau nom et cliquez sur "Appliquer Renommage".
|
| 1204 |
- **Exporter** :
|
| 1205 |
- **CSV/Excel** : Téléchargez les données actuellement chargées.
|
|
|
|
| 1206 |
|
| 1207 |
---
|
| 1208 |
### 3. Analyses (Zone Principale 📊)
|
|
|
|
| 1212 |
- **Supprimer une Analyse** : Cliquez sur l'icône poubelle 🗑️ du bloc.
|
| 1213 |
- **Analyses Avancées** : Cochez la case "Afficher les analyses avancées", sélectionnez un test/modèle, configurez-le et cliquez sur "Effectuer...".
|
| 1214 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1215 |
---
|
| 1216 |
### 💡 Conseils & Dépannage
|
| 1217 |
- **Types de Colonnes** : Vérifiez les types détectés dans la section "Afficher détails colonnes". Corrigez vos données sources si nécessaire (ex: formats de date, nombres avec texte). Une mauvaise détection peut limiter les options d'analyse.
|
|
|
|
| 1225 |
📞 Contact : +229 96911346
|
| 1226 |
🔗 [Profil LinkedIn](https://www.linkedin.com/in/sidoineko) | 📂 [Portfolio](https://huggingface.co/spaces/Sidoineko/portfolio)
|
| 1227 |
""")
|
|
|
|
| 1228 |
# ==============================================================================
|
| 1229 |
# ONGLET CHAT IA
|
| 1230 |
# ==============================================================================
|