histlearn commited on
Commit
354a499
·
verified ·
1 Parent(s): 1235712

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +78 -132
app.py CHANGED
@@ -80,82 +80,53 @@ def extrair_tabelas_pdf(pdf_path):
80
  if len(tables) == 0:
81
  raise ValueError("Nenhuma tabela foi extraída do PDF.")
82
 
83
- # Processar todas as tabelas para informações do aluno
 
 
 
84
  info_aluno = {}
85
- tabela_notas = None
 
 
 
 
 
 
 
 
86
 
87
- for table in tables:
 
88
  df_temp = table.df
89
- # Procurar informações do aluno
90
- for i, row in df_temp.iterrows():
91
- row_text = ' '.join(str(x) for x in row if str(x).strip())
92
-
93
- if 'Nome do Aluno' in row_text:
94
- # Pegar próxima linha não vazia
95
- next_rows = df_temp.iloc[i+1:]
96
- for _, next_row in next_rows.iterrows():
97
- nome = ' '.join(str(x).strip() for x in next_row if str(x).strip() and str(x).strip() != 'nan')
98
- if nome:
99
- info_aluno['nome'] = nome
100
- break
101
-
102
- elif 'RA' in row_text and not row_text.startswith('AC'):
103
- next_rows = df_temp.iloc[i+1:]
104
- for _, next_row in next_rows.iterrows():
105
- ra = ' '.join(str(x).strip() for x in next_row if str(x).strip() and str(x).strip() != 'nan')
106
- if ra:
107
- info_aluno['ra'] = ra
108
- break
109
-
110
- elif 'Escola' in row_text:
111
- next_rows = df_temp.iloc[i+1:]
112
- for _, next_row in next_rows.iterrows():
113
- escola = ' '.join(str(x).strip() for x in next_row if str(x).strip() and str(x).strip() != 'nan')
114
- if escola:
115
- info_aluno['escola'] = escola
116
- break
117
-
118
- elif 'Turma' in row_text:
119
- next_rows = df_temp.iloc[i+1:]
120
- for _, next_row in next_rows.iterrows():
121
- turma = ' '.join(str(x).strip() for x in next_row if str(x).strip() and str(x).strip() != 'nan')
122
- if turma:
123
- info_aluno['turma'] = turma
124
- break
125
-
126
  # Verificar se é a tabela de notas
127
  if any('Disciplina' in str(col) for col in df_temp.iloc[0]) or \
128
  any('Bimestre' in str(col) for col in df_temp.iloc[0]):
129
- tabela_notas = df_temp
130
-
131
- if tabela_notas is None:
132
- raise ValueError("Não foi possível encontrar a tabela de notas.")
133
-
134
- # Processar tabela de notas
135
- df = tabela_notas.copy()
 
 
 
 
136
 
137
- # Renomear colunas
138
- df = df.rename(columns={
139
- 0: 'Disciplina',
140
- 1: 'Nota B1', 2: 'Freq B1', 3: '%Freq B1', 4: 'AC B1',
141
- 5: 'Nota B2', 6: 'Freq B2', 7: '%Freq B2', 8: 'AC B2',
142
- 9: 'Nota B3', 10: 'Freq B3', 11: '%Freq B3', 12: 'AC B3',
143
- 13: 'Nota B4', 14: 'Freq B4', 15: '%Freq B4', 16: 'AC B4',
144
- 17: 'CF', 18: 'Nota Final', 19: 'Freq Final', 20: 'AC Final'
145
- })
146
 
147
- # Limpar dados
148
- for col in df.columns:
149
- df[col] = df[col].apply(lambda x: str(x).strip())
150
 
151
- # Filtrar linhas vazias ou cabeçalhos repetidos
152
- df = df[df['Disciplina'].apply(lambda x: x != '' and 'Disciplina' not in x)]
153
 
154
- if df.empty:
155
- raise ValueError("A tabela de notas está vazia após o processamento.")
 
