""" 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']}")