histlearn commited on
Commit
1a31ba8
·
verified ·
1 Parent(s): 404c2a9

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +115 -42
app.py CHANGED
@@ -9,14 +9,9 @@ import tempfile
9
  import os
10
  import matplotlib
11
  import shutil
 
12
  matplotlib.use('Agg')
13
 
14
- # Lista global de disciplinas básicas
15
- DISCIPLINAS_BASICAS = [
16
- 'LINGUA PORTUGUESA', 'ARTE', 'LINGUA ESTRANGEIRA INGLES',
17
- 'GEOGRAFIA', 'CIENCIAS', 'HISTORIA', 'MATEMATICA'
18
- ]
19
-
20
  def extrair_tabelas_pdf(pdf_path):
21
  """Extrai tabelas do PDF e retorna um DataFrame processado."""
22
  try:
@@ -59,28 +54,66 @@ def converter_nota(valor):
59
  except:
60
  return 0
61
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
  def plotar_evolucao_bimestres(df_filtrado, temp_dir):
63
  """Plota gráfico de evolução das notas por bimestre."""
64
- plt.figure(figsize=(12, 6))
 
 
65
 
66
- # Filtrar apenas disciplinas básicas
67
- df_filtrado = df_filtrado[df_filtrado['Disciplina'].isin(DISCIPLINAS_BASICAS)]
 
68
 
69
- estilos = {
70
- 'LINGUA PORTUGUESA': {'cor': '#DC143C', 'marcador': 'p', 'zorder': 1, 'linestyle': '-', 'desloc': 0.1},
71
- 'ARTE': {'cor': '#4169E1', 'marcador': 'D', 'zorder': 2, 'linestyle': '--', 'desloc': 0.08},
72
- 'LINGUA ESTRANGEIRA INGLES': {'cor': '#9370DB', 'marcador': 'h', 'zorder': 3, 'linestyle': '-.', 'desloc': 0.06},
73
- 'GEOGRAFIA': {'cor': '#32CD32', 'marcador': '^', 'zorder': 4, 'linestyle': ':', 'desloc': 0.04},
74
- 'CIENCIAS': {'cor': '#FF8C00', 'marcador': 's', 'zorder': 5, 'linestyle': '-', 'desloc': 0.02},
75
- 'HISTORIA': {'cor': '#00CED1', 'marcador': '*', 'zorder': 6, 'linestyle': '--', 'desloc': -0.02},
76
- 'MATEMATICA': {'cor': '#FF69B4', 'marcador': 'o', 'zorder': 7, 'linestyle': '-.', 'desloc': -0.04}
77
- }
78
 
79
  plt.grid(True, linestyle='--', alpha=0.3, zorder=0)
80
 
81
  colunas_notas = ['Nota B1', 'Nota B2', 'Nota B3', 'Nota B4']
82
 
83
- for disciplina in DISCIPLINAS_BASICAS:
84
  dados_disciplina = df_filtrado[df_filtrado['Disciplina'] == disciplina]
85
  if not dados_disciplina.empty:
86
  notas = dados_disciplina[colunas_notas].values[0]
@@ -90,28 +123,34 @@ def plotar_evolucao_bimestres(df_filtrado, temp_dir):
90
  bimestres = np.arange(1, len(colunas_notas) + 1)[notas_validas]
91
  notas_filtradas = notas[notas_validas]
92
 
93
- estilo = estilos[disciplina]
94
- notas_deslocadas = notas_filtradas + estilo['desloc']
95
-
96
- plt.plot(bimestres, notas_deslocadas,
97
- color=estilo['cor'],
98
- marker=estilo['marcador'],
99
- markersize=10,
100
- linewidth=2.5,
101
  label=disciplina,
102
- zorder=estilo['zorder'],
103
- linestyle=estilo['linestyle'],
104
  alpha=0.8)
105
 
106
  for x, y in zip(bimestres, notas_filtradas):