156
 
157
  # Adicionar informações do aluno ao DataFrame
158
- print("Informações do aluno encontradas:", info_aluno)
159
  for key, value in info_aluno.items():
160
  df.attrs[key] = value
161
 
@@ -301,17 +272,16 @@ def plotar_graficos_destacados(disciplinas_dados, temp_dir):
301
  if not n_disciplinas:
302
  raise ValueError("Nenhuma disciplina válida encontrada no boletim.")
303
 
304
- # Criar figura e subplots com mais espaço
305
- fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(14, 12), height_ratios=[1, 1])
306
- plt.subplots_adjust(hspace=0.6) # Aumentar espaço entre os gráficos
307
 
308
  disciplinas = [d['disciplina'] for d in disciplinas_dados]
309
  medias_notas = [d['media_notas'] for d in disciplinas_dados]
310
  medias_freq = [d['media_freq'] for d in disciplinas_dados]
311
 
312
- # Adicionar margem para evitar sobreposição
313
- ax1.margins(x=0.1)
314
- ax2.margins(x=0.1)
315
 
316
  cores_notas = ['red' if media < LIMITE_APROVACAO_NOTA else '#2ecc71' for media in medias_notas]
317
  cores_freq = ['red' if media < LIMITE_APROVACAO_FREQ else '#2ecc71' for media in medias_freq]
@@ -326,54 +296,42 @@ def plotar_graficos_destacados(disciplinas_dados, temp_dir):
326
  ax1.grid(True, axis='y', alpha=0.3, linestyle='--')
327
 
328
  # Melhorar a apresentação dos rótulos
329
- ax1.tick_params(axis='x', rotation=45, labelsize=8)
330
- ax1.set_xticklabels(disciplinas, ha='right', va='center')
331
  ax1.set_ylabel('Notas', fontsize=10, labelpad=10)
332
 
333
- # Adicionar linha de média mínima com área sombreada
334
  ax1.axhline(y=LIMITE_APROVACAO_NOTA, color='r', linestyle='--', alpha=0.3)
335
- ax1.fill_between([ax1.get_xlim()[0], ax1.get_xlim()[1]],
336
- 0, LIMITE_APROVACAO_NOTA,
337
- color='red', alpha=0.1)
338
  ax1.text(0.02, LIMITE_APROVACAO_NOTA + 0.1, 'Média mínima (5,0)',
339
- transform=ax1.get_yaxis_transform(), color='r', alpha=0.7,
340
- bbox=dict(facecolor='white', edgecolor='none', alpha=0.8))
341
 
342
  # Valores nas barras
343
  for barra in barras_notas:
344
  altura = barra.get_height()
345
- ax1.text(barra.get_x() + barra.get_width()/2., altura + 0.1,
346
  f'{altura:.1f}',
347
- ha='center', va='bottom', fontsize=8,
348
- bbox=dict(facecolor='white', edgecolor='none', alpha=0.8))
349
 
350
  # Gráfico de frequências
351
  barras_freq = ax2.bar(disciplinas, medias_freq, color=cores_freq)
352
  ax2.set_title('Frequência Média por Disciplina', pad=20, fontsize=12, fontweight='bold')
353
- ax2.set_ylim(0, 110) # Aumentar limite para acomodar rótulos
354
  ax2.grid(True, axis='y', alpha=0.3, linestyle='--')
355
 
356
  # Melhorar a apresentação dos rótulos
357
- ax2.tick_params(axis='x', rotation=45, labelsize=8)
358
- ax2.set_xticklabels(disciplinas, ha='right', va='center')
359
  ax2.set_ylabel('Frequência (%)', fontsize=10, labelpad=10)
360
 
361
- # Adicionar linha de frequência mínima com área sombreada
362
  ax2.axhline(y=LIMITE_APROVACAO_FREQ, color='r', linestyle='--', alpha=0.3)
