Spaces:
Sleeping
Sleeping
Update app.py
Browse files
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
|
|
|
|
|
|
|
84 |
info_aluno = {}
|
85 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
86 |
|
87 |
-
|
|
|
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 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
|
|
|
|
|
|
|
|
136 |
|
137 |
-
|
138 |
-
|
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 |
-
#
|
148 |
-
for
|
149 |
-
df[
|
150 |
|
151 |
-
|
152 |
-
df = df[df['Disciplina'].apply(lambda x: x != '' and 'Disciplina' not in x)]
|
153 |
|
154 |
-
|
155 |
-
|
|
|
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 |
-
#
|
305 |
-
|
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 |
-
#
|
313 |
-
ax1.
|
314 |
-
|
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.
|
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
|
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
|
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)
|
354 |
ax2.grid(True, axis='y', alpha=0.3, linestyle='--')
|
355 |
|
356 |
# Melhorar a apresentação dos rótulos
|
357 |
-
ax2.
|
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
|
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
|
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.
|
386 |
-
"
|
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=
|
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.
|
411 |
-
pdf.
|
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 |
-
|
418 |
-
('nome', '
|
419 |
-
|
420 |
-
('
|
421 |
-
|
422 |
-
|
423 |
-
|
424 |
-
|
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.
|
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.
|
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
|
465 |
pdf.set_font('Helvetica', 'B', 12)
|
466 |
-
pdf.
|
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 |
-
#
|
476 |
pdf.set_font('Helvetica', 'B', 12)
|
477 |
-
pdf.
|
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(
|
500 |
-
pdf.cell(0,
|
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(-
|
507 |
pdf.line(10, pdf.get_y(), 200, pdf.get_y())
|
508 |
pdf.ln(5)
|
509 |
pdf.set_font('Helvetica', 'I', 8)
|
510 |
-
pdf.
|
511 |
-
|
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')
|