public/RepoIssueTimeTracking.ps1
$TT_START = "<TT>" $TT_END = "</TT>" Set-MyInvokeCommandAlias -Alias "GetIssueComments" -Command "gh issue view {number} -R {owner}/{repo} --json {attributes}" <# .SYNOPSIS Adds time tracking to an issue in a GitHub repository. #> function Add-RepoIssueTimeTracking{ [CmdletBinding(SupportsShouldProcess)] [Alias("att")] param( [Parameter(Mandatory,Position=0)] [int]$Number, [Parameter(Mandatory,Position=1)] [string]$Time, [Parameter(Mandatory,Position=2)] [string]$Comment, [Parameter()] [switch]$NoCheckbox, [Parameter()] [string]$Owner, [Parameter()] [string]$Repo ) begin{ $owner,$repo = Get-Environment $Owner $Repo # Error if parameters not set. No need to check repo too. if([string]::IsNullOrEmpty($Owner) -or [string]::IsNullOrEmpty($Repo)){ "[Add-RepoIssueTimeTracking] Owner and Repo parameters are required" | Write-Error return $null } } process { # Test time format $result = ConvertTo-Minutes -Tag $Time if($null -eq $result){ "Wrong time format [$time]" | Write-Error return $null } # Build tag string $timeTag = $TT_START+"{time}"+$TT_END $timeTag = $timeTag -replace "{time}",$Time $body = "$timeTag $Comment" #Add checkbox $body = $NoCheckbox ? $body : "- [ ] $body" $result = Add-RepoIssueComment -Number $Number -Owner $Owner -Repo $Repo -Comment $body -WhatIf:$WhatIfPreference return $result } } Export-ModuleMember -Function Add-RepoIssueTimeTracking -Alias "att" <# .SYNOPSIS Gets time tracking from an issue in a GitHub repository. #> function Get-RepoIssueTimeTracking{ [CmdletBinding()] [Alias("gtt")] param( [Parameter(Mandatory,ValueFromPipeline,ValueFromPipelineByPropertyName,Position=0)][int]$Number, [Parameter()] [string]$Owner, [Parameter()] [string]$Repo ) begin{ $owner,$repo = Get-Environment $Owner $Repo # Error if parameters not set. No need to check repo too. if([string]::IsNullOrEmpty($Owner) -or [string]::IsNullOrEmpty($Repo)){ "[Get-RepoIssueTimeTracking] Owner and Repo parameters are required" | Write-Error return $null } } process{ $result = GetRepoIssueTimeTracking -Number $Number -Owner $Owner -Repo $Repo if($null -eq $result){ return $null } $ret = [PSCustomObject]@{ Title = $result.title Repo = $result.Repo Owner = $result.Owner Number = $result.Number Comments = $result.Comments Times = $result.Times TotalMinutes = $result.TotalMinutes Total = $result.Total Url = $result.url } return $ret } } Export-ModuleMember -Function Get-RepoIssueTimeTracking -Alias "gtt" <# .SYNOPSIS Gets time tracking records from an issue in a GitHub repository. #> function Get-RepoIssueTimeTrackingRecords{ [CmdletBinding()] [Alias("gttr")] param( [Parameter(Mandatory,ValueFromPipeline,ValueFromPipelineByPropertyName,Position=0)][int]$Number, [Parameter()] [string]$Owner, [Parameter()] [string]$Repo ) begin{ $owner,$repo = Get-Environment $Owner $Repo # Error if parameters not set. No need to check repo too. if([string]::IsNullOrEmpty($Owner) -or [string]::IsNullOrEmpty($Repo)){ "[Get-RepoIssueTimeTrackingRecords] Owner and Repo parameters are required" | Write-Error return $null } } process{ $result = GetRepoIssueTimeTracking -Number $Number -Owner $Owner -Repo $Repo $ret = @() foreach($record in $result.Records){ $ret += [PSCustomObject]@{ Number = $Number Text = $record.Text Time = $record.Time CreatedAt = $record.CreatedAt Repo = $Repo Owner = $Owner Url = $record.Url } } return $ret } } Export-ModuleMember -Function Get-RepoIssueTimeTrackingRecords -Alias "gttr" function GetRepoIssueTimeTracking{ [CmdletBinding()] param( [Parameter(Mandatory)] [int]$Number, [Parameter(Mandatory)] [string]$Owner, [Parameter(Mandatory)] [string]$Repo ) process { $param = @{ owner = $Owner ; repo = $Repo ; number = $Number ; attributes = 'title,comments,url'} $result = Invoke-MyCommandJson -Command "GetIssueComments" -Parameters $param if(($null -eq $result) -or ($result | Test-NotFound )){ "Error getting comments for issue $Number for $Owner/$Repo" | Write-Error return $null } $title = $result.title $url = $result.url $comments = $result.comments $totalMinutes = 0 $totalTimes = 0 $records = @() foreach ($comment in $comments){ $body = $comment.body $md = $body | ConvertFrom-Markdown $tags = $md.Tokens.inline | Where-Object {$_.Tag -eq $TT_END} | Select-Object -Property PreviousSibling | Select-Object -ExpandProperty PreviousSibling $contents = $tags.Content # Skipp comments without time tags If($null -eq $contents){ continue } $commentTotalMinutes = 0 # We allow to have more than one time tag in a comment foreach($content in $contents){ $totalTimes++ $time = $content.Text.Substring($content.Start,$content.Length) #Control the time tag syntax $commentTime = ConvertTo-Minutes $time if($null -eq $commentTime){ Write-Warning -Message "Skipping wrong time [ $time ] $($content.text)" continue } # Add to total comment minutes $commentTotalMinutes += $commentTime } $totalMinutes += $commentTotalMinutes $records += [PSCustomObject]@{ Time = $commentTotalMinutes Text = $comment.body CreatedAt = $comment.createdAt Url = $comment.url } } $ret = [PSCustomObject]@{ Title = $title Repo = $Repo Owner = $Owner Number = $Number Comments = $comments.Count Times = $totalTimes Records = $records TotalMinutes = $totalMinutes Total = ConvertTo-TimeString -Minutes $totalMinutes Url = $url } return $ret } } <# .SYNOPSIS Converts a time tag to minutes. #> function ConvertTo-Minutes([string] $Tag){ # TODO: Implemnet # calculate the time based on m,h,d if (-not ($Tag -match "^\d+[mhd]$")) { "Invalid time tag: $Tag" | Write-Verbose return $null } "Match found: $($matches[0])" | Write-Verbose [int] $number = $Tag -replace '\D', '' # Remove non-digit characters $char = $Tag -replace '\d', '' # Remove digit characters switch ($char.ToLower()) { 'm' { $time = $number } # Minutes 'h' { $time = $number * 60 } # Hours 'd' { $time = $number * 60 * 8 } # Days Default { "Invalid time tag: $Tag" | Write-Verbose return $null } } return $time } Export-ModuleMember -Function ConvertTo-Minutes function ConvertTo-TimeString([int] $Minutes){ $hours = [math]::Floor($Minutes / 60) $minutes = $Minutes % 60 $ret = "{0}h {1}m" -f $hours,$minutes return $ret } |