363
- ax2.fill_between([ax2.get_xlim()[0], ax2.get_xlim()[1]],
364
- 0, LIMITE_APROVACAO_FREQ,
365
- color='red', alpha=0.1)
366
  ax2.text(0.02, LIMITE_APROVACAO_FREQ + 1, 'Frequência mínima (75%)',
367
- transform=ax2.get_yaxis_transform(), color='r', alpha=0.7,
368
- bbox=dict(facecolor='white', edgecolor='none', alpha=0.8))
369
 
370
  # Valores nas barras
371
  for barra in barras_freq:
372
  altura = barra.get_height()
373
- ax2.text(barra.get_x() + barra.get_width()/2., altura + 1,
374
  f'{altura:.1f}%',
375
- ha='center', va='bottom', fontsize=8,
376
- bbox=dict(facecolor='white', edgecolor='none', alpha=0.8))
377
 
378
  # Título global com informações de média
379
  plt.suptitle(
@@ -382,10 +340,9 @@ def plotar_graficos_destacados(disciplinas_dados, temp_dir):
382
  )
383
 
384
  if freq_global < LIMITE_APROVACAO_FREQ:
385
- plt.figtext(0.5, 0.01,
386
- "ATENÇÃO: Risco de Reprovação por Baixa Frequência",
387
- ha="center", fontsize=11, color="red", weight='bold',
388
- bbox=dict(facecolor='white', edgecolor='red', pad=5))
389
 
390
  plt.tight_layout()
391
 
@@ -397,7 +354,7 @@ def plotar_graficos_destacados(disciplinas_dados, temp_dir):
397
  def gerar_relatorio_pdf(df, disciplinas_dados, grafico1_path, grafico2_path):
398
  """Gera relatório PDF com os gráficos e análises."""
399
  pdf = FPDF()
400
- pdf.set_auto_page_break(auto=True, margin=20) # Aumentar margem
401
  pdf.add_page()
402
 
403
  # Cabeçalho
@@ -407,26 +364,20 @@ def gerar_relatorio_pdf(df, disciplinas_dados, grafico1_path, grafico2_path):
407
 
408
  # Informações do aluno
409
  pdf.set_font('Helvetica', 'B', 12)
410
- pdf.set_fill_color(240, 240, 240) # Cinza claro para o fundo
411
- pdf.cell(0, 10, 'Informações do Aluno', 0, new_x=XPos.LMARGIN, new_y=YPos.NEXT, align='L', fill=True)
412
- pdf.line(10, pdf.get_y(), 200, pdf.get_y())
413
  pdf.ln(5)
414
 
415
  pdf.set_font('Helvetica', '', 11)
416
  if hasattr(df, 'attrs'):
417
- for campo, valor in [
418
- ('nome', 'Nome'),
419
- ('ra', 'RA'),
420
- ('escola', 'Escola'),
421
- ('turma', 'Turma')
422
- ]:
423
- if campo in df.attrs and df.attrs[campo].strip():
424
- # Rótulo em negrito
425
- pdf.set_font('Helvetica', 'B', 11)
426
- pdf.cell(20, 7, f'{valor}:', 0, new_x=XPos.RIGHT, new_y=YPos.TOP)
427
- # Valor normal
428
- pdf.set_font('Helvetica', '', 11)
429
- pdf.cell(0, 7, df.attrs[campo], 0, new_x=XPos.LMARGIN, new_y=YPos.NEXT)
430
 
431
  pdf.ln(10)
432
 
@@ -438,8 +389,7 @@ def gerar_relatorio_pdf(df, disciplinas_dados, grafico1_path, grafico2_path):
438
 
439
  # Seção de gráficos
440
  pdf.set_font('Helvetica', 'B', 14)
