Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -433,7 +433,7 @@ def plotar_graficos_destacados(disciplinas_dados: List[Dict], temp_dir: str) ->
|
|
433 |
raise ValueError("Nenhuma disciplina válida encontrada no boletim.")
|
434 |
|
435 |
# Configuração do estilo
|
436 |
-
plt.style.use('seaborn')
|
437 |
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 10),
|
438 |
height_ratios=[1, 1])
|
439 |
plt.subplots_adjust(hspace=0.4)
|
@@ -459,129 +459,75 @@ def plotar_graficos_destacados(disciplinas_dados: List[Dict], temp_dir: str) ->
|
|
459 |
ax.spines['top'].set_visible(False)
|
460 |
ax.spines['right'].set_visible(False)
|
461 |
|
|
|
|
|
|
|
|
|
462 |
# Gráfico de notas
|
463 |
barras_notas = ax1.bar(disciplinas, medias_notas, color=cores_notas)
|
464 |
ax1.set_title('Média de Notas por Disciplina',
|
465 |
pad=20, fontsize=14, fontweight='bold')
|
466 |
ax1.set_ylim(0, ESCALA_MAXIMA_NOTAS)
|
467 |
-
ax1.set_xticklabels(disciplinas, rotation=45,
|
468 |
-
ha='right', va='top', fontsize=10)
|
469 |
ax1.set_ylabel('Notas', fontsize=12, labelpad=10)
|
470 |
|
471 |
# Linha de média mínima
|
472 |
-
ax1.axhline(y=LIMITE_APROVACAO_NOTA,
|
473 |
-
|
474 |
-
|
475 |
-
alpha=0.3,
|
476 |
-
linewidth=2)
|
477 |
-
ax1.text(0.02, LIMITE_APROVACAO_NOTA + 0.1,
|
478 |
-
'Média mínima (5,0)',
|
479 |
-
transform=ax1.get_yaxis_transform(),
|
480 |
-
color=COR_REPROVADO,
|
481 |
-
alpha=0.7,
|
482 |
-
fontsize=10)
|
483 |
|
484 |
# Valores nas barras de notas
|
485 |
for barra in barras_notas:
|
486 |
altura = barra.get_height()
|
487 |
cor_texto = 'white' if altura >= LIMITE_APROVACAO_NOTA else 'black'
|
488 |
ax1.text(barra.get_x() + barra.get_width()/2., altura,
|
489 |
-
|
490 |
-
|
491 |
-
|
492 |
-
fontsize=10,
|
493 |
-
bbox=dict(
|
494 |
-
facecolor='white',
|
495 |
-
edgecolor='none',
|
496 |
-
alpha=0.7,
|
497 |
-
pad=1
|
498 |
-
),
|
499 |
-
color=cor_texto if altura >= 8 else 'black')
|
500 |
|
501 |
# Gráfico de frequências
|
502 |
barras_freq = ax2.bar(disciplinas, medias_freq, color=cores_freq)
|
503 |
-
ax2.set_title('Frequência Média por Disciplina',
|
504 |
-
pad=20, fontsize=14, fontweight='bold')
|
505 |
ax2.set_ylim(0, 110)
|
506 |
-
ax2.set_xticklabels(disciplinas, rotation=45,
|
507 |
-
ha='right', va='top', fontsize=10)
|
508 |
ax2.set_ylabel('Frequência (%)', fontsize=12, labelpad=10)
|
509 |
|
510 |
# Linha de frequência mínima
|
511 |
-
ax2.axhline(y=LIMITE_APROVACAO_FREQ,
|
512 |
-
|
513 |
-
|
514 |
-
alpha=0.3,
|
515 |
-
linewidth=2)
|
516 |
-
ax2.text(0.02, LIMITE_APROVACAO_FREQ + 1,
|
517 |
-
'Frequência mínima (75%)',
|
518 |
-
transform=ax2.get_yaxis_transform(),
|
519 |
-
color=COR_REPROVADO,
|
520 |
-
alpha=0.7,
|
521 |
-
fontsize=10)
|
522 |
|
523 |
# Valores nas barras de frequência
|
524 |
for barra in barras_freq:
|
525 |
altura = barra.get_height()
|
526 |
cor_texto = 'white' if altura >= LIMITE_APROVACAO_FREQ else 'black'
|
527 |
ax2.text(barra.get_x() + barra.get_width()/2., altura,
|
528 |
-
|
529 |
-
|
530 |
-
|
531 |
-
fontsize=10,
|
532 |
-
bbox=dict(
|
533 |
-
facecolor='white',
|
534 |
-
edgecolor='none',
|
535 |
-
alpha=0.7,
|
536 |
-
pad=1
|
537 |
-
),
|
538 |
-
color=cor_texto if altura >= 90 else 'black')
|
539 |
|
540 |
# Título global com estilo
|
541 |
plt.suptitle(
|
542 |
f'Desempenho Geral\nMédia Global: {media_global:.1f} | Frequência Global: {freq_global:.1f}%',
|
543 |
-
y=0.98,
|
544 |
-
|
545 |
-
fontweight='bold',
|
546 |
-
bbox=dict(
|
547 |
-
facecolor='white',
|
548 |
-
edgecolor='none',
|
549 |
-
alpha=0.8,
|
550 |
-
pad=5,
|
551 |
-
boxstyle='round,pad=0.5'
|
552 |
-
)
|
553 |
)
|
554 |
|
555 |
# Aviso de reprovação estilizado
|
556 |
if freq_global < LIMITE_APROVACAO_FREQ:
|
557 |
-
plt.figtext(0.5, 0.02,
|
558 |
-
|
559 |
-
|
560 |
-
fontsize=12,
|
561 |
-
color=COR_REPROVADO,
|
562 |
-
weight='bold',
|
563 |
-
bbox=dict(
|
564 |
-
facecolor='#FFEBEE',
|
565 |
-
edgecolor=COR_REPROVADO,
|
566 |
-
alpha=0.9,
|
567 |
-
pad=5,
|
568 |
-
boxstyle='round,pad=0.5'
|
569 |
-
))
|
570 |
|
571 |
plt.tight_layout()
|
572 |
|
573 |
# Salvar com alta qualidade
|
574 |
plot_path = os.path.join(temp_dir, 'medias_frequencias.png')
|
575 |
-
plt.savefig(plot_path,
|
576 |
-
bbox_inches='tight',
|
577 |
-
dpi=300,
|
578 |
-
facecolor='white',
|
579 |
-
edgecolor='none')
|
580 |
plt.close()
|
581 |
|
582 |
return plot_path
|
583 |
|
584 |
-
|
585 |
# Funções de processamento do PDF e geração de relatórios
|
586 |
def gerar_relatorio_pdf(df: pd.DataFrame, disciplinas_dados: List[Dict],
|
587 |
grafico_basica: str, grafico_diversificada: str,
|
|
|
433 |
raise ValueError("Nenhuma disciplina válida encontrada no boletim.")
|
434 |
|
435 |
# Configuração do estilo
|
436 |
+
plt.style.use('seaborn-v0_8-darkgrid')
|
437 |
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 10),
|
438 |
height_ratios=[1, 1])
|
439 |
plt.subplots_adjust(hspace=0.4)
|
|
|
459 |
ax.spines['top'].set_visible(False)
|
460 |
ax.spines['right'].set_visible(False)
|
461 |
|
462 |
+
# Definir os ticks do eixo x para corresponder ao número de disciplinas
|
463 |
+
ax1.set_xticks(range(n_disciplinas))
|
464 |
+
ax2.set_xticks(range(n_disciplinas))
|
465 |
+
|
466 |
# Gráfico de notas
|
467 |
barras_notas = ax1.bar(disciplinas, medias_notas, color=cores_notas)
|
468 |
ax1.set_title('Média de Notas por Disciplina',
|
469 |
pad=20, fontsize=14, fontweight='bold')
|
470 |
ax1.set_ylim(0, ESCALA_MAXIMA_NOTAS)
|
471 |
+
ax1.set_xticklabels(disciplinas, rotation=45, ha='right', va='top', fontsize=10)
|
|
|
472 |
ax1.set_ylabel('Notas', fontsize=12, labelpad=10)
|
473 |
|
474 |
# Linha de média mínima
|
475 |
+
ax1.axhline(y=LIMITE_APROVACAO_NOTA, color=COR_REPROVADO, linestyle='--', alpha=0.3, linewidth=2)
|
476 |
+
ax1.text(0.02, LIMITE_APROVACAO_NOTA + 0.1, 'Média mínima (5,0)',
|
477 |
+
transform=ax1.get_yaxis_transform(), color=COR_REPROVADO, alpha=0.7, fontsize=10)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
478 |
|
479 |
# Valores nas barras de notas
|
480 |
for barra in barras_notas:
|
481 |
altura = barra.get_height()
|
482 |
cor_texto = 'white' if altura >= LIMITE_APROVACAO_NOTA else 'black'
|
483 |
ax1.text(barra.get_x() + barra.get_width()/2., altura,
|
484 |
+
f'{altura:.1f}', ha='center', va='bottom', fontsize=10,
|
485 |
+
bbox=dict(facecolor='white', edgecolor='none', alpha=0.7, pad=1),
|
486 |
+
color=cor_texto if altura >= 8 else 'black')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
487 |
|
488 |
# Gráfico de frequências
|
489 |
barras_freq = ax2.bar(disciplinas, medias_freq, color=cores_freq)
|
490 |
+
ax2.set_title('Frequência Média por Disciplina', pad=20, fontsize=14, fontweight='bold')
|
|
|
491 |
ax2.set_ylim(0, 110)
|
492 |
+
ax2.set_xticklabels(disciplinas, rotation=45, ha='right', va='top', fontsize=10)
|
|
|
493 |
ax2.set_ylabel('Frequência (%)', fontsize=12, labelpad=10)
|
494 |
|
495 |
# Linha de frequência mínima
|
496 |
+
ax2.axhline(y=LIMITE_APROVACAO_FREQ, color=COR_REPROVADO, linestyle='--', alpha=0.3, linewidth=2)
|
497 |
+
ax2.text(0.02, LIMITE_APROVACAO_FREQ + 1, 'Frequência mínima (75%)',
|
498 |
+
transform=ax2.get_yaxis_transform(), color=COR_REPROVADO, alpha=0.7, fontsize=10)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
499 |
|
500 |
# Valores nas barras de frequência
|
501 |
for barra in barras_freq:
|
502 |
altura = barra.get_height()
|
503 |
cor_texto = 'white' if altura >= LIMITE_APROVACAO_FREQ else 'black'
|
504 |
ax2.text(barra.get_x() + barra.get_width()/2., altura,
|
505 |
+
f'{altura:.1f}%', ha='center', va='bottom', fontsize=10,
|
506 |
+
bbox=dict(facecolor='white', edgecolor='none', alpha=0.7, pad=1),
|
507 |
+
color=cor_texto if altura >= 90 else 'black')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
508 |
|
509 |
# Título global com estilo
|
510 |
plt.suptitle(
|
511 |
f'Desempenho Geral\nMédia Global: {media_global:.1f} | Frequência Global: {freq_global:.1f}%',
|
512 |
+
y=0.98, fontsize=16, fontweight='bold',
|
513 |
+
bbox=dict(facecolor='white', edgecolor='none', alpha=0.8, pad=5, boxstyle='round,pad=0.5')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
514 |
)
|
515 |
|
516 |
# Aviso de reprovação estilizado
|
517 |
if freq_global < LIMITE_APROVACAO_FREQ:
|
518 |
+
plt.figtext(0.5, 0.02, "Atenção: Risco de Reprovação por Baixa Frequência",
|
519 |
+
ha="center", fontsize=12, color=COR_REPROVADO, weight='bold',
|
520 |
+
bbox=dict(facecolor='#FFEBEE', edgecolor=COR_REPROVADO, alpha=0.9, pad=5, boxstyle='round,pad=0.5'))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
521 |
|
522 |
plt.tight_layout()
|
523 |
|
524 |
# Salvar com alta qualidade
|
525 |
plot_path = os.path.join(temp_dir, 'medias_frequencias.png')
|
526 |
+
plt.savefig(plot_path, bbox_inches='tight', dpi=300, facecolor='white', edgecolor='none')
|
|
|
|
|
|
|
|
|
527 |
plt.close()
|
528 |
|
529 |
return plot_path
|
530 |
|
|
|
531 |
# Funções de processamento do PDF e geração de relatórios
|
532 |
def gerar_relatorio_pdf(df: pd.DataFrame, disciplinas_dados: List[Dict],
|
533 |
grafico_basica: str, grafico_diversificada: str,
|