histlearn commited on
Commit
0817777
·
verified ·
1 Parent(s): 533ca79

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +64 -75
app.py CHANGED
@@ -13,7 +13,6 @@ import logging
13
  import warnings
14
  warnings.filterwarnings('ignore')
15
 
16
- # Configuração de logging
17
  logging.basicConfig(
18
  level=logging.INFO,
19
  format='%(asctime)s - %(levelname)s - %(message)s'
@@ -22,7 +21,6 @@ logging.basicConfig(
22
  class DataProcessor:
23
  @staticmethod
24
  def parse_duration(duration_str: str) -> timedelta:
25
- """Converte string de duração em objeto timedelta."""
26
  try:
27
  h, m, s = map(int, duration_str.split(':'))
28
  return timedelta(hours=h, minutes=m, seconds=s)
@@ -31,7 +29,6 @@ class DataProcessor:
31
 
32
  @staticmethod
33
  def format_timedelta(td: timedelta) -> str:
34
- """Formata timedelta para string no formato HH:MM:SS."""
35
  total_seconds = int(td.total_seconds())
36
  hours, remainder = divmod(total_seconds, 3600)
37
  minutes, seconds = divmod(remainder, 60)
@@ -39,7 +36,6 @@ class DataProcessor:
39
 
40
  @staticmethod
41
  def normalize_html_to_csv(input_html_path: str, output_csv_path: str) -> None:
42
- """Converte arquivo HTML para CSV."""
43
  try:
44
  html_data = pd.read_html(input_html_path)
45
  data = html_data[0]
@@ -51,7 +47,6 @@ class DataProcessor:
51
 
52
  @staticmethod
53
  def normalize_excel_to_csv(input_excel_path: str, output_csv_path: str) -> None:
54
- """Converte arquivo Excel para CSV."""
55
  try:
56
  excel_data = pd.read_excel(input_excel_path)
57
  unnecessary_columns = [col for col in excel_data.columns if 'Unnamed' in str(col)]
@@ -70,23 +65,17 @@ class StudentAnalyzer:
70
  self.processor = DataProcessor()
71
 
72
  def prepare_data(self) -> pd.DataFrame:
73
- """Prepara e limpa os dados para análise."""
74
- # Limpeza de colunas
75
  self.tarefas_df.columns = self.tarefas_df.columns.str.strip()
76
  self.alunos_df.columns = self.alunos_df.columns.str.strip()
77
 
78
- # Verifica colunas necessárias
79
  required_columns = ['Aluno', 'Nota', 'Duração']
80
  if not all(col in self.tarefas_df.columns for col in required_columns):
81
  raise ValueError("Colunas obrigatórias não encontradas no arquivo de tarefas")
82
 
83
- # Processamento de duração
84
  self.tarefas_df['Duração'] = self.tarefas_df['Duração'].apply(self.processor.parse_duration)
85
-
86
  return self.match_students()
87
 
88
  def match_students(self) -> pd.DataFrame:
89
- """Relaciona dados de alunos com suas tarefas."""
90
  def generate_aluno_pattern(ra, dig_ra):
91
  ra_str = str(ra).zfill(9)
92
  return f"{ra_str[1]}{ra_str[2:]}{dig_ra}-sp".lower()
@@ -102,11 +91,9 @@ class StudentAnalyzer:
102
  return None
103
 
104
  self.tarefas_df['Aluno_Pattern'] = self.tarefas_df['Aluno'].apply(extract_pattern)
105
-
106
  return self.calculate_metrics()
107
 
108
  def calculate_metrics(self) -> pd.DataFrame:
109
- """Calcula métricas de desempenho dos alunos."""
110
  metrics_df = pd.DataFrame()
111
 
112
  for _, aluno in self.alunos_df.iterrows():
@@ -114,14 +101,16 @@ class StudentAnalyzer:
114
  aluno_tarefas = self.tarefas_df[self.tarefas_df['Aluno_Pattern'] == aluno_pattern]
115
 
116
  if not aluno_tarefas.empty:
 
 
 
117
  metrics = {
118
  'Nome do Aluno': aluno['Nome do Aluno'],
119
  'Tarefas Completadas': len(aluno_tarefas),
120
- 'Acertos Absolutos': aluno_tarefas['Nota'].sum(),
121
- 'Total Tempo': str(aluno_tarefas['Duração'].sum()),
122
- 'Média de Acertos': f"{(aluno_tarefas['Nota'].sum() / (len(aluno_tarefas) * 2) * 100):.2f}%",
123
- 'Tempo Médio por Tarefa': str(aluno_tarefas['Duração'].mean()),
124
- 'Eficiência': f"{(aluno_tarefas['Nota'].sum() / aluno_tarefas['Duração'].sum().total_seconds() * 3600):.2f}"
125
  }
126
  metrics_df = pd.concat([metrics_df, pd.DataFrame([metrics])], ignore_index=True)
127
 
@@ -131,44 +120,42 @@ class ReportGenerator:
131
  def __init__(self, data: pd.DataFrame):
132
  self.data = data
133
  self.stats = self.calculate_statistics()
134
- # Classificar alunos por níveis
135
- self.data['Nível'] = self.data['Média de Acertos'].str.rstrip('%').astype(float).apply(self.classify_performance)
136
- self.data = self.data.sort_values('Média de Acertos'.str.rstrip('%').astype(float), ascending=False)
137
 
138
- def classify_performance(self, score):
139
- if score >= 70:
140
  return 'Avançado'
141
- elif score >= 40:
142
  return 'Intermediário'
143
  else:
144
  return 'Necessita Atenção'
145
 
146
  def calculate_statistics(self) -> Dict:
147
- """Calcula estatísticas gerais e por grupo."""
148
  basic_stats = {
149
- 'media_acertos': float(self.data['Média de Acertos'].str.rstrip('%').astype(float).mean()),
150
- 'desvio_padrao': float(self.data['Média de Acertos'].str.rstrip('%').astype(float).std()),
151
- 'mediana_acertos': float(self.data['Média de Acertos'].str.rstrip('%').astype(float).median()),
152
  'total_alunos': len(self.data),
153
  'media_tarefas': float(self.data['Tarefas Completadas'].mean()),
154
  'media_tempo': str(pd.to_timedelta(self.data['Total Tempo']).mean())
155
  }
156
 
157
- # Top performers
158
- top_students = self.data.nlargest(3, 'Média de Acertos')
159
- basic_stats['top_performers'] = top_students[['Nome do Aluno', 'Média de Acertos']].values.tolist()
160
 
161
- # Mais eficientes (melhor relação acerto/tempo)
162
- efficient_students = self.data.nlargest(3, 'Eficiência')
163
- basic_stats['most_efficient'] = efficient_students[['Nome do Aluno', 'Eficiência', 'Média de Acertos']].values.tolist()
 
164
 
165
  return basic_stats
166
 
167
  def generate_graphs(self) -> List[plt.Figure]:
168
- """Gera gráficos para o relatório."""
169
  graphs = []
 
170
 
171
- # 1. Distribuição de notas por nível
172
  plt.figure(figsize=(12, 6))
173
  nivel_counts = self.data['Nível'].value_counts()
174
  colors = {'Avançado': 'green', 'Intermediário': 'yellow', 'Necessita Atenção': 'red'}
@@ -186,36 +173,48 @@ class ReportGenerator:
186
  # 2. Top 10 alunos
187
  plt.figure(figsize=(12, 6))
188
  top_10 = self.data.head(10)
189
- acertos = top_10['Média de Acertos'].str.rstrip('%').astype(float)
190
- plt.barh(top_10['Nome do Aluno'], acertos)
191
- plt.title('Top 10 Alunos por Desempenho')
192
- plt.xlabel('Percentual de Acertos')
193
- for i, v in enumerate(acertos):
194
- plt.text(v, i, f'{v:.1f}%', va='center')
195
  plt.tight_layout()
196
  graphs.append(plt.gcf())
197
  plt.close()
198
 
199
- # 3. Relação tempo x desempenho com níveis
200
  plt.figure(figsize=(10, 6))
201
- colors = {'Avançado': 'green', 'Intermediário': 'yellow', 'Necessita Atenção': 'red'}
202
  for nivel in colors:
203
  mask = self.data['Nível'] == nivel
204
  tempo = pd.to_timedelta(self.data[mask]['Total Tempo']).dt.total_seconds() / 60
205
- acertos = self.data[mask]['Média de Acertos'].str.rstrip('%').astype(float)
206
- plt.scatter(tempo, acertos, c=colors[nivel], label=nivel, alpha=0.6)
207
- plt.title('Relação Tempo x Desempenho por Nível')
208
  plt.xlabel('Tempo Total (minutos)')
209
- plt.ylabel('Percentual de Acertos')
210
  plt.legend()
211
  plt.grid(True, alpha=0.3)
212
  graphs.append(plt.gcf())
213
  plt.close()
214
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
215
  return graphs
216
 
217
  def generate_pdf(self, output_path: str, graphs: List[plt.Figure]) -> None:
218
- """Gera relatório em PDF com análise detalhada."""
219
  class PDF(FPDF):
220
  def header(self):
221
  self.set_font('Arial', 'B', 15)
@@ -232,26 +231,25 @@ class ReportGenerator:
232
 
233
  summary_text = f"""
234
  Visão Geral da Turma:
235
- - Média de Acertos: {self.stats['media_acertos']:.1f}%
236
- - Desvio Padrão: {self.stats['desvio_padrao']:.1f}%
237
- - Mediana: {self.stats['mediana_acertos']:.1f}%
238
  - Total de Alunos: {self.stats['total_alunos']}
239
-
240
- Destaques:
241
- Top 3 Melhores Desempenhos:
242
  """
243
  pdf.multi_cell(0, 10, summary_text)
244
 
245
- # Adicionar top performers
246
- for aluno, nota in self.stats['top_performers']:
247
- pdf.cell(0, 10, f"- {aluno}: {nota}", 0, 1)
 
248
 
249
  pdf.ln()
250
  pdf.cell(0, 10, "Alunos Mais Eficientes:", 0, 1)
251
- for aluno, eficiencia, nota in self.stats['most_efficient']:
252
- pdf.cell(0, 10, f"- {aluno}: Eficiência {eficiencia:.1f} (Acertos: {nota})", 0, 1)
253
 
254
- # Gráficos e análises
255
  for i, graph in enumerate(graphs):
256
  pdf.add_page()
257
  graph_path = f'temp_graph_{i}.png'
@@ -259,7 +257,6 @@ class ReportGenerator:
259
  pdf.image(graph_path, x=10, y=30, w=270)
260
  os.remove(graph_path)
261
 
262
- # Tabela detalhada por nível
263
  for nivel in ['Avançado', 'Intermediário', 'Necessita Atenção']:
264
  alunos_nivel = self.data[self.data['Nível'] == nivel]
265
  if not alunos_nivel.empty:
@@ -267,19 +264,17 @@ class ReportGenerator:
267
  pdf.set_font('Arial', 'B', 12)
268
  pdf.cell(0, 10, f'Alunos - Nível {nivel}', 0, 1)
269
 
270
- # Cabeçalhos
271
- columns = ['Nome do Aluno', 'Média de Acertos', 'Tarefas', 'Tempo Total', 'Eficiência']
272
  widths = [80, 30, 30, 30, 30]
273
  pdf.set_font('Arial', 'B', 8)
274
  for i, col in enumerate(columns):
275
  pdf.cell(widths[i], 7, col, 1)
276
  pdf.ln()
277
 
278
- # Dados
279
  pdf.set_font('Arial', '', 8)
280
  for _, row in alunos_nivel.iterrows():
281
  pdf.cell(widths[0], 6, str(row['Nome do Aluno'])[:40], 1)
282
- pdf.cell(widths[1], 6, str(row['Média de Acertos']), 1)
283
  pdf.cell(widths[2], 6, str(row['Tarefas Completadas']), 1)
284
  pdf.cell(widths[3], 6, str(row['Total Tempo']), 1)
285
  pdf.cell(widths[4], 6, str(row['Eficiência']), 1)
@@ -288,17 +283,13 @@ class ReportGenerator:
288
  pdf.output(output_path)
289
 
290
  def process_files(html_file, excel_files) -> Tuple[str, str, str]:
291
- """Processa arquivos e gera relatório."""
292
  try:
293
- # Criar diretório temporário
294
  temp_dir = "temp_files"
295
  os.makedirs(temp_dir, exist_ok=True)
296
 
297
- # Limpar diretório temporário
298
  for file in os.listdir(temp_dir):
299
  os.remove(os.path.join(temp_dir, file))
300
 
301
- # Salvar arquivos
302
  html_path = os.path.join(temp_dir, "alunos.htm")
303
  with open(html_path, "wb") as f:
304
  f.write(html_file)
@@ -310,7 +301,6 @@ def process_files(html_file, excel_files) -> Tuple[str, str, str]:
310
  f.write(excel_file)
311
  excel_paths.append(excel_path)
312
 
313
- # Processar arquivos
314
  processor = DataProcessor()
315
  alunos_csv_path = os.path.join(temp_dir, "alunos.csv")
316
  processor.normalize_html_to_csv(html_path, alunos_csv_path)
@@ -322,16 +312,13 @@ def process_files(html_file, excel_files) -> Tuple[str, str, str]:
322
  df = pd.read_csv(csv_path)
323
  tarefas_df = pd.concat([tarefas_df, df], ignore_index=True)
324
 
325
- # Análise
326
  alunos_df = pd.read_csv(alunos_csv_path)
327
  analyzer = StudentAnalyzer(tarefas_df, alunos_df)
328
  results_df = analyzer.prepare_data()
329
 
330
- # Gerar relatório
331
  report_generator = ReportGenerator(results_df)
332
  graphs = report_generator.generate_graphs()
333
 
334
- # Salvar outputs
335
  output_html = os.path.join(temp_dir, "relatorio.html")
336
  output_pdf = os.path.join(temp_dir, "relatorio.pdf")
337
  results_df.to_html(output_html, index=False)
@@ -374,4 +361,6 @@ with gr.Blocks(theme=theme) as interface:
374
  outputs=[output_html, download_html_btn, download_pdf_btn]
375
  )
376
 
377
- interface.launch()
 
 
 
13
  import warnings
14
  warnings.filterwarnings('ignore')
15
 
 
16
  logging.basicConfig(
17
  level=logging.INFO,
18
  format='%(asctime)s - %(levelname)s - %(message)s'
 
21
  class DataProcessor:
22
  @staticmethod
23
  def parse_duration(duration_str: str) -> timedelta:
 
24
  try:
25
  h, m, s = map(int, duration_str.split(':'))
26
  return timedelta(hours=h, minutes=m, seconds=s)
 
29
 
30
  @staticmethod
31
  def format_timedelta(td: timedelta) -> str:
 
32
  total_seconds = int(td.total_seconds())
33
  hours, remainder = divmod(total_seconds, 3600)
34
  minutes, seconds = divmod(remainder, 60)
 
36
 
37
  @staticmethod
38
  def normalize_html_to_csv(input_html_path: str, output_csv_path: str) -> None:
 
39
  try:
40
  html_data = pd.read_html(input_html_path)
41
  data = html_data[0]
 
47
 
48
  @staticmethod
49
  def normalize_excel_to_csv(input_excel_path: str, output_csv_path: str) -> None:
 
50
  try:
51
  excel_data = pd.read_excel(input_excel_path)
52
  unnecessary_columns = [col for col in excel_data.columns if 'Unnamed' in str(col)]
 
65
  self.processor = DataProcessor()
66
 
67
  def prepare_data(self) -> pd.DataFrame:
 
 
68
  self.tarefas_df.columns = self.tarefas_df.columns.str.strip()
69
  self.alunos_df.columns = self.alunos_df.columns.str.strip()
70
 
 
71
  required_columns = ['Aluno', 'Nota', 'Duração']
72
  if not all(col in self.tarefas_df.columns for col in required_columns):
73
  raise ValueError("Colunas obrigatórias não encontradas no arquivo de tarefas")
74
 
 
75
  self.tarefas_df['Duração'] = self.tarefas_df['Duração'].apply(self.processor.parse_duration)
 
76
  return self.match_students()
77
 
78
  def match_students(self) -> pd.DataFrame:
 
79
  def generate_aluno_pattern(ra, dig_ra):
80
  ra_str = str(ra).zfill(9)
81
  return f"{ra_str[1]}{ra_str[2:]}{dig_ra}-sp".lower()
 
91
  return None
92
 
93
  self.tarefas_df['Aluno_Pattern'] = self.tarefas_df['Aluno'].apply(extract_pattern)
 
94
  return self.calculate_metrics()
95
 
96
  def calculate_metrics(self) -> pd.DataFrame:
 
97
  metrics_df = pd.DataFrame()
98
 
99
  for _, aluno in self.alunos_df.iterrows():
 
101
  aluno_tarefas = self.tarefas_df[self.tarefas_df['Aluno_Pattern'] == aluno_pattern]
102
 
103
  if not aluno_tarefas.empty:
104
+ duracao_total = aluno_tarefas['Duração'].sum()
105
+ acertos_total = aluno_tarefas['Nota'].sum()
106
+
107
  metrics = {
108
  'Nome do Aluno': aluno['Nome do Aluno'],
109
  'Tarefas Completadas': len(aluno_tarefas),
110
+ 'Acertos Absolutos': acertos_total,
111
+ 'Total Tempo': str(duracao_total),
112
+ 'Tempo Médio por Tarefa': str(duracao_total / len(aluno_tarefas)),
113
+ 'Eficiência': f"{(acertos_total / duracao_total.total_seconds() * 3600):.2f}"
 
114
  }
115
  metrics_df = pd.concat([metrics_df, pd.DataFrame([metrics])], ignore_index=True)
116
 
 
120
  def __init__(self, data: pd.DataFrame):
121
  self.data = data
122
  self.stats = self.calculate_statistics()
123
+ self.data['Nível'] = self.data['Acertos Absolutos'].apply(self.classify_performance)
124
+ self.data = self.data.sort_values('Acertos Absolutos', ascending=False)
 
125
 
126
+ def classify_performance(self, acertos):
127
+ if acertos >= 10:
128
  return 'Avançado'
129
+ elif acertos >= 5:
130
  return 'Intermediário'
131
  else:
132
  return 'Necessita Atenção'
133
 
134
  def calculate_statistics(self) -> Dict:
 
135
  basic_stats = {
136
+ 'media_acertos': float(self.data['Acertos Absolutos'].mean()),
137
+ 'desvio_padrao': float(self.data['Acertos Absolutos'].std()),
138
+ 'mediana_acertos': float(self.data['Acertos Absolutos'].median()),
139
  'total_alunos': len(self.data),
140
  'media_tarefas': float(self.data['Tarefas Completadas'].mean()),
141
  'media_tempo': str(pd.to_timedelta(self.data['Total Tempo']).mean())
142
  }
143
 
144
+ top_students = self.data.nlargest(3, 'Acertos Absolutos')
145
+ basic_stats['top_performers'] = top_students[['Nome do Aluno', 'Acertos Absolutos']].values.tolist()
 
146
 
147
+ efficient_students = self.data.nlargest(3, 'Eficiência')[
148
+ ['Nome do Aluno', 'Eficiência', 'Acertos Absolutos']
149
+ ].values.tolist()
150
+ basic_stats['most_efficient'] = efficient_students
151
 
152
  return basic_stats
153
 
154
  def generate_graphs(self) -> List[plt.Figure]:
 
155
  graphs = []
156
+ plt.style.use('seaborn')
157
 
158
+ # 1. Distribuição por nível
159
  plt.figure(figsize=(12, 6))
160
  nivel_counts = self.data['Nível'].value_counts()
161
  colors = {'Avançado': 'green', 'Intermediário': 'yellow', 'Necessita Atenção': 'red'}
 
173
  # 2. Top 10 alunos
174
  plt.figure(figsize=(12, 6))
175
  top_10 = self.data.head(10)
176
+ plt.barh(top_10['Nome do Aluno'], top_10['Acertos Absolutos'])
177
+ plt.title('Top 10 Alunos - Acertos Absolutos')
178
+ plt.xlabel('Número de Acertos')
179
+ for i, v in enumerate(top_10['Acertos Absolutos']):
180
+ plt.text(v, i, f'{v:.0f}', va='center')
 
181
  plt.tight_layout()
182
  graphs.append(plt.gcf())
183
  plt.close()
184
 
185
+ # 3. Relação tempo x acertos
186
  plt.figure(figsize=(10, 6))
 
187
  for nivel in colors:
188
  mask = self.data['Nível'] == nivel
189
  tempo = pd.to_timedelta(self.data[mask]['Total Tempo']).dt.total_seconds() / 60
190
+ plt.scatter(tempo, self.data[mask]['Acertos Absolutos'],
191
+ c=colors[nivel], label=nivel, alpha=0.6)
192
+ plt.title('Relação Tempo x Acertos por Nível')
193
  plt.xlabel('Tempo Total (minutos)')
194
+ plt.ylabel('Número de Acertos')
195
  plt.legend()
196
  plt.grid(True, alpha=0.3)
197
  graphs.append(plt.gcf())
198
  plt.close()
199
 
200
+ # 4. Relação Tarefas x Acertos
201
+ plt.figure(figsize=(10, 6))
202
+ plt.scatter(self.data['Tarefas Completadas'], self.data['Acertos Absolutos'])
203
+ plt.title('Relação entre Tarefas Completadas e Acertos')
204
+ plt.xlabel('Número de Tarefas Completadas')
205
+ plt.ylabel('Número de Acertos')
206
+ plt.grid(True, alpha=0.3)
207
+ z = np.polyfit(self.data['Tarefas Completadas'], self.data['Acertos Absolutos'], 1)
208
+ p = np.poly1d(z)
209
+ plt.plot(self.data['Tarefas Completadas'], p(self.data['Tarefas Completadas']),
210
+ "r--", alpha=0.8, label='Tendência')
211
+ plt.legend()
212
+ graphs.append(plt.gcf())
213
+ plt.close()
214
+
215
  return graphs
216
 
217
  def generate_pdf(self, output_path: str, graphs: List[plt.Figure]) -> None:
 
218
  class PDF(FPDF):
219
  def header(self):
220
  self.set_font('Arial', 'B', 15)
 
231
 
232
  summary_text = f"""
233
  Visão Geral da Turma:
234
+ - Média de Acertos: {self.stats['media_acertos']:.1f}
235
+ - Desvio Padrão: {self.stats['desvio_padrao']:.1f}
236
+ - Mediana: {self.stats['mediana_acertos']:.1f}
237
  - Total de Alunos: {self.stats['total_alunos']}
238
+ - Média de Tarefas por Aluno: {self.stats['media_tarefas']:.1f}
239
+ - Tempo Médio Total: {self.stats['media_tempo']}
 
240
  """
241
  pdf.multi_cell(0, 10, summary_text)
242
 
243
+ pdf.ln()
244
+ pdf.cell(0, 10, "Top 3 - Maiores Números de Acertos:", 0, 1)
245
+ for aluno, acertos in self.stats['top_performers']:
246
+ pdf.cell(0, 10, f"- {aluno}: {acertos:.0f} acertos", 0, 1)
247
 
248
  pdf.ln()
249
  pdf.cell(0, 10, "Alunos Mais Eficientes:", 0, 1)
250
+ for aluno, eficiencia, acertos in self.stats['most_efficient']:
251
+ pdf.cell(0, 10, f"- {aluno}: Eficiência {eficiencia} (Acertos: {acertos:.0f})", 0, 1)
252
 
 
253
  for i, graph in enumerate(graphs):
254
  pdf.add_page()
255
  graph_path = f'temp_graph_{i}.png'
 
257
  pdf.image(graph_path, x=10, y=30, w=270)
258
  os.remove(graph_path)
259
 
 
260
  for nivel in ['Avançado', 'Intermediário', 'Necessita Atenção']:
261
  alunos_nivel = self.data[self.data['Nível'] == nivel]
262
  if not alunos_nivel.empty:
 
264
  pdf.set_font('Arial', 'B', 12)
265
  pdf.cell(0, 10, f'Alunos - Nível {nivel}', 0, 1)
266
 
267
+ columns = ['Nome do Aluno', 'Acertos Absolutos', 'Tarefas', 'Tempo Total', 'Eficiência']
 
268
  widths = [80, 30, 30, 30, 30]
269
  pdf.set_font('Arial', 'B', 8)
270
  for i, col in enumerate(columns):
271
  pdf.cell(widths[i], 7, col, 1)
272
  pdf.ln()
273
 
 
274
  pdf.set_font('Arial', '', 8)
275
  for _, row in alunos_nivel.iterrows():
276
  pdf.cell(widths[0], 6, str(row['Nome do Aluno'])[:40], 1)
277
+ pdf.cell(widths[1], 6, str(row['Acertos Absolutos']), 1)
278
  pdf.cell(widths[2], 6, str(row['Tarefas Completadas']), 1)
279
  pdf.cell(widths[3], 6, str(row['Total Tempo']), 1)
280
  pdf.cell(widths[4], 6, str(row['Eficiência']), 1)
 
283
  pdf.output(output_path)
284
 
285
  def process_files(html_file, excel_files) -> Tuple[str, str, str]:
 
286
  try:
 
287
  temp_dir = "temp_files"
288
  os.makedirs(temp_dir, exist_ok=True)
289
 
 
290
  for file in os.listdir(temp_dir):
291
  os.remove(os.path.join(temp_dir, file))
292
 
 
293
  html_path = os.path.join(temp_dir, "alunos.htm")
294
  with open(html_path, "wb") as f:
295
  f.write(html_file)
 
301
  f.write(excel_file)
302
  excel_paths.append(excel_path)
303
 
 
304
  processor = DataProcessor()
305
  alunos_csv_path = os.path.join(temp_dir, "alunos.csv")
306
  processor.normalize_html_to_csv(html_path, alunos_csv_path)
 
312
  df = pd.read_csv(csv_path)
313
  tarefas_df = pd.concat([tarefas_df, df], ignore_index=True)
314
 
 
315
  alunos_df = pd.read_csv(alunos_csv_path)
316
  analyzer = StudentAnalyzer(tarefas_df, alunos_df)
317
  results_df = analyzer.prepare_data()
318
 
 
319
  report_generator = ReportGenerator(results_df)
320
  graphs = report_generator.generate_graphs()
321
 
 
322
  output_html = os.path.join(temp_dir, "relatorio.html")
323
  output_pdf = os.path.join(temp_dir, "relatorio.pdf")
324
  results_df.to_html(output_html, index=False)
 
361
  outputs=[output_html, download_html_btn, download_pdf_btn]
362
  )
363
 
364
+ # Iniciar a aplicação
365
+ if __name__ == "__main__":
366
+ interface.launch()