255 lines
7.5 KiB
TypeScript
255 lines
7.5 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
|
||
}
|
||
|
||
/**
|
||
* Интерфейс для данных с разделением по типам событий
|
||
*/
|
||
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
|
||
}
|