Files
Orchestrator/bahn/project-audit/scripts/jira-critical-scan.py
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

115 lines
4.6 KiB
Python

"""
Systematischer Scan nach geschaeftskritischen Themen in Jira.
Strategie: Suche nach Signalen die auf politische/regulatorische/kundenrelevante Risiken hindeuten.
Signale:
1. Labels: TopThema, Konzernreporting, Taskforce-*, TTT_offenePunkte
2. Prioritaet: Highest + Blocked
3. Eskalation im Titel/Summary
4. Kundenrelevant: DBFernverkehr, DBCargo, DBRegio, TTT_Kunde
5. Regulatorisch: BNetzA, ERegG, SNB
6. Lange offen + hohe Prio
"""
import urllib.request, json, ssl, urllib.parse
from collections import defaultdict
from datetime import datetime
secrets = {}
with open("project-audit/.secrets", "r", encoding="utf-8-sig") as f:
for line in f:
if "=" in line and not line.startswith("#"):
k, v = line.strip().split("=", 1)
secrets[k.strip()] = v.strip()
TOKEN = secrets.get("JIRA_TOKEN", "")
ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
def search(jql, max_results=50):
url = f"https://arija.jaas.service.deutschebahn.com/rest/api/2/search?jql={urllib.parse.quote(jql)}&maxResults={max_results}&fields=key,summary,status,priority,assignee,updated,created,labels,issuetype"
req = urllib.request.Request(url)
req.add_header("Authorization", f"Bearer {TOKEN}")
try:
resp = urllib.request.urlopen(req, context=ctx, timeout=30)
return json.loads(resp.read().decode("utf-8"))
except Exception as e:
print(f" API-Fehler: {e}")
return {"issues": [], "total": 0}
def print_issues(issues, max_show=15):
for i in issues[:max_show]:
f = i["fields"]
s = f["status"]["name"]
p = (f.get("priority") or {}).get("name", "?")
labels = f.get("labels", [])
key_labels = [l for l in labels if any(x in l for x in ["TopThema", "Konzern", "Taskforce", "Kunde", "offenePunkte", "Eskalation", "MUSS"])]
label_str = " [" + ", ".join(key_labels) + "]" if key_labels else ""
print(f" {i['key']:<15} [{s:<14}] {p:<10} {f['summary'][:60]}{label_str}")
print("=" * 100)
print("SYSTEMATISCHER SCAN: Geschaeftskritische Risiken")
print("=" * 100)
# 1. TopThema Label
print("\n--- 1. Label 'TopThema' (alle offenen) ---")
r = search('labels = TopThema AND statusCategory != Done ORDER BY priority DESC')
print(f" Total: {r['total']}")
print_issues(r["issues"])
# 2. Konzernreporting
print("\n--- 2. Label 'Konzernreporting' (offen) ---")
r = search('labels = Konzernreporting AND statusCategory != Done ORDER BY priority DESC')
print(f" Total: {r['total']}")
print_issues(r["issues"])
# 3. Taskforce-FplW (Fahrplanwechsel)
print("\n--- 3. Label 'Taskforce-FplW' (offen) ---")
r = search('labels = "Taskforce-FplW" AND statusCategory != Done ORDER BY priority DESC')
print(f" Total: {r['total']}")
print_issues(r["issues"])
# 4. Blocked + Highest/High
print("\n--- 4. Blocked + Highest/High Prioritaet ---")
r = search('status = Blocked AND priority in (Highest, High, Zeitkritisch, Hoch) AND (project = TTTSOL OR project = TTTI OR project = O2CCIB OR project = O2C404 OR project = O2CZERO) ORDER BY priority DESC')
print(f" Total: {r['total']}")
print_issues(r["issues"])
# 5. Eskalation im Titel
print("\n--- 5. 'Eskalation' im Titel (offen) ---")
r = search('summary ~ "Eskalation" AND statusCategory != Done ORDER BY updated DESC')
print(f" Total: {r['total']}")
print_issues(r["issues"])
# 6. Kundenrelevant (DBFernverkehr, DBCargo, DBRegio)
print("\n--- 6. Kundenrelevant (DB Fernverkehr/Cargo/Regio, offen) ---")
r = search('labels in (DBFernverkehr, DBCargo, DBRegio) AND statusCategory != Done ORDER BY priority DESC')
print(f" Total: {r['total']}")
print_issues(r["issues"])
# 7. TTT_Kunde Label
print("\n--- 7. Label 'TTT_Kunde' (offen) ---")
r = search('labels = TTT_Kunde AND statusCategory != Done ORDER BY priority DESC')
print(f" Total: {r['total']}")
print_issues(r["issues"])
# 8. TTT_offenePunkte + Highest
print("\n--- 8. TTT_offenePunkte + Highest ---")
r = search('labels = TTT_offenePunkte AND priority = Highest AND statusCategory != Done ORDER BY updated DESC')
print(f" Total: {r['total']}")
print_issues(r["issues"])
# 9. MUSS-Label (regulatorisch)
print("\n--- 9. Label 'TTT_MUSS' (offen) ---")
r = search('labels = TTT_MUSS AND statusCategory != Done ORDER BY priority DESC', 30)
print(f" Total: {r['total']}")
print_issues(r["issues"], 10)
# 10. Aelteste offene Highest-Tickets
print("\n--- 10. Aelteste offene Highest-Tickets (>60 Tage) ---")
r = search('priority = Highest AND statusCategory != Done AND (project = TTTSOL OR project = TTTI OR project = O2CCIB OR project = O2C404) AND created <= -60d ORDER BY created ASC')
print(f" Total: {r['total']}")
print_issues(r["issues"])
print("\n" + "=" * 100)