Унификация расчета процентов с бэкендом
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 1m8s

This commit is contained in:
poignatov
2026-01-21 20:01:24 +03:00
parent 6578db6ec4
commit 0adf81cf6a
5 changed files with 118 additions and 65 deletions

View File

@@ -1 +1 @@
3.25.3 3.25.4

View File

@@ -108,9 +108,18 @@ type WeeklyProjectStats struct {
CalculatedScore float64 `json:"calculated_score"` CalculatedScore float64 `json:"calculated_score"`
} }
type GroupsProgress struct {
Group1 *float64 `json:"group1,omitempty"`
Group2 *float64 `json:"group2,omitempty"`
Group0 *float64 `json:"group0,omitempty"`
}
type WeeklyStatsResponse struct { type WeeklyStatsResponse struct {
Total *float64 `json:"total,omitempty"` Total *float64 `json:"total,omitempty"`
Projects []WeeklyProjectStats `json:"projects"` GroupProgress1 *float64 `json:"group_progress_1,omitempty"`
GroupProgress2 *float64 `json:"group_progress_2,omitempty"`
GroupProgress0 *float64 `json:"group_progress_0,omitempty"`
Projects []WeeklyProjectStats `json:"projects"`
} }
type MessagePostRequest struct { type MessagePostRequest struct {
@@ -2538,12 +2547,18 @@ func (a *App) getWeeklyStatsHandler(w http.ResponseWriter, r *http.Request) {
projects = append(projects, project) projects = append(projects, project)
} }
// Вычисляем проценты для каждой группы
groupsProgress := calculateGroupsProgress(groups)
// Вычисляем общий процент выполнения // Вычисляем общий процент выполнения
total := calculateOverallProgress(groups) total := calculateOverallProgress(groupsProgress)
response := WeeklyStatsResponse{ response := WeeklyStatsResponse{
Total: total, Total: total,
Projects: projects, GroupProgress1: groupsProgress.Group1,
GroupProgress2: groupsProgress.Group2,
GroupProgress0: groupsProgress.Group0,
Projects: projects,
} }
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
@@ -3727,12 +3742,18 @@ func (a *App) getWeeklyStatsData() (*WeeklyStatsResponse, error) {
projects = append(projects, project) projects = append(projects, project)
} }
// Вычисляем проценты для каждой группы
groupsProgress := calculateGroupsProgress(groups)
// Вычисляем общий процент выполнения // Вычисляем общий процент выполнения
total := calculateOverallProgress(groups) total := calculateOverallProgress(groupsProgress)
response := WeeklyStatsResponse{ response := WeeklyStatsResponse{
Total: total, Total: total,
Projects: projects, GroupProgress1: groupsProgress.Group1,
GroupProgress2: groupsProgress.Group2,
GroupProgress0: groupsProgress.Group0,
Projects: projects,
} }
return &response, nil return &response, nil
@@ -3857,12 +3878,18 @@ func (a *App) getWeeklyStatsDataForUser(userID int) (*WeeklyStatsResponse, error
} }
} }
// Вычисляем проценты для каждой группы
groupsProgress := calculateGroupsProgress(groups)
// Вычисляем общий процент выполнения // Вычисляем общий процент выполнения
total := calculateOverallProgress(groups) total := calculateOverallProgress(groupsProgress)
response := WeeklyStatsResponse{ response := WeeklyStatsResponse{
Total: total, Total: total,
Projects: projects, GroupProgress1: groupsProgress.Group1,
GroupProgress2: groupsProgress.Group2,
GroupProgress0: groupsProgress.Group0,
Projects: projects,
} }
return &response, nil return &response, nil
@@ -4386,16 +4413,16 @@ func roundToFourDecimals(val float64) float64 {
return float64(int(val*10000+0.5)) / 10000.0 return float64(int(val*10000+0.5)) / 10000.0
} }
// calculateOverallProgress вычисляет общий процент выполнения на основе групп проектов по приоритетам // calculateGroupsProgress вычисляет проценты выполнения для каждой группы приоритетов
// groups - карта приоритетов к спискам calculatedScore проектов // groups - карта приоритетов к спискам calculatedScore проектов
// Возвращает указатель на float64 с общим процентом выполнения или nil, если нет данных // Возвращает структуру GroupsProgress с процентами для каждой группы
// Если какая-то группа отсутствует, она считается как 100% // Если какая-то группа отсутствует, она считается как 100%
func calculateOverallProgress(groups map[int][]float64) *float64 { func calculateGroupsProgress(groups map[int][]float64) GroupsProgress {
// Всего есть 3 группы: приоритет 1, приоритет 2, приоритет 0 // Всего есть 3 группы: приоритет 1, приоритет 2, приоритет 0
// Вычисляем среднее для каждой группы, если она есть // Вычисляем среднее для каждой группы, если она есть
// Если группы нет, считаем её как 100% // Если группы нет, считаем её как 100%
groupAverages := make(map[int]float64) result := GroupsProgress{}
// Обрабатываем все 3 возможных приоритета // Обрабатываем все 3 возможных приоритета
priorities := []int{1, 2, 0} priorities := []int{1, 2, 0}
@@ -4403,13 +4430,12 @@ func calculateOverallProgress(groups map[int][]float64) *float64 {
for _, priorityVal := range priorities { for _, priorityVal := range priorities {
scores, exists := groups[priorityVal] scores, exists := groups[priorityVal]
var avg float64
if !exists || len(scores) == 0 { if !exists || len(scores) == 0 {
// Если группы нет, считаем как 100% // Если группы нет, считаем как 100%
groupAverages[priorityVal] = 100.0 avg = 100.0
} else { } else {
// Вычисляем среднее для группы // Вычисляем среднее для группы
var avg float64
// Для приоритета 1 и 2 - обычное среднее // Для приоритета 1 и 2 - обычное среднее
if priorityVal == 1 || priorityVal == 2 { if priorityVal == 1 || priorityVal == 2 {
sum := 0.0 sum := 0.0
@@ -4431,18 +4457,52 @@ func calculateOverallProgress(groups map[int][]float64) *float64 {
avg = math.Min(120.0, sum) avg = math.Min(120.0, sum)
} }
}
groupAverages[priorityVal] = avg
// Сохраняем результат в соответствующее поле
avgRounded := roundToFourDecimals(avg)
switch priorityVal {
case 1:
result.Group1 = &avgRounded
case 2:
result.Group2 = &avgRounded
case 0:
result.Group0 = &avgRounded
} }
} }
return result
}
// calculateOverallProgress вычисляет общий процент выполнения на основе процентов групп
// groupsProgress - структура с процентами для каждой группы приоритетов
// Возвращает указатель на float64 с общим процентом выполнения
// Всегда вычисляет среднее всех трех групп (даже если какая-то группа отсутствует, она считается как 100%)
func calculateOverallProgress(groupsProgress GroupsProgress) *float64 {
// Находим среднее между всеми тремя группами // Находим среднее между всеми тремя группами
sum := 0.0 // Если какая-то группа отсутствует (nil), считаем её как 100%
for _, priorityVal := range priorities {
sum += groupAverages[priorityVal] var group1Val, group2Val, group0Val float64
if groupsProgress.Group1 != nil {
group1Val = *groupsProgress.Group1
} else {
group1Val = 100.0
} }
overallProgress := sum / 3.0 // Всегда делим на 3, так как групп всегда 3 if groupsProgress.Group2 != nil {
group2Val = *groupsProgress.Group2
} else {
group2Val = 100.0
}
if groupsProgress.Group0 != nil {
group0Val = *groupsProgress.Group0
} else {
group0Val = 100.0
}
overallProgress := (group1Val + group2Val + group0Val) / 3.0 // Всегда делим на 3, так как групп всегда 3
overallProgressRounded := roundToFourDecimals(overallProgress) overallProgressRounded := roundToFourDecimals(overallProgress)
total := &overallProgressRounded total := &overallProgressRounded

View File

@@ -1,6 +1,6 @@
{ {
"name": "play-life-web", "name": "play-life-web",
"version": "3.25.3", "version": "3.25.4",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",

View File

@@ -307,6 +307,9 @@ function AppContent() {
// Обрабатываем ответ: приходит массив с одним объектом [{total: ..., projects: [...]}] // Обрабатываем ответ: приходит массив с одним объектом [{total: ..., projects: [...]}]
let projects = [] let projects = []
let total = null let total = null
let groupProgress1 = null
let groupProgress2 = null
let groupProgress0 = null
if (Array.isArray(jsonData) && jsonData.length > 0) { if (Array.isArray(jsonData) && jsonData.length > 0) {
// Если ответ - массив, проверяем первый элемент // Если ответ - массив, проверяем первый элемент
@@ -316,6 +319,9 @@ function AppContent() {
if (firstItem.projects && Array.isArray(firstItem.projects)) { if (firstItem.projects && Array.isArray(firstItem.projects)) {
projects = firstItem.projects projects = firstItem.projects
total = firstItem.total !== undefined ? firstItem.total : null total = firstItem.total !== undefined ? firstItem.total : null
groupProgress1 = firstItem.group_progress_1 !== undefined ? firstItem.group_progress_1 : null
groupProgress2 = firstItem.group_progress_2 !== undefined ? firstItem.group_progress_2 : null
groupProgress0 = firstItem.group_progress_0 !== undefined ? firstItem.group_progress_0 : null
} else { } else {
// Если это просто массив проектов // Если это просто массив проектов
projects = jsonData projects = jsonData
@@ -328,11 +334,17 @@ function AppContent() {
// Если ответ - объект напрямую // Если ответ - объект напрямую
projects = jsonData.projects || jsonData.data || [] projects = jsonData.projects || jsonData.data || []
total = jsonData.total !== undefined ? jsonData.total : null total = jsonData.total !== undefined ? jsonData.total : null
groupProgress1 = jsonData.group_progress_1 !== undefined ? jsonData.group_progress_1 : null
groupProgress2 = jsonData.group_progress_2 !== undefined ? jsonData.group_progress_2 : null
groupProgress0 = jsonData.group_progress_0 !== undefined ? jsonData.group_progress_0 : null
} }
setCurrentWeekData({ setCurrentWeekData({
projects: Array.isArray(projects) ? projects : [], projects: Array.isArray(projects) ? projects : [],
total: total total: total,
group_progress_1: groupProgress1,
group_progress_2: groupProgress2,
group_progress_0: groupProgress0
}) })
} catch (err) { } catch (err) {
setCurrentWeekError(err.message) setCurrentWeekError(err.message)

View File

@@ -356,46 +356,27 @@ function CurrentWeek({ onProjectClick, data, loading, error, onRetry, allProject
}) })
} }
// Вычисляем процент выполнения для каждой группы // Получаем проценты групп из API данных
const calculateGroupProgress = (projects) => { const mainProgress = (() => {
if (projects.length === 0) return 0 const rawValue = data?.group_progress_1
const parsedValue = rawValue === undefined || rawValue === null ? null : parseFloat(rawValue)
let totalProgress = 0 return Number.isFinite(parsedValue) && parsedValue >= 0 ? parsedValue : 0
let validProjects = 0
projects.forEach(project => {
const safeTotal = Number.isFinite(project.total_score) ? project.total_score : 0
const safeMinGoal = Number.isFinite(project.min_goal_score) ? project.min_goal_score : 0
if (safeMinGoal > 0) {
const projectProgress = Math.min((safeTotal / safeMinGoal) * 100, 100)
totalProgress += projectProgress
validProjects++
}
})
return validProjects > 0 ? totalProgress / validProjects : 0
}
const mainProgress = calculateGroupProgress(priorityGroups.main)
const importantProgress = calculateGroupProgress(priorityGroups.important)
const othersProgress = calculateGroupProgress(priorityGroups.others)
// Пересчитываем общий прогресс как среднее от групповых процентов
const recalculatedOverallProgress = (() => {
const groups = []
if (priorityGroups.main.length > 0) groups.push(mainProgress)
if (priorityGroups.important.length > 0) groups.push(importantProgress)
if (priorityGroups.others.length > 0) groups.push(othersProgress)
if (groups.length === 0) return null
const average = groups.reduce((sum, progress) => sum + progress, 0) / groups.length
return Math.max(0, average) // Убираем ограничение на 100% для текста
})() })()
// Используем пересчитанный общий прогресс вместо API данных const importantProgress = (() => {
const displayOverallProgress = recalculatedOverallProgress !== null ? recalculatedOverallProgress : (hasProgressData ? overallProgress : null) const rawValue = data?.group_progress_2
const parsedValue = rawValue === undefined || rawValue === null ? null : parseFloat(rawValue)
return Number.isFinite(parsedValue) && parsedValue >= 0 ? parsedValue : 0
})()
const othersProgress = (() => {
const rawValue = data?.group_progress_0
const parsedValue = rawValue === undefined || rawValue === null ? null : parseFloat(rawValue)
return Number.isFinite(parsedValue) && parsedValue >= 0 ? parsedValue : 0
})()
// Используем общий прогресс из API данных
const displayOverallProgress = overallProgress
return ( return (
<div className="relative pt-8"> <div className="relative pt-8">