/** * Утилита для агрегации часовых данных в дневные */ 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() 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() 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() 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 }