Spaces:
Running
Running
import gradio as gr | |
import camelot | |
import pandas as pd | |
import matplotlib.pyplot as plt | |
import numpy as np | |
from fpdf import FPDF | |
import tempfile | |
import os | |
import matplotlib | |
matplotlib.use('Agg') # Usar backend não-interativo | |
def converter_nota(valor): | |
if pd.isna(valor) or valor == '-' or valor == 'N': | |
return 0 | |
try: | |
return float(valor) | |
except: | |
return 0 | |
def plotar_evolucao_bimestres(df_filtrado, temp_dir): | |
plt.figure(figsize=(12, 6)) | |
disciplinas_basicas = ['LINGUA PORTUGUESA', 'ARTE', 'LINGUA ESTRANGEIRA INGLES', | |
'GEOGRAFIA', 'CIENCIAS', 'HISTORIA', 'MATEMATICA'] | |
estilos = { | |
'LINGUA PORTUGUESA': {'cor': '#DC143C', 'marcador': 'p', 'zorder': 1, 'linestyle': '-', 'desloc': 0.1}, | |
'ARTE': {'cor': '#4169E1', 'marcador': 'D', 'zorder': 2, 'linestyle': '--', 'desloc': 0.08}, | |
'LINGUA ESTRANGEIRA INGLES': {'cor': '#9370DB', 'marcador': 'h', 'zorder': 3, 'linestyle': '-.', 'desloc': 0.06}, | |
'GEOGRAFIA': {'cor': '#32CD32', 'marcador': '^', 'zorder': 4, 'linestyle': ':', 'desloc': 0.04}, | |
'CIENCIAS': {'cor': '#FF8C00', 'marcador': 's', 'zorder': 5, 'linestyle': '-', 'desloc': 0.02}, | |
'HISTORIA': {'cor': '#00CED1', 'marcador': '*', 'zorder': 6, 'linestyle': '--', 'desloc': -0.02}, | |
'MATEMATICA': {'cor': '#FF69B4', 'marcador': 'o', 'zorder': 7, 'linestyle': '-.', 'desloc': -0.04} | |
} | |
plt.grid(True, linestyle='--', alpha=0.3, zorder=0) | |
colunas_notas = ['Nota B1', 'Nota B2', 'Nota B3', 'Nota B4'] | |
for disciplina in disciplinas_basicas: | |
dados_disciplina = df_filtrado[df_filtrado['Disciplina'] == disciplina] | |
if not dados_disciplina.empty: | |
notas = dados_disciplina[colunas_notas].values[0] | |
notas_validas = notas > 0 | |
if any(notas_validas): | |
bimestres = np.arange(1, len(colunas_notas) + 1)[notas_validas] | |
notas_filtradas = notas[notas_validas] | |
estilo = estilos[disciplina] | |
notas_deslocadas = notas_filtradas + estilo['desloc'] | |
plt.plot(bimestres, notas_deslocadas, | |
color=estilo['cor'], | |
marker=estilo['marcador'], | |
markersize=10, | |
linewidth=2.5, | |
label=disciplina, | |
zorder=estilo['zorder'], | |
linestyle=estilo['linestyle'], | |
alpha=0.8) | |
for x, y in zip(bimestres, notas_filtradas): | |
plt.annotate(str(y), (x, y), textcoords="offset points", xytext=(0, 10), ha='center') | |
plt.title('Evolução das Médias por Disciplina ao Longo dos Bimestres') | |
plt.xlabel('Bimestres') | |
plt.ylabel('Média de Notas') | |
plt.xticks([1, 2, 3, 4], ['B1', 'B2', 'B3', 'B4']) | |
plt.ylim(0, 10) | |
plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left') | |
plt.tight_layout() | |
plot_path = os.path.join(temp_dir, 'evolucao_notas.png') | |
plt.savefig(plot_path, bbox_inches='tight', dpi=300) | |
plt.close() | |
return plot_path | |
def plotar_graficos_destacados(df_boletim_clean, temp_dir): | |
plt.figure(figsize=(12, 6)) | |
disciplinas = df_boletim_clean['Disciplina'].astype(str) | |
medias_frequencia = df_boletim_clean[['Freq B1', 'Freq B2', 'Freq B3', 'Freq B4']].apply(pd.to_numeric, errors='coerce').mean(axis=1) | |
medias_notas = df_boletim_clean[['Nota B1', 'Nota B2', 'Nota B3', 'Nota B4']].apply(pd.to_numeric, errors='coerce').mean(axis=1) | |
cores_notas = ['red' if media < 5 else 'blue' for media in medias_notas] | |
cores_frequencias = ['red' if media < 75 else 'green' for media in medias_frequencia] | |
frequencia_global_media = medias_frequencia.mean() | |
plt.subplot(1, 2, 1) | |
plt.bar(disciplinas, medias_notas, color=cores_notas) | |
plt.title('Média de Notas por Disciplina (Vermelho: < 5)') | |
plt.xticks(rotation=90) | |
plt.ylim(0, 10) | |
plt.subplot(1, 2, 2) | |
plt.bar(disciplinas, medias_frequencia, color=cores_frequencias) | |
plt.title('Média de Frequência por Disciplina (Vermelho: < 75%)') | |
plt.xticks(rotation=90) | |
plt.ylim(0, 100) | |
plt.suptitle(f"Frequência Global Média: {frequencia_global_media:.2f}%") | |
if frequencia_global_media < 75: | |
plt.figtext(0.5, 0.01, "Cuidado: Risco de Reprovação por Baixa Frequência", ha="center", fontsize=12, color="red") | |
plt.tight_layout() | |
plot_path = os.path.join(temp_dir, 'medias_frequencias.png') | |
plt.savefig(plot_path, bbox_inches='tight', dpi=300) | |
plt.close() | |
return plot_path | |
def gerar_relatorio_pdf(df, grafico1_path, grafico2_path): | |
pdf = FPDF() | |
pdf.add_page() | |
pdf.set_font('Arial', 'B', 16) | |
pdf.cell(0, 10, 'Relatório de Desempenho Escolar', 0, 1, 'C') | |
pdf.ln(10) | |
pdf.image(grafico1_path, x=10, w=190) | |
pdf.ln(10) | |
pdf.image(grafico2_path, x=10, w=190) | |
pdf.ln(10) | |
pdf.set_font('Arial', 'B', 12) | |
pdf.cell(0, 10, 'Avisos Importantes:', 0, 1, 'L') | |
pdf.set_font('Arial', '', 10) | |
medias_notas = df[['Nota B1', 'Nota B2', 'Nota B3', 'Nota B4']].apply(pd.to_numeric, errors='coerce').mean(axis=1) | |
medias_freq = df[['Freq B1', 'Freq B2', 'Freq B3', 'Freq B4']].apply(pd.to_numeric, errors='coerce').mean(axis=1) | |
for idx, (disciplina, media_nota, media_freq) in enumerate(zip(df['Disciplina'], medias_notas, medias_freq)): | |
if media_nota < 5: | |
pdf.cell(0, 10, f'- {disciplina}: Média de notas abaixo de 5 ({media_nota:.1f})', 0, 1, 'L') | |
if media_freq < 75: | |
pdf.cell(0, 10, f'- {disciplina}: Frequência abaixo de 75% ({media_freq:.1f}%)', 0, 1, 'L') | |
temp_pdf = tempfile.NamedTemporaryFile(delete=False, suffix='.pdf') | |
pdf_path = temp_pdf.name | |
pdf.output(pdf_path) | |
return pdf_path | |
def processar_boletim(pdf_file): | |
try: | |
temp_dir = tempfile.mkdtemp() | |
# Ler o arquivo temporário e salvar seu conteúdo | |
temp_pdf = os.path.join(temp_dir, 'boletim.pdf') | |
with open(temp_pdf, 'wb') as f: | |
f.write(pdf_file.read()) # Usar .read() para obter o conteúdo do arquivo | |
tables = camelot.read_pdf(temp_pdf, pages='all', flavor='lattice') | |
if len(tables) == 0: | |
return None, "Nenhuma tabela encontrada no PDF." | |
df = tables[0].df | |
df.columns = ['Disciplina', 'Nota B1', 'Freq B1', '%Freq B1', 'AC B1', | |
'Nota B2', 'Freq B2', '%Freq B2', 'AC B2', | |
'Nota B3', 'Freq B3', '%Freq B3', 'AC B3', | |
'Nota B4', 'Freq B4', '%Freq B4', 'AC B4', | |
'CF', 'Nota Final', 'Freq Final', 'AC Final'] | |
colunas_notas = ['Nota B1', 'Nota B2', 'Nota B3', 'Nota B4'] | |
for col in colunas_notas: | |
df[col] = df[col].apply(converter_nota) | |
grafico1_path = plotar_evolucao_bimestres(df, temp_dir) | |
grafico2_path = plotar_graficos_destacados(df, temp_dir) | |
pdf_path = gerar_relatorio_pdf(df, grafico1_path, grafico2_path) | |
with open(pdf_path, 'rb') as f: | |
pdf_content = f.read() | |
# Limpar arquivos temporários | |
os.remove(pdf_path) | |
for file in os.listdir(temp_dir): | |
os.remove(os.path.join(temp_dir, file)) | |
os.rmdir(temp_dir) | |
return pdf_content, "Relatório gerado com sucesso!" | |
except Exception as e: | |
if 'temp_dir' in locals(): | |
for file in os.listdir(temp_dir): | |
os.remove(os.path.join(temp_dir, file)) | |
os.rmdir(temp_dir) | |
return None, f"Erro ao processar o boletim: {str(e)}" | |
iface = gr.Interface( | |
fn=processar_boletim, | |
inputs=gr.File(label="Upload do Boletim (PDF)"), | |
outputs=[ | |
gr.File(label="Relatório (PDF)"), | |
gr.Textbox(label="Status") | |
], | |
title="Análise de Boletim Escolar", | |
description="Faça upload do boletim em PDF para gerar um relatório com análises e visualizações.", | |
allow_flagging="never" | |
) | |
if __name__ == "__main__": | |
iface.launch(server_name="0.0.0.0") |