441
- pdf.set_fill_color(240, 240, 240)
442
- pdf.cell(0, 10, 'Análise Gráfica', 0, new_x=XPos.LMARGIN, new_y=YPos.NEXT, align='L', fill=True)
443
  pdf.line(10, pdf.get_y(), 200, pdf.get_y())
444
  pdf.ln(10)
445
 
@@ -450,8 +400,7 @@ def gerar_relatorio_pdf(df, disciplinas_dados, grafico1_path, grafico2_path):
450
 
451
  # Seção de Análise
452
  pdf.set_font('Helvetica', 'B', 14)
453
- pdf.set_fill_color(240, 240, 240)
454
- pdf.cell(0, 10, 'Análise Detalhada', 0, new_x=XPos.LMARGIN, new_y=YPos.NEXT, align='L', fill=True)
455
  pdf.line(10, pdf.get_y(), 200, pdf.get_y())
456
  pdf.ln(10)
457
 
@@ -461,10 +410,9 @@ def gerar_relatorio_pdf(df, disciplinas_dados, grafico1_path, grafico2_path):
461
  media_global = np.mean(medias_notas)
462
  freq_global = np.mean(medias_freq)
463
 
464
- # Resumo geral com box
465
  pdf.set_font('Helvetica', 'B', 12)
466
- pdf.set_fill_color(245, 245, 245)
467
- pdf.cell(0, 8, 'Resumo Geral:', 0, new_x=XPos.LMARGIN, new_y=YPos.NEXT, align='L', fill=True)
468
  pdf.ln(5)
469
 
470
  pdf.set_font('Helvetica', '', 11)
@@ -472,12 +420,13 @@ def gerar_relatorio_pdf(df, disciplinas_dados, grafico1_path, grafico2_path):
472
  pdf.cell(0, 7, f'Frequência Global: {freq_global:.1f}%', 0, new_x=XPos.LMARGIN, new_y=YPos.NEXT, align='L')
473
  pdf.ln(10)
474
 
475
- # Pontos de Atenção
476
  pdf.set_font('Helvetica', 'B', 12)
477
- pdf.set_fill_color(245, 245, 245)
478
- pdf.cell(0, 8, 'Pontos de Atenção:', 0, new_x=XPos.LMARGIN, new_y=YPos.NEXT, align='L', fill=True)
479
  pdf.ln(5)
480
 
 
 
481
  # Disciplinas com baixo desempenho
482
  disciplinas_risco = []
483
  for disc_data in disciplinas_dados:
@@ -496,21 +445,18 @@ def gerar_relatorio_pdf(df, disciplinas_dados, grafico1_path, grafico2_path):
496
  pdf.cell(0, 7, f'- {disc}:', 0, new_x=XPos.LMARGIN, new_y=YPos.NEXT, align='L')
497
  pdf.set_font('Helvetica', '', 10)
498
  for aviso in avisos:
499
- pdf.cell(15) # Aumentar indentação
500
- pdf.cell(0, 6, f'- {aviso}', 0, new_x=XPos.LMARGIN, new_y=YPos.NEXT, align='L')
501
- pdf.ln(2) # Espaço entre disciplinas
502
  else:
503
  pdf.cell(0, 7, 'Nenhum problema identificado.', 0, new_x=XPos.LMARGIN, new_y=YPos.NEXT, align='L')
504
 
505
  # Rodapé
506
- pdf.set_y(-35) # Aumentar espaço do rodapé
507
  pdf.line(10, pdf.get_y(), 200, pdf.get_y())
508
  pdf.ln(5)
509
  pdf.set_font('Helvetica', 'I', 8)
510
- pdf.multi_cell(0, 5,
511
- 'Este relatório é uma análise automática e deve ser validado junto à secretaria da escola.\n' +
512
- 'Dados gerados com base nas informações disponíveis até o momento da geração deste documento.',
513
- align='C')
514
 
515
  # Salvar PDF
516
  temp_pdf = tempfile.NamedTemporaryFile(delete=False, suffix='.pdf')
 
