Files
aerbim-ht-monitor/frontend/lib/chartDataAggregator.ts

255 lines
7.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Утилита для агрегации часовых данных в дневные
*/
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
}
/**
* Интерфейс для данных с разделением по типам событий
*/
export interface SeverityChartDataPoint {
timestamp: string
critical: number
warning: number
label?: string
}
/**
* Агрегирует данные тревог по дням с разделением по severity
* @param alerts - Массив тревог с полями timestamp и severity
* @param timePeriod - Период в часах ('24', '72', '168', '720')
* @returns Агрегированные данные с разделением по critical/warning
*/
export function aggregateAlertsBySeverity(
alerts: Array<{ timestamp?: string; created_at?: string; severity?: string }>,
timePeriod: string
): SeverityChartDataPoint[] {
if (!Array.isArray(alerts) || alerts.length === 0) {
return []
}
// Группируем по дням
const dailyMap = new Map<string, { critical: number; warning: number; date: Date }>()
alerts.forEach(alert => {
const timestampField = alert.timestamp || alert.created_at
if (!timestampField) return
const date = new Date(timestampField)
const dateKey = date.toISOString().split('T')[0]
if (!dailyMap.has(dateKey)) {
dailyMap.set(dateKey, { critical: 0, warning: 0, date })
}
const entry = dailyMap.get(dateKey)!
if (alert.severity === 'critical') {
entry.critical += 1
} else if (alert.severity === 'warning') {
entry.warning += 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 === '24') {
// День - показываем время
label = date.toLocaleDateString('ru-RU', {
day: '2-digit',
month: '2-digit',
hour: '2-digit'
})
} else 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(),
critical: data.critical,
warning: data.warning,
label
}
})
return dailyData
}