| 1 | SET search_path TO trekr;
|
|---|
| 2 |
|
|---|
| 3 | -- P6 Q3 (Annual): Discipline adherence, completion quality, and streak behavior.
|
|---|
| 4 | -- Change report_year in params CTE when needed.
|
|---|
| 5 | WITH params AS (
|
|---|
| 6 | SELECT 2026::int AS report_year
|
|---|
| 7 | ),
|
|---|
| 8 | discipline_base AS (
|
|---|
| 9 | SELECT
|
|---|
| 10 | du.user_id,
|
|---|
| 11 | u.username,
|
|---|
| 12 | u.email
|
|---|
| 13 | FROM discipline_users du
|
|---|
| 14 | JOIN users u ON u.user_id = du.user_id
|
|---|
| 15 | ),
|
|---|
| 16 | task_mix AS (
|
|---|
| 17 | SELECT
|
|---|
| 18 | COALESCE(t.discipline_user_id, c.user_id) AS user_id,
|
|---|
| 19 | COUNT(*) AS total_tasks_defined,
|
|---|
| 20 | COUNT(*) FILTER (WHERE t.custom_tracking_id IS NULL) AS core_tasks,
|
|---|
| 21 | COUNT(*) FILTER (WHERE t.custom_tracking_id IS NOT NULL) AS custom_tasks,
|
|---|
| 22 | COUNT(DISTINCT COALESCE(t.custom_tracking_id::text, 'core')) AS task_category_span
|
|---|
| 23 | FROM tasks t
|
|---|
| 24 | LEFT JOIN custom_tracking_categories c
|
|---|
| 25 | ON c.custom_tracking_id = t.custom_tracking_id
|
|---|
| 26 | WHERE t.discipline_user_id IS NOT NULL
|
|---|
| 27 | OR t.custom_tracking_id IS NOT NULL
|
|---|
| 28 | GROUP BY COALESCE(t.discipline_user_id, c.user_id)
|
|---|
| 29 | ),
|
|---|
| 30 | annual_daily_completion AS (
|
|---|
| 31 | SELECT
|
|---|
| 32 | dc.user_id,
|
|---|
| 33 | dc.date,
|
|---|
| 34 | COALESCE(dc.procent, 0) AS procent,
|
|---|
| 35 | CASE WHEN COALESCE(dc.procent, 0) >= 80 THEN 1 ELSE 0 END AS strong_day
|
|---|
| 36 | FROM daily_completion dc
|
|---|
| 37 | WHERE EXTRACT(YEAR FROM dc.date)::int = (SELECT report_year FROM params)
|
|---|
| 38 | ),
|
|---|
| 39 | daily_completion_stats AS (
|
|---|
| 40 | SELECT
|
|---|
| 41 | adc.user_id,
|
|---|
| 42 | COUNT(*) AS tracked_days,
|
|---|
| 43 | AVG(adc.procent) AS avg_completion_percent,
|
|---|
| 44 | PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY adc.procent) AS median_completion_percent,
|
|---|
| 45 | COUNT(*) FILTER (WHERE adc.procent = 100) AS perfect_days,
|
|---|
| 46 | COUNT(*) FILTER (WHERE adc.procent >= 80) AS strong_days,
|
|---|
| 47 | STDDEV_SAMP(adc.procent) AS completion_variability
|
|---|
| 48 | FROM annual_daily_completion adc
|
|---|
| 49 | GROUP BY adc.user_id
|
|---|
| 50 | ),
|
|---|
| 51 | strong_day_streaks AS (
|
|---|
| 52 | SELECT
|
|---|
| 53 | user_id,
|
|---|
| 54 | date,
|
|---|
| 55 | date - (ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY date))::int AS grp
|
|---|
| 56 | FROM annual_daily_completion
|
|---|
| 57 | WHERE strong_day = 1
|
|---|
| 58 | ),
|
|---|
| 59 | longest_strong_streak AS (
|
|---|
| 60 | SELECT
|
|---|
| 61 | user_id,
|
|---|
| 62 | MAX(streak_len) AS longest_strong_day_streak
|
|---|
| 63 | FROM (
|
|---|
| 64 | SELECT user_id, grp, COUNT(*) AS streak_len
|
|---|
| 65 | FROM strong_day_streaks
|
|---|
| 66 | GROUP BY user_id, grp
|
|---|
| 67 | ) s
|
|---|
| 68 | GROUP BY user_id
|
|---|
| 69 | ),
|
|---|
| 70 | annual_task_execution AS (
|
|---|
| 71 | SELECT
|
|---|
| 72 | dc.user_id,
|
|---|
| 73 | COUNT(tdc.task_id) AS completed_task_events
|
|---|
| 74 | FROM daily_completion dc
|
|---|
| 75 | LEFT JOIN task_daily_completion tdc
|
|---|
| 76 | ON tdc.daily_completion_id = dc.daily_completion_id
|
|---|
| 77 | WHERE EXTRACT(YEAR FROM dc.date)::int = (SELECT report_year FROM params)
|
|---|
| 78 | GROUP BY dc.user_id
|
|---|
| 79 | )
|
|---|
| 80 | SELECT
|
|---|
| 81 | db.user_id,
|
|---|
| 82 | db.username,
|
|---|
| 83 | db.email,
|
|---|
| 84 | COALESCE(tm.total_tasks_defined, 0) AS total_tasks_defined,
|
|---|
| 85 | COALESCE(tm.core_tasks, 0) AS core_tasks,
|
|---|
| 86 | COALESCE(tm.custom_tasks, 0) AS custom_tasks,
|
|---|
| 87 | COALESCE(tm.task_category_span, 0) AS task_category_span,
|
|---|
| 88 | COALESCE(dcs.tracked_days, 0) AS tracked_days,
|
|---|
| 89 | ROUND(COALESCE(dcs.avg_completion_percent, 0)::numeric, 2) AS avg_completion_percent,
|
|---|
| 90 | ROUND(COALESCE(dcs.median_completion_percent, 0)::numeric, 2) AS median_completion_percent,
|
|---|
| 91 | COALESCE(dcs.perfect_days, 0) AS perfect_days,
|
|---|
| 92 | COALESCE(dcs.strong_days, 0) AS strong_days,
|
|---|
| 93 | ROUND(COALESCE(dcs.completion_variability, 0)::numeric, 4) AS completion_variability,
|
|---|
| 94 | COALESCE(ate.completed_task_events, 0) AS completed_task_events,
|
|---|
| 95 | COALESCE(lss.longest_strong_day_streak, 0) AS longest_strong_day_streak,
|
|---|
| 96 | ROUND(
|
|---|
| 97 | COALESCE((COALESCE(dcs.strong_days, 0) / NULLIF(COALESCE(dcs.tracked_days, 0), 0)::numeric), 0),
|
|---|
| 98 | 4
|
|---|
| 99 | ) AS strong_day_ratio,
|
|---|
| 100 | ROUND(
|
|---|
| 101 | (
|
|---|
| 102 | COALESCE(dcs.avg_completion_percent, 0) * 0.45
|
|---|
| 103 | + COALESCE(lss.longest_strong_day_streak, 0) * 2.00
|
|---|
| 104 | + COALESCE(ate.completed_task_events, 0) * 0.35
|
|---|
| 105 | )::numeric,
|
|---|
| 106 | 2
|
|---|
| 107 | ) AS discipline_composite_score,
|
|---|
| 108 | DENSE_RANK() OVER (
|
|---|
| 109 | ORDER BY
|
|---|
| 110 | (
|
|---|
| 111 | COALESCE(dcs.avg_completion_percent, 0) * 0.45
|
|---|
| 112 | + COALESCE(lss.longest_strong_day_streak, 0) * 2.00
|
|---|
| 113 | + COALESCE(ate.completed_task_events, 0) * 0.35
|
|---|
| 114 | ) DESC,
|
|---|
| 115 | db.user_id ASC
|
|---|
| 116 | ) AS discipline_annual_rank
|
|---|
| 117 | FROM discipline_base db
|
|---|
| 118 | LEFT JOIN task_mix tm ON tm.user_id = db.user_id
|
|---|
| 119 | LEFT JOIN daily_completion_stats dcs ON dcs.user_id = db.user_id
|
|---|
| 120 | LEFT JOIN annual_task_execution ate ON ate.user_id = db.user_id
|
|---|
| 121 | LEFT JOIN longest_strong_streak lss ON lss.user_id = db.user_id
|
|---|
| 122 | ORDER BY discipline_annual_rank, db.user_id;
|
|---|
| 123 |
|
|---|
| 124 |
|
|---|