first2
This commit is contained in:
160
frontend/lib/chartDataAggregator.ts
Normal file
160
frontend/lib/chartDataAggregator.ts
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
/**
|
||||||
|
* Утилита для агрегации часовых данных в дневные
|
||||||
|
*/
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user