emanoelopes commited on
Commit
a1cb950
·
1 Parent(s): 70c4eaa

Atualizar versão para 0.1.1 e melhorias no rodapé

Browse files

- Atualizada versão do sistema de 0.1.0 para 0.1.1
- Adicionados badges de status no rodapé de todas as páginas
- Padronizado rodapé com informações do sistema em todas as sidebars
- Reorganizadas páginas (UCI, OULAD, Painel Analítico, Análise Exploratória)
- Removidos arquivos antigos e adicionadas novas páginas

pyproject.toml CHANGED
@@ -1,6 +1,6 @@
1
  [project]
2
  name = "sida"
3
- version = "0.1.0"
4
  description = "Sistema de Identificação de Dificuldades de Aprendizagem (SIDA)"
5
  readme = "README.md"
6
  requires-python = ">=3.9"
 
1
  [project]
2
  name = "sida"
3
+ version = "0.1.1"
4
  description = "Sistema de Identificação de Dificuldades de Aprendizagem (SIDA)"
5
  readme = "README.md"
6
  requires-python = ">=3.9"
setup.py CHANGED
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
2
 
3
  setup(
4
  name='SIDA',
5
- version='0.1.0',
6
  packages=find_packages(),
7
  install_requires=[
8
  'pandas',
 
2
 
3
  setup(
4
  name='SIDA',
5
+ version='0.1.1',
6
  packages=find_packages(),
7
  install_requires=[
8
  'pandas',
webapp/home.py CHANGED
@@ -151,5 +151,5 @@ Programa de Pós-Graduação em Tecnologias Educacionais (PPGTE)
151
  Instituto UFC Virtual (IUVI)
152
  Universidade Federal do Ceará (UFC)
153
 
154
- Versão 1.0.0 - 2025
155
  """)
 
151
  Instituto UFC Virtual (IUVI)
152
  Universidade Federal do Ceará (UFC)
153
 
154
+ Versão 0.1.1 - 2025
155
  """)
webapp/oulad.pkl CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:4601482a182ed9a7adaf0e93e3b2d4732e4efbb4d081000003cc0d090990ab0b
3
- size 143341
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:75c62b6e90e29e9bc916a9b3cad92be50d432d4c1124600bd37352c43b2a9207
3
+ size 143750
webapp/pages/{1_dashboard.py → 1_Painel Analítico.py} RENAMED
@@ -1,5 +1,5 @@
1
  """
2
- Dashboard Consolidado - Visão geral dos datasets UCI e OULAD
3
  Página separada para análise consolidada dos datasets
4
  """
5
 
@@ -15,7 +15,7 @@ from src.openai_interpreter import criar_sidebar_padrao
15
 
16
  # Configuração da página
17
  st.set_page_config(
18
- page_title="Dashboard Educacional",
19
  page_icon="📊",
20
  layout="wide"
21
  )
@@ -24,7 +24,7 @@ st.set_page_config(
24
  criar_sidebar_padrao()
25
 
26
  # Título principal
27
- st.title("📊 Dashboard Consolidado")
28
  st.markdown("Visão geral dos datasets UCI e OULAD")
29
 
30
  # Métricas principais
@@ -131,7 +131,7 @@ st.markdown("""
131
  - `utilidades.py`: Lógica de negócio
132
  - `openai_interpreter.py`: Interpretação IA
133
  - `home.py`: Landing page
134
- - `1_dashboard.py`: Dashboard consolidado
135
 
136
  2. **Extensibilidade**
137
  - Fácil adição de novos datasets
@@ -150,5 +150,5 @@ Programa de Pós-Graduação em Tecnologias Educacionais (PPGTE)
150
  Instituto UFC Virtual (IUVI)
151
  Universidade Federal do Ceará (UFC)
152
 
153
- Versão 1.0.0 - 2025
154
  """)
 
1
  """
2
+ Painel Analítico - Visão geral dos datasets UCI e OULAD
3
  Página separada para análise consolidada dos datasets
4
  """
5
 
 
15
 
16
  # Configuração da página
17
  st.set_page_config(
18
+ page_title="Painel Analítico",
19
  page_icon="📊",
20
  layout="wide"
21
  )
 
24
  criar_sidebar_padrao()
25
 
26
  # Título principal
27
+ st.title("📊 Painel Analítico")
28
  st.markdown("Visão geral dos datasets UCI e OULAD")
29
 
30
  # Métricas principais
 
131
  - `utilidades.py`: Lógica de negócio
132
  - `openai_interpreter.py`: Interpretação IA
133
  - `home.py`: Landing page
134
+ - `1_Painel Analítico.py`: Painel Analítico consolidado
135
 
136
  2. **Extensibilidade**
137
  - Fácil adição de novos datasets
 
150
  Instituto UFC Virtual (IUVI)
151
  Universidade Federal do Ceará (UFC)
152
 
153
+ Versão 0.1.1 - 2025
154
  """)
webapp/pages/{1_uci.py → 1_UCI.py} RENAMED
@@ -6,6 +6,7 @@ import matplotlib.pyplot as plt
6
  import seaborn as sns
7
  import numpy as np
8
  import pickle
 
9
 
10
  st.set_page_config(
11
  page_title="Análise Exploratória dos Dados - UCI",
@@ -63,12 +64,6 @@ with st.sidebar:
63
  ["Box Plot", "Histograma", "Violin Plot"]
64
  )
65
  st.markdown("---")
66
- st.markdown("### Experimente")
67
- analyzer = st.selectbox(
68
- "Analisador",
69
- ["UCI EDA", "OULAD EDA"]
70
- )
71
- st.markdown("---")
72
  st.markdown("## Informações")
73
  # Calcular estudantes únicos baseado em características demográficas
74
  colunas_id = ['school', 'sex', 'age', 'address', 'famsize', 'Pstatus', 'Medu', 'Fedu', 'Mjob', 'Fjob', 'reason', 'guardian']
@@ -83,8 +78,8 @@ with st.sidebar:
83
  st.write(f"**Número de Valores Duplicados:** {df.duplicated().sum()}")
84
  st.markdown("---")
85
 
86
- ### footer
87
- st.markdown("Mestrado em Tecnologia Educacional - UFC")
88
 
89
  # Create visualization section
90
  st.markdown("### Visualização das distribuições dos dados numéricos ")
@@ -489,4 +484,4 @@ with open('uci.pkl', 'wb') as f:
489
  # Seção de análise interativa (PyGWalker movido para o dashboard principal)
490
  st.markdown("---")
491
  st.markdown("### 🔍 Análise Interativa")
492
- st.info("💡 Para análise interativa dos dados, utilize a aba 'Feature Importance' no dashboard principal, onde você pode ativar o PyGWalker de forma opcional.")
 
6
  import seaborn as sns
7
  import numpy as np
8
  import pickle
9
+ from src.openai_interpreter import criar_rodape_sidebar
10
 
11
  st.set_page_config(
12
  page_title="Análise Exploratória dos Dados - UCI",
 
64
  ["Box Plot", "Histograma", "Violin Plot"]
65
  )
66
  st.markdown("---")
 
 
 
 
 
 
67
  st.markdown("## Informações")
68
  # Calcular estudantes únicos baseado em características demográficas
69
  colunas_id = ['school', 'sex', 'age', 'address', 'famsize', 'Pstatus', 'Medu', 'Fedu', 'Mjob', 'Fjob', 'reason', 'guardian']
 
78
  st.write(f"**Número de Valores Duplicados:** {df.duplicated().sum()}")
79
  st.markdown("---")
80
 
81
+ # Rodapé com badges de status (igual ao da home)
82
+ criar_rodape_sidebar()
83
 
84
  # Create visualization section
85
  st.markdown("### Visualização das distribuições dos dados numéricos ")
 
484
  # Seção de análise interativa (PyGWalker movido para o dashboard principal)
485
  st.markdown("---")
486
  st.markdown("### 🔍 Análise Interativa")
487
+ st.info("💡 Para análise interativa dos dados, utilize a aba 'Feature Importance' no painel analítico principal, onde você pode ativar o PyGWalker de forma opcional.")
webapp/pages/{2_oulad.py → 2_OULAD.py} RENAMED
@@ -7,6 +7,7 @@ import matplotlib.pyplot as plt
7
  import missingno as msno
8
  import numpy as np
9
  import pickle
 
10
 
11
 
12
  st.set_page_config(
@@ -183,6 +184,16 @@ for col in merged_df.select_dtypes(include='object').columns:
183
  # st.write("Merged DataFrame after handling missing values:")
184
  # st.dataframe(merged_df.isnull().sum())
185
 
 
 
 
 
 
 
 
 
 
 
186
  st.write('# Análise Exploratória de Dados (EDA) - OULAD')
187
 
188
  '''
 
7
  import missingno as msno
8
  import numpy as np
9
  import pickle
10
+ from src.openai_interpreter import criar_rodape_sidebar
11
 
12
 
13
  st.set_page_config(
 
184
  # st.write("Merged DataFrame after handling missing values:")
185
  # st.dataframe(merged_df.isnull().sum())
186
 
187
+ # Sidebar com rodapé
188
+ with st.sidebar:
189
+ st.markdown("### 📊 Informações")
190
+ st.info("""
191
+ Esta página apresenta uma análise exploratória dos dados do OULAD (Open University Learning Analytics Dataset).
192
+ """)
193
+ st.markdown("---")
194
+ # Rodapé com badges de status (igual ao da home)
195
+ criar_rodape_sidebar()
196
+
197
  st.write('# Análise Exploratória de Dados (EDA) - OULAD')
198
 
199
  '''
webapp/pages/3_Análise Exploratória_Autosserviço.py ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pandas as pd
2
+ import pygwalker as pyg
3
+ from pygwalker.api.streamlit import StreamlitRenderer
4
+ import streamlit as st
5
+ from src.openai_interpreter import criar_rodape_sidebar
6
+
7
+ st.set_page_config(
8
+ page_title="Análise Exploratória - Autosserviço",
9
+ page_icon="📊",
10
+ layout="wide",
11
+ initial_sidebar_state="expanded",
12
+ )
13
+
14
+ # Sidebar com rodapé
15
+ with st.sidebar:
16
+ st.markdown("### 📊 Sobre")
17
+ st.info("""
18
+ Esta página oferece uma ferramenta de análise interativa usando PygWalker, permitindo que você explore os dados de forma autônoma.
19
+ """)
20
+ st.markdown("---")
21
+ # Rodapé com badges de status (igual ao da home)
22
+ criar_rodape_sidebar()
23
+
24
+ st.title("📊 Análise Exploratória - Autosserviço (PygWalker)")
25
+ st.divider()
26
+
27
+ st.markdown("""
28
+ Esta página oferece uma ferramenta de análise interativa usando PygWalker, permitindo que você explore os dados de forma autônoma.
29
+ """)
30
+
31
+ if "df_uci" in st.session_state:
32
+ df = st.session_state['df_uci']
33
+ st.info("💡 Usando dados UCI. Para usar dados OULAD, navegue primeiro para a página OULAD.")
34
+ walker = pyg.walk(df)
35
+ else:
36
+ st.warning("⚠️ Nenhum dado disponível. Por favor, navegue para a página UCI ou OULAD primeiro para carregar os dados.")
37
+
webapp/pages/3_analisador.py DELETED
@@ -1,919 +0,0 @@
1
- import streamlit as st
2
- import pandas as pd
3
- import io
4
- import plotly.express as px # FIX: Correct Plotly Express import
5
- import tabula # For PDF reading
6
- import tempfile # To save PDF temporarily for Tabula
7
- import os
8
- import numpy as np # For numeric checks
9
- import logging # For better error logging (optional but good practice)
10
- import matplotlib.pyplot as plt # For EDA visualizations
11
- import seaborn as sns # For EDA visualizations
12
-
13
- # Import custom functions
14
- import sys
15
- sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'src'))
16
- from utilidades import (
17
- gerar_template_features,
18
- gerar_template_unificado,
19
- converter_template_para_excel,
20
- validar_template_usuario,
21
- realizar_eda_automatica
22
- )
23
- from vizualizacoes import criar_graficos_eda_usuario, criar_grafico_metricas_modelo
24
-
25
- # --- Basic Logging Setup ---
26
- # Helps in debugging, especially if deployed
27
- logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
28
-
29
- # --- Page Configuration (MUST be the first Streamlit command) ---
30
- # Initialize theme based on session state *before* first run
31
- if 'dark_mode' not in st.session_state:
32
- st.session_state.dark_mode = False # Default to light mode
33
-
34
- # NOTE: Streamlit's native theme setting (3 dots menu > Settings) is often preferred.
35
- # This programmatic toggle is an alternative but less standard.
36
- # We set the initial config, but CSS will handle the dynamic switching visually.
37
- st.set_page_config(
38
- page_title='Ferramenta de Análise de Dados Educacionais',
39
- layout="wide",
40
- initial_sidebar_state="expanded"
41
- # theme="dark" if st.session_state.dark_mode else "light" # This doesn't work dynamically after first run
42
- )
43
-
44
- # --- Custom CSS Injection ---
45
- # FIX: Corrected CSS injection with markdown and modern styles
46
- # Enhancement: Added styles for dark mode toggle and improved button/sidebar look
47
- def apply_custom_css():
48
- """Applies custom CSS for styling and dark mode."""
49
- light_mode_css = """
50
- <style>
51
- /* --- Light Mode Variables --- */
52
- :root {
53
- --primary-bg: #FFFFFF;
54
- --secondary-bg: #f8f9fa;
55
- --text-color: #212529;
56
- --primary-color: #007bff; /* Blue */
57
- --primary-color-hover: #0056b3;
58
- --secondary-color: #6c757d; /* Gray */
59
- --border-color: #dee2e6;
60
- --button-bg: var(--primary-color);
61
- --button-text: white;
62
- --button-hover-bg: var(--primary-color-hover);
63
- --button-hover-text: white;
64
- --button-border: var(--primary-color);
65
- --sidebar-bg: var(--secondary-bg);
66
- }
67
-
68
- /* --- General Styling --- */
69
- body {
70
- font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
71
- color: var(--text-color);
72
- background-color: var(--primary-bg);
73
- }
74
- .main .block-container {
75
- padding-top: 2rem; padding-bottom: 2rem; padding-left: 3rem; padding-right: 3rem;
76
- }
77
- h1, h2, h3 { color: var(--primary-color); }
78
-
79
- /* --- Sidebar Styling (Modern) --- */
80
- [data-testid="stSidebar"] {
81
- background-color: var(--sidebar-bg);
82
- border-right: 1px solid var(--border-color);
83
- }
84
- [data-testid="stSidebar"] h1, [data-testid="stSidebar"] h2, [data-testid="stSidebar"] h3 {
85
- color: var(--primary-color); /* Match main area heading color */
86
- }
87
- [data-testid="stSidebar"] .stButton>button { /* Specificity for sidebar buttons */
88
- border-color: var(--secondary-color);
89
- color: var(--secondary-color);
90
- background-color: transparent;
91
- }
92
- [data-testid="stSidebar"] .stButton>button:hover {
93
- background-color: var(--secondary-color);
94
- color: white;
95
- }
96
-
97
-
98
- /* --- Button Styling (Modern) --- */
99
- .stButton>button {
100
- border: 2px solid var(--button-border);
101
- border-radius: 20px; /* Rounded */
102
- padding: 0.5rem 1.25rem;
103
- color: var(--button-text);
104
- background-color: var(--button-bg);
105
- transition: all 0.3s ease-in-out;
106
- font-weight: 500;
107
- }
108
- .stButton>button:hover {
109
- background-color: var(--button-hover-bg);
110
- color: var(--button-hover-text);
111
- border-color: var(--button-hover-bg);
112
- transform: translateY(-2px); /* Slight lift */
113
- box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
114
- }
115
- .stButton>button:active {
116
- transform: translateY(0);
117
- box-shadow: none;
118
- }
119
-
120
- /* --- DataFrame Styling --- */
121
- .stDataFrame { border: 1px solid var(--border-color); border-radius: 5px; }
122
-
123
- /* --- Expander Styling --- */
124
- .stExpander { border: 1px solid var(--border-color); border-radius: 5px; margin-bottom: 1rem; }
125
- .stExpander header { background-color: var(--secondary-bg); border-radius: 5px 5px 0 0; }
126
-
127
- /* --- Download Button --- */
128
- /* Might need more specific selector if default styling overrides */
129
- div[data-testid="stDownloadButton"] > button {
130
- background-color: #28a745; /* Green */
131
- border-color: #28a745;
132
- }
133
- div[data-testid="stDownloadButton"] > button:hover {
134
- background-color: #218838;
135
- border-color: #1e7e34;
136
- }
137
-
138
- </style>
139
- """
140
-
141
- dark_mode_css = """
142
- <style>
143
- /* --- Dark Mode Variables --- */
144
- body.dark-mode {
145
- --primary-bg: #0e1117; /* Streamlit dark background */
146
- --secondary-bg: #1c1e24; /* Slightly lighter dark */
147
- --text-color: #fafafa; /* Light text */
148
- --primary-color: #00aeff; /* Brighter Blue for dark */
149
- --primary-color-hover: #008ecc;
150
- --secondary-color: #8e949c; /* Lighter Gray */
151
- --border-color: #31333F;
152
- --button-bg: var(--primary-color);
153
- --button-text: var(--primary-bg); /* Dark text on bright button */
154
- --button-hover-bg: var(--primary-color-hover);
155
- --button-hover-text: var(--primary-bg);
156
- --button-border: var(--primary-color);
157
- --sidebar-bg: #15181e; /* Darker sidebar */
158
- }
159
-
160
- /* --- Dark Mode Overrides --- */
161
- body.dark-mode { background-color: var(--primary-bg); color: var(--text-color); }
162
- body.dark-mode h1, body.dark-mode h2, body.dark-mode h3 { color: var(--primary-color); }
163
- body.dark-mode [data-testid="stSidebar"] { background-color: var(--sidebar-bg); border-right-color: var(--border-color); }
164
- body.dark-mode [data-testid="stSidebar"] h1, body.dark-mode [data-testid="stSidebar"] h2, body.dark-mode [data-testid="stSidebar"] h3 {
165
- color: var(--primary-color);
166
- }
167
- body.dark-mode .stButton>button {
168
- border-color: var(--button-border);
169
- color: var(--button-text);
170
- background-color: var(--button-bg);
171
- }
172
- body.dark-mode .stButton>button:hover {
173
- background-color: var(--button-hover-bg);
174
- color: var(--button-hover-text);
175
- border-color: var(--button-hover-bg);
176
- }
177
- body.dark-mode .stDataFrame { border-color: var(--border-color); }
178
- body.dark-mode .stExpander { border-color: var(--border-color); }
179
- body.dark-mode .stExpander header { background-color: var(--secondary-bg); }
180
-
181
- body.dark-mode div[data-testid="stDownloadButton"] > button {
182
- background-color: #34c759; /* Brighter Green */
183
- border-color: #34c759;
184
- color: var(--primary-bg);
185
- }
186
- body.dark-mode div[data-testid="stDownloadButton"] > button:hover {
187
- background-color: #2fad4f;
188
- border-color: #2fad4f;
189
- }
190
-
191
- body.dark-mode [data-testid="stSidebar"] .stButton>button { /* Sidebar buttons in dark mode */
192
- border-color: var(--secondary-color);
193
- color: var(--secondary-color);
194
- background-color: transparent;
195
- }
196
- body.dark-mode [data-testid="stSidebar"] .stButton>button:hover {
197
- background-color: var(--secondary-color);
198
- color: var(--primary-bg);
199
- }
200
-
201
- </style>
202
- """
203
-
204
- # JavaScript to add/remove 'dark-mode' class from body
205
- # FIX: Correct use of unsafe_allow_html for CSS and JS
206
- js_code = f"""
207
- <script>
208
- function addDarkTheme() {{
209
- document.body.classList.add('dark-mode');
210
- }}
211
- function removeDarkTheme() {{
212
- document.body.classList.remove('dark-mode');
213
- }}
214
- {'addDarkTheme();' if st.session_state.dark_mode else 'removeDarkTheme();'}
215
- </script>
216
- """
217
-
218
- st.markdown(light_mode_css, unsafe_allow_html=True)
219
- st.markdown(dark_mode_css, unsafe_allow_html=True)
220
- st.markdown(js_code, unsafe_allow_html=True)
221
-
222
- apply_custom_css() # Apply the styling
223
-
224
- # --- Helper Functions ---
225
-
226
- # FIX: Added show_spinner for user feedback during caching
227
- # FIX: Enhanced error handling within load_data
228
- @st.cache_data(show_spinner="Reading file...")
229
- def load_data(uploaded_file):
230
- """Loads data from CSV, Excel, or PDF into a DataFrame list."""
231
- if uploaded_file is None:
232
- return None # Should not happen if called correctly, but good practice
233
-
234
- dfs_list = None
235
- file_extension = os.path.splitext(uploaded_file.name)[1].lower()
236
- logging.info(f"Attempting to load file: {uploaded_file.name}, extension: {file_extension}")
237
-
238
- try:
239
- if file_extension == '.csv':
240
- df = pd.read_csv(uploaded_file)
241
- dfs_list = [df]
242
- elif file_extension in ['.xlsx', '.xls']:
243
- # Consider sheet selection for robustness later
244
- df = pd.read_excel(uploaded_file, sheet_name=0)
245
- dfs_list = [df]
246
- elif file_extension == '.pdf':
247
- # FIX: Robust PDF handling with Tabula and error checking
248
- # ENHANCEMENT: Use with statement for temp file ensures cleanup
249
- with tempfile.NamedTemporaryFile(delete=False, suffix=".pdf") as tmp_file:
250
- tmp_file.write(uploaded_file.getvalue())
251
- tmp_file_path = tmp_file.name
252
- logging.info(f"Saved PDF to temporary file: {tmp_file_path}")
253
-
254
- read_successful = False
255
- try:
256
- # Try reading with lattice=True (good for tables with lines)
257
- logging.info("Trying tabula.read_pdf with lattice=True")
258
- dfs_tabula = tabula.read_pdf(tmp_file_path, pages='all', multiple_tables=True, lattice=True)
259
- read_successful = True
260
- except Exception as e_lattice:
261
- logging.warning(f"Tabula lattice=True failed: {e_lattice}. Trying stream=True.")
262
- try:
263
- # Fallback to stream=True (good for whitespace separation)
264
- logging.info("Trying tabula.read_pdf with stream=True")
265
- dfs_tabula = tabula.read_pdf(tmp_file_path, pages='all', multiple_tables=True, stream=True)
266
- read_successful = True
267
- except Exception as e_stream:
268
- logging.error(f"Tabula stream=True also failed: {e_stream}")
269
- # Check for common Java error
270
- if "java" in str(e_stream).lower() or isinstance(e_stream, FileNotFoundError):
271
- st.error("Error processing PDF: Java not found or not configured correctly. Please ensure Java Development Kit (JDK) is installed and in your system's PATH.")
272
- else:
273
- st.error(f"Failed to read PDF tables using Tabula: {e_stream}")
274
- dfs_tabula = None # Ensure it's None on failure
275
- finally:
276
- # FIX: Ensure temporary file is always removed
277
- if os.path.exists(tmp_file_path):
278
- os.remove(tmp_file_path)
279
- logging.info(f"Removed temporary PDF file: {tmp_file_path}")
280
-
281
- if read_successful and isinstance(dfs_tabula, list):
282
- # Filter out empty DataFrames and check if any tables were found
283
- dfs_list = [df for df in dfs_tabula if isinstance(df, pd.DataFrame) and not df.empty]
284
- if not dfs_list:
285
- st.warning("Tabula processed the PDF, but no data tables were found.")
286
- logging.warning(f"No non-empty tables extracted from PDF: {uploaded_file.name}")
287
- dfs_list = None # Set back to None if only empty tables found
288
- else:
289
- logging.info(f"Successfully extracted {len(dfs_list)} table(s) from PDF.")
290
-
291
- else:
292
- st.error(f"Unsupported file format: '{file_extension}'. Please upload CSV, Excel, or PDF.")
293
- logging.error(f"Unsupported file type uploaded: {uploaded_file.name}")
294
- return None # Explicitly return None for unsupported types
295
-
296
- return dfs_list
297
-
298
- # FIX: Catch specific library errors and general exceptions during file reading
299
- except ImportError as ie:
300
- if 'tabula' in str(ie).lower():
301
- st.error("Processing Error: `tabula-py` library not installed or Java not found/configured. Cannot process PDF files.")
302
- logging.error("ImportError related to tabula-py or Java.")
303
- else:
304
- st.error(f"Import Error: {ie}. Please ensure required libraries (pandas, openpyxl, tabula-py) are installed.")
305
- logging.error(f"ImportError: {ie}")
306
- return None
307
- except Exception as e:
308
- st.error(f"An error occurred while reading '{uploaded_file.name}': {e}")
309
- st.warning("Please ensure the file is valid, not password-protected or corrupted, and that Java is installed (for PDF).")
310
- logging.error(f"Failed to load file {uploaded_file.name}: {e}", exc_info=True) # Log traceback
311
- return None
312
-
313
-
314
- # FIX: Added show_spinner
315
- @st.cache_data(show_spinner="Preparing download...")
316
- def convert_df_to_csv(df):
317
- """Converts DataFrame to CSV bytes for download."""
318
- buffer = io.StringIO()
319
- df.to_csv(buffer, index=False)
320
- return buffer.getvalue().encode('utf-8')
321
-
322
- # --- Session State Initialization ---
323
- # (Ensures variables persist across reruns)
324
- if 'dataframes_list' not in st.session_state:
325
- st.session_state.dataframes_list = None
326
- if 'selected_df_index' not in st.session_state:
327
- st.session_state.selected_df_index = 0
328
- if 'original_df' not in st.session_state:
329
- st.session_state.original_df = None
330
- if 'current_df' not in st.session_state:
331
- st.session_state.current_df = None
332
- if 'uploaded_file_name' not in st.session_state:
333
- st.session_state.uploaded_file_name = None
334
- if 'data_loaded' not in st.session_state:
335
- st.session_state.data_loaded = False
336
- if 'template_dataset_type' not in st.session_state:
337
- st.session_state.template_dataset_type = 'uci'
338
- if 'template_downloaded' not in st.session_state:
339
- st.session_state.template_downloaded = False
340
- if 'user_data_uploaded' not in st.session_state:
341
- st.session_state.user_data_uploaded = None
342
- if 'eda_results' not in st.session_state:
343
- st.session_state.eda_results = None
344
- # 'dark_mode' initialized near st.set_page_config
345
-
346
- # --- Sidebar ---
347
- with st.sidebar: # ENHANCEMENT: Use 'with' context manager for sidebar clarity
348
- st.header("⚙️ Upload & Options")
349
-
350
- # ENHANCEMENT: Dark mode toggle removed
351
- def apply_custom_css():
352
- pass # No custom css applied
353
-
354
-
355
- uploaded_file = st.file_uploader(
356
- "Choose a CSV, Excel, or PDF file",
357
- type=['csv', 'xlsx', 'xls', 'pdf'],
358
- accept_multiple_files=False,
359
- help="Upload your dataset (CSV, Excel, PDF)."
360
- )
361
-
362
- # --- File Processing Logic ---
363
- if uploaded_file is not None:
364
- # Process only if it's a new file or data hasn't been loaded successfully before
365
- if not st.session_state.data_loaded or uploaded_file.name != st.session_state.get('uploaded_file_name'):
366
- st.session_state.dataframes_list = load_data(uploaded_file) # Call robust load_data
367
- st.session_state.uploaded_file_name = uploaded_file.name
368
- st.session_state.selected_df_index = 0 # Reset index for new file
369
-
370
- if st.session_state.dataframes_list: # Check if load_data returned a valid list
371
- st.session_state.data_loaded = True
372
- # Handle multiple tables from PDF
373
- if len(st.session_state.dataframes_list) > 1:
374
- st.success(f"Found {len(st.session_state.dataframes_list)} tables.")
375
- table_options = {i: f"Table {i+1} (Shape: {df.shape})" for i, df in enumerate(st.session_state.dataframes_list)}
376
- st.session_state.selected_df_index = st.selectbox(
377
- "Select table to analyze:",
378
- options=list(table_options.keys()),
379
- format_func=lambda x: table_options[x],
380
- key='pdf_table_select'
381
- )
382
- else:
383
- st.success(f"File '{uploaded_file.name}' loaded.")
384
-
385
- # Set initial dataframes in session state
386
- st.session_state.original_df = st.session_state.dataframes_list[st.session_state.selected_df_index].copy()
387
- st.session_state.current_df = st.session_state.original_df.copy()
388
- # Force a rerun to update the main panel immediately after successful load
389
- # st.rerun() # Careful with rerun, can cause loops if not managed well. Often UI updates automatically.
390
-
391
- else: # load_data returned None or empty list
392
- st.session_state.data_loaded = False
393
- st.session_state.original_df = None
394
- st.session_state.current_df = None
395
- # Error/Warning message is handled inside load_data()
396
-
397
- # Handling selection change for multi-table PDFs *after* initial load
398
- elif st.session_state.data_loaded and st.session_state.dataframes_list and len(st.session_state.dataframes_list) > 1:
399
- table_options = {i: f"Table {i+1} (Shape: {df.shape})" for i, df in enumerate(st.session_state.dataframes_list)}
400
- new_index = st.selectbox(
401
- "Select table to analyze:",
402
- options=list(table_options.keys()),
403
- index=st.session_state.selected_df_index, # Default to current selection
404
- format_func=lambda x: table_options[x],
405
- key='pdf_table_select_rerun'
406
- )
407
- if new_index != st.session_state.selected_df_index:
408
- st.session_state.selected_df_index = new_index
409
- # Update dfs based on new selection
410
- st.session_state.original_df = st.session_state.dataframes_list[st.session_state.selected_df_index].copy()
411
- st.session_state.current_df = st.session_state.original_df.copy()
412
- st.rerun() # Rerun needed to reflect the change in the main panel
413
-
414
- # --- Sidebar Analysis Options (Conditional Display) ---
415
- if st.session_state.data_loaded and st.session_state.current_df is not None:
416
- st.markdown("---") # Visual separator
417
- st.header("📊 Analysis Options")
418
- df = st.session_state.current_df # Use current (possibly cleaned) df
419
-
420
- analysis_options = ['Data Preview & Info', 'Data Cleaning', 'Feature Importance Template']
421
- numeric_cols_exist = any(pd.api.types.is_numeric_dtype(dtype) for dtype in df.dtypes)
422
-
423
- if numeric_cols_exist:
424
- analysis_options.extend(['Column Statistics', 'Visualizations', 'Correlation Analysis'])
425
- else:
426
- st.warning("No numeric columns detected. Some analysis options disabled.", icon="⚠️")
427
-
428
- # Use session state to remember the choice, preventing reset on reruns
429
- if 'analysis_choice' not in st.session_state:
430
- st.session_state.analysis_choice = analysis_options[0] # Default choice
431
-
432
- st.session_state.analysis_choice = st.radio(
433
- "Choose Analysis:",
434
- analysis_options,
435
- index=analysis_options.index(st.session_state.analysis_choice), # Set index based on state
436
- key='analysis_radio' # Consistent key
437
- )
438
- else:
439
- # Clear analysis choice if data is unloaded
440
- if 'analysis_choice' in st.session_state:
441
- del st.session_state.analysis_choice
442
-
443
-
444
- # --- Footer ---
445
- st.markdown("---")
446
- st.caption("PPGTE | IUVI | UFC - SIDA 0.2.1")
447
-
448
-
449
- # --- Main Application Area ---
450
- st.title("📈 Ferramenta de Análise de Dados")
451
-
452
- if not st.session_state.data_loaded or st.session_state.current_df is None:
453
- st.info("👈 Enviar um conjunto de dados (CSV, Excel, PDF) usando a barra lateral para iniciar a análise.")
454
- st.markdown("""
455
- **instruções:**
456
- 1. Use o botão na barra lateral para enviar o seu arquivo com o conjunto de dados que deseja analisar.
457
- 2. Se desejar enviar um PDF com múltiplas tabelas, selecione uma para analisar na lista suspensa da barra lateral.
458
- 3. Use as **Opções de Análise** na barra lateral para explorar e limpar seus dados.
459
- 4. Ative o **Modo Escuro** na barra lateral para o tema de visualização que preferir.
460
- """)
461
- else:
462
- df = st.session_state.current_df # Work with the current dataframe
463
- analysis_type = st.session_state.analysis_choice # Get selected analysis from state
464
-
465
- st.header(f"🔎 {analysis_type}") # Show title for the selected analysis
466
-
467
- # --- Data Preview & Info ---
468
- if analysis_type == 'Data Preview & Info':
469
- # ENHANCEMENT: Use st.columns for better layout
470
- col1, col2 = st.columns(2)
471
- with col1:
472
- st.subheader("Informação básica do conjunto de dados")
473
- st.write(f"**Nome do Arquivo:** `{st.session_state.uploaded_file_name}`")
474
- if st.session_state.dataframes_list and len(st.session_state.dataframes_list) > 1:
475
- st.write(f"**Tabela Selecionada:** `Table {st.session_state.selected_df_index + 1}`")
476
- st.write(f"**Linhas:** `{df.shape[0]}`")
477
- st.write(f"**Colunas:** `{df.shape[1]}`")
478
- with col2:
479
- st.subheader("Tipos de Colunas")
480
- # Improve display of dtypes
481
- st.dataframe(df.dtypes.astype(str).reset_index().rename(columns={'index': 'Column', 0: 'DataType'}), height=200, use_container_width=True, hide_index=True)
482
-
483
- st.subheader("Data Preview")
484
- st.dataframe(df.head()) # Show first 5 rows by default
485
-
486
- # ENHANCEMENT: Use st.expander for download section
487
- with st.expander("Baixar Dados"):
488
- st.markdown("Baixe os dados *conforme exibido atualmente* (incluindo qualquer limpeza).")
489
- csv_data = convert_df_to_csv(df) # Use cached function
490
- st.download_button(
491
- label="⬇️ Baixar Dados como CSV",
492
- data=csv_data,
493
- file_name=f"current_data_{st.session_state.uploaded_file_name.split('.')[0]}{'_T'+str(st.session_state.selected_df_index+1) if st.session_state.dataframes_list and len(st.session_state.dataframes_list)>1 else ''}.csv",
494
- mime='text/csv',
495
- key='download-current-csv'
496
- )
497
-
498
- # --- Data Cleaning ---
499
- elif analysis_type == 'Data Cleaning':
500
- st.subheader("Tratar Valores Ausentes")
501
-
502
- # ENHANCEMENT: Use spinner for calculations if dataset is large
503
- with st.spinner("Analisando valores ausentes..."):
504
- missing_values = df.isnull().sum()
505
- missing_summary = missing_values[missing_values > 0].sort_values(ascending=False)
506
-
507
- # ENHANCEMENT: Use st.columns for layout
508
- col1, col2 = st.columns([1, 2]) # Column for summary, column for options
509
-
510
- with col1:
511
- st.write("**Resumo de Valores Ausentes:**")
512
- if not missing_summary.empty:
513
- st.dataframe(missing_summary.reset_index().rename(columns={'index': 'Coluna', 0: 'Contagem de Ausentes'}), use_container_width=True, hide_index=True)
514
- st.metric(label="Total de Valores Ausentes", value=f"{missing_summary.sum():,}") # Formatted number
515
- else:
516
- st.success("✅ Nenhum valor ausente encontrado!")
517
-
518
- if st.button("🔄 Resetar para os Dados Originais", key='reset_data', help="Descarta todas as etapas de limpeza aplicadas nesta sessão."):
519
- # ENHANCEMENT: Use spinner for reset action
520
- with st.spinner("Resetando dados..."):
521
- st.session_state.current_df = st.session_state.original_df.copy()
522
- st.success("Dados restaurados para o estado original.")
523
- st.rerun() # Rerun to reflect the reset
524
-
525
- with col2:
526
- if not missing_summary.empty:
527
- st.write("**Opções de Limpeza:**")
528
- # FIX: Ensure unique keys for widgets if they might reappear
529
- cleaning_strategy = st.radio(
530
- "Escolha uma estrat��gia:",
531
- ('Remover linhas com valores ausentes',
532
- 'Remover colunas com valores ausentes',
533
- 'Impute missing values (Fill)'),
534
- key='clean_strategy_radio'
535
- )
536
-
537
- imputation_method = None
538
- constant_value = None
539
- cols_to_impute = None
540
-
541
- if cleaning_strategy == 'Impute missing values (Fill)':
542
- imputation_method = st.selectbox(
543
- "Método de Imputação:",
544
- ('Média (Apenas Numérico)', 'Mediana (Apenas Numérico)', 'Moda (Mais Frequente)', 'Valor Constante', 'Preencher para Frente (ffill)', 'Preencher para Trás (bfill)'), # Added ffill/bfill
545
- key='impute_method_select'
546
- )
547
- if imputation_method == 'Valor Constante':
548
- constant_value = st.text_input("Digite o valor constante para preencher:", value="NA", key='impute_constant_input')
549
-
550
- # Option to select columns for imputation
551
- all_cols_with_na = missing_summary.index.tolist()
552
- cols_to_impute = st.multiselect(
553
- "Selecione as colunas para imputação (padrão: todas com valores ausentes):",
554
- options=df.columns.tolist(),
555
- default=all_cols_with_na,
556
- key='impute_cols_multi'
557
- )
558
-
559
- if st.button("✨ Aplicar Limpeza", key='apply_cleaning_button'):
560
- # ENHANCEMENT: Use spinner for cleaning action
561
- with st.spinner("Aplicando regras de limpeza..."):
562
- df_cleaned = df.copy() # Work on a copy
563
- action_taken = "Nenhuma alteração aplicada." # Default message
564
- try:
565
- if cleaning_strategy == 'Drop rows with any missing values':
566
- rows_before = len(df_cleaned)
567
- df_cleaned.dropna(axis=0, inplace=True)
568
- rows_after = len(df_cleaned)
569
- action_taken = f"Removidas {rows_before - rows_after} linhas com valores ausentes."
570
- elif cleaning_strategy == 'Drop columns with any missing values':
571
- cols_before = df_cleaned.shape[1]
572
- df_cleaned.dropna(axis=1, inplace=True)
573
- cols_after = df_cleaned.shape[1]
574
- action_taken = f"Removidas {cols_before - cols_after} colunas com valores ausentes."
575
-
576
- elif cleaning_strategy == 'Impute missing values (Fill)':
577
- if not cols_to_impute:
578
- st.warning("Por favor, selecione pelo menos uma coluna para imputação.")
579
- action_taken = "Nenhuma coluna selecionada para imputação."
580
- else:
581
- imputed_cols_count = 0
582
- skipped_cols = []
583
- for col in cols_to_impute:
584
- if col not in df_cleaned.columns or not df_cleaned[col].isnull().any():
585
- continue # Skip if column doesn't exist or has no NaNs
586
-
587
- if imputation_method in ['Mean (Numeric Only)', 'Median (Numeric Only)']:
588
- if pd.api.types.is_numeric_dtype(df_cleaned[col]):
589
- fill_value = df_cleaned[col].mean() if 'Mean' in imputation_method else df_cleaned[col].median()
590
- df_cleaned[col].fillna(fill_value, inplace=True)
591
- imputed_cols_count += 1
592
- else:
593
- skipped_cols.append(col)
594
- elif imputation_method == 'Mode (Most Frequent)':
595
- mode_val = df_cleaned[col].mode()
596
- if not mode_val.empty:
597
- df_cleaned[col].fillna(mode_val[0], inplace=True)
598
- imputed_cols_count += 1
599
- else: skipped_cols.append(col) # Handle cases with no mode
600
- elif imputation_method == 'Constant Value':
601
- try: # Attempt type conversion for consistency
602
- typed_value = pd.Series([constant_value]).astype(df_cleaned[col].dtype).iloc[0]
603
- except Exception:
604
- typed_value = constant_value # Use raw value if conversion fails
605
- df_cleaned[col].fillna(typed_value, inplace=True)
606
- imputed_cols_count += 1
607
- elif imputation_method == 'Forward Fill (ffill)':
608
- df_cleaned[col].ffill(inplace=True)
609
- imputed_cols_count += 1
610
- elif imputation_method == 'Backward Fill (bfill)':
611
- df_cleaned[col].bfill(inplace=True)
612
- imputed_cols_count += 1
613
-
614
- action_taken = f"Imputed {imputed_cols_count} column(s) using {imputation_method}."
615
- if skipped_cols:
616
- st.warning(f"Skipped non-numeric columns for Mean/Median or columns with no mode: {', '.join(skipped_cols)}", icon="⚠️")
617
-
618
- # Update the main dataframe in session state
619
- st.session_state.current_df = df_cleaned
620
- st.success(f"Cleaning applied: {action_taken}")
621
- logging.info(f"Data cleaning action: {action_taken}")
622
- st.rerun() # Rerun to update display (summary, preview)
623
-
624
- except Exception as clean_e:
625
- st.error(f"Error during cleaning: {clean_e}")
626
- logging.error(f"Cleaning failed: {clean_e}", exc_info=True)
627
- else:
628
- st.info("Select a cleaning strategy and click 'Apply Cleaning'.")
629
-
630
-
631
- # --- Column Statistics ---
632
- elif analysis_type == 'Column Statistics':
633
- st.subheader("Descriptive Statistics (Numeric Columns)")
634
- numeric_cols = df.select_dtypes(include=np.number).columns.tolist()
635
- if not numeric_cols:
636
- st.warning("No numeric columns found in the current dataset.")
637
- else:
638
- # ENHANCEMENT: Use spinner for describe() if large
639
- with st.spinner("Calculating statistics..."):
640
- stats_df = df[numeric_cols].describe()
641
- st.dataframe(stats_df, use_container_width=True)
642
-
643
- # --- Visualizations ---
644
- elif analysis_type == 'Visualizations':
645
- st.subheader("Interactive Plots")
646
- numeric_cols = df.select_dtypes(include=np.number).columns.tolist()
647
- all_cols = df.columns.tolist()
648
-
649
- if not numeric_cols:
650
- st.warning("No numeric columns available for plotting.")
651
- else:
652
- # ENHANCEMENT: Use st.tabs for different plot types
653
- tab1, tab2 = st.tabs(["📊 Histogram", "📈 Scatter Plot"])
654
-
655
- with tab1:
656
- st.markdown("#### Histogram")
657
- hist_col = st.selectbox("Select column:", numeric_cols, key='hist_col_select_tab')
658
- # ENHANCEMENT: Add optional color dimension
659
- hist_color = st.selectbox("Optional: Color by:", [None] + all_cols, key='hist_color_select_tab')
660
-
661
- if hist_col:
662
- # ENHANCEMENT: Use spinner for plotting
663
- with st.spinner("Generating Histogram..."):
664
- try:
665
- fig_hist = px.histogram(df, x=hist_col, color=hist_color, title=f'Histogram of {hist_col}' + (f' (Colored by {hist_color})' if hist_color else ''))
666
- fig_hist.update_layout(bargap=0.1)
667
- st.plotly_chart(fig_hist, use_container_width=True)
668
- except Exception as e:
669
- st.error(f"Could not generate histogram: {e}")
670
- logging.error(f"Histogram error for {hist_col}: {e}", exc_info=True)
671
-
672
- with tab2:
673
- st.markdown("#### Scatter Plot")
674
- if len(numeric_cols) >= 2:
675
- # FIX: Ensure unique keys for widgets in tabs
676
- scatter_x = st.selectbox("Select X-axis:", numeric_cols, index=0, key='scatter_x_select_tab')
677
- # Try to select a different default Y axis
678
- default_y_index = 1 if len(numeric_cols) > 1 and numeric_cols[1] != scatter_x else 0
679
- scatter_y = st.selectbox("Select Y-axis:", numeric_cols, index=default_y_index, key='scatter_y_select_tab')
680
- scatter_color = st.selectbox("Optional: Color by:", [None] + all_cols, key='scatter_color_select_tab')
681
- scatter_size = st.selectbox("Optional: Size by (Numeric):", [None] + numeric_cols, key='scatter_size_select_tab') # ENHANCEMENT: Size dimension
682
-
683
-
684
- if scatter_x and scatter_y:
685
- # ENHANCEMENT: Use spinner for plotting
686
- with st.spinner("Generating Scatter Plot..."):
687
- try:
688
- fig_scatter = px.scatter(df, x=scatter_x, y=scatter_y,
689
- color=scatter_color, size=scatter_size,
690
- title=f'Scatter: {scatter_x} vs {scatter_y}' + (f' (Color: {scatter_color})' if scatter_color else '') + (f' (Size: {scatter_size})' if scatter_size else ''))
691
- st.plotly_chart(fig_scatter, use_container_width=True)
692
- except Exception as e:
693
- st.error(f"Could not generate scatter plot: {e}")
694
- logging.error(f"Scatter plot error ({scatter_x} vs {scatter_y}): {e}", exc_info=True)
695
- else:
696
- st.warning("Requires at least two numeric columns for a scatter plot.")
697
-
698
- # --- Correlation Analysis ---
699
- elif analysis_type == 'Correlation Analysis':
700
- st.subheader("Correlation Analysis (Numeric Columns)")
701
- numeric_cols = df.select_dtypes(include=np.number).columns.tolist()
702
-
703
- if len(numeric_cols) < 2:
704
- st.warning("Requires at least two numeric columns to calculate correlations.")
705
- else:
706
- # ENHANCEMENT: Use spinner
707
- with st.spinner("Calculating correlations..."):
708
- try:
709
- # FIX: Ensure only numeric calculations if mixed types are suspected
710
- correlation_matrix = df[numeric_cols].corr(numeric_only=True)
711
-
712
- # ENHANCEMENT: Use st.tabs for table vs heatmap
713
- tab1, tab2 = st.tabs(["🔢 Matrix Table", "🔥 Heatmap"])
714
-
715
- with tab1:
716
- st.dataframe(correlation_matrix, use_container_width=True)
717
-
718
- with tab2:
719
- # ENHANCEMENT: Use spinner for potentially large heatmap render
720
- with st.spinner("Generating Heatmap..."):
721
- try:
722
- fig_corr = px.imshow(
723
- correlation_matrix,
724
- text_auto=True, # Show values on heatmap
725
- aspect="auto",
726
- color_continuous_scale='RdBu_r', # Red-Blue diverging scale good for correlation
727
- title='Correlation Heatmap'
728
- )
729
- fig_corr.update_layout(coloraxis_colorbar=dict(title='Correlation'))
730
- st.plotly_chart(fig_corr, use_container_width=True)
731
- except Exception as e_heatmap:
732
- st.error(f"Could not generate correlation heatmap: {e_heatmap}")
733
- logging.error(f"Correlation heatmap error: {e_heatmap}", exc_info=True)
734
-
735
- except Exception as corr_e:
736
- st.error(f"Could not calculate correlations. Ensure numeric columns contain valid numbers. Error: {corr_e}")
737
- logging.error(f"Correlation calculation error: {corr_e}", exc_info=True)
738
-
739
- # --- Feature Importance Template ---
740
- elif analysis_type == 'Feature Importance Template':
741
- st.subheader("🎯 Template Unificado de Feature Importance")
742
-
743
- # Seção de informações sobre o template unificado
744
- col1, col2 = st.columns([2, 1])
745
-
746
- with col1:
747
- st.markdown("""
748
- **Como usar o Template Unificado:**
749
- 1. Baixe o template Excel com as **2 features mais importantes** dos datasets UCI e OULAD
750
- 2. Preencha o template com seus dados (incluindo o nome do aluno)
751
- 3. Mantenha a coluna 'resultado_final' com os resultados esperados
752
- 4. Faça upload do template preenchido para análise automática
753
-
754
- **Vantagens do Template Unificado:**
755
- - Combina insights de educação tradicional (UCI) e online (OULAD)
756
- - Inclui campo para nome do aluno para personalização
757
- - Análise mais abrangente com features diversificadas
758
- - Template otimizado com apenas as features mais relevantes
759
- """)
760
-
761
- with col2:
762
- st.info("""
763
- **Template inclui:**
764
- - Campo para nome do aluno
765
- - **Top 2 features do UCI**
766
- - **Top 2 features do OULAD**
767
- - Coluna de resultado final
768
- - **Total: 6 colunas organizadas logicamente**
769
- """)
770
-
771
- # Gerar e baixar template unificado
772
- if st.button("📥 Gerar Template Unificado", key='generate_unified_template'):
773
- with st.spinner("Gerando template unificado..."):
774
- df_template = gerar_template_unificado()
775
-
776
- if not df_template.empty:
777
- st.session_state.template_downloaded = True
778
- feature_cols = [col for col in df_template.columns if col not in ['nome_aluno', 'resultado_final']]
779
- st.success(f"Template unificado gerado com sucesso! Inclui {len(feature_cols)} features: {', '.join(feature_cols)}")
780
-
781
- # Mostrar preview do template
782
- st.markdown("**Preview do Template Unificado:**")
783
- st.dataframe(df_template.head(), use_container_width=True)
784
-
785
- # Botão de download
786
- excel_data = converter_template_para_excel(df_template)
787
- if excel_data:
788
- st.download_button(
789
- label="⬇️ Baixar Template Excel Unificado",
790
- data=excel_data,
791
- file_name="template_unificado_features.xlsx",
792
- mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
793
- key='download_unified_template'
794
- )
795
- else:
796
- st.error("Erro ao gerar template unificado. Verifique se os dados estão carregados.")
797
-
798
- # Seção de upload do template preenchido
799
- if st.session_state.template_downloaded:
800
- st.markdown("---")
801
- st.subheader("📤 Upload do Template Preenchido")
802
-
803
- uploaded_template = st.file_uploader(
804
- "Faça upload do template preenchido:",
805
- type=['csv', 'xlsx', 'xls'],
806
- accept_multiple_files=False,
807
- help="Upload do template Excel preenchido com seus dados",
808
- key='template_uploader'
809
- )
810
-
811
- if uploaded_template is not None:
812
- try:
813
- # Carregar dados do template
814
- if uploaded_template.name.endswith('.csv'):
815
- df_usuario = pd.read_csv(uploaded_template)
816
- else:
817
- df_usuario = pd.read_excel(uploaded_template)
818
-
819
- # Validar template (não precisa de template de referência para unificado)
820
- is_valid, validation_msg = validar_template_usuario(df_usuario)
821
-
822
- if is_valid:
823
- st.success(f"✅ {validation_msg}")
824
- st.session_state.user_data_uploaded = df_usuario
825
-
826
- # Mostrar preview dos dados
827
- st.markdown("**Preview dos Dados Carregados:**")
828
- st.dataframe(df_usuario.head(), use_container_width=True)
829
-
830
- # Botão para executar EDA
831
- if st.button("🔍 Executar Análise Exploratória", key='run_eda'):
832
- with st.spinner("Executando análise exploratória..."):
833
- resultado_eda = realizar_eda_automatica(df_usuario)
834
-
835
- if resultado_eda:
836
- st.session_state.eda_results = resultado_eda
837
- st.success("Análise concluída com sucesso!")
838
- st.rerun()
839
- else:
840
- st.error("Erro na análise exploratória")
841
- else:
842
- st.error(f"❌ {validation_msg}")
843
-
844
- except Exception as e:
845
- st.error(f"Erro ao processar arquivo: {e}")
846
-
847
- # Exibir resultados da EDA
848
- if st.session_state.eda_results and st.session_state.user_data_uploaded is not None:
849
- st.markdown("---")
850
- st.subheader("📊 Resultados da Análise Exploratória")
851
-
852
- df_usuario = st.session_state.user_data_uploaded
853
- resultado_eda = st.session_state.eda_results
854
-
855
- # Informações básicas
856
- col1, col2, col3 = st.columns(3)
857
- with col1:
858
- st.metric("Total de Registros", df_usuario.shape[0])
859
- with col2:
860
- st.metric("Total de Features", df_usuario.shape[1] - 1) # -1 para excluir target
861
- with col3:
862
- problem_type = "Regressão" if resultado_eda.get('is_regression', True) else "Classificação"
863
- st.metric("Tipo de Problema", problem_type)
864
-
865
- # Métricas do modelo
866
- st.markdown("### 📈 Métricas do Modelo")
867
- metrics = resultado_eda.get('metrics', {})
868
-
869
- if metrics.get('type') == 'regression':
870
- col1, col2, col3 = st.columns(3)
871
- with col1:
872
- st.metric("MAE", f"{metrics.get('mae', 0):.3f}")
873
- with col2:
874
- st.metric("RMSE", f"{metrics.get('rmse', 0):.3f}")
875
- with col3:
876
- st.metric("R²", f"{metrics.get('r2', 0):.3f}")
877
- else:
878
- col1, col2 = st.columns(2)
879
- with col1:
880
- st.metric("Acurácia", f"{metrics.get('accuracy', 0):.3f}")
881
- with col2:
882
- st.metric("Tipo", "Classificação")
883
-
884
- # Gráfico de métricas
885
- fig_metrics = criar_grafico_metricas_modelo(resultado_eda)
886
- if fig_metrics:
887
- st.pyplot(fig_metrics)
888
-
889
- # Visualizações EDA
890
- st.markdown("### 📊 Visualizações")
891
-
892
- # Criar e exibir gráficos
893
- figuras = criar_graficos_eda_usuario(df_usuario, resultado_eda)
894
-
895
- if figuras:
896
- # Usar tabs para organizar os gráficos
897
- tab_names = [f"Gráfico {i+1}" for i in range(len(figuras))]
898
- tabs = st.tabs(tab_names)
899
-
900
- for i, (tab, figura) in enumerate(zip(tabs, figuras)):
901
- with tab:
902
- st.pyplot(figura)
903
-
904
- # Feature importance
905
- if not resultado_eda.get('feature_importance', pd.DataFrame()).empty:
906
- st.markdown("### 🎯 Importância das Features")
907
- df_importance = resultado_eda['feature_importance']
908
- st.dataframe(df_importance, use_container_width=True)
909
-
910
- # Botão para baixar resultados
911
- st.markdown("### 💾 Download dos Resultados")
912
- csv_data = convert_df_to_csv(df_usuario)
913
- st.download_button(
914
- label="⬇️ Baixar Dados Processados (CSV)",
915
- data=csv_data,
916
- file_name="dados_processados_eda.csv",
917
- mime="text/csv",
918
- key='download_processed_data'
919
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
webapp/pages/4_pygwalker.py DELETED
@@ -1,10 +0,0 @@
1
- import pandas as pd
2
- import pygwalker as pyg
3
- from pygwalker.api.streamlit import StreamlitRenderer
4
- import streamlit as st
5
-
6
- if "df_uci" in st.session_state:
7
- df = st.session_state['df_uci']
8
- walker = pyg.walk(df)
9
- else:
10
- st.write("Nenhum dado disponível. Por favor, navegue para a página UCI primeiro.")
 
 
 
 
 
 
 
 
 
 
 
webapp/pages/5_pandas_profiling.py DELETED
@@ -1,185 +0,0 @@
1
- import pandas as pd
2
- import pandas_profiling
3
- import streamlit as st
4
- from streamlit_pandas_profiling import st_profile_report
5
- from pathlib import Path
6
- import os
7
- import pickle
8
- import sys
9
-
10
- # Adicionar o diretório src ao path
11
- sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'src'))
12
-
13
- from carregar_dados import carregar_uci_dados, carregar_oulad_dados
14
-
15
- st.set_page_config(
16
- page_title="Pandas Profiling - Análise de Dados",
17
- page_icon="📊",
18
- layout="wide",
19
- initial_sidebar_state="expanded",
20
- )
21
-
22
- st.title("📊 Pandas Profiling - Análise de Dados")
23
- st.divider()
24
-
25
- # Carregar dados se não estiverem no session_state
26
- if "df_uci" not in st.session_state:
27
- try:
28
- with st.spinner("🔄 Carregando dados UCI..."):
29
- st.session_state.df_uci = carregar_uci_dados()
30
- except Exception as e:
31
- st.error(f"Erro ao carregar dados UCI: {e}")
32
- st.session_state.df_uci = pd.DataFrame()
33
-
34
- if "df_oulad" not in st.session_state:
35
- try:
36
- with st.spinner("🔄 Carregando dados OULAD (pode levar alguns minutos na primeira vez)..."):
37
- st.session_state.df_oulad = carregar_oulad_dados()
38
- except Exception as e:
39
- st.error(f"Erro ao carregar dados OULAD: {e}")
40
- st.session_state.df_oulad = pd.DataFrame()
41
-
42
- # Sidebar para seleção do dataset
43
- with st.sidebar:
44
- st.markdown("### 📊 Seleção do Dataset")
45
-
46
- # Caixa de seleção para escolher o dataset
47
- dataset_options = {
48
- "📚 UCI Dataset": "df_uci",
49
- "🌐 OULAD Dataset": "df_oulad"
50
- }
51
-
52
- selected_dataset = st.selectbox(
53
- "Escolha o dataset para análise:",
54
- options=list(dataset_options.keys()),
55
- index=0,
56
- help="Selecione qual dataset você deseja analisar com o Pandas Profiling"
57
- )
58
-
59
- st.markdown("---")
60
-
61
- # Informações sobre o dataset selecionado
62
- if selected_dataset == "📚 UCI Dataset":
63
- st.markdown("### 📚 Sobre o Dataset UCI")
64
- st.markdown("""
65
- **Dataset UCI:**
66
- - Escolas públicas portuguesas
67
- - Dados demográficos e acadêmicos
68
- - Análise de fatores de sucesso
69
- - Variáveis: notas, faltas, tempo de estudo, etc.
70
- """)
71
-
72
- if not st.session_state.df_uci.empty:
73
- st.metric("Total de Registros", f"{len(st.session_state.df_uci):,}")
74
- st.metric("Total de Colunas", f"{len(st.session_state.df_uci.columns)}")
75
- else:
76
- st.warning("⚠️ Dados UCI não disponíveis")
77
-
78
- else: # OULAD Dataset
79
- st.markdown("### 🌐 Sobre o Dataset OULAD")
80
- st.markdown("""
81
- **Dataset OULAD:**
82
- - Plataforma de aprendizado online
83
- - Dados de engajamento digital
84
- - Análise de atividades online
85
- - Variáveis: cliques, atividades, resultados, etc.
86
- """)
87
-
88
- if not st.session_state.df_oulad.empty:
89
- st.metric("Total de Registros", f"{len(st.session_state.df_oulad):,}")
90
- st.metric("Total de Colunas", f"{len(st.session_state.df_oulad.columns)}")
91
- else:
92
- st.warning("⚠️ Dados OULAD não disponíveis")
93
-
94
- st.markdown("---")
95
- st.markdown("### ℹ️ Sobre o Pandas Profiling")
96
- st.markdown("""
97
- O Pandas Profiling gera um relatório completo de análise exploratória de dados, incluindo:
98
-
99
- - **Visão geral** dos dados
100
- - **Correlações** entre variáveis
101
- - **Valores ausentes** e duplicados
102
- - **Distribuições** estatísticas
103
- - **Avisos** sobre qualidade dos dados
104
- """)
105
-
106
- st.markdown("---")
107
- st.markdown("**Mestrado em Tecnologia Educacional - UFC**")
108
-
109
- # Obter o dataset selecionado
110
- df_key = dataset_options[selected_dataset]
111
- df = st.session_state[df_key]
112
-
113
- # Verificar se o dataset está disponível
114
- if df.empty:
115
- st.error(f"❌ O dataset {selected_dataset} não está disponível. Verifique se os dados foram carregados corretamente.")
116
- st.stop()
117
-
118
- # Verificar se o dataset é muito grande e oferecer opção de amostragem
119
- if len(df) > 10000:
120
- st.warning(f"⚠️ O dataset {selected_dataset} tem {len(df):,} registros. Para melhor performance, considere usar uma amostra.")
121
-
122
- col1, col2 = st.columns([1, 1])
123
- with col1:
124
- usar_amostra = st.checkbox("Usar amostra de 10.000 registros", value=True)
125
- with col2:
126
- if usar_amostra:
127
- df = df.sample(n=10000, random_state=42)
128
- st.info(f"📊 Usando amostra de {len(df):,} registros")
129
-
130
- # Exibir informações básicas do dataset selecionado
131
- st.markdown(f"### 📋 Informações do Dataset: {selected_dataset}")
132
-
133
- col1, col2, col3, col4 = st.columns(4)
134
- with col1:
135
- st.metric("📊 Total de Registros", f"{len(df):,}")
136
- with col2:
137
- st.metric("📋 Total de Colunas", f"{len(df.columns)}")
138
- with col3:
139
- st.metric("❌ Valores Ausentes", f"{df.isnull().sum().sum():,}")
140
- with col4:
141
- st.metric("🔄 Valores Duplicados", f"{df.duplicated().sum():,}")
142
-
143
- st.markdown("---")
144
-
145
- # Gerar e exibir o relatório do Pandas Profiling
146
- st.markdown("### 🔍 Relatório de Análise Exploratória de Dados")
147
-
148
- with st.spinner("🔄 Gerando relatório do Pandas Profiling... Isso pode levar alguns minutos para datasets grandes."):
149
- try:
150
- # Configurar o relatório do Pandas Profiling
151
- pr = df.profile_report(
152
- title=f"Relatório de Análise - {selected_dataset}",
153
- explorative=True,
154
- minimal=False,
155
- progress_bar=True,
156
- correlations={
157
- "auto": {"calculate": False},
158
- "pearson": {"calculate": True},
159
- "spearman": {"calculate": True},
160
- "kendall": {"calculate": False}
161
- }
162
- )
163
-
164
- # Exibir o relatório
165
- st_profile_report(pr)
166
-
167
- except Exception as e:
168
- st.error(f"❌ Erro ao gerar relatório do Pandas Profiling: {e}")
169
- st.markdown("""
170
- **Possíveis soluções:**
171
- - Verifique se o dataset não está vazio
172
- - Tente com um dataset menor
173
- - Verifique se há problemas de memória
174
- """)
175
-
176
- # Informações adicionais
177
- st.markdown("---")
178
- st.markdown("### 💡 Dicas de Uso")
179
- st.markdown("""
180
- - **Navegue** pelas diferentes seções do relatório usando o menu lateral
181
- - **Explore** as correlações entre variáveis
182
- - **Analise** os avisos sobre qualidade dos dados
183
- - **Use** os filtros interativos para explorar os dados
184
- - **Exporte** o relatório se necessário usando o botão de download
185
- """)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
webapp/src/openai_interpreter.py CHANGED
@@ -140,7 +140,7 @@ def configurar_openai_key():
140
  Instituto UFC Virtual (IUVI)
141
  Universidade Federal do Ceará (UFC)
142
 
143
- Versão 1.0.0 - 2025
144
  """)
145
 
146
  def interpretar_grafico(tipo_grafico: str, dados_contexto: Dict[str, Any]) -> str:
@@ -438,30 +438,18 @@ def criar_sidebar_landpage():
438
  4. Faça upload para análise
439
  """)
440
 
441
- # Rodapé padrão
442
- st.markdown("---")
443
- st.markdown("### ℹ️ Sobre o Sistema")
444
- st.caption("""
445
- **SIDA - Sistema Inteligente de Análise Educacional**
446
-
447
- Mestrado em Tecnologia Educacional
448
- Programa de Pós-Graduação em Tecnologias Educacionais (PPGTE)
449
- Instituto UFC Virtual (IUVI)
450
- Universidade Federal do Ceará (UFC)
451
-
452
- Versão 1.0.0 - 2025
453
- """)
454
 
455
  def criar_sidebar_padrao():
456
- """Sidebar padrão para páginas internas (Dashboard, UCI, OULAD, etc.)"""
457
  with st.sidebar:
458
  st.markdown("### 📊 Navegação")
459
  st.markdown("""
460
  - 🏠 **Home**: Análise Customizada
461
- - 📊 **Dashboard**: Visão Consolidada
462
- - 📈 **UCI**: Análise Detalhada
463
- - 🌐 **OULAD**: Análise Detalhada
464
- - 🔍 **Analisador**: Ferramenta de Análise
465
  """)
466
 
467
  st.markdown("---")
@@ -476,21 +464,10 @@ def criar_sidebar_padrao():
476
  st.warning("⚠️ Configure na página inicial")
477
 
478
  # Rodapé padrão (mesmo em todas as páginas)
479
- st.markdown("---")
480
- st.markdown("### ℹ️ Sobre o Sistema")
481
- st.caption("""
482
- **SIDA - Sistema Inteligente de Análise Educacional**
483
-
484
- Mestrado em Tecnologia Educacional
485
- Programa de Pós-Graduação em Tecnologias Educacionais (PPGTE)
486
- Instituto UFC Virtual (IUVI)
487
- Universidade Federal do Ceará (UFC)
488
-
489
- Versão 1.0.0 - 2025
490
- """)
491
 
492
  def criar_rodape_sidebar():
493
- """Rodapé padronizado para todas as sidebars"""
494
  st.markdown("---")
495
  st.markdown("### ℹ️ Sobre o Sistema")
496
  st.caption("""
@@ -501,5 +478,16 @@ def criar_rodape_sidebar():
501
  Instituto UFC Virtual (IUVI)
502
  Universidade Federal do Ceará (UFC)
503
 
504
- Versão 1.0.0 - 2025
505
  """)
 
 
 
 
 
 
 
 
 
 
 
 
140
  Instituto UFC Virtual (IUVI)
141
  Universidade Federal do Ceará (UFC)
142
 
143
+ Versão 0.1.1 - 2025
144
  """)
145
 
146
  def interpretar_grafico(tipo_grafico: str, dados_contexto: Dict[str, Any]) -> str:
 
438
  4. Faça upload para análise
439
  """)
440
 
441
+ # Rodapé - apenas badges de status
442
+ criar_rodape_sidebar()
 
 
 
 
 
 
 
 
 
 
 
443
 
444
  def criar_sidebar_padrao():
445
+ """Sidebar padrão para páginas internas (Painel Analítico, Análise Exploratória, etc.)"""
446
  with st.sidebar:
447
  st.markdown("### 📊 Navegação")
448
  st.markdown("""
449
  - 🏠 **Home**: Análise Customizada
450
+ - 📊 **Painel Analítico**: Visão Consolidada
451
+ - 📈 **Análise Exploratória**
452
+ - Autosserviço
 
453
  """)
454
 
455
  st.markdown("---")
 
464
  st.warning("⚠️ Configure na página inicial")
465
 
466
  # Rodapé padrão (mesmo em todas as páginas)
467
+ criar_rodape_sidebar()
 
 
 
 
 
 
 
 
 
 
 
468
 
469
  def criar_rodape_sidebar():
470
+ """Rodapé padronizado para todas as sidebars - informações do sistema e badges"""
471
  st.markdown("---")
472
  st.markdown("### ℹ️ Sobre o Sistema")
473
  st.caption("""
 
478
  Instituto UFC Virtual (IUVI)
479
  Universidade Federal do Ceará (UFC)
480
 
481
+ Versão 0.1.1 - 2025
482
  """)
483
+
484
+ # Badges de status do projeto
485
+ st.markdown("""
486
+ <div style="margin-top: 10px; text-align: center;">
487
+ <img src="https://img.shields.io/badge/Python-3.9%2B-blue?logo=python&logoColor=white" alt="Python 3.9+"/>
488
+ <img src="https://img.shields.io/badge/Streamlit-1.28%2B-FF4B4B?logo=streamlit&logoColor=white" alt="Streamlit"/>
489
+ <img src="https://img.shields.io/badge/Docker-Supported-2496ED?logo=docker&logoColor=white" alt="Docker"/>
490
+ <img src="https://img.shields.io/badge/License-GPL--3.0-green" alt="License GPL-3.0"/>
491
+ <img src="https://img.shields.io/badge/Version-0.1.1-orange" alt="Version"/>
492
+ </div>
493
+ """, unsafe_allow_html=True)
webapp/src/utilidades.py CHANGED
@@ -28,7 +28,7 @@ def carregar_dados_oulad_cached():
28
  return carregar_oulad_dados()
29
 
30
  def carregar_dados_dashboard():
31
- """Carrega os dados processados para o dashboard com cache"""
32
  try:
33
  # Carregar dados UCI com cache
34
  df_uci = carregar_dados_uci_cached()
@@ -273,7 +273,7 @@ def calcular_metricas_oulad(df_oulad):
273
  return metricas
274
 
275
  def gerar_metricas_consolidadas(df_uci, df_oulad):
276
- """Gera métricas consolidadas para o dashboard"""
277
  metricas_uci = calcular_metricas_uci(df_uci)
278
  metricas_oulad = calcular_metricas_oulad(df_oulad)
279
 
@@ -292,9 +292,9 @@ def gerar_metricas_consolidadas(df_uci, df_oulad):
292
  }
293
 
294
  def criar_sidebar_dashboard():
295
- """Cria a barra lateral do dashboard"""
296
  with st.sidebar:
297
- st.markdown("### 📊 Dashboard Educacional")
298
 
299
  # Carregar métricas dinâmicas
300
  metricas_uci = obter_metricas_principais_uci()
@@ -634,7 +634,7 @@ def treinar_modelo_uci_on_demand():
634
  else:
635
  print("🔄 Preparando dados...")
636
 
637
- # Preparar dados como na página 1_uci.py
638
  Y = df_uci['G3']
639
  X = df_uci.drop('G3', axis=1)
640
 
@@ -743,7 +743,7 @@ def treinar_modelo_oulad_on_demand():
743
  else:
744
  print("🔄 Preparando dados...")
745
 
746
- # Preparar dados como na página 2_oulad.py
747
  Y = df_oulad['final_result']
748
  X = df_oulad.loc[:, df_oulad.columns != 'final_result']
749
 
 
28
  return carregar_oulad_dados()
29
 
30
  def carregar_dados_dashboard():
31
+ """Carrega os dados processados para o painel analítico com cache"""
32
  try:
33
  # Carregar dados UCI com cache
34
  df_uci = carregar_dados_uci_cached()
 
273
  return metricas
274
 
275
  def gerar_metricas_consolidadas(df_uci, df_oulad):
276
+ """Gera métricas consolidadas para o painel analítico"""
277
  metricas_uci = calcular_metricas_uci(df_uci)
278
  metricas_oulad = calcular_metricas_oulad(df_oulad)
279
 
 
292
  }
293
 
294
  def criar_sidebar_dashboard():
295
+ """Cria a barra lateral do painel analítico"""
296
  with st.sidebar:
297
+ st.markdown("### 📊 Painel Analítico")
298
 
299
  # Carregar métricas dinâmicas
300
  metricas_uci = obter_metricas_principais_uci()
 
634
  else:
635
  print("🔄 Preparando dados...")
636
 
637
+ # Preparar dados como na página 1_UCI.py
638
  Y = df_uci['G3']
639
  X = df_uci.drop('G3', axis=1)
640
 
 
743
  else:
744
  print("🔄 Preparando dados...")
745
 
746
+ # Preparar dados como na página 2_OULAD.py
747
  Y = df_oulad['final_result']
748
  X = df_oulad.loc[:, df_oulad.columns != 'final_result']
749
 
webapp/uci.pkl CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:d1a10bb28929402a6f7f24f23f2ed2ccdf8031345a43410c2a021b8460662507
3
- size 3177017
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e1ad80d80c823f44658a7cec11db12756906e4df85750213e7aceb43920f5edd
3
+ size 3177025