histlearn commited on
Commit
0a9f35f
·
verified ·
1 Parent(s): 09333d4

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +72 -44
app.py CHANGED
@@ -272,68 +272,77 @@ def plotar_graficos_destacados(disciplinas_dados, temp_dir):
272
  if not n_disciplinas:
273
  raise ValueError("Nenhuma disciplina válida encontrada no boletim.")
274
 
275
- # Calcular tamanho da figura para A4 (proporção 1:√2)
276
- plt.figure(figsize=(11.69, 8.27)) # Tamanho A4 em polegadas
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
- cores_notas = ['red' if media < LIMITE_APROVACAO_NOTA else 'green'
283
- for media in medias_notas]
284
- cores_freq = ['red' if media < LIMITE_APROVACAO_FREQ else 'green'
285
- for media in medias_freq]
 
 
286
 
287
  media_global = np.mean(medias_notas)
288
  freq_global = np.mean(medias_freq)
289
 
290
  # Gráfico de notas
291
- plt.subplot(2, 1, 1)
292
- barras_notas = plt.bar(disciplinas, medias_notas, color=cores_notas)
293
- plt.title('Média de Notas por Disciplina', pad=20, fontsize=12, fontweight='bold')
294
- plt.xticks(rotation=45, ha='right', fontsize=8)
295
- plt.ylim(0, ESCALA_MAXIMA_NOTAS)
 
 
 
296
 
297
  # Adicionar linha de média mínima
298
- plt.axhline(y=LIMITE_APROVACAO_NOTA, color='r', linestyle='--', alpha=0.3)
299
- plt.text(0.02, LIMITE_APROVACAO_NOTA + 0.1, 'Média mínima',
300
- transform=plt.gca().get_yaxis_transform(), color='r', alpha=0.5)
301
 
302
  # Valores nas barras
303
  for barra in barras_notas:
304
  altura = barra.get_height()
305
- plt.text(barra.get_x() + barra.get_width()/2., altura,
306
  f'{altura:.1f}',
307
  ha='center', va='bottom', fontsize=8)
308
 
309
  # Gráfico de frequências
310
- plt.subplot(2, 1, 2)
311
- barras_freq = plt.bar(disciplinas, medias_freq, color=cores_freq)
312
- plt.title('Frequência Média por Disciplina', pad=20, fontsize=12, fontweight='bold')
313
- plt.xticks(rotation=45, ha='right', fontsize=8)
314
- plt.ylim(0, 110) # Deixar espaço para os valores acima das barras
 
 
 
315
 
316
  # Adicionar linha de frequência mínima
317
- plt.axhline(y=LIMITE_APROVACAO_FREQ, color='r', linestyle='--', alpha=0.3)
318
- plt.text(0.02, LIMITE_APROVACAO_FREQ + 1, 'Frequência mínima',
319
- transform=plt.gca().get_yaxis_transform(), color='r', alpha=0.5)
320
 
321
  # Valores nas barras
322
  for barra in barras_freq:
323
  altura = barra.get_height()
324
- plt.text(barra.get_x() + barra.get_width()/2., altura,
325
  f'{altura:.1f}%',
326
  ha='center', va='bottom', fontsize=8)
327
 
 
328
  plt.suptitle(
329
- f'Média Global: {media_global:.1f} | Frequência Global: {freq_global:.1f}%',
330
- y=0.95, fontsize=12, fontweight='bold'
331
  )
332
 
333
  if freq_global < LIMITE_APROVACAO_FREQ:
334
  plt.figtext(0.5, 0.02,
335
- "Atenção: Risco de Reprovação por Baixa Frequência",
336
- ha="center", fontsize=10, color="red")
337
 
338
  plt.tight_layout()
339
 
@@ -349,15 +358,20 @@ def gerar_relatorio_pdf(df, disciplinas_dados, grafico1_path, grafico2_path):
349
  pdf.add_page()
350
 
351
  # Cabeçalho
352
- pdf.set_font('Helvetica', 'B', 16)
353
  pdf.cell(0, 10, 'Relatório de Desempenho Escolar', 0, new_x=XPos.LMARGIN, new_y=YPos.NEXT, align='C')
354
- pdf.ln(5)
355
 
356
  # Informações do aluno
 
 
 
 
 
357
  pdf.set_font('Helvetica', '', 11)
358
  if hasattr(df, 'attrs'):
359
  if 'nome' in df.attrs:
360
- pdf.cell(0, 7, f'Aluno: {df.attrs["nome"]}', 0, new_x=XPos.LMARGIN, new_y=YPos.NEXT, align='L')
361
  if 'ra' in df.attrs:
362
  pdf.cell(0, 7, f'RA: {df.attrs["ra"]}', 0, new_x=XPos.LMARGIN, new_y=YPos.NEXT, align='L')
363
  if 'escola' in df.attrs:
@@ -365,24 +379,30 @@ def gerar_relatorio_pdf(df, disciplinas_dados, grafico1_path, grafico2_path):
365
  if 'turma' in df.attrs:
366
  pdf.cell(0, 7, f'Turma: {df.attrs["turma"]}', 0, new_x=XPos.LMARGIN, new_y=YPos.NEXT, align='L')
367
 
368
- pdf.ln(5)
369
 
370
  # Data do relatório