80
  if len(tables) == 0:
81
  raise ValueError("Nenhuma tabela foi extraída do PDF.")
82
 
83
+ # Processar a primeira tabela
84
+ df = tables[0].df
85
+
86
+ # Extrair nome do aluno e outras informações se disponível
87
  info_aluno = {}
88
+ for i, row in df.iterrows():
89
+ if 'Nome do Aluno' in str(row[0]):
90
+ info_aluno['nome'] = row[1].strip() if len(row) > 1 else ''
91
+ elif 'RA' in str(row[0]):
92
+ info_aluno['ra'] = row[1].strip() if len(row) > 1 else ''
93
+ elif 'Escola' in str(row[0]):
94
+ info_aluno['escola'] = row[1].strip() if len(row) > 1 else ''
95
+ elif 'Turma' in str(row[0]):
96
+ info_aluno['turma'] = row[1].strip() if len(row) > 1 else ''
97
 
98
+ # Encontrar a tabela de notas
99
+ for i, table in enumerate(tables):
100
  df_temp = table.df
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
101
  # Verificar se é a tabela de notas
102
  if any('Disciplina' in str(col) for col in df_temp.iloc[0]) or \
103
  any('Bimestre' in str(col) for col in df_temp.iloc[0]):
104
+ df = df_temp
105
+ # Renomear as colunas corretamente
106
+ df = df.rename(columns={
107
+ 0: 'Disciplina',
108
+ 1: 'Nota B1', 2: 'Freq B1', 3: '%Freq B1', 4: 'AC B1',
109
+ 5: 'Nota B2', 6: 'Freq B2', 7: '%Freq B2', 8: 'AC B2',
110
+ 9: 'Nota B3', 10: 'Freq B3', 11: '%Freq B3', 12: 'AC B3',
111
+ 13: 'Nota B4', 14: 'Freq B4', 15: '%Freq B4', 16: 'AC B4',
112
+ 17: 'CF', 18: 'Nota Final', 19: 'Freq Final', 20: 'AC Final'
113
+ })
114
+ break
115
 
116
+ if df.empty:
117
+ raise ValueError("A tabela extraída está vazia.")
 
 
 
 
 
 
 
118
 
119
+ # Adicionar informações do aluno ao DataFrame
120
+ for key, value in info_aluno.items():
121
+ df.attrs[key] = value
122
 
123
+ return df
 
124
 
125
+ except Exception as e:
126
+ print(f"Erro na extração das tabelas: {str(e)}")
127
+ raise
128
 
129
  # Adicionar informações do aluno ao DataFrame
 
130
  for key, value in info_aluno.items():
131
  df.attrs[key] = value
132
 
 
272
  if not n_disciplinas:
273
  raise ValueError("Nenhuma disciplina válida encontrada no boletim.")
274
 
275
+ # Aumentar a figura para melhor visualização
276
+ plt.figure(figsize=(12, 10))
 
277
 
278
  disciplinas = [d['disciplina'] for d in disciplinas_dados]
279
  medias_notas = [d['media_notas'] for d in disciplinas_dados]
280
  medias_freq = [d['media_freq'] for d in disciplinas_dados]
281
 
282
+ # Criar subplot com mais espaço entre os gráficos
283
+ fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 10), height_ratios=[1, 1])
284
+ plt.subplots_adjust(hspace=0.5) # Aumentar espaço entre os gráficos
285
 
286
  cores_notas = ['red' if media < LIMITE_APROVACAO_NOTA else '#2ecc71' for media in medias_notas]
287
  cores_freq = ['red' if media < LIMITE_APROVACAO_FREQ else '#2ecc71' for media in medias_freq]
 
296
  ax1.grid(True, axis='y', alpha=0.3, linestyle='--')
297
 
298
  # Melhorar a apresentação dos rótulos
299
+ ax1.set_xticklabels(disciplinas, rotation=45, ha='right', va='top')
 
