feat(statistics): persist ranges and align history columns
Persist per-day download volume, outcomes, active transfer time, and provider results so today, seven-day, and 30-day views use real partial-window data. Preserve existing all-time counters while extending totals with newly recorded result metrics. Rebuild the history table around one resizable persisted grid shared by headers and rows, with synchronized overflow and consistent alignment across window sizes.
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
import { getProviderUsageDayKey } from "./provider-daily-limits";
|
||||
import type { DebridProvider, StatisticsLedger, StatisticsProviderBucket } from "./types";
|
||||
|
||||
export interface StatisticsAggregate {
|
||||
downloadedBytes: number;
|
||||
measuredBytes: number;
|
||||
completedFiles: number;
|
||||
failedFiles: number;
|
||||
activeDownloadMs: number;
|
||||
averageSpeedBps: number | null;
|
||||
coveredDays: number;
|
||||
startedAt: number;
|
||||
providers: Partial<Record<DebridProvider, StatisticsProviderBucket>>;
|
||||
}
|
||||
|
||||
function emptyProviderBucket(): StatisticsProviderBucket {
|
||||
return { bytes: 0, completed: 0, failed: 0 };
|
||||
}
|
||||
|
||||
function localWindowStart(nowMs: number, days: number): string {
|
||||
const date = new Date(nowMs);
|
||||
date.setHours(0, 0, 0, 0);
|
||||
date.setDate(date.getDate() - Math.max(0, Math.floor(days) - 1));
|
||||
return getProviderUsageDayKey(date.getTime());
|
||||
}
|
||||
|
||||
export function aggregateStatisticsRange(
|
||||
ledger: StatisticsLedger | null | undefined,
|
||||
days: number | null,
|
||||
nowMs = Date.now()
|
||||
): StatisticsAggregate {
|
||||
const endKey = getProviderUsageDayKey(nowMs);
|
||||
const startKey = days === null ? "0000-00-00" : localWindowStart(nowMs, days);
|
||||
const aggregate: StatisticsAggregate = {
|
||||
downloadedBytes: 0,
|
||||
measuredBytes: 0,
|
||||
completedFiles: 0,
|
||||
failedFiles: 0,
|
||||
activeDownloadMs: 0,
|
||||
averageSpeedBps: null,
|
||||
coveredDays: 0,
|
||||
startedAt: Math.max(0, Number(ledger?.startedAt) || nowMs),
|
||||
providers: {}
|
||||
};
|
||||
for (const day of ledger?.days ?? []) {
|
||||
if (day.day < startKey || day.day > endKey) continue;
|
||||
aggregate.coveredDays += 1;
|
||||
aggregate.downloadedBytes += day.downloadedBytes;
|
||||
aggregate.measuredBytes += day.measuredBytes;
|
||||
aggregate.completedFiles += day.completedFiles;
|
||||
aggregate.failedFiles += day.failedFiles;
|
||||
aggregate.activeDownloadMs += day.activeDownloadMs;
|
||||
for (const [provider, bucket] of Object.entries(day.providers) as Array<[DebridProvider, StatisticsProviderBucket | undefined]>) {
|
||||
if (!bucket) continue;
|
||||
const target = aggregate.providers[provider] ?? emptyProviderBucket();
|
||||
target.bytes += bucket.bytes;
|
||||
target.completed += bucket.completed;
|
||||
target.failed += bucket.failed;
|
||||
aggregate.providers[provider] = target;
|
||||
}
|
||||
}
|
||||
if (aggregate.activeDownloadMs > 0 && aggregate.measuredBytes > 0) {
|
||||
aggregate.averageSpeedBps = Math.floor(aggregate.measuredBytes * 1000 / aggregate.activeDownloadMs);
|
||||
}
|
||||
return aggregate;
|
||||
}
|
||||
+29
-6
@@ -33,15 +33,37 @@ export type ExtractCpuPriority = "high" | "middle" | "low";
|
||||
export type HistoryRetentionMode = "never" | "session" | "permanent";
|
||||
export type LogStorageLocation = "appdata" | "desktop";
|
||||
|
||||
export interface BandwidthScheduleEntry {
|
||||
export interface BandwidthScheduleEntry {
|
||||
id: string;
|
||||
startHour: number;
|
||||
endHour: number;
|
||||
speedLimitKbps: number;
|
||||
enabled: boolean;
|
||||
}
|
||||
|
||||
export interface DownloadStats {
|
||||
}
|
||||
|
||||
export interface StatisticsProviderBucket {
|
||||
bytes: number;
|
||||
completed: number;
|
||||
failed: number;
|
||||
}
|
||||
|
||||
export interface StatisticsDayBucket {
|
||||
day: string;
|
||||
downloadedBytes: number;
|
||||
measuredBytes: number;
|
||||
completedFiles: number;
|
||||
failedFiles: number;
|
||||
activeDownloadMs: number;
|
||||
providers: Partial<Record<DebridProvider, StatisticsProviderBucket>>;
|
||||
}
|
||||
|
||||
export interface StatisticsLedger {
|
||||
version: 1;
|
||||
startedAt: number;
|
||||
days: StatisticsDayBucket[];
|
||||
}
|
||||
|
||||
export interface DownloadStats {
|
||||
totalDownloaded: number;
|
||||
totalDownloadedAllTime: number;
|
||||
totalFiles?: number;
|
||||
@@ -52,8 +74,9 @@ export interface DownloadStats {
|
||||
appSessionStartedAt: number;
|
||||
sessionRuntimeMs: number;
|
||||
totalRuntimeMs: number;
|
||||
runtimeMeasuredAt: number;
|
||||
}
|
||||
runtimeMeasuredAt: number;
|
||||
statistics?: StatisticsLedger;
|
||||
}
|
||||
|
||||
export interface DebridAccountStatus {
|
||||
accountId: string;
|
||||
|
||||
Reference in New Issue
Block a user