Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -406,32 +406,26 @@ class ReportGenerator:
|
|
| 406 |
"""Cria o gráfico de relação entre tarefas e acertos com visualização otimizada."""
|
| 407 |
plt.figure(figsize=(15, 10))
|
| 408 |
ax = plt.gca()
|
| 409 |
-
|
| 410 |
# Configuração inicial
|
| 411 |
plt.grid(True, alpha=0.2, linestyle='--')
|
| 412 |
ax.set_facecolor('#f8f9fa')
|
| 413 |
-
|
| 414 |
def calculate_distance(p1, p2):
|
| 415 |
return np.sqrt((p1[0] - p2[0])**2 + (p1[1] - p2[1])**2)
|
| 416 |
|
| 417 |
-
def create_smooth_connection(start, end, offset=0.5):
|
| 418 |
-
"""Cria uma conexão suave entre dois pontos."""
|
| 419 |
-
mid_x = (start[0] + end[0]) / 2 + offset
|
| 420 |
-
return f'M{start[0]},{start[1]} ' \
|
| 421 |
-
f'C{mid_x},{start[1]} {mid_x},{end[1]} {end[0]},{end[1]}'
|
| 422 |
-
|
| 423 |
# Estruturas para armazenamento
|
| 424 |
points_by_level = {level: [] for level in self.colors.keys()}
|
| 425 |
used_positions = []
|
| 426 |
-
|
| 427 |
# Coletar e plotar pontos
|
| 428 |
for nivel, color in self.colors.items():
|
| 429 |
mask = self.data['Nível'] == nivel
|
| 430 |
tarefas = self.data[mask]['Tarefas Completadas']
|
| 431 |
acertos = self.data[mask]['Acertos Absolutos']
|
| 432 |
-
|
| 433 |
-
|
| 434 |
-
|
| 435 |
for t, a, nome in zip(tarefas, acertos, self.data[mask]['Nome do Aluno']):
|
| 436 |
points_by_level[nivel].append((t, a, nome))
|
| 437 |
|
|
@@ -466,39 +460,31 @@ class ReportGenerator:
|
|
| 466 |
for (x, y), group in point_groups.items():
|
| 467 |
if len(group) == 1:
|
| 468 |
label = group[0][2].split()[0]
|
| 469 |
-
offset = 0
|
| 470 |
else:
|
| 471 |
label = f"{len(group)} alunos com\n{group[0][0]:.0f} tarefas:\n" + \
|
| 472 |
"\n".join(sorted(p[2].split()[0] for p in group))
|
| 473 |
-
offset = len(group) * 0.2
|
| 474 |
|
| 475 |
# Encontrar posição livre para o label
|
| 476 |
-
label_pos = None
|
| 477 |
radius = 2.0 + len(group) * 0.5
|
| 478 |
-
|
| 479 |
|
| 480 |
-
|
| 481 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 482 |
break
|
| 483 |
-
|
| 484 |
-
test_x = x + r * np.cos(angle)
|
| 485 |
-
test_y = y + r * np.sin(angle)
|
| 486 |
-
|
| 487 |
-
# Verificar se posição está livre
|
| 488 |
-
if not any(calculate_distance((test_x, test_y), pos) < 2.0
|
| 489 |
-
for pos in used_positions):
|
| 490 |
-
label_pos = (test_x, test_y)
|
| 491 |
-
used_positions.append(label_pos)
|
| 492 |
-
break
|
| 493 |
-
|
| 494 |
-
if not label_pos:
|
| 495 |
-
label_pos = (x + radius, y + radius)
|
| 496 |
|
| 497 |
-
# Criar anotação
|
| 498 |
plt.annotate(
|
| 499 |
label,
|
| 500 |
(x, y),
|
| 501 |
-
xytext=
|
| 502 |
textcoords='data',
|
| 503 |
bbox=dict(
|
| 504 |
facecolor='white',
|
|
@@ -510,7 +496,7 @@ class ReportGenerator:
|
|
| 510 |
fontsize=9,
|
| 511 |
arrowprops=dict(
|
| 512 |
arrowstyle='-|>',
|
| 513 |
-
connectionstyle=
|
| 514 |
color='gray',
|
| 515 |
alpha=0.6,
|
| 516 |
mutation_scale=15
|
|
@@ -521,10 +507,10 @@ class ReportGenerator:
|
|
| 521 |
plt.title('Relação entre Tarefas Completadas e Acertos', pad=20, fontsize=14)
|
| 522 |
plt.xlabel('Número de Tarefas Completadas', fontsize=12)
|
| 523 |
plt.ylabel('Número de Acertos', fontsize=12)
|
| 524 |
-
|
| 525 |
# Forçar ticks inteiros no eixo x
|
| 526 |
ax.xaxis.set_major_locator(MaxNLocator(integer=True))
|
| 527 |
-
|
| 528 |
# Ajustar limites
|
| 529 |
plt.xlim(
|
| 530 |
self.data['Tarefas Completadas'].min() - 1,
|
|
@@ -534,8 +520,8 @@ class ReportGenerator:
|
|
| 534 |
max(0, self.data['Acertos Absolutos'].min() - 1),
|
| 535 |
self.data['Acertos Absolutos'].max() + 2
|
| 536 |
)
|
| 537 |
-
|
| 538 |
-
# Legenda
|
| 539 |
handles, labels = plt.gca().get_legend_handles_labels()
|
| 540 |
by_label = dict(zip(labels, handles))
|
| 541 |
plt.legend(
|
|
@@ -547,7 +533,7 @@ class ReportGenerator:
|
|
| 547 |
frameon=True,
|
| 548 |
fancybox=True
|
| 549 |
)
|
| 550 |
-
|
| 551 |
plt.tight_layout()
|
| 552 |
return plt.gcf()
|
| 553 |
|
|
|
|
| 406 |
"""Cria o gráfico de relação entre tarefas e acertos com visualização otimizada."""
|
| 407 |
plt.figure(figsize=(15, 10))
|
| 408 |
ax = plt.gca()
|
| 409 |
+
|
| 410 |
# Configuração inicial
|
| 411 |
plt.grid(True, alpha=0.2, linestyle='--')
|
| 412 |
ax.set_facecolor('#f8f9fa')
|
| 413 |
+
|
| 414 |
def calculate_distance(p1, p2):
|
| 415 |
return np.sqrt((p1[0] - p2[0])**2 + (p1[1] - p2[1])**2)
|
| 416 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 417 |
# Estruturas para armazenamento
|
| 418 |
points_by_level = {level: [] for level in self.colors.keys()}
|
| 419 |
used_positions = []
|
| 420 |
+
|
| 421 |
# Coletar e plotar pontos
|
| 422 |
for nivel, color in self.colors.items():
|
| 423 |
mask = self.data['Nível'] == nivel
|
| 424 |
tarefas = self.data[mask]['Tarefas Completadas']
|
| 425 |
acertos = self.data[mask]['Acertos Absolutos']
|
| 426 |
+
|
| 427 |
+
plt.scatter(tarefas, acertos, c=color, label=nivel, alpha=0.7, s=100)
|
| 428 |
+
|
| 429 |
for t, a, nome in zip(tarefas, acertos, self.data[mask]['Nome do Aluno']):
|
| 430 |
points_by_level[nivel].append((t, a, nome))
|
| 431 |
|
|
|
|
| 460 |
for (x, y), group in point_groups.items():
|
| 461 |
if len(group) == 1:
|
| 462 |
label = group[0][2].split()[0]
|
|
|
|
| 463 |
else:
|
| 464 |
label = f"{len(group)} alunos com\n{group[0][0]:.0f} tarefas:\n" + \
|
| 465 |
"\n".join(sorted(p[2].split()[0] for p in group))
|
|
|
|
| 466 |
|
| 467 |
# Encontrar posição livre para o label
|
|
|
|
| 468 |
radius = 2.0 + len(group) * 0.5
|
| 469 |
+
angle = np.random.uniform(0, 2 * np.pi)
|
| 470 |
|
| 471 |
+
# Tentar diferentes posições até encontrar uma livre
|
| 472 |
+
for r in np.arange(radius, radius * 3, radius/2):
|
| 473 |
+
label_x = x + r * np.cos(angle)
|
| 474 |
+
label_y = y + r * np.sin(angle)
|
| 475 |
+
|
| 476 |
+
# Verificar se posição está livre
|
| 477 |
+
if not any(calculate_distance((label_x, label_y), pos) < 2.0
|
| 478 |
+
for pos in used_positions):
|
| 479 |
+
used_positions.append((label_x, label_y))
|
| 480 |
break
|
| 481 |
+
angle += np.pi/4
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 482 |
|
| 483 |
+
# Criar anotação com estilo de conexão padrão
|
| 484 |
plt.annotate(
|
| 485 |
label,
|
| 486 |
(x, y),
|
| 487 |
+
xytext=(label_x, label_y),
|
| 488 |
textcoords='data',
|
| 489 |
bbox=dict(
|
| 490 |
facecolor='white',
|
|
|
|
| 496 |
fontsize=9,
|
| 497 |
arrowprops=dict(
|
| 498 |
arrowstyle='-|>',
|
| 499 |
+
connectionstyle='arc3,rad=0.2', # Estilo de conexão padrão
|
| 500 |
color='gray',
|
| 501 |
alpha=0.6,
|
| 502 |
mutation_scale=15
|
|
|
|
| 507 |
plt.title('Relação entre Tarefas Completadas e Acertos', pad=20, fontsize=14)
|
| 508 |
plt.xlabel('Número de Tarefas Completadas', fontsize=12)
|
| 509 |
plt.ylabel('Número de Acertos', fontsize=12)
|
| 510 |
+
|
| 511 |
# Forçar ticks inteiros no eixo x
|
| 512 |
ax.xaxis.set_major_locator(MaxNLocator(integer=True))
|
| 513 |
+
|
| 514 |
# Ajustar limites
|
| 515 |
plt.xlim(
|
| 516 |
self.data['Tarefas Completadas'].min() - 1,
|
|
|
|
| 520 |
max(0, self.data['Acertos Absolutos'].min() - 1),
|
| 521 |
self.data['Acertos Absolutos'].max() + 2
|
| 522 |
)
|
| 523 |
+
|
| 524 |
+
# Legenda
|
| 525 |
handles, labels = plt.gca().get_legend_handles_labels()
|
| 526 |
by_label = dict(zip(labels, handles))
|
| 527 |
plt.legend(
|
|
|
|
| 533 |
frameon=True,
|
| 534 |
fancybox=True
|
| 535 |
)
|
| 536 |
+
|
| 537 |
plt.tight_layout()
|
| 538 |
return plt.gcf()
|
| 539 |
|