300
  ax1.set_ylabel('Notas', fontsize=10, labelpad=10)
301
 
302
+ # Adicionar linha de média mínima
303
  ax1.axhline(y=LIMITE_APROVACAO_NOTA, color='r', linestyle='--', alpha=0.3)
 
 
 
304
  ax1.text(0.02, LIMITE_APROVACAO_NOTA + 0.1, 'Média mínima (5,0)',
305
+ transform=ax1.get_yaxis_transform(), color='r', alpha=0.7)
 
306
 
307
  # Valores nas barras
308
  for barra in barras_notas:
309
  altura = barra.get_height()
310
+ ax1.text(barra.get_x() + barra.get_width()/2., altura,
311
  f'{altura:.1f}',
312
+ ha='center', va='bottom', fontsize=8)
 
313
 
314
  # Gráfico de frequências
315
  barras_freq = ax2.bar(disciplinas, medias_freq, color=cores_freq)
316
  ax2.set_title('Frequência Média por Disciplina', pad=20, fontsize=12, fontweight='bold')
317
+ ax2.set_ylim(0, 110)
318
  ax2.grid(True, axis='y', alpha=0.3, linestyle='--')
319
 
320
  # Melhorar a apresentação dos rótulos
321
+ ax2.set_xticklabels(disciplinas, rotation=45, ha='right', va='top')
 
322
  ax2.set_ylabel('Frequência (%)', fontsize=10, labelpad=10)
323
 
324
+ # Adicionar linha de frequência mínima
325
  ax2.axhline(y=LIMITE_APROVACAO_FREQ, color='r', linestyle='--', alpha=0.3)
 
 
 
326
  ax2.text(0.02, LIMITE_APROVACAO_FREQ + 1, 'Frequência mínima (75%)',
327
+ transform=ax2.get_yaxis_transform(), color='r', alpha=0.7)
 
328
 
329
  # Valores nas barras
330
  for barra in barras_freq:
331
  altura = barra.get_height()
332
+ ax2.text(barra.get_x() + barra.get_width()/2., altura,
333
  f'{altura:.1f}%',
334
+ ha='center', va='bottom', fontsize=8)
 
335
 
336
  # Título global com informações de média
337
  plt.suptitle(
 
340
  )
341
 
342
  if freq_global < LIMITE_APROVACAO_FREQ:
343
+ plt.figtext(0.5, 0.02,
344
+ "Atenção: Risco de Reprovação por Baixa Frequência",
345
+ ha="center", fontsize=11, color="red", weight='bold')
 
346
 
347
  plt.tight_layout()
348
 
 
354
  def gerar_relatorio_pdf(df, disciplinas_dados, grafico1_path, grafico2_path):
355
  """Gera relatório PDF com os gráficos e análises."""
356
  pdf = FPDF()
357
+ pdf.set_auto_page_break(auto=True, margin=15)
358
  pdf.add_page()
359
 
360
  # Cabeçalho
 
364
 
365
  # Informações do aluno
366
  pdf.set_font('Helvetica', 'B', 12)
367
+ pdf.cell(0, 10, 'Informações do Aluno', 0, new_x=XPos.LMARGIN, new_y=YPos.NEXT, align='L')
368
+ pdf.line(10, pdf.get_y(), 200, pdf.get_y()) # Adicionar linha divisória
 
369
  pdf.ln(5)
370
 
371
  pdf.set_font('Helvetica', '', 11)
372
  if hasattr(df, 'attrs'):