107
- plt.annotate(f"{y:.1f}", (x, y), textcoords="offset points", xytext=(0, 10), ha='center')
 
 
 
 
108
 
109
  plt.title('Evolução das Médias por Disciplina ao Longo dos Bimestres')
110
  plt.xlabel('Bimestres')
111
  plt.ylabel('Média de Notas')
112
  plt.xticks([1, 2, 3, 4], ['B1', 'B2', 'B3', 'B4'])
113
  plt.ylim(0, 10)
114
- plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left')
 
 
 
 
 
 
115
  plt.tight_layout()
116
 
117
  plot_path = os.path.join(temp_dir, 'evolucao_notas.png')
@@ -121,10 +160,15 @@ def plotar_evolucao_bimestres(df_filtrado, temp_dir):
121
 
122
  def plotar_graficos_destacados(df_boletim_clean, temp_dir):
123
  """Plota gráficos de médias e frequências com destaques."""
124
- plt.figure(figsize=(12, 6))
 
 
125
 
126
- # Filtrar apenas disciplinas básicas
127
- df_filtrado = df_boletim_clean[df_boletim_clean['Disciplina'].isin(DISCIPLINAS_BASICAS)]
 
 
 
128
  disciplinas = df_filtrado['Disciplina'].astype(str)
129
 
130
  # Processar frequências (remover % e converter para número)
@@ -140,21 +184,36 @@ def plotar_graficos_destacados(df_boletim_clean, temp_dir):
140
  frequencia_global_media = medias_frequencia.mean()
141
 
142
  plt.subplot(1, 2, 1)
143
- plt.bar(disciplinas, medias_notas, color=cores_notas)
144
  plt.title('Média de Notas por Disciplina (Vermelho: < 5)')
145
- plt.xticks(rotation=90)
146
  plt.ylim(0, 10)
147
 
 
 
 
 
 
 
 
148
  plt.subplot(1, 2, 2)
149
- plt.bar(disciplinas, medias_frequencia, color=cores_frequencias)
150
  plt.title('Média de Frequência por Disciplina (Vermelho: < 75%)')
151
- plt.xticks(rotation=90)
152
  plt.ylim(0, 100)
153
 
 
 
 
 
 
 
 
154
  plt.suptitle(f"Frequência Global Média: {frequencia_global_media:.2f}%")
155
 
156
  if frequencia_global_media < 75:
157
- plt.figtext(0.5, 0.01, "Cuidado: Risco de Reprovação por Baixa Frequência", ha="center", fontsize=12, color="red")
 
158
 
159
  plt.tight_layout()
160
 
@@ -172,6 +231,11 @@ def gerar_relatorio_pdf(df, grafico1_path, grafico2_path):
172
  pdf.cell(0, 10, 'Relatório de Desempenho Escolar', 0, new_x=XPos.LMARGIN, new_y=YPos.NEXT, align='C')
173
  pdf.ln(10)
174
 
 
 
 
 
 
175
  pdf.image(grafico1_path, x=10, w=190)
176
  pdf.ln(10)
177
  pdf.image(grafico2_path, x=10, w=190)
@@ -181,8 +245,9 @@ def gerar_relatorio_pdf(df, grafico1_path, grafico2_path):
181
  pdf.cell(0, 10, 'Avisos Importantes:', 0, new_x=XPos.LMARGIN, new_y=YPos.NEXT, align='L')
182
  pdf.set_font('Helvetica', '', 10)
183
 
184
- # Filtrar apenas disciplinas básicas
185
- df_filtrado = df[df['Disciplina'].isin(DISCIPLINAS_BASICAS)]
 
186
 
187
  # Calcular médias
188
  medias_notas = df_filtrado[['Nota B1', 'Nota B2', 'Nota B3', 'Nota B4']].apply(pd.to_numeric, errors='coerce').mean(axis=1)
@@ -192,6 +257,14 @@ def gerar_relatorio_pdf(df, grafico1_path, grafico2_path):
192
  freq_data = df_filtrado[colunas_freq].replace('%', '', regex=True)
