Files
aerbim-ht-monitor/frontend/lib/chartDataAggregator.ts
2026-02-02 13:40:13 +03:00

161 lines
4.6 KiB
TypeScript

/**
* Утилита для агрегации часовых данных в дневные
*/
export interface ChartDataPoint {
timestamp: string
value: number
label?: string
}
/**
* Агрегирует часовые данные в дневные на основе периода
* @param hourlyData - Массив часовых данных
* @param timePeriod - Период в часах ('24', '72', '168', '720')
* @returns Агрегированные дневные данные
*/
export function aggregateChartDataByDays(
hourlyData: ChartDataPoint[],
timePeriod: string
): ChartDataPoint[] {
if (!Array.isArray(hourlyData) || hourlyData.length === 0) {
return []
}
// Для периода в 24 часа - возвращаем часовые данные как есть
if (timePeriod === '24') {
return hourlyData.map(d => ({
...d,
label: d.label || new Date(d.timestamp).toLocaleTimeString('ru-RU', {
hour: '2-digit',
minute: '2-digit'
})
}))
}
// Для остальных периодов - агрегируем по дням
const dailyMap = new Map<string, { sum: number; count: number; date: Date }>()
hourlyData.forEach(point => {
const date = new Date(point.timestamp)
// Получаем дату в формате YYYY-MM-DD
const dateKey = date.toISOString().split('T')[0]
if (!dailyMap.has(dateKey)) {
dailyMap.set(dateKey, { sum: 0, count: 0, date })
}
const entry = dailyMap.get(dateKey)!
entry.sum += point.value
entry.count += 1
})
// Преобразуем в массив и сортируем по дате
const dailyData = Array.from(dailyMap.entries())
.sort((a, b) => new Date(a[0]).getTime() - new Date(b[0]).getTime())
.map(([dateKey, data]) => {
const date = new Date(dateKey)
// Форматируем подпись в зависимости от периода
let label = ''
if (timePeriod === '72') {
// 3 дня - показываем день недели и дату
label = date.toLocaleDateString('ru-RU', {
weekday: 'short',
day: '2-digit',
month: '2-digit'
})
} else if (timePeriod === '168') {
// Неделя - показываем день недели
label = date.toLocaleDateString('ru-RU', {
weekday: 'short',
day: '2-digit'
})
} else if (timePeriod === '720') {
// Месяц - показываем дату
label = date.toLocaleDateString('ru-RU', {
day: '2-digit',
month: '2-digit'
})
}
return {
timestamp: date.toISOString(),
value: data.sum,
label
}
})
return dailyData
}
/**
* Получает среднее значение за день (для альтернативной агрегации)
*/
export function aggregateChartDataByDaysAverage(
hourlyData: ChartDataPoint[],
timePeriod: string
): ChartDataPoint[] {
if (!Array.isArray(hourlyData) || hourlyData.length === 0) {
return []
}
if (timePeriod === '24') {
return hourlyData.map(d => ({
...d,
label: d.label || new Date(d.timestamp).toLocaleTimeString('ru-RU', {
hour: '2-digit',
minute: '2-digit'
})
}))
}
const dailyMap = new Map<string, { sum: number; count: number; date: Date }>()
hourlyData.forEach(point => {
const date = new Date(point.timestamp)
const dateKey = date.toISOString().split('T')[0]
if (!dailyMap.has(dateKey)) {
dailyMap.set(dateKey, { sum: 0, count: 0, date })
}
const entry = dailyMap.get(dateKey)!
entry.sum += point.value
entry.count += 1
})
const dailyData = Array.from(dailyMap.entries())
.sort((a, b) => new Date(a[0]).getTime() - new Date(b[0]).getTime())
.map(([dateKey, data]) => {
const date = new Date(dateKey)
let label = ''
if (timePeriod === '72') {
label = date.toLocaleDateString('ru-RU', {
weekday: 'short',
day: '2-digit',
month: '2-digit'
})
} else if (timePeriod === '168') {
label = date.toLocaleDateString('ru-RU', {
weekday: 'short',
day: '2-digit'
})
} else if (timePeriod === '720') {
label = date.toLocaleDateString('ru-RU', {
day: '2-digit',
month: '2-digit'
})
}
return {
timestamp: date.toISOString(),
value: Math.round(data.sum / data.count), // Среднее значение
label
}
})
return dailyData
}