histlearn commited on
Commit
5c63b41
·
verified ·
1 Parent(s): f715cf9

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +155 -54
app.py CHANGED
@@ -276,45 +276,95 @@ class ReportGenerator:
276
  plt.grid(True, alpha=0.2, linestyle='--')
277
  plt.gca().set_facecolor('#f8f9fa')
278
 
279
- texts = [] # Lista para armazenar todos os textos
 
280
 
281
- # Scatter plot com cores por nível
282
  for nivel, color in self.colors.items():
283
  mask = self.data['Nível'] == nivel
284
  tempo = pd.to_timedelta(self.data[mask]['Total Tempo']).dt.total_seconds() / 60
285
  acertos = self.data[mask]['Acertos Absolutos']
286
 
287
- # Criar scatter plot
288
- plt.scatter(tempo, acertos, c=color, label=nivel, alpha=0.7, s=150)
289
 
290
- # Adicionar textos à lista
291
  for x, y, nome in zip(tempo, acertos, self.data[mask]['Nome do Aluno']):
292
- texts.append(plt.text(x, y, nome, fontsize=8,
293
- bbox=dict(facecolor='white', edgecolor='none', alpha=0.8, pad=0.5)))
294
-
295
- # Ajustar posição dos textos automaticamente
296
- adjust_text(texts,
297
- force_points=0.2, # Força entre pontos
298
- force_text=0.2, # Força entre textos
299
- expand_points=(1.2, 1.2), # Expansão dos pontos
300
- expand_text=(1.2, 1.2), # Expansão dos textos
301
- arrowprops=dict(arrowstyle='-', color='gray', alpha=0.3, lw=0.5))
302
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
303
  plt.title('Relação entre Tempo e Acertos por Nível', pad=20)
304
  plt.xlabel('Tempo Total (minutos)')
305
  plt.ylabel('Número de Acertos')
306
 
307
  # Melhorar posição e aparência da legenda
308
- plt.legend(bbox_to_anchor=(1.05, 1),
309
- loc='upper left',
310
- borderaxespad=0,
311
- frameon=True,
312
- facecolor='white',
313
- edgecolor='none')
 
 
314
 
315
- # Ajustar margens para acomodar a legenda
316
  plt.tight_layout()
317
-
318
  return plt.gcf()
319
 
320
  def create_tasks_performance_plot(self) -> plt.Figure:
@@ -325,55 +375,106 @@ class ReportGenerator:
325
  plt.grid(True, alpha=0.2, linestyle='--')
326
  plt.gca().set_facecolor('#f8f9fa')
327
 
328
- texts = [] # Lista para armazenar todos os textos
 
329
 
330
- # Scatter plot com cores por nível
331
  for nivel, color in self.colors.items():
332
  mask = self.data['Nível'] == nivel
333
  tarefas = self.data[mask]['Tarefas Completadas']
334
  acertos = self.data[mask]['Acertos Absolutos']
335
 
336
- # Criar scatter plot
337
  plt.scatter(tarefas, acertos, c=color, label=nivel, alpha=0.7, s=150)
338
 
339
- # Adicionar textos à lista
340
  for x, y, nome in zip(tarefas, acertos, self.data[mask]['Nome do Aluno']):
341
- texts.append(plt.text(x, y, nome, fontsize=8,
342
- bbox=dict(facecolor='white', edgecolor='none', alpha=0.8, pad=0.5)))
343
-
344
- # Linha de tendência
 
 
345
  z = np.polyfit(self.data['Tarefas Completadas'],
346
  self.data['Acertos Absolutos'], 1)
347
  p = np.poly1d(z)
348
- x_range = np.linspace(self.data['Tarefas Completadas'].min(),
349
- self.data['Tarefas Completadas'].max(), 100)
350
-
 
 
351
  plt.plot(x_range, p(x_range), "--", color='#e74c3c', alpha=0.8,
352
- label='Tendência', linewidth=2)
353
-
354
- # Ajustar posição dos textos automaticamente
355
- adjust_text(texts,
356
- force_points=0.2,
357
- force_text=0.2,
358
- expand_points=(1.2, 1.2),
359
- expand_text=(1.2, 1.2),
360
- arrowprops=dict(arrowstyle='-', color='gray', alpha=0.3, lw=0.5))
361
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
362
  plt.title('Relação entre Tarefas Completadas e Acertos', pad=20)
