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,138 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Findet das aelteste und neueste Ticket pro Person (Tenure-Analyse)
|
||||
.NOTES
|
||||
BULK-Ansatz: Nur 2-3 API-Calls statt 70+
|
||||
Holt ALLE Issues der relevanten Projekte und gruppiert lokal nach Assignee
|
||||
#>
|
||||
|
||||
$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 " Tenure-Analyse (Bulk-Abfrage)"
|
||||
Write-Host " Strategie: Alle Issues holen, lokal nach Assignee gruppieren"
|
||||
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=assignee,created,updated,project"
|
||||
Write-Host " Lade Issues $StartAt - $($StartAt + $PageSize)..." -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
|
||||
}
|
||||
|
||||
# EINE Bulk-Abfrage: Alle Issues aus allen Team-Projekten
|
||||
$Projects = "O2C404, O2CCIB, O2CZERO, O2COS, O2CDEVOPS, O2CSYS, O2CBS, O2CQST, TTTI"
|
||||
$Jql = "project in ($Projects) ORDER BY created ASC"
|
||||
|
||||
Write-Host ""
|
||||
Write-Host ">> Bulk-Abfrage: Alle Issues aus $Projects" -ForegroundColor Yellow
|
||||
$AllIssues = Invoke-JiraBulk -Jql $Jql -MaxTotal 25000
|
||||
|
||||
Write-Host ""
|
||||
Write-Host " Gesamt: $($AllIssues.Count) Issues geladen" -ForegroundColor Cyan
|
||||
|
||||
# Lokal nach Assignee gruppieren
|
||||
Write-Host ""
|
||||
Write-Host ">> Gruppiere nach Assignee..." -ForegroundColor Yellow
|
||||
|
||||
$ByAssignee = @{}
|
||||
foreach ($Issue in $AllIssues) {
|
||||
if ($Issue.fields.assignee) {
|
||||
$Name = $Issue.fields.assignee.displayName
|
||||
if (-not $ByAssignee[$Name]) {
|
||||
$ByAssignee[$Name] = @{ Issues = @(); FirstCreated = $null; LastUpdated = $null }
|
||||
}
|
||||
$Created = $Issue.fields.created.Substring(0, 10)
|
||||
$Updated = $Issue.fields.updated.Substring(0, 10)
|
||||
$ByAssignee[$Name].Issues += $Issue.key
|
||||
if (-not $ByAssignee[$Name].FirstCreated -or $Created -lt $ByAssignee[$Name].FirstCreated) {
|
||||
$ByAssignee[$Name].FirstCreated = $Created
|
||||
$ByAssignee[$Name].FirstTicket = $Issue.key
|
||||
$ByAssignee[$Name].FirstProject = $Issue.fields.project.key
|
||||
}
|
||||
if (-not $ByAssignee[$Name].LastUpdated -or $Updated -gt $ByAssignee[$Name].LastUpdated) {
|
||||
$ByAssignee[$Name].LastUpdated = $Updated
|
||||
$ByAssignee[$Name].LastTicket = $Issue.key
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host " $($ByAssignee.Count) verschiedene Assignees gefunden"
|
||||
|
||||
# Ergebnisse aufbereiten
|
||||
$Results = @()
|
||||
foreach ($Entry in ($ByAssignee.GetEnumerator() | Sort-Object { $_.Value.FirstCreated })) {
|
||||
$Name = $Entry.Key
|
||||
$Data = $Entry.Value
|
||||
$DaysAgo = ((Get-Date) - [DateTime]::Parse($Data.LastUpdated)).Days
|
||||
$Status = if ($DaysAgo -le 30) { "Aktiv" } elseif ($DaysAgo -le 90) { "Wenig aktiv" } else { "INAKTIV ($DaysAgo Tage)" }
|
||||
|
||||
$Results += [PSCustomObject]@{
|
||||
Person = $Name
|
||||
FirstTicket = $Data.FirstTicket
|
||||
FirstDate = $Data.FirstCreated
|
||||
LastDate = $Data.LastUpdated
|
||||
LastTicket = $Data.LastTicket
|
||||
Status = $Status
|
||||
Project = $Data.FirstProject
|
||||
IssueCount = $Data.Issues.Count
|
||||
}
|
||||
}
|
||||
|
||||
# CSV speichern
|
||||
$Results | Export-Csv "$OutputDir\tenure.csv" -Delimiter ";" -Encoding UTF8 -NoTypeInformation
|
||||
|
||||
# Ausgabe
|
||||
Write-Host ""
|
||||
Write-Host "=== Sortiert nach Startdatum (Top 40) ==="
|
||||
$Results | Select-Object -First 40 | ForEach-Object {
|
||||
$Color = if ($_.Status -eq "Aktiv") { "Green" } elseif ($_.Status -match "INAKTIV") { "Red" } else { "Yellow" }
|
||||
Write-Host (" {0,-35} {1} bis {2} [{3}] ({4} Issues)" -f $_.Person, $_.FirstDate, $_.LastDate, $_.Status, $_.IssueCount) -ForegroundColor $Color
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "FERTIG! $($Results.Count) Personen in $OutputDir\tenure.csv" -ForegroundColor Green
|
||||
Write-Host "API-Calls: ~$([Math]::Ceiling($AllIssues.Count / 1000) + 1) (statt $($ByAssignee.Count * 2))" -ForegroundColor Cyan
|
||||
Reference in New Issue
Block a user