371
  data_atual = datetime.now().strftime('%d/%m/%Y')
372
  pdf.set_font('Helvetica', 'I', 10)
373
- pdf.cell(0, 5, f'Relatório gerado em: {data_atual}', 0, new_x=XPos.LMARGIN, new_y=YPos.NEXT, align='R')
 
 
 
 
 
 
374
  pdf.ln(10)
375
 
376
- # Gráficos
377
  pdf.image(grafico1_path, x=10, w=190)
378
- pdf.ln(10)
379
  pdf.image(grafico2_path, x=10, w=190)
380
- pdf.ln(10)
381
 
382
  # Seção de Análise
383
  pdf.set_font('Helvetica', 'B', 14)
384
- pdf.cell(0, 10, 'Análise de Desempenho', 0, new_x=XPos.LMARGIN, new_y=YPos.NEXT, align='L')
385
- pdf.ln(5)
 
386
 
387
  # Calcular médias globais
388
  medias_notas = [d['media_notas'] for d in disciplinas_dados]
@@ -391,15 +411,19 @@ def gerar_relatorio_pdf(df, disciplinas_dados, grafico1_path, grafico2_path):
391
  freq_global = np.mean(medias_freq)
392
 
393
  # Resumo geral
 
 
 
 
394
  pdf.set_font('Helvetica', '', 11)
395
  pdf.cell(0, 7, f'Média Global: {media_global:.1f}', 0, new_x=XPos.LMARGIN, new_y=YPos.NEXT, align='L')
396
  pdf.cell(0, 7, f'Frequência Global: {freq_global:.1f}%', 0, new_x=XPos.LMARGIN, new_y=YPos.NEXT, align='L')
397
- pdf.ln(5)
398
 
399
  # Avisos Importantes
400
  pdf.set_font('Helvetica', 'B', 12)
401
- pdf.cell(0, 10, 'Avisos Importantes:', 0, new_x=XPos.LMARGIN, new_y=YPos.NEXT, align='L')
402
- pdf.ln(2)
403
 
404
  pdf.set_font('Helvetica', '', 10)
405
 
@@ -417,15 +441,19 @@ def gerar_relatorio_pdf(df, disciplinas_dados, grafico1_path, grafico2_path):
417
 
418
  if disciplinas_risco:
419
  for disc, avisos in disciplinas_risco:
 
 
 
420
  for aviso in avisos:
421
- pdf.cell(0, 7, f'- {disc}: {aviso}', 0, new_x=XPos.LMARGIN, new_y=YPos.NEXT, align='L')
 
422
  else:
423
  pdf.cell(0, 7, 'Nenhum problema identificado.', 0, new_x=XPos.LMARGIN, new_y=YPos.NEXT, align='L')
424
 
425
- pdf.ln(5)
426
-
427
  # Rodapé
428
  pdf.set_y(-30)
 
 
429
  pdf.set_font('Helvetica', 'I', 8)
430
  pdf.cell(0, 10, 'Este relatório é uma análise automática e deve ser validado junto à secretaria da escola.',
431
  0, new_x=XPos.LMARGIN, new_y=YPos.NEXT, align='C')
 
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]
288
 
289
  media_global = np.mean(medias_notas)
290
  freq_global = np.mean(medias_freq)
291
 
292
  # Gráfico de notas
293
+ barras_notas = ax1.bar(disciplinas, medias_notas, color=cores_notas)
294
+ ax1.set_title('Média de Notas por Disciplina', pad=20, fontsize=12, fontweight='bold')
295
+ ax1.set_ylim(0, ESCALA_MAXIMA_NOTAS)
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(
338
+ f'Desempenho Geral\nMédia Global: {media_global:.1f} | Frequência Global: {freq_global:.1f}%',
339
+ y=0.98, fontsize=14, fontweight='bold'
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
 
 
358
  pdf.add_page()
359
 
360
  # Cabeçalho
361
+ pdf.set_font('Helvetica', 'B', 18)
362
  pdf.cell(0, 10, 'Relatório de Desempenho Escolar', 0, new_x=XPos.LMARGIN, new_y=YPos.NEXT, align='C')
363
+ pdf.ln(15) # Aumentar espaço após título
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:
 
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
 
384
  # Data do relatório
385
  data_atual = datetime.now().strftime('%d/%m/%Y')
386
  pdf.set_font('Helvetica', 'I', 10)
387
+ pdf.cell(0, 5, f'Data de geração: {data_atual}', 0, new_x=XPos.LMARGIN, new_y=YPos.NEXT, align='R')
388
+ pdf.ln(15)
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
 
 
396
  pdf.image(grafico1_path, x=10, w=190)
397
+ pdf.ln(15)
398
  pdf.image(grafico2_path, x=10, w=190)
399
+ pdf.ln(15)
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
 
407
  # Calcular médias globais
408
  medias_notas = [d['media_notas'] for d in disciplinas_dados]
 
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)
419
  pdf.cell(0, 7, f'Média Global: {media_global:.1f}', 0, new_x=XPos.LMARGIN, new_y=YPos.NEXT, align='L')
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
 
 
441
 
442
  if disciplinas_risco:
443
  for disc, avisos in disciplinas_risco:
444
+ pdf.set_font('Helvetica', 'B', 10)
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')