src/PowerShell/Frecency.ps1
|
# cdp PowerShell domain: Frecency.ps1 # Loaded by src/cdp.psm1; do not dot-source peer files. $script:CdpFrecencyHistoryLimit = 10000 $script:CdpFrecencyScoreScale = 1000000 $script:CdpSecondsPerDay = 86400 $script:CdpUnixEpochTicks = 621355968000000000 function Test-CdpFrecencyEnabled { if ([string]::IsNullOrWhiteSpace($env:CDP_FRECENCY)) { return $true } $env:CDP_FRECENCY.Trim().ToLowerInvariant() -notin @('0', 'false', 'off', 'no') } function Get-CdpUtcEpoch { param([Parameter(Mandatory = $false)][DateTime]$Value = [DateTime]::UtcNow) [long][Math]::Floor( ($Value.ToUniversalTime().Ticks - $script:CdpUnixEpochTicks) / 10000000 ) } function ConvertTo-CdpFrecencyEpoch { param([Parameter(Mandatory = $false)][AllowNull()][object]$Value) if ($Value -is [DateTimeOffset]) { return Get-CdpUtcEpoch -Value $Value.UtcDateTime } if ($Value -is [DateTime]) { return Get-CdpUtcEpoch -Value $Value } if ($Value -isnot [string]) { return $null } $match = [regex]::Match($Value, '^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2})') if (-not $match.Success) { return $null } $timestamp = [DateTime]::MinValue $styles = [Globalization.DateTimeStyles]::AssumeUniversal -bor [Globalization.DateTimeStyles]::AdjustToUniversal if (-not [DateTime]::TryParseExact( $match.Groups[1].Value, "yyyy-MM-dd'T'HH:mm:ss", [Globalization.CultureInfo]::InvariantCulture, $styles, [ref]$timestamp )) { return $null } Get-CdpUtcEpoch -Value $timestamp } function ConvertTo-CdpFrecencyVisits { param([Parameter(Mandatory = $false)][AllowNull()][object]$Value) if ($null -eq $Value) { return $null } $numericTypes = @( [TypeCode]::Byte, [TypeCode]::SByte, [TypeCode]::Int16, [TypeCode]::UInt16, [TypeCode]::Int32, [TypeCode]::UInt32, [TypeCode]::Int64, [TypeCode]::UInt64, [TypeCode]::Single, [TypeCode]::Double, [TypeCode]::Decimal ) if ([Type]::GetTypeCode($Value.GetType()) -notin $numericTypes) { return $null } $number = [double]$Value if ([double]::IsNaN($number) -or [double]::IsInfinity($number) -or $number -lt 0 -or [Math]::Floor($number) -ne $number) { return $null } [long][Math]::Min(1000, [Math]::Max(1, $number)) } function Get-CdpFrecencyMetric { param( [Parameter(Mandatory = $true)][object]$RecentProject, [Parameter(Mandatory = $true)][long]$NowEpoch ) $last = ConvertTo-CdpFrecencyEpoch -Value $RecentProject.lastVisitedAt $visits = ConvertTo-CdpFrecencyVisits -Value $RecentProject.visitCount if ($null -eq $last -or $null -eq $visits) { return [PSCustomObject]@{ Last = [long]0; Visits = [long]0; Score = [long]0 } } $ageSeconds = [Math]::Max([long]0, $NowEpoch - $last) $ageDays = [long][Math]::Floor($ageSeconds / $script:CdpSecondsPerDay) $score = [long][Math]::Floor( ($visits * $script:CdpFrecencyScoreScale) / ($ageDays + 1) ) [PSCustomObject]@{ Last = [long]$last; Visits = [long]$visits; Score = $score } } function Get-CdpFrecencyMap { param( [Parameter(Mandatory = $true)][AllowEmptyCollection()][object[]]$RecentProjects, [Parameter(Mandatory = $true)][long]$NowEpoch ) $map = New-Object 'System.Collections.Generic.Dictionary[string,object]' ` ([StringComparer]::Ordinal) foreach ($recent in @($RecentProjects | Select-Object -First $script:CdpFrecencyHistoryLimit)) { if ($null -eq $recent -or $recent.rootPath -isnot [string]) { continue } $rootPath = [string]$recent.rootPath $candidate = Get-CdpFrecencyMetric -RecentProject $recent -NowEpoch $NowEpoch if (-not $map.ContainsKey($rootPath)) { $map[$rootPath] = $candidate; continue } $current = $map[$rootPath] if ($candidate.Last -gt $current.Last -or ($candidate.Last -eq $current.Last -and $candidate.Visits -gt $current.Visits)) { $map[$rootPath] = $candidate } } $map } function Sort-CdpProjectsForDisplay { param( [Parameter(Mandatory = $true)][AllowEmptyCollection()][object[]]$Projects, [Parameter(Mandatory = $false)][AllowNull()][object]$State, [Parameter(Mandatory = $false)][long]$NowEpoch = 0 ) if ($NowEpoch -le 0) { $NowEpoch = Get-CdpUtcEpoch } if (-not $PSBoundParameters.ContainsKey('State')) { $State = Get-CdpState } $recentProjects = [object[]]@() if ($null -ne $State -and $null -ne $State.PSObject.Properties['recentProjects'] -and $State.recentProjects -is [System.Array]) { $recentProjects = (ConvertTo-CdpJsonArrayValue -Value $State.recentProjects).Value } $history = Get-CdpFrecencyMap -RecentProjects $recentProjects -NowEpoch $NowEpoch $enabled = Test-CdpFrecencyEnabled $ranked = for ($index = 0; $index -lt $Projects.Count; $index++) { $project = $Projects[$index] $metric = [PSCustomObject]@{ Last = [long]0; Visits = [long]0; Score = [long]0 } if ($enabled -and $project.rootPath -is [string] -and $history.ContainsKey($project.rootPath)) { $metric = $history[$project.rootPath] } [PSCustomObject]@{ Project = $project; Index = $index PinRank = if (Test-CdpProjectPinned -Project $project) { 0 } else { 1 } Score = $metric.Score; Last = $metric.Last; Visits = $metric.Visits } } @($ranked | Sort-Object -Property ` @{ Expression = 'PinRank'; Ascending = $true }, @{ Expression = 'Score'; Descending = $true }, @{ Expression = 'Last'; Descending = $true }, @{ Expression = 'Visits'; Descending = $true }, @{ Expression = 'Index'; Ascending = $true } | ForEach-Object { $_.Project }) } |