Advertisement
Old-Lost

ProgressManager Class

May 31st, 2017
242
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. <#
  2.     ProgressManager class
  3.  
  4.     .SYNOPSIS
  5.         Implements the ProgressManager class.
  6.  
  7.     .DESCRIPTION
  8.         The ProgressManager class handles the work of creating and managing a progress bar.
  9.  
  10.     .PROPERTY Activity
  11.         Specifies the first line of text in the heading above the status bar. This text describes the activity whose progress is being reported.
  12.  
  13.     .PROPERTY Id
  14.         Specifies an ID that distinguishes each progress bar from the others. Use this parameter when you are creating more than one progress bar in a single command. If the progress bars do not have different IDs, they are superimposed instead of being displayed in a series.
  15.  
  16.     .PROPERTY ParentId
  17.         Specifies the parent activity of the current activity. The default is -1, meaning the current activity has no parent activity.
  18.  
  19.     .PROPERTY Format
  20.         Specifies a scriptblock that is used to turn the InputObject into a string for display in the progress bar. The default is { $_ | Out-String }.
  21.  
  22.     .PROPERTY Total
  23.         Specifies the total number of items. This is used to calculate almost every value displayed.
  24.  
  25.     .PROPERTY Completed
  26.         Specifies the number of items that have been processed thus far.
  27.  
  28.     .PROPERTY StartTime
  29.         Specifies the starting time of the operation. The default is the time the ProgressManager object is instantiated. Normally this value does not need to be set manually.
  30.  
  31.     .METHOD PercentComplete
  32.         Returns the percent complete, based on the Completed and Total properties.
  33.  
  34.     .METHOD PerSec
  35.         Returns the number of items processed per second.
  36.  
  37.     .METHOD Pending
  38.         Returns the number of items yet to be processed, based on the Completed and Total properties.
  39.  
  40.     .METHOD Remaining
  41.         Returns an estimate of the amount of time needed to process the remaining items.
  42.  
  43.     .METHOD Status
  44.         Returns the text that would be displayed in the progress bar.
  45.  
  46.     .METHOD WriteProgress
  47.         Displays the progress bar based on current values.
  48.  
  49.     .METHOD Update
  50.         Increments the number of items completed by one.
  51.  
  52.     .METHOD Complete
  53.         Hides the progress bar. Normally used after all objects have been processed.
  54.  
  55.     .NOTES
  56.         Uses [TimeSpan].Approximate script method from https://pastebin.com/Y55bA77H
  57.         Based on Staffan Gustafsson's presentation from PS Conference EU 2017.
  58.  
  59. #>
  60. class ProgressManagerTime {
  61.     [datetime]$Date
  62.     [long]$Completed
  63.     ProgressManagerTime([long]$Completed) {
  64.         $this.Date = Get-Date
  65.         $this.Completed = $Completed
  66.     }
  67. }
  68.  
  69. class ProgressManager {
  70.     [string]$Activity
  71.     [int]$Id
  72.     [int]$ParentId
  73.     [scriptblock]$Format
  74.     [long]$Total
  75.     [long]$Completed
  76.     [datetime]$StartTime
  77.     hidden [datetime]$LastUpdate
  78.     hidden [long]$ListSize
  79.     hidden [System.Collections.Generic.List`1[ProgressManagerTime]]$Times
  80.     hidden static [string]$DefaultActivity = 'Watching Progress'
  81.     hidden static [scriptblock]$DefaultFormat = { $_.ToString() }
  82.     ProgressManager() {
  83.         $this.Init(0, (Get-Random -Maximum 1000), -1, [ProgressManager]::DefaultActivity, [ProgressManager]::DefaultFormat)
  84.     }
  85.     ProgressManager([long]$Total) {
  86.         $this.Init($Total, (Get-Random -Maximum 1000), -1, [ProgressManager]::DefaultActivity, [ProgressManager]::DefaultFormat)
  87.     }
  88.     ProgressManager([long]$Total, [int]$Id, [int]$ParentId) {
  89.         $this.Init($Total, $Id, $ParentId, [ProgressManager]::DefaultActivity, [ProgressManager]::DefaultFormat)
  90.     }
  91.     ProgressManager([long]$Total, [int]$Id, [int]$ParentId, [string]$Activity) {
  92.         $this.Init($Total, $Id, $ParentId, $Activity, [ProgressManager]::DefaultFormat)
  93.     }
  94.     ProgressManager([long]$Total, [int]$Id, [int]$ParentId, [string]$Activity, [scriptblock]$Format) {
  95.         $this.Init($Total, $Id, $ParentId, $Activity, $Format)
  96.     }
  97.     hidden Init([long]$Total, [int]$Id, [int]$ParentId, [string]$Activity, [scriptblock]$Format) {
  98.         $this.Id = $Id
  99.         $this.ParentId = $ParentId
  100.         $this.Activity = $Activity
  101.         $this.Total = $Total
  102.         $this.Format = $Format
  103.         $this.Completed = 0
  104.         $this.StartTime = Get-Date
  105.         $this.LastUpdate = (Get-Date).AddMilliSeconds(-501)
  106.         $this.ListSize = [Math]::Max(10, [long]($Total / 10))
  107.         $this.Times = @()
  108.         [Void]$this.Times.Add([ProgressManagerTime]::new($this.Completed))
  109.         $this.Times[0].Date = $this.LastUpdate
  110.     }
  111.     [int]PercentComplete() {
  112.         if ($this.Total -lt 1) { return 0 }
  113.         return ([Math]::Min(($this.Completed / $this.Total * 100), 100))
  114.     }
  115.     [double]PerSec() {
  116.         return (1 / [Math]::Max((($this.Times[-1].Date - $this.Times[0].Date).TotalMilliseconds / [Math]::Max(($this.Times[-1].Completed - $this.Times[0].Completed), 1) / 1000), 0.000001))
  117.     }
  118.     [long]Pending() { return ($this.Total - $this.Completed) }
  119.     [string]Remaining() {
  120.         if ($this.Completed -eq 0) {
  121.             return 'Unknown'
  122.         } else {
  123.             return ('~' + (New-TimeSpan -Seconds ([Math]::Min(($this.Pending() / $this.PerSec()), [int32]::MaxValue))).Approximate())
  124.         }
  125.     }
  126.     [string]Status($Item) {
  127.         [timespan]$Duration = (New-TimeSpan $this.StartTime) + [timespan]1000
  128.         $RunTime = (New-TimeSpan -Days $Duration.Days -Hours $Duration.Hours -Minutes $Duration.Minutes -Seconds $Duration.Seconds).ToString()
  129.         $PerSecond = $this.PerSec()
  130.         [double]$Per, [string]$Unit = switch ($PerSecond) { { $_ -gt 10 } { $PerSecond, 'Sec'; break }
  131.             { $_ -gt 0.166667 } { ($PerSecond * 60), 'Min'; break }
  132.             default { ($PerSecond * 3600), 'Hr' }
  133.         }
  134.         [string]$ItemString = try { $Item | % $this.Format | Out-String -Stream } catch { $Item | Out-String -Stream }
  135.         return (('StartTime: {0} | RunTime: {3} | Remaining: {6} | {8:N0} = {1:N0} << {2:N0} | {4:N0} / {5} | Processing: {7}...' -f $this.StartTime, $this.Completed, $this.Pending(), $RunTime, $Per, $Unit, $this.Remaining(), ($ItemString -replace '(.{1,60})(.*)', '$1'), $this.Total))
  136.     }
  137.     [void]WriteProgress($Item) {
  138.         [boolean]$ShouldOutput = $true
  139.         if ($this.Times[-1].Completed -lt $this.Completed) {
  140.             [Void]$this.Times.Add([ProgressManagerTime]::new($this.Completed))
  141.             $ShouldOutput = (($this.Times[-1].Date - $this.LastUpdate).TotalMilliseconds -gt 500)
  142.         }
  143.         if ($ShouldOutput) {
  144.             if ($this.Times.Count -gt $this.ListSize) { $this.Times.RemoveRange(0, ($this.Times.Count - $this.ListSize)) }
  145.             Write-Progress -Activity $this.Activity -Id $this.Id -ParentId $this.ParentId -Status $this.Status($Item) -PercentComplete $this.PercentComplete()
  146.             $this.LastUpdate = Get-Date
  147.         }
  148.     }
  149.     [void]Update() { $this.Completed += 1 }
  150.     [void]Complete() {
  151.         Write-Progress -Activity $this.Activity -Id $this.Id -Completed
  152.     }
  153. }
  154.  
  155. Update-TypeData -Force -TypeName TimeSpan -MemberType ScriptMethod -MemberName Approximate -Value {
  156.     if ($args.Count -gt 1) {
  157.         throw ('Cannot find an overload for "{0}" and the argument count: "{1}".' -f 'Approximate', $Args.Count)
  158.     }
  159.     function Round([double]$Number, [string]$Unit) {
  160.         $Number, [string]$Half = if ((($Number % 1) -gt 0.25) -and (($Number % 1) -lt 0.75)) { ($Number - 0.5), [char][byte]189 } else { $Number, '' }
  161.         '{0}{1} {2}' -f [System.Math]::Round($Number, 0), $Half, $Unit
  162.     }
  163.     $Units = @('seconds', 'a minute', 'minutes', 'an hour', 'hours', 'days')
  164.     if ($args.Count -gt 0 -and $args[0] -is [array]) {
  165.         for ($i = 0; ($i -lt $Units.Count) -and ($i -lt $args[0].Count); $i++) {
  166.             $Units[$i] = $args[0][$i]
  167.         }
  168.     }
  169.     $result = switch ($this.TotalSeconds) {
  170.         { $_ -le 3 }      { "<5 $($Units[0])"; break }
  171.         { $_ -le 7.5 }      { "5 $($Units[0])"; break }
  172.         { $_ -le 47.5 }     {
  173.             "$([System.Math]::Round((($this.TotalSeconds - 2.5) - (($this.TotalSeconds - 2.5) % 5) + 5), 0)) $($Units[0])"
  174.             break
  175.         }
  176.         { $_ -le 75 }    { $Units[1]; break }
  177.         { $_ -le 2700 }   {
  178.             Round -Number $this.TotalMinutes -Unit $Units[2]
  179.             break
  180.         } # 45 minutes or less
  181.         { $_ -le 4500 }   { $Units[3]; break } # 45 minutes to 1.25 hours
  182.         { $_ -le 173700 }  {
  183.             Round -Number $this.TotalHours -Unit $Units[4]
  184.             break
  185.         } # less than 2 days 15 minutes
  186.         default {
  187.             Round -Number $this.TotalDays -Unit $Units[5]
  188.         }
  189.     }
  190.     return $result
  191. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement