Files
Orchestrator/bahn/project-audit/scripts/sonarqube-analyse.py
T
ankn a5f8fb49ab Migrate all repos into monorepo context folders
Bahn: aisupport, Analyse-O2C-C2S, awesome-bahn-mcp-servers, beam-mcp,
      Confluence_Bot, db-planet-mcp-server, O2C-Harness, project-audit,
      Projekt-KIQ-HP, teamlandkarte-mcp
Dhive: Jury-Voting
Privat: CV, NoteGraph (NOTE: NoteGraph needs complete redo after consolidation)
Shared: AI-Orchestrator, OrgMyLife, power_skills_and_more
Shared/references: symphony (read-only)

Bahn repos remain available as independent remotes - this monorepo
pulls them in via subtree, the originals are untouched.
2026-06-30 20:39:52 +02:00

121 lines
4.3 KiB
Python

"""
SonarQube-Daten analysieren: Coverage und Smells pro Service/Team.
Filtert auf master/main Branches und ordnet Services den Teams zu.
"""
import csv
from collections import defaultdict
CSV_PATH = "project-audit/data/sonarqube/sonarqube-metrics.csv"
# Team-Zuordnung der Services
TEAM_MAP = {
"portal-ui": "404",
"portal-middleware": "404",
"portal-mw": "404",
"steuerung-vertrieb": "CIB",
"sv-": "CIB",
"auftrags-verwaltung": "CIB",
"av-": "CIB",
"auftrag-service": "CIB",
"archivierungsservice": "CIB",
"common-interface": "Zero",
"ci-": "Zero",
"tadef": "Zero",
"stammdaten": "Zero",
"stb-": "Zero",
"acat": "Zero",
"vertragsdaten": "Zero",
"vdv": "Zero",
"ifp": "CIB",
"kundendaten": "CIB",
"kdv": "CIB",
"rabatt": "CIB",
"core-component": "Shared",
"signature": "Shared",
"logging": "Shared",
}
def get_team(name):
name_lower = name.lower()
for pattern, team in TEAM_MAP.items():
if pattern in name_lower:
return team
return "Andere"
# Daten laden (nur master/main)
data = []
with open(CSV_PATH, "r", encoding="utf-8") as f:
reader = csv.DictReader(f, delimiter=";", quotechar='"')
for row in reader:
name = row.get("Name", "")
if name.endswith("-master") or name.endswith("-main"):
lines = int(row.get("Lines", "0") or "0")
if lines > 0: # Nur Projekte mit Code
data.append({
"name": name.replace("-master", "").replace("-main", ""),
"lines": lines,
"bugs": int(row.get("Bugs", "0") or "0"),
"smells": int(row.get("CodeSmells", "0") or "0"),
"coverage": float(row.get("Coverage", "0") or "0"),
"duplications": float(row.get("Duplications", "0") or "0"),
"qgate": row.get("QualityGate", "?"),
"team": get_team(name),
})
# Sortiert nach Lines
data.sort(key=lambda x: x["lines"], reverse=True)
print(f"=== SonarQube Analyse (nur master/main, {len(data)} Projekte mit Code) ===")
print()
# Top 20 nach Groesse
print("--- Top 20 Projekte nach Groesse ---")
print(f"{'Projekt':<40} {'Lines':>7} {'Bugs':>5} {'Smells':>7} {'Cov%':>6} {'QGate':>6} {'Team':<8}")
print("-" * 85)
for row in data[:20]:
cov_str = f"{row['coverage']:.1f}" if row['coverage'] > 0 else "-"
print(f"{row['name']:<40} {row['lines']:>7} {row['bugs']:>5} {row['smells']:>7} {cov_str:>6} {row['qgate']:>6} {row['team']:<8}")
# Team-Aggregation
print()
print("--- Aggregation pro Team ---")
teams = defaultdict(lambda: {"lines": 0, "bugs": 0, "smells": 0, "cov_weighted": 0, "projects": 0, "qgate_ok": 0, "qgate_err": 0})
for row in data:
t = teams[row["team"]]
t["lines"] += row["lines"]
t["bugs"] += row["bugs"]
t["smells"] += row["smells"]
t["cov_weighted"] += row["coverage"] * row["lines"]
t["projects"] += 1
if row["qgate"] == "OK":
t["qgate_ok"] += 1
else:
t["qgate_err"] += 1
print(f"{'Team':<10} {'Projekte':>8} {'Lines':>8} {'Bugs':>5} {'Smells':>7} {'Smells/1K':>9} {'Cov%':>6} {'QGate OK':>8}")
print("-" * 75)
for team in ["404", "CIB", "Zero", "Shared", "Andere"]:
if team in teams:
t = teams[team]
smells_per_k = (t["smells"] / t["lines"] * 1000) if t["lines"] > 0 else 0
avg_cov = (t["cov_weighted"] / t["lines"]) if t["lines"] > 0 else 0
qgate_str = f"{t['qgate_ok']}/{t['projects']}"
print(f"{team:<10} {t['projects']:>8} {t['lines']:>8} {t['bugs']:>5} {t['smells']:>7} {smells_per_k:>9.1f} {avg_cov:>6.1f} {qgate_str:>8}")
# Projekte ohne Coverage
print()
print("--- Projekte OHNE Coverage (0% oder keine Daten) ---")
no_cov = [r for r in data if r["coverage"] == 0 and r["lines"] > 500]
no_cov.sort(key=lambda x: x["lines"], reverse=True)
for row in no_cov:
print(f" {row['name']:<40} {row['lines']:>7} Lines {row['team']:<8}")
# Quality Gate Failures
print()
print("--- Quality Gate FAILED (ERROR) ---")
failed = [r for r in data if r["qgate"] == "ERROR"]
failed.sort(key=lambda x: x["lines"], reverse=True)
for row in failed[:15]:
cov_str = f"{row['coverage']:.1f}%" if row['coverage'] > 0 else "0%"
print(f" {row['name']:<40} {row['lines']:>7} Lines Bugs={row['bugs']} Smells={row['smells']} Cov={cov_str} {row['team']}")