-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathUsageData.cs
More file actions
97 lines (81 loc) · 3.51 KB
/
Copy pathUsageData.cs
File metadata and controls
97 lines (81 loc) · 3.51 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
namespace ClaudeUsageMonitor;
/// <summary>
/// Data model for GET https://api.anthropic.com/api/oauth/usage
///
/// Response:
/// {
/// "five_hour": { "utilization": 42.5, "resets_at": "2025-02-17T18:00:00Z" },
/// "seven_day": { "utilization": 13.0, "resets_at": "2025-02-19T07:00:00Z" },
/// "extra_usage": { "is_enabled": true, "monthly_limit": 5000, "used_credits": 1250, "utilization": 25.0 }
/// }
/// </summary>
public sealed class UsageData
{
public double SessionPercent { get; set; }
public DateTime? SessionResetsAt { get; set; }
public double WeeklyPercent { get; set; }
public DateTime? WeeklyResetsAt { get; set; }
public bool HasWeekly { get; set; }
// Extra Usage (Pay-as-you-go Overage)
public bool ExtraEnabled { get; set; }
public double ExtraPercent { get; set; }
public decimal ExtraUsedDollars { get; set; }
public decimal ExtraLimitDollars { get; set; }
public DateTime FetchedAt { get; set; } = DateTime.Now;
// --- Computed Properties ---
public TimeSpan SessionResetIn => TimeUntil(SessionResetsAt);
public TimeSpan WeeklyResetIn => TimeUntil(WeeklyResetsAt);
public string SessionResetText => FormatSpan(SessionResetIn);
public string WeeklyResetText => FormatSpan(WeeklyResetIn);
// Pacing: linear expected usage based on elapsed time in the window
private static readonly TimeSpan SessionWindow = TimeSpan.FromHours(5);
private static readonly TimeSpan WeeklyWindow = TimeSpan.FromDays(7);
public double SessionExpectedPercent => ExpectedPercent(SessionResetsAt, SessionWindow);
public double WeeklyExpectedPercent => ExpectedPercent(WeeklyResetsAt, WeeklyWindow);
public double SessionPaceDiff => SessionPercent - SessionExpectedPercent;
public double WeeklyPaceDiff => WeeklyPercent - WeeklyExpectedPercent;
public string SessionPaceText => FormatPace(SessionPaceDiff);
public string WeeklyPaceText => FormatPace(WeeklyPaceDiff);
public string TooltipText
{
get
{
var s = $"Session: {SessionPercent:0}% | Reset: {SessionResetText}";
if (HasWeekly)
{
s += $"\nWeekly: {WeeklyPercent:0}% ({WeeklyPaceText})";
}
if (ExtraEnabled) s += $"\nExtra: ${ExtraUsedDollars:F2}/${ExtraLimitDollars:F2}";
s += $"\nUpdated: {FetchedAt:HH:mm:ss}";
return s.Length > 127 ? s[..127] : s;
}
}
private static TimeSpan TimeUntil(DateTime? utc)
{
if (!utc.HasValue) return TimeSpan.Zero;
var d = utc.Value.ToLocalTime() - DateTime.Now;
return d > TimeSpan.Zero ? d : TimeSpan.Zero;
}
private static string FormatSpan(TimeSpan ts)
{
if (ts <= TimeSpan.Zero) return "--:--";
if (ts.TotalDays >= 1) return $"{(int)ts.TotalDays}d {ts.Hours}h";
if (ts.TotalHours >= 1) return $"{(int)ts.TotalHours}h {ts.Minutes}m";
return $"{ts.Minutes}m";
}
private static double ExpectedPercent(DateTime? resetsAtUtc, TimeSpan window)
{
if (!resetsAtUtc.HasValue) return 0;
var windowStart = resetsAtUtc.Value.ToLocalTime() - window;
var elapsed = DateTime.Now - windowStart;
if (elapsed <= TimeSpan.Zero) return 0;
var pct = elapsed.TotalSeconds / window.TotalSeconds * 100;
return Math.Clamp(pct, 0, 100);
}
private static string FormatPace(double diff)
{
if (diff >= 5) return $"+{diff:0}% ahead";
if (diff <= -5) return $"{diff:0}% under";
return "on pace";
}
}