363
  plt.xlabel('Número de Tarefas Completadas')
364
  plt.ylabel('Número de Acertos')
365
 
366
- # Melhorar posição e aparência da legenda
367
- plt.legend(bbox_to_anchor=(1.05, 1),
368
- loc='upper left',
369
- borderaxespad=0,
370
- frameon=True,
371
- facecolor='white',
372
- edgecolor='none')
 
373
 
374
- # Ajustar margens para acomodar a legenda
375
  plt.tight_layout()
376
-
377
  return plt.gcf()
378
 
379
  def generate_graphs(self) -> List[plt.Figure]:
 
276
  plt.grid(True, alpha=0.2, linestyle='--')
277
  plt.gca().set_facecolor('#f8f9fa')
278
 
279
+ # Criar dicionário para armazenar pontos próximos
280
+ proximity_groups = {}
281
 
282
+ # Primeiro, plotar os pontos
283
  for nivel, color in self.colors.items():
284
  mask = self.data['Nível'] == nivel
285
  tempo = pd.to_timedelta(self.data[mask]['Total Tempo']).dt.total_seconds() / 60
286
  acertos = self.data[mask]['Acertos Absolutos']
287
 
288
+ scatter = plt.scatter(tempo, acertos, c=color, label=nivel, alpha=0.7, s=150)
 
289
 
290
+ # Agrupar pontos próximos
291
  for x, y, nome in zip(tempo, acertos, self.data[mask]['Nome do Aluno']):
292
+ key = (round(x, 1), round(y, 1)) # Arredondar para agrupar pontos próximos
293
+ if key not in proximity_groups:
294
+ proximity_groups[key] = []
295
+ proximity_groups[key].append((x, y, nome))
296
+
297
+ # Adicionar anotações com tratamento especial para pontos próximos
298
+ for group in proximity_groups.values():
299
+ if len(group) == 1:
300
+ # Ponto único - anotação normal
301
+ x, y, nome = group[0]
302
+ plt.annotate(
303
+ nome.split()[0], # Usar apenas primeiro nome
304
+ (x, y),
305
+ xytext=(5, 5),
306
+ textcoords='offset points',
307
+ bbox=dict(
308
+ facecolor='white',
309
+ edgecolor='none',
310
+ alpha=0.8,
311
+ pad=0.5
312
+ ),
313
+ fontsize=8,
314
+ arrowprops=dict(
315
+ arrowstyle='-',
316
+ color='gray',
317
+ alpha=0.3,
318
+ connectionstyle='arc3,rad=0.3'
319
+ )
320
+ )
321
+ else:
322
+ # Múltiplos pontos próximos - criar lista vertical
323
+ x_base = sum(p[0] for p in group) / len(group)
324
+ y_base = sum(p[1] for p in group) / len(group)
325
+ nomes = [p[2].split()[0] for p in group]
326
+
327
+ # Calcular posição do texto para evitar sobreposição
328
+ x_text = x_base + 10
329
+ y_text = y_base
330
+
331
+ # Criar caixa com lista de nomes
332
+ nome_text = '\n'.join(nomes)
333
+ plt.annotate(
334
+ nome_text,
335
+ (x_base, y_base),
336
+ xytext=(x_text, y_text),
337
+ bbox=dict(
338
+ facecolor='white',
339
+ edgecolor='lightgray',
340
+ alpha=0.9,
341
+ pad=0.5,
342
+ boxstyle='round,pad=0.5'
343
+ ),
344
+ fontsize=8,
345
+ arrowprops=dict(
346
+ arrowstyle='->',
347
+ color='gray',
348
+ alpha=0.5,
349
+ connectionstyle='arc3,rad=0.2'
350
+ )
351
+ )
352
+
353
  plt.title('Relação entre Tempo e Acertos por Nível', pad=20)
354
  plt.xlabel('Tempo Total (minutos)')
355
  plt.ylabel('Número de Acertos')
356
 
357
  # Melhorar posição e aparência da legenda
358
+ plt.legend(
359
+ bbox_to_anchor=(1.05, 1),
360
+ loc='upper left',
361
+ borderaxespad=0,
362
+ frameon=True,
363
+ facecolor='white',
364
+ edgecolor='none'
365
+ )
366
 
 
367
  plt.tight_layout()
 
368
  return plt.gcf()
369
 
370
  def create_tasks_performance_plot(self) -> plt.Figure:
 
375
  plt.grid(True, alpha=0.2, linestyle='--')
376
  plt.gca().set_facecolor('#f8f9fa')
377
 
378
+ # Dicionário para armazenar pontos próximos
379
+ proximity_groups = {}
380
 
381
+ # Primeiro, plotar os pontos e a linha de tendência
382
  for nivel, color in self.colors.items():
383
  mask = self.data['Nível'] == nivel
384
  tarefas = self.data[mask]['Tarefas Completadas']
385
  acertos = self.data[mask]['Acertos Absolutos']
386
 
 
387
  plt.scatter(tarefas, acertos, c=color, label=nivel, alpha=0.7, s=150)
388
 
389
+ # Agrupar pontos próximos
390
  for x, y, nome in zip(tarefas, acertos, self.data[mask]['Nome do Aluno']):
391
+ key = (x, y) # Usar valores exatos pois tarefas são discretas
392
+ if key not in proximity_groups:
393
+ proximity_groups[key] = []
394
+ proximity_groups[key].append((x, y, nome))
395
+
396
+ # Adicionar linha de tendência
397
  z = np.polyfit(self.data['Tarefas Completadas'],
398
  self.data['Acertos Absolutos'], 1)
399
  p = np.poly1d(z)
400
+ x_range = np.linspace(
401
+ self.data['Tarefas Completadas'].min(),
402
+ self.data['Tarefas Completadas'].max(),
403
+ 100
404
+ )
405
  plt.plot(x_range, p(x_range), "--", color='#e74c3c', alpha=0.8,
406
+ label='Tendência', linewidth=2)
407
+
408
+ # Adicionar anotações com tratamento especial para pontos próximos
409
+ for group in proximity_groups.values():
410
+ if len(group) == 1:
411
+ # Ponto único - anotação normal
412
+ x, y, nome = group[0]
413
+ plt.annotate(
414
+ nome.split()[0], # Usar apenas primeiro nome
415
+ (x, y),
416
+ xytext=(5, 5),
417
+ textcoords='offset points',
418
+ bbox=dict(
419
+ facecolor='white',
420
+ edgecolor='none',
421
+ alpha=0.8,
422
+ pad=0.5
423
+ ),
424
+ fontsize=8,
425
+ arrowprops=dict(
426
+ arrowstyle='-',
427
+ color='gray',
428
+ alpha=0.3,
429
+ connectionstyle='arc3,rad=0.3'
430
+ )
431
+ )
432
+ else:
433
+ # Múltiplos pontos próximos - criar caixa com lista
434
+ x_base = sum(p[0] for p in group) / len(group)
435
+ y_base = sum(p[1] for p in group) / len(group)
436
+ nomes = [p[2].split()[0] for p in group]
437
+
438
+ # Determinar posição do texto baseado no quadrante do gráfico
439
+ x_text = x_base + (0.5 if x_base < np.mean(self.data['Tarefas Completadas']) else -2)
440
+ y_text = y_base + (0.5 if y_base < np.mean(self.data['Acertos Absolutos']) else -2)
441
+
442
+ # Criar caixa com lista de nomes
443
+ nome_text = '\n'.join(nomes)
444
+ plt.annotate(
445
+ nome_text,
446
+ (x_base, y_base),
447
+ xytext=(x_text, y_text),
448
+ bbox=dict(
449
+ facecolor='white',
450
+ edgecolor='lightgray',
451
+ alpha=0.9,
452
+ pad=0.5,
453
+ boxstyle='round,pad=0.5'
454
+ ),
455
+ fontsize=8,
456
+ arrowprops=dict(
457
+ arrowstyle='->',
458
+ color='gray',
459
+ alpha=0.5,
460
+ connectionstyle='arc3,rad=0.2'
461
+ )
462
+ )
463
+
464
  plt.title('Relação entre Tarefas Completadas e Acertos', pad=20)
465
  plt.xlabel('Número de Tarefas Completadas')
466
  plt.ylabel('Número de Acertos')
467
 
468
+ plt.legend(
469
+ bbox_to_anchor=(1.05, 1),
470
+ loc='upper left',
471
+ borderaxespad=0,
472
+ frameon=True,
473
+ facecolor='white',
474
+ edgecolor='none'
475
+ )
476
 
 
477
  plt.tight_layout()
 
478
  return plt.gcf()
479
 
480
  def generate_graphs(self) -> List[plt.Figure]: