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.
121 lines
4.3 KiB
Python
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']}")
|