| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889 |
- import dayjs from 'dayjs'
- import type { LogsBarChartDatum } from './ProjectUsage.metrics'
- /**
- * Configuration for chart bucket sizes based on time interval
- */
- const BUCKET_CONFIG = {
- '1hr': {
- bucketMinutes: 2, // 2-minute buckets
- expectedBuckets: 30, // 60 minutes / 2 = 30 buckets
- },
- '1day': {
- bucketMinutes: 60, // 1-hour buckets
- expectedBuckets: 24, // 24 hours
- },
- '7day': {
- bucketMinutes: 360, // 6-hour buckets
- expectedBuckets: 28, // 168 hours / 6 = 28 buckets
- },
- } as const
- type IntervalKey = keyof typeof BUCKET_CONFIG
- /**
- * Normalizes chart data to consistent bucket sizes regardless of backend data density.
- *
- * For 1hr interval: Creates 30 buckets of 2 minutes each
- * For 1day interval: Creates 24 buckets of 1 hour each
- * For 7day interval: Creates 28 buckets of 6 hours each
- *
- * This ensures consistent bar width in charts and proper data aggregation.
- *
- * @param data - Raw chart data from backend
- * @param interval - Time interval key ('1hr', '1day', '7day')
- * @param endDate - End date for the chart (defaults to now)
- * @returns Array of exactly the expected number of buckets with aggregated data
- */
- export function normalizeChartBuckets(
- data: LogsBarChartDatum[],
- interval: IntervalKey,
- endDate: Date = new Date()
- ): LogsBarChartDatum[] {
- const config = BUCKET_CONFIG[interval]
- const { bucketMinutes, expectedBuckets } = config
- // Calculate start time based on expected buckets
- const end = dayjs(endDate)
- const start = end.subtract(expectedBuckets * bucketMinutes, 'minute')
- // Create empty buckets
- const buckets: LogsBarChartDatum[] = []
- let currentBucketStart = start
- for (let i = 0; i < expectedBuckets; i++) {
- buckets.push({
- timestamp: currentBucketStart.toISOString(),
- ok_count: 0,
- warning_count: 0,
- error_count: 0,
- })
- currentBucketStart = currentBucketStart.add(bucketMinutes, 'minute')
- }
- // If no data, return empty buckets
- if (!data || data.length === 0) {
- return buckets
- }
- // Aggregate data into buckets
- for (const datum of data) {
- const datumTime = dayjs(datum.timestamp)
- // Find which bucket this datum belongs to
- const bucketIndex = Math.floor(datumTime.diff(start, 'minute') / bucketMinutes)
- // Skip data points outside our time range
- if (bucketIndex < 0 || bucketIndex >= expectedBuckets) {
- continue
- }
- // Aggregate counts into the appropriate bucket
- buckets[bucketIndex].ok_count += datum.ok_count || 0
- buckets[bucketIndex].warning_count += datum.warning_count || 0
- buckets[bucketIndex].error_count += datum.error_count || 0
- }
- return buckets
- }
|