373
+ if 'nome' in df.attrs:
374
+ pdf.cell(0, 7, f'Nome: {df.attrs["nome"]}', 0, new_x=XPos.LMARGIN, new_y=YPos.NEXT, align='L')
375
+ if 'ra' in df.attrs:
376
+ pdf.cell(0, 7, f'RA: {df.attrs["ra"]}', 0, new_x=XPos.LMARGIN, new_y=YPos.NEXT, align='L')
377
+ if 'escola' in df.attrs:
378
+ pdf.cell(0, 7, f'Escola: {df.attrs["escola"]}', 0, new_x=XPos.LMARGIN, new_y=YPos.NEXT, align='L')
379
+ if 'turma' in df.attrs:
380
+ pdf.cell(0, 7, f'Turma: {df.attrs["turma"]}', 0, new_x=XPos.LMARGIN, new_y=YPos.NEXT, align='L')
 
 
 
 
 
381
 
382
  pdf.ln(10)
383
 
 
389
 
390
  # Seção de gráficos
391
  pdf.set_font('Helvetica', 'B', 14)
392
+ pdf.cell(0, 10, 'Análise Gráfica', 0, new_x=XPos.LMARGIN, new_y=YPos.NEXT, align='L')
 
393
  pdf.line(10, pdf.get_y(), 200, pdf.get_y())
394
  pdf.ln(10)
395
 
 
400
 
401
  # Seção de Análise
402
  pdf.set_font('Helvetica', 'B', 14)
403
+ pdf.cell(0, 10, 'Análise Detalhada', 0, new_x=XPos.LMARGIN, new_y=YPos.NEXT, align='L')
 
404
  pdf.line(10, pdf.get_y(), 200, pdf.get_y())
405
  pdf.ln(10)
406
 
 
410
  media_global = np.mean(medias_notas)
411
  freq_global = np.mean(medias_freq)
412
 
413
+ # Resumo geral
414
  pdf.set_font('Helvetica', 'B', 12)
415
+ pdf.cell(0, 7, 'Resumo Geral:', 0, new_x=XPos.LMARGIN, new_y=YPos.NEXT, align='L')
 
416
  pdf.ln(5)
417
 
418
  pdf.set_font('Helvetica', '', 11)
 
420
  pdf.cell(0, 7, f'Frequência Global: {freq_global:.1f}%', 0, new_x=XPos.LMARGIN, new_y=YPos.NEXT, align='L')
421
  pdf.ln(10)
422
 
423
+ # Avisos Importantes
424
  pdf.set_font('Helvetica', 'B', 12)
425
+ pdf.cell(0, 10, 'Pontos de Atenção:', 0, new_x=XPos.LMARGIN, new_y=YPos.NEXT, align='L')
 
426
  pdf.ln(5)
427
 
428
+ pdf.set_font('Helvetica', '', 10)
429
+
430
  # Disciplinas com baixo desempenho
431
  disciplinas_risco = []
432
  for disc_data in disciplinas_dados:
 
445
  pdf.cell(0, 7, f'- {disc}:', 0, new_x=XPos.LMARGIN, new_y=YPos.NEXT, align='L')
446
  pdf.set_font('Helvetica', '', 10)
447
  for aviso in avisos:
448
+ pdf.cell(10) # Indentação
449
+ pdf.cell(0, 7, f'- {aviso}', 0, new_x=XPos.LMARGIN, new_y=YPos.NEXT, align='L')
 
450
  else:
451
  pdf.cell(0, 7, 'Nenhum problema identificado.', 0, new_x=XPos.LMARGIN, new_y=YPos.NEXT, align='L')
452
 
453
  # Rodapé
454
+ pdf.set_y(-30)
455
  pdf.line(10, pdf.get_y(), 200, pdf.get_y())
456
  pdf.ln(5)
457
  pdf.set_font('Helvetica', 'I', 8)
458
+ pdf.cell(0, 10, 'Este relatório é uma análise automática e deve ser validado junto à secretaria da escola.',
459
+ 0, new_x=XPos.LMARGIN, new_y=YPos.NEXT, align='C')
 
 
460
 
461
  # Salvar PDF
462
  temp_pdf = tempfile.NamedTemporaryFile(delete=False, suffix='.pdf')