193
  medias_freq = freq_data.apply(pd.to_numeric, errors='coerce').mean(axis=1)
194
 
 
 
 
 
 
 
 
 
195
  for idx, (disciplina, media_nota, media_freq) in enumerate(zip(df_filtrado['Disciplina'], medias_notas, medias_freq)):
196
  if media_nota < 5:
197
  pdf.cell(0, 10, f'- {disciplina}: Média de notas abaixo de 5 ({media_nota:.1f})', 0,
@@ -267,7 +340,7 @@ def processar_boletim(file):
267
  print("Gerando relatório PDF...")
268
  pdf_path = gerar_relatorio_pdf(df, grafico1_path, grafico2_path)
269
  print("Relatório PDF gerado")
270
-
271
  # Criar arquivo temporário para retorno
272
  output_file = tempfile.NamedTemporaryFile(delete=False, suffix='.pdf')
273
  output_path = output_file.name
 
9
  import os
10
  import matplotlib
11
  import shutil
12
+ import colorsys
13
  matplotlib.use('Agg')
14
 
 
 
 
 
 
 
15
  def extrair_tabelas_pdf(pdf_path):
16
  """Extrai tabelas do PDF e retorna um DataFrame processado."""
17
  try:
 
54
  except:
55
  return 0
56
 
57
+ def obter_disciplinas_validas(df):
58
+ """Identifica disciplinas válidas no boletim."""
59
+ # Colunas de notas e frequências
60
+ colunas_notas = ['Nota B1', 'Nota B2', 'Nota B3', 'Nota B4']
61
+ colunas_freq = ['%Freq B1', '%Freq B2', '%Freq B3', '%Freq B4']
62
+
63
+ # Converter notas para numérico
64
+ for col in colunas_notas:
65
+ if col in df.columns:
66
+ df[col] = df[col].apply(converter_nota)
67
+
68
+ # Identificar disciplinas que têm pelo menos uma nota ou frequência
69
+ disciplinas_validas = []
70
+ for _, row in df.iterrows():
71
+ disciplina = row['Disciplina']
72
+ notas = row[colunas_notas].astype(float)
73
+ freq = row[colunas_freq].replace('%', '', regex=True).astype(float)
74
+
75
+ if (notas > 0).any() or (freq > 0).any():
76
+ disciplinas_validas.append(disciplina)
77
+
78
+ return disciplinas_validas
79
+
80
+ def gerar_paleta_cores(n_cores):
81
+ """Gera uma paleta de cores distintas para o número de disciplinas."""
82
+ cores_base = [
83
+ '#DC143C', '#4169E1', '#9370DB', '#32CD32', '#FF8C00',
84
+ '#00CED1', '#FF69B4', '#8B4513', '#4B0082', '#556B2F',
85
+ '#B8860B', '#483D8B', '#008B8B', '#8B008B', '#8B0000'
86
+ ]
87
+
88
+ # Se precisar de mais cores, gerar automaticamente
89
+ if n_cores > len(cores_base):
90
+ HSV_tuples = [(x/n_cores, 0.8, 0.9) for x in range(n_cores)]
91
+ cores_extras = ['#%02x%02x%02x' % tuple(int(x*255) for x in colorsys.hsv_to_rgb(*hsv))
92
+ for hsv in HSV_tuples]
93
+ return cores_extras
94
+
95
+ return cores_base[:n_cores]
96
+
97
  def plotar_evolucao_bimestres(df_filtrado, temp_dir):
98
  """Plota gráfico de evolução das notas por bimestre."""
99
+ # Obter disciplinas válidas
100
+ disciplinas_validas = obter_disciplinas_validas(df_filtrado)
101
+ n_disciplinas = len(disciplinas_validas)
102
 
103
+ # Calcular tamanho da figura baseado no número de disciplinas
104
+ altura_figura = max(6, n_disciplinas * 0.4)
105
+ plt.figure(figsize=(14, altura_figura))
106
 
107
+ # Gerar cores para as disciplinas
108
+ cores = gerar_paleta_cores(n_disciplinas)
109
+ marcadores = ['o', 's', '^', 'D', 'v', '<', '>', 'p', 'h', '8', '*', 'H', '+', 'x', 'd']
110
+ estilos_linha = ['-', '--', '-.', ':', '-', '--', '-.', ':', '-', '--', '-.', ':', '-', '--', '-.']
 
 
 
 
 
111
 
112
  plt.grid(True, linestyle='--', alpha=0.3, zorder=0)
113
 
114
  colunas_notas = ['Nota B1', 'Nota B2', 'Nota B3', 'Nota B4']
115
 
116
+ for idx, disciplina in enumerate(disciplinas_validas):
117
  dados_disciplina = df_filtrado[df_filtrado['Disciplina'] == disciplina]
118
  if not dados_disciplina.empty:
119
  notas = dados_disciplina[colunas_notas].values[0]
 
123
  bimestres = np.arange(1, len(colunas_notas) + 1)[notas_validas]
124
  notas_filtradas = notas[notas_validas]
125
 
126
+ plt.plot(bimestres, notas_filtradas,
127
+ color=cores[idx % len(cores)],
128
+ marker=marcadores[idx % len(marcadores)],
129
+ markersize=8,
130
+ linewidth=2,
 
 
 
131
  label=disciplina,
132
+ linestyle=estilos_linha[idx % len(estilos_linha)],
 
133
  alpha=0.8)
134
 
135
  for x, y in zip(bimestres, notas_filtradas):
136
+ plt.annotate(f"{y:.1f}", (x, y),
137
+ textcoords="offset points",
138
+ xytext=(0, 5),
139
+ ha='center',
140
+ fontsize=8)
141
 
142
  plt.title('Evolução das Médias por Disciplina ao Longo dos Bimestres')
143
  plt.xlabel('Bimestres')
144
  plt.ylabel('Média de Notas')
145
  plt.xticks([1, 2, 3, 4], ['B1', 'B2', 'B3', 'B4'])
146
  plt.ylim(0, 10)
147
+
148
+ # Ajustar legenda baseado no número de disciplinas
149
+ if n_disciplinas > 10:
150
+ plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left', fontsize=8)
151
+ else:
152
+ plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left')
153
+
154
  plt.tight_layout()
155
 
156
  plot_path = os.path.join(temp_dir, 'evolucao_notas.png')
 
160
 
161
  def plotar_graficos_destacados(df_boletim_clean, temp_dir):
162
  """Plota gráficos de médias e frequências com destaques."""
163
+ # Obter disciplinas válidas
164
+ disciplinas_validas = obter_disciplinas_validas(df_boletim_clean)
165
+ n_disciplinas = len(disciplinas_validas)
166
 
167
+ # Calcular tamanho da figura baseado no número de disciplinas
168
+ altura_figura = max(6, n_disciplinas * 0.4)
169
+ plt.figure(figsize=(14, altura_figura))
170
+
171
+ df_filtrado = df_boletim_clean[df_boletim_clean['Disciplina'].isin(disciplinas_validas)]
172
  disciplinas = df_filtrado['Disciplina'].astype(str)
173
 
174
  # Processar frequências (remover % e converter para número)
 
184
  frequencia_global_media = medias_frequencia.mean()
185
 
186
  plt.subplot(1, 2, 1)
187
+ barras_notas = plt.bar(disciplinas, medias_notas, color=cores_notas)
188
  plt.title('Média de Notas por Disciplina (Vermelho: < 5)')
189
+ plt.xticks(rotation=45, ha='right')
190
  plt.ylim(0, 10)
191
 
192
+ # Adicionar valores nas barras
193
+ for barra in barras_notas:
194
+ altura = barra.get_height()
195
+ plt.text(barra.get_x() + barra.get_width()/2., altura,
196
+ f'{altura:.1f}',
197
+ ha='center', va='bottom')
198
+
199
  plt.subplot(1, 2, 2)
200
+ barras_freq = plt.bar(disciplinas, medias_frequencia, color=cores_frequencias)
201
  plt.title('Média de Frequência por Disciplina (Vermelho: < 75%)')
202
+ plt.xticks(rotation=45, ha='right')
203
  plt.ylim(0, 100)
204
 
205
+ # Adicionar valores nas barras
206
+ for barra in barras_freq:
207
+ altura = barra.get_height()
208
+ plt.text(barra.get_x() + barra.get_width()/2., altura,
209
+ f'{altura:.1f}%',
210
+ ha='center', va='bottom')
211
+
212
  plt.suptitle(f"Frequência Global Média: {frequencia_global_media:.2f}%")
213
 
214
  if frequencia_global_media < 75:
215
+ plt.figtext(0.5, 0.02, "Cuidado: Risco de Reprovação por Baixa Frequência",
216
+ ha="center", fontsize=12, color="red")
217
 
218
  plt.tight_layout()
219
 
 
231
  pdf.cell(0, 10, 'Relatório de Desempenho Escolar', 0, new_x=XPos.LMARGIN, new_y=YPos.NEXT, align='C')
232
  pdf.ln(10)
233
 
234
+ # Informações do aluno se disponíveis
235
+ if 'Nome do Aluno' in df.columns:
236
+ pdf.set_font('Helvetica', '', 12)
237
+ pdf.cell(0, 10, f'Aluno: {df["Nome do Aluno"].iloc[0]}', 0, new_x=XPos.LMARGIN, new_y=YPos.NEXT, align='L')
238
+
239
  pdf.image(grafico1_path, x=10, w=190)
240
  pdf.ln(10)
241
  pdf.image(grafico2_path, x=10, w=190)
 
245
  pdf.cell(0, 10, 'Avisos Importantes:', 0, new_x=XPos.LMARGIN, new_y=YPos.NEXT, align='L')
246
  pdf.set_font('Helvetica', '', 10)
247
 
248
+ # Obter disciplinas válidas
249
+ disciplinas_validas = obter_disciplinas_validas(df)
250
+ df_filtrado = df[df['Disciplina'].isin(disciplinas_validas)]
251
 
252
  # Calcular médias
253
  medias_notas = df_filtrado[['Nota B1', 'Nota B2', 'Nota B3', 'Nota B4']].apply(pd.to_numeric, errors='coerce').mean(axis=1)
 
257
  freq_data = df_filtrado[colunas_freq].replace('%', '', regex=True)
258
  medias_freq = freq_data.apply(pd.to_numeric, errors='coerce').mean(axis=1)
259
 
260
+ # Adicionar média global
261
+ media_global = medias_notas.mean()
262
+ freq_global = medias_freq.mean()
263
+
264
+ pdf.cell(0, 10, f'Média Global: {media_global:.1f}', 0, new_x=XPos.LMARGIN, new_y=YPos.NEXT, align='L')
265
+ pdf.cell(0, 10, f'Frequência Global: {freq_global:.1f}%', 0, new_x=XPos.LMARGIN, new_y=YPos.NEXT, align='L')
266
+ pdf.ln(5)
267
+
268
  for idx, (disciplina, media_nota, media_freq) in enumerate(zip(df_filtrado['Disciplina'], medias_notas, medias_freq)):
269
  if media_nota < 5:
270
  pdf.cell(0, 10, f'- {disciplina}: Média de notas abaixo de 5 ({media_nota:.1f})', 0,
 
340
  print("Gerando relatório PDF...")
341
  pdf_path = gerar_relatorio_pdf(df, grafico1_path, grafico2_path)
342
  print("Relatório PDF gerado")
343
+
344
  # Criar arquivo temporário para retorno
345
  output_file = tempfile.NamedTemporaryFile(delete=False, suffix='.pdf')
346
  output_path = output_file.name