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,154 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Bug-Verursachungs-Analyse (Bulk-Ansatz)
|
||||
Holt alle Bugs + Features der letzten 6 Monate in 2 Bulk-Abfragen
|
||||
und korreliert lokal.
|
||||
#>
|
||||
|
||||
$JiraUrl = "https://arija.jaas.service.deutschebahn.com"
|
||||
$ApiBase = "$JiraUrl/rest"
|
||||
$OutputDir = "project-audit\data\jira-bugs"
|
||||
|
||||
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
||||
|
||||
Write-Host "============================================================"
|
||||
Write-Host " Bug-Verursachungs-Analyse (Bulk)"
|
||||
Write-Host " 2 API-Calls statt 60+"
|
||||
Write-Host "============================================================"
|
||||
|
||||
$Token = $null
|
||||
$SecretsPath = Join-Path (Split-Path -Parent $MyInvocation.MyCommand.Path) "..\.secrets"
|
||||
if (Test-Path $SecretsPath) {
|
||||
Get-Content $SecretsPath | ForEach-Object {
|
||||
if ($_ -match "^JIRA_TOKEN=(.+)$") {
|
||||
$val = $Matches[1].Trim()
|
||||
if ($val -ne "HIER_TOKEN_EINTRAGEN") { $Token = $val }
|
||||
}
|
||||
}
|
||||
}
|
||||
if (-not $Token) { $Token = Read-Host "Jira PAT" }
|
||||
$Headers = @{ "Authorization" = "Bearer $Token"; "Content-Type" = "application/json" }
|
||||
|
||||
New-Item -ItemType Directory -Path $OutputDir -Force | Out-Null
|
||||
|
||||
function Invoke-JiraBulk {
|
||||
param([string]$Jql, [int]$MaxTotal = 5000)
|
||||
$AllIssues = @()
|
||||
$StartAt = 0
|
||||
$PageSize = 1000
|
||||
do {
|
||||
$Uri = "$ApiBase/api/2/search?jql=$([Uri]::EscapeDataString($Jql))&startAt=$StartAt&maxResults=$PageSize&fields=summary,created,resolutiondate,assignee,issuetype,project,reporter"
|
||||
Write-Host " Lade $StartAt..." -NoNewline
|
||||
try {
|
||||
$R = Invoke-WebRequest -Uri $Uri -Headers $Headers -UseBasicParsing -ErrorAction Stop
|
||||
$Data = $R.Content | ConvertFrom-Json
|
||||
$AllIssues += $Data.issues
|
||||
$Total = $Data.total
|
||||
Write-Host " $($AllIssues.Count)/$Total" -ForegroundColor Green
|
||||
$StartAt += $PageSize
|
||||
Start-Sleep -Seconds 2
|
||||
} catch {
|
||||
$StatusCode = 0
|
||||
if ($_.Exception.Response) { $StatusCode = [int]$_.Exception.Response.StatusCode }
|
||||
if ($StatusCode -eq 429) {
|
||||
Write-Host " [429 - warte 30s]" -ForegroundColor Yellow
|
||||
Start-Sleep -Seconds 30
|
||||
} else {
|
||||
Write-Host " FEHLER: $($_.Exception.Message)" -ForegroundColor Red
|
||||
break
|
||||
}
|
||||
}
|
||||
} while ($AllIssues.Count -lt $Total -and $AllIssues.Count -lt $MaxTotal)
|
||||
return $AllIssues
|
||||
}
|
||||
|
||||
$Projects = "O2C404, O2CCIB, O2CZERO"
|
||||
|
||||
# Abfrage 1: Alle Bugs (6 Monate)
|
||||
Write-Host ""
|
||||
Write-Host ">> Bugs (6 Monate)..." -ForegroundColor Yellow
|
||||
$BugJql = "project in ($Projects) AND issuetype = Bug AND created >= -180d ORDER BY created DESC"
|
||||
$Bugs = Invoke-JiraBulk -Jql $BugJql -MaxTotal 2000
|
||||
Write-Host " $($Bugs.Count) Bugs geladen"
|
||||
|
||||
# Abfrage 2: Abgeschlossene Features/Stories (6 Monate)
|
||||
Write-Host ""
|
||||
Write-Host ">> Features/Stories (6 Monate, abgeschlossen)..." -ForegroundColor Yellow
|
||||
$FeatureJql = "project in ($Projects) AND issuetype in (Feature, Enabler, Story, Aufgabe, Unteraufgabe) AND status in (Fertig, Done, Closed) AND resolved >= -180d ORDER BY resolved DESC"
|
||||
$Features = Invoke-JiraBulk -Jql $FeatureJql -MaxTotal 3000
|
||||
Write-Host " $($Features.Count) Features geladen"
|
||||
|
||||
# Lokale Analyse pro Team
|
||||
Write-Host ""
|
||||
Write-Host ">> Korrelationsanalyse..." -ForegroundColor Yellow
|
||||
|
||||
foreach ($ProjectKey in @("O2C404", "O2CCIB", "O2CZERO")) {
|
||||
$TeamName = switch ($ProjectKey) { "O2C404" { "Team 404" }; "O2CCIB" { "Team CIB" }; "O2CZERO" { "Team Zero" } }
|
||||
Write-Host ""
|
||||
Write-Host " === $TeamName ===" -ForegroundColor Cyan
|
||||
|
||||
$TeamBugs = $Bugs | Where-Object { $_.fields.project.key -eq $ProjectKey }
|
||||
$TeamFeatures = $Features | Where-Object { $_.fields.project.key -eq $ProjectKey }
|
||||
|
||||
Write-Host " $($TeamBugs.Count) Bugs, $($TeamFeatures.Count) Features"
|
||||
|
||||
# Korrelation: Wer hat in 14 Tagen vor Bug-Erstellung Features abgeschlossen?
|
||||
$Causation = @{}
|
||||
foreach ($Bug in $TeamBugs) {
|
||||
$BugCreated = [DateTime]::Parse($Bug.fields.created)
|
||||
$WindowStart = $BugCreated.AddDays(-14)
|
||||
|
||||
$RecentFeatures = $TeamFeatures | Where-Object {
|
||||
$_.fields.resolutiondate -and
|
||||
[DateTime]::Parse($_.fields.resolutiondate) -ge $WindowStart -and
|
||||
[DateTime]::Parse($_.fields.resolutiondate) -le $BugCreated
|
||||
}
|
||||
|
||||
foreach ($F in $RecentFeatures) {
|
||||
if ($F.fields.assignee) {
|
||||
$Dev = $F.fields.assignee.displayName
|
||||
if (-not $Causation[$Dev]) { $Causation[$Dev] = @{ Features = 0; BugKeys = @() } }
|
||||
if ($Causation[$Dev].BugKeys -notcontains $Bug.key) {
|
||||
$Causation[$Dev].BugKeys += $Bug.key
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Feature-Count pro Dev
|
||||
foreach ($F in $TeamFeatures) {
|
||||
if ($F.fields.assignee) {
|
||||
$Dev = $F.fields.assignee.displayName
|
||||
if (-not $Causation[$Dev]) { $Causation[$Dev] = @{ Features = 0; BugKeys = @() } }
|
||||
$Causation[$Dev].Features++
|
||||
}
|
||||
}
|
||||
|
||||
# Ausgabe Top 10
|
||||
Write-Host " --- Potenzielle Bug-Verursachung (14-Tage-Fenster) ---"
|
||||
$Sorted = $Causation.GetEnumerator() | Sort-Object { $_.Value.BugKeys.Count } -Descending
|
||||
foreach ($E in ($Sorted | Select-Object -First 10)) {
|
||||
$UniqueBugs = $E.Value.BugKeys.Count
|
||||
$Feats = $E.Value.Features
|
||||
$Rate = if ($Feats -gt 0) { [Math]::Round($UniqueBugs / $Feats * 100, 0) } else { 0 }
|
||||
Write-Host (" {0,-30} Features:{1,4} | Bugs:{2,4} | Rate:{3,3}%" -f $E.Key, $Feats, $UniqueBugs, $Rate)
|
||||
}
|
||||
|
||||
# CSV
|
||||
$CsvData = $Sorted | ForEach-Object {
|
||||
[PSCustomObject]@{
|
||||
Team = $TeamName
|
||||
Developer = $_.Key
|
||||
Features = $_.Value.Features
|
||||
CorrelatedBugs = $_.Value.BugKeys.Count
|
||||
BugRate = if ($_.Value.Features -gt 0) { [Math]::Round($_.Value.BugKeys.Count / $_.Value.Features * 100, 0) } else { 0 }
|
||||
}
|
||||
}
|
||||
$CsvData | Export-Csv "$OutputDir\$ProjectKey-causation.csv" -Delimiter ";" -Encoding UTF8 -NoTypeInformation
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "============================================================" -ForegroundColor Green
|
||||
Write-Host " FERTIG! API-Calls: ~$([Math]::Ceiling(($Bugs.Count + $Features.Count) / 1000) + 2)" -ForegroundColor Green
|
||||
Write-Host " Ergebnisse in $OutputDir" -ForegroundColor Green
|
||||
Write-Host "============================================================" -ForegroundColor Green
|
||||
Reference in New Issue
Block a user