Skip to content

Commit 4a761d5

Browse files
add metrics after
1 parent a6d21c5 commit 4a761d5

29 files changed

Lines changed: 118926 additions & 12 deletions

.gitignore

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -127,15 +127,17 @@ TEST-**.xml
127127
*.env*
128128

129129
# # Métricas
130-
/metrics-before-radon
131-
/metrics-before-pylint
132-
/metrics-after-pylint
133-
/metrics-before-codecarbon
134-
/metrics-before-pytest
135-
136-
extract_metrics_before_radon.py
137-
extract_metrics_before_pylint.py
138-
extract_metrics_after_pylint.py
139-
extract_score_before_pylint.py
140-
extract_metrics_before_codecarbon.py
141-
extract_metrics_before_pytest.py
130+
# /metrics-before-radon
131+
# /metrics-before-pylint
132+
# /metrics-after-pylint
133+
# /metrics-before-codecarbon
134+
# /metrics-after-codecarbon
135+
# /metrics-before-pytest
136+
137+
# extract_metrics_before_radon.py
138+
# extract_metrics_before_pylint.py
139+
# extract_metrics_after_pylint.py
140+
# extract_score_before_pylint.py
141+
# extract_metrics_before_codecarbon.py
142+
# extract_metrics_after_codecarbon.py
143+
# extract_metrics_before_pytest.py
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
from codecarbon import EmissionsTracker
2+
import subprocess
3+
import sys
4+
import os
5+
6+
# Configuração
7+
os.environ["BOT_TOKEN"] = (
8+
""
9+
)
10+
11+
# Nome do projeto
12+
PROJETO = "bot"
13+
14+
# Ponto de entrada do projeto (define como o Python vai executar o projeto).
15+
SCRIPT = "-m"
16+
17+
# Argumentos necessários para a execução do projeto.
18+
# Se o projeto não precisar de argumentos, deixe vazio: ARGS = []
19+
ARGS = ["bot"]
20+
21+
22+
# Tempo máximo que o CodeCarbon vai aguardar a execução do projeto antes de encerrar a
23+
# medição e salvar os resultados.
24+
# None -> sem limite — o CodeCarbon aguarda o projeto terminar sozinho.
25+
# Use para scripts e pipelines que executam e terminam naturalmente.
26+
#
27+
# 60 -> encerra após 60 segundos, mesmo que o projeto ainda esteja rodando.
28+
# Use para servidores (Flask, FastAPI, Django) que ficam rodando continuamente e nunca terminariam sozinhos.
29+
TIMEOUT = 60
30+
31+
# Não altere o nome dessa pasta, os relatórios vão ser salvos nela.
32+
PASTA = "metrics-after-codecarbon"
33+
34+
# Executa com medição
35+
os.makedirs(PASTA, exist_ok=True)
36+
37+
tracker = EmissionsTracker(
38+
project_name=PROJETO,
39+
measure_power_secs=1,
40+
output_dir=PASTA,
41+
output_file="emissions_depois.csv",
42+
allow_multiple_runs=True,
43+
log_level="error",
44+
)
45+
46+
print(f"Iniciando medição de emissões para: {PROJETO}")
47+
print(f"Comando: python {SCRIPT} {' '.join(ARGS)}")
48+
if TIMEOUT:
49+
print(f"Timeout: {TIMEOUT} segundos")
50+
51+
tracker.start()
52+
53+
try:
54+
resultado = subprocess.run([sys.executable, SCRIPT] + ARGS, timeout=TIMEOUT)
55+
exit_code = resultado.returncode
56+
except subprocess.TimeoutExpired:
57+
print("Tempo de medição encerrado.")
58+
exit_code = 0
59+
60+
emissions = tracker.stop()
61+
62+
print(f"\nResultados:")
63+
print(f" Exit code: {exit_code}")
64+
print(f" COâ‚‚ emitido: {emissions * 1000:.6f} g COâ‚‚")
65+
print(f" Arquivo salvo em: {os.path.join(PASTA, 'emissions.csv')}")
66+
print("\nConcluído.")

extract_metrics_after_pylint.py

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import json
2+
import os
3+
import subprocess
4+
import sys
5+
from collections import defaultdict, Counter
6+
7+
# Configuração
8+
PROJETO = "./bot" # diretório do código fonte
9+
PASTA = "metrics-after-pylint" # Não altere o nome dessa pasta, os relatórios vão ser salvos nela.
10+
11+
os.makedirs(PASTA, exist_ok=True)
12+
13+
# Roda o Pylint
14+
print(f"Rodando pylint em {PROJETO}...")
15+
16+
resultado = subprocess.run(
17+
["pylint", PROJETO, "--output-format=json", "--score=y"],
18+
capture_output=True,
19+
text=True,
20+
encoding="utf-8",
21+
)
22+
23+
# Salva o JSON bruto
24+
caminho_json = os.path.join(PASTA, "pylint_depois.json")
25+
with open(caminho_json, "w", encoding="utf-8") as f:
26+
f.write(resultado.stdout)
27+
print(f"JSON completo salvo em: {caminho_json}")
28+
29+
# Processa mensagens
30+
try:
31+
mensagens = json.loads(resultado.stdout)
32+
except json.JSONDecodeError:
33+
print("Erro ao processar JSON do Pylint.")
34+
sys.exit(1)
35+
36+
if not mensagens:
37+
print("Nenhuma mensagem encontrada.")
38+
sys.exit(0)
39+
40+
# Salva JSONs por categoria
41+
por_tipo = defaultdict(list)
42+
for msg in mensagens:
43+
tipo = msg.get("type", "unknown")
44+
por_tipo[tipo].append(msg)
45+
46+
tipos_nomes = {
47+
"convention": "pylint_convention_depois.json",
48+
"refactor": "pylint_refactor_depois.json",
49+
"warning": "pylint_warning_depois.json",
50+
"error": "pylint_error_depois.json",
51+
"fatal": "pylint_fatal_depois.json",
52+
}
53+
54+
for tipo, nome_arquivo in tipos_nomes.items():
55+
caminho = os.path.join(PASTA, nome_arquivo)
56+
with open(caminho, "w", encoding="utf-8") as f:
57+
json.dump(por_tipo.get(tipo, []), f, indent=2, ensure_ascii=False)
58+
print(f"{len(por_tipo.get(tipo, [])):>5} mensagens → {caminho}")
59+
60+
print(f"\nTotal: {len(mensagens)} mensagens encontradas.")
61+
62+
# Ranking da da categoria refactor
63+
mensagens_refactor = [msg for msg in mensagens if msg.get("type") == "refactor"]
64+
contagem_simbolos = Counter(msg["symbol"] for msg in mensagens_refactor)
65+
caminho_ranking = os.path.join(PASTA, "pylint_ranking_smells_depois.json")
66+
with open(caminho_ranking, "w", encoding="utf-8") as f:
67+
json.dump(
68+
[{"simbolo": s, "ocorrencias": t} for s, t in contagem_simbolos.most_common()],
69+
f,
70+
indent=2,
71+
ensure_ascii=False,
72+
)
73+
print(f"Ranking de símbolos salvo em: {caminho_ranking}")
74+
75+
# Arquivos com mais problemas
76+
por_arquivo = defaultdict(lambda: defaultdict(int))
77+
for msg in mensagens:
78+
path = msg.get("path", "desconhecido")
79+
tipo = msg.get("type", "unknown")
80+
por_arquivo[path][tipo] += 1
81+
por_arquivo[path]["total"] += 1
82+
83+
arquivos_ordenados = sorted(
84+
[{"arquivo": path, **contagens} for path, contagens in por_arquivo.items()],
85+
key=lambda x: x["total"],
86+
reverse=True,
87+
)
88+
caminho_arquivos = os.path.join(PASTA, "pylint_arquivos_criticos_depois.json")
89+
with open(caminho_arquivos, "w", encoding="utf-8") as f:
90+
json.dump(arquivos_ordenados, f, indent=2, ensure_ascii=False)
91+
print(f"Arquivos críticos salvo em: {caminho_arquivos}")
92+
93+
# Distribuição por categoria
94+
total = len(mensagens)
95+
distribuicao = [
96+
{
97+
"categoria": tipo,
98+
"ocorrencias": len(msgs),
99+
"percentual": round(len(msgs) / total * 100, 2),
100+
}
101+
for tipo, msgs in por_tipo.items()
102+
]
103+
distribuicao.sort(key=lambda x: x["ocorrencias"], reverse=True)
104+
caminho_dist = os.path.join(PASTA, "pylint_distribuicao_categorias_depois.json")
105+
with open(caminho_dist, "w", encoding="utf-8") as f:
106+
json.dump(distribuicao, f, indent=2, ensure_ascii=False)
107+
print(f"Distribuição por categoria salva em: {caminho_dist}")
108+
109+
print("\nConcluído.")

extract_metrics_after_pytest.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import os
2+
import subprocess
3+
import sys
4+
from pathlib import Path
5+
6+
# Configuração, ajuste apenas se necessário.
7+
8+
# Diretório raiz do projeto clonado, os testes vai começar a execução a petir dele.
9+
PROJETO = "."
10+
11+
# Diretório dos testes detectado automaticamente, mas pode forçar manualmente
12+
# Exemplos: TESTES = "./tests" ou TESTES = "./test"
13+
TESTES = None
14+
15+
# Pasta onde os relatórios serão salvos (não altere)
16+
PASTA = "metrics-after-pytest"
17+
18+
# Detecção automática do diretório de testes
19+
CANDIDATOS = ["tests"]
20+
21+
if TESTES is None:
22+
for candidato in CANDIDATOS:
23+
if Path(candidato).exists():
24+
TESTES = candidato
25+
break
26+
27+
if TESTES is None:
28+
print("Erro: diretório de testes não encontrado.")
29+
print(f"Procurado em: {CANDIDATOS}")
30+
print("Defina manualmente a variável TESTES no script.")
31+
sys.exit(1)
32+
33+
# Execução
34+
os.makedirs(PASTA, exist_ok=True)
35+
36+
print(f"Projeto : {os.path.abspath(PROJETO)}")
37+
print(f"Testes : {TESTES}")
38+
print(f"Relatórios em: {PASTA}/")
39+
print()
40+
41+
resultado = subprocess.run(
42+
[
43+
sys.executable,
44+
"-m",
45+
"pytest",
46+
TESTES,
47+
"-v",
48+
f"--junit-xml={os.path.join(PASTA, 'pytest_depois.xml')}",
49+
f"--html={os.path.join(PASTA, 'pytest_depois.html')}",
50+
"--self-contained-html",
51+
f"--cov={PROJETO}",
52+
"--cov-branch",
53+
f"--cov-report=xml:{os.path.join(PASTA, 'coverage_depois.xml')}",
54+
f"--cov-report=json:{os.path.join(PASTA, 'coverage_depois.json')}",
55+
f"--cov-report=html:{os.path.join(PASTA, 'coverage_depois_html')}",
56+
"--cov-report=term-missing",
57+
],
58+
cwd=PROJETO,
59+
text=True,
60+
encoding="utf-8",
61+
)
62+
63+
print(f"\nExit code: {resultado.returncode}")
64+
print(f"\nArquivos gerados em '{PASTA}':")
65+
print(f" pytest_depois.xml → resultados dos testes em XML")
66+
print(f" pytest_depois.html → relatório visual dos testes")
67+
print(f" coverage_depois.xml → cobertura de código em XML")
68+
print(f" coverage_depois.json → cobertura de código em JSON")
69+
print(f" coverage_depois_html/ → relatório visual de cobertura")
70+
print("\nConcluído.")

0 commit comments

Comments
 (0)