5.0.6: Удаление max_score, норма по max_goal_score
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 1m20s
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 1m20s
This commit is contained in:
@@ -5202,7 +5202,6 @@ func (a *App) setupWeeklyGoals() error {
|
|||||||
goal_week,
|
goal_week,
|
||||||
min_goal_score,
|
min_goal_score,
|
||||||
max_goal_score,
|
max_goal_score,
|
||||||
max_score,
|
|
||||||
priority,
|
priority,
|
||||||
user_id
|
user_id
|
||||||
)
|
)
|
||||||
@@ -5212,20 +5211,13 @@ func (a *App) setupWeeklyGoals() error {
|
|||||||
ci.c_week,
|
ci.c_week,
|
||||||
-- Если нет данных (gm.median_score IS NULL), используем 0 (значение по умолчанию)
|
-- Если нет данных (gm.median_score IS NULL), используем 0 (значение по умолчанию)
|
||||||
COALESCE(gm.median_score, 0) AS min_goal_score,
|
COALESCE(gm.median_score, 0) AS min_goal_score,
|
||||||
-- Логика max_score в зависимости от приоритета (только если есть данные)
|
-- Логика max_goal_score в зависимости от приоритета (только если есть данные)
|
||||||
CASE
|
CASE
|
||||||
WHEN gm.median_score IS NULL THEN NULL
|
WHEN gm.median_score IS NULL THEN NULL
|
||||||
WHEN p.priority = 1 THEN gm.median_score * 2.0
|
WHEN p.priority = 1 THEN gm.median_score * 2.0
|
||||||
WHEN p.priority = 2 THEN gm.median_score * 1.7
|
WHEN p.priority = 2 THEN gm.median_score * 1.7
|
||||||
ELSE gm.median_score * 1.4
|
ELSE gm.median_score * 1.4
|
||||||
END AS max_goal_score,
|
END AS max_goal_score,
|
||||||
-- max_score (snapshot) заполняется при INSERT, но НЕ обновляется при конфликте
|
|
||||||
CASE
|
|
||||||
WHEN gm.median_score IS NULL THEN NULL
|
|
||||||
WHEN p.priority = 1 THEN gm.median_score * 2.0
|
|
||||||
WHEN p.priority = 2 THEN gm.median_score * 1.7
|
|
||||||
ELSE gm.median_score * 1.4
|
|
||||||
END AS max_score,
|
|
||||||
p.priority,
|
p.priority,
|
||||||
p.user_id
|
p.user_id
|
||||||
FROM projects p
|
FROM projects p
|
||||||
@@ -5597,8 +5589,8 @@ func (a *App) recreateMaterializedViewHandler(w http.ResponseWriter, r *http.Req
|
|||||||
agg.report_week,
|
agg.report_week,
|
||||||
COALESCE(agg.total_score, 0.0000) AS total_score,
|
COALESCE(agg.total_score, 0.0000) AS total_score,
|
||||||
CASE
|
CASE
|
||||||
WHEN wg.max_score IS NULL THEN COALESCE(agg.total_score, 0.0000)
|
WHEN wg.max_goal_score IS NULL THEN COALESCE(agg.total_score, 0.0000)
|
||||||
ELSE LEAST(COALESCE(agg.total_score, 0.0000), wg.max_score)
|
ELSE LEAST(COALESCE(agg.total_score, 0.0000), wg.max_goal_score)
|
||||||
END AS normalized_total_score
|
END AS normalized_total_score
|
||||||
FROM
|
FROM
|
||||||
projects p
|
projects p
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
-- Migration: Restore max_score column and MV using max_score for normalized_total_score
|
||||||
|
|
||||||
|
ALTER TABLE weekly_goals ADD COLUMN max_score NUMERIC(10,4);
|
||||||
|
UPDATE weekly_goals SET max_score = max_goal_score WHERE max_score IS NULL;
|
||||||
|
|
||||||
|
DROP MATERIALIZED VIEW IF EXISTS weekly_report_mv;
|
||||||
|
|
||||||
|
CREATE MATERIALIZED VIEW weekly_report_mv AS
|
||||||
|
SELECT
|
||||||
|
p.id AS project_id,
|
||||||
|
agg.report_year,
|
||||||
|
agg.report_week,
|
||||||
|
COALESCE(agg.total_score, 0.0000) AS total_score,
|
||||||
|
CASE
|
||||||
|
WHEN wg.max_score IS NULL THEN COALESCE(agg.total_score, 0.0000)
|
||||||
|
ELSE LEAST(COALESCE(agg.total_score, 0.0000), wg.max_score)
|
||||||
|
END AS normalized_total_score
|
||||||
|
FROM
|
||||||
|
projects p
|
||||||
|
LEFT JOIN
|
||||||
|
(
|
||||||
|
SELECT
|
||||||
|
n.project_id,
|
||||||
|
EXTRACT(ISOYEAR FROM n.created_date)::INTEGER AS report_year,
|
||||||
|
EXTRACT(WEEK FROM n.created_date)::INTEGER AS report_week,
|
||||||
|
SUM(n.score) AS total_score
|
||||||
|
FROM
|
||||||
|
nodes n
|
||||||
|
WHERE
|
||||||
|
(EXTRACT(ISOYEAR FROM n.created_date)::INTEGER < EXTRACT(ISOYEAR FROM CURRENT_DATE)::INTEGER)
|
||||||
|
OR (EXTRACT(ISOYEAR FROM n.created_date)::INTEGER = EXTRACT(ISOYEAR FROM CURRENT_DATE)::INTEGER
|
||||||
|
AND EXTRACT(WEEK FROM n.created_date)::INTEGER < EXTRACT(WEEK FROM CURRENT_DATE)::INTEGER)
|
||||||
|
GROUP BY
|
||||||
|
1, 2, 3
|
||||||
|
) agg
|
||||||
|
ON p.id = agg.project_id
|
||||||
|
LEFT JOIN
|
||||||
|
weekly_goals wg
|
||||||
|
ON wg.project_id = p.id
|
||||||
|
AND wg.goal_year = agg.report_year
|
||||||
|
AND wg.goal_week = agg.report_week
|
||||||
|
WHERE
|
||||||
|
p.deleted = FALSE
|
||||||
|
ORDER BY
|
||||||
|
p.id, agg.report_year, agg.report_week
|
||||||
|
WITH DATA;
|
||||||
|
|
||||||
|
CREATE INDEX idx_weekly_report_mv_project_year_week
|
||||||
|
ON weekly_report_mv(project_id, report_year, report_week);
|
||||||
|
|
||||||
|
COMMENT ON MATERIALIZED VIEW weekly_report_mv IS 'Materialized view aggregating weekly scores by project using ISOYEAR for correct week calculations at year boundaries. Includes all projects via LEFT JOIN. Adds normalized_total_score using weekly_goals.max_score snapshot. Contains only historical data (excludes current week). Uses nodes.created_date (denormalized) instead of entries.created_date.';
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
-- Migration: Remove max_score from weekly_goals, use max_goal_score for normalized_total_score
|
||||||
|
-- normalized_total_score is now computed from max_goal_score (current goal) instead of max_score (snapshot).
|
||||||
|
|
||||||
|
DROP MATERIALIZED VIEW IF EXISTS weekly_report_mv;
|
||||||
|
|
||||||
|
CREATE MATERIALIZED VIEW weekly_report_mv AS
|
||||||
|
SELECT
|
||||||
|
p.id AS project_id,
|
||||||
|
agg.report_year,
|
||||||
|
agg.report_week,
|
||||||
|
COALESCE(agg.total_score, 0.0000) AS total_score,
|
||||||
|
CASE
|
||||||
|
WHEN wg.max_goal_score IS NULL THEN COALESCE(agg.total_score, 0.0000)
|
||||||
|
ELSE LEAST(COALESCE(agg.total_score, 0.0000), wg.max_goal_score)
|
||||||
|
END AS normalized_total_score
|
||||||
|
FROM
|
||||||
|
projects p
|
||||||
|
LEFT JOIN
|
||||||
|
(
|
||||||
|
SELECT
|
||||||
|
n.project_id,
|
||||||
|
EXTRACT(ISOYEAR FROM n.created_date)::INTEGER AS report_year,
|
||||||
|
EXTRACT(WEEK FROM n.created_date)::INTEGER AS report_week,
|
||||||
|
SUM(n.score) AS total_score
|
||||||
|
FROM
|
||||||
|
nodes n
|
||||||
|
WHERE
|
||||||
|
(EXTRACT(ISOYEAR FROM n.created_date)::INTEGER < EXTRACT(ISOYEAR FROM CURRENT_DATE)::INTEGER)
|
||||||
|
OR (EXTRACT(ISOYEAR FROM n.created_date)::INTEGER = EXTRACT(ISOYEAR FROM CURRENT_DATE)::INTEGER
|
||||||
|
AND EXTRACT(WEEK FROM n.created_date)::INTEGER < EXTRACT(WEEK FROM CURRENT_DATE)::INTEGER)
|
||||||
|
GROUP BY
|
||||||
|
1, 2, 3
|
||||||
|
) agg
|
||||||
|
ON p.id = agg.project_id
|
||||||
|
LEFT JOIN
|
||||||
|
weekly_goals wg
|
||||||
|
ON wg.project_id = p.id
|
||||||
|
AND wg.goal_year = agg.report_year
|
||||||
|
AND wg.goal_week = agg.report_week
|
||||||
|
WHERE
|
||||||
|
p.deleted = FALSE
|
||||||
|
ORDER BY
|
||||||
|
p.id, agg.report_year, agg.report_week
|
||||||
|
WITH DATA;
|
||||||
|
|
||||||
|
CREATE INDEX idx_weekly_report_mv_project_year_week
|
||||||
|
ON weekly_report_mv(project_id, report_year, report_week);
|
||||||
|
|
||||||
|
COMMENT ON MATERIALIZED VIEW weekly_report_mv IS 'Materialized view aggregating weekly scores by project using ISOYEAR for correct week calculations at year boundaries. Includes all projects via LEFT JOIN. Adds normalized_total_score using weekly_goals.max_goal_score. Contains only historical data (excludes current week). Uses nodes.created_date (denormalized) instead of entries.created_date.';
|
||||||
|
|
||||||
|
ALTER TABLE weekly_goals DROP COLUMN max_score;
|
||||||
@@ -45,7 +45,6 @@ docker-compose exec db psql -U playeng -d playeng -f /migrations/001_create_sche
|
|||||||
- `goal_week` (INTEGER NOT NULL)
|
- `goal_week` (INTEGER NOT NULL)
|
||||||
- `min_goal_score` (NUMERIC(10,4) NOT NULL, DEFAULT 0)
|
- `min_goal_score` (NUMERIC(10,4) NOT NULL, DEFAULT 0)
|
||||||
- `max_goal_score` (NUMERIC(10,4))
|
- `max_goal_score` (NUMERIC(10,4))
|
||||||
- `max_score` (NUMERIC(10,4), NULL) — snapshot max на неделю (заполняется только для новых недель)
|
|
||||||
- `priority` (SMALLINT)
|
- `priority` (SMALLINT)
|
||||||
- UNIQUE CONSTRAINT: `(project_id, goal_year, goal_week)`
|
- UNIQUE CONSTRAINT: `(project_id, goal_year, goal_week)`
|
||||||
|
|
||||||
@@ -56,7 +55,7 @@ docker-compose exec db psql -U playeng -d playeng -f /migrations/001_create_sche
|
|||||||
- `report_year` (INTEGER)
|
- `report_year` (INTEGER)
|
||||||
- `report_week` (INTEGER)
|
- `report_week` (INTEGER)
|
||||||
- `total_score` (NUMERIC)
|
- `total_score` (NUMERIC)
|
||||||
- `normalized_total_score` (NUMERIC)
|
- `normalized_total_score` (NUMERIC) — ограничение total_score по `max_goal_score` (миграция 000020 удалила колонку `max_score`, normalized считается по `max_goal_score`)
|
||||||
|
|
||||||
## Миграции
|
## Миграции
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "play-life-web",
|
"name": "play-life-web",
|
||||||
"version": "5.0.5",
|
"version": "5.0.6",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|||||||
Reference in New Issue
Block a user