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.
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
"""
|
||||
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']}")
|
||||
Reference in New Issue
Block a user