Repository navigation
Expand file tree
/
Copy pathupdate_rpc.sql
More file actions
183 lines (174 loc) · 6.81 KB
/
Copy pathupdate_rpc.sql
File metadata and controls
183 lines (174 loc) · 6.81 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
CREATE OR REPLACE FUNCTION public.get_dashboard_metrics(
p_org_id uuid,
p_start_iso timestamptz,
p_end_iso timestamptz,
p_prev_start_iso timestamptz,
p_prev_end_iso timestamptz,
p_member_ids text[] DEFAULT NULL,
p_project_ids text[] DEFAULT NULL
)
RETURNS jsonb
SECURITY DEFINER
STABLE
AS $$
DECLARE
v_total_mins int := 0;
v_activity_sum bigint := 0;
v_activity_count int := 0;
v_prev_total_mins int := 0;
v_prev_activity_sum bigint := 0;
v_prev_activity_count int := 0;
v_app_usage jsonb;
v_proj_stats jsonb;
v_user_stats jsonb;
v_user_screenshots jsonb;
v_daily_stats jsonb;
v_screenshot_count int := 0;
v_projects_worked int := 0;
v_active_members int := 0;
BEGIN
-- 1. Get total screenshot count in current period
SELECT COUNT(*)
INTO v_screenshot_count
FROM screenshots ss
JOIN sessions s ON ss.session_id = s.id
WHERE ss.organization_id = p_org_id
AND ss.recorded_at >= p_start_iso
AND ss.recorded_at <= p_end_iso
AND (
(p_member_ids IS NULL AND p_project_ids IS NULL)
OR (p_member_ids IS NOT NULL AND s.user_id = ANY(p_member_ids::uuid[]))
OR (p_project_ids IS NOT NULL AND s.project_id = ANY(p_project_ids))
);
-- 2. Massive CTE to unify everything safely without deduplication bugs
WITH current_sessions AS (
SELECT id as session_id, user_id, project_id, organization_id, manual, started_at, COALESCE(ended_at, now()) as ended_at
FROM sessions s
WHERE s.organization_id = p_org_id
AND (
(p_member_ids IS NULL AND p_project_ids IS NULL)
OR (p_member_ids IS NOT NULL AND s.user_id = ANY(p_member_ids::uuid[]))
OR (p_project_ids IS NOT NULL AND s.project_id = ANY(p_project_ids))
)
),
unified_current AS (
-- Deduped Real Samples
SELECT DISTINCT ON (a.session_id, date_trunc('minute', a.recorded_at))
a.id, a.session_id, a.activity_percent, a.app_name, a.recorded_at, s.project_id, s.user_id
FROM activity_samples a
JOIN current_sessions s ON a.session_id = s.session_id
WHERE a.organization_id = p_org_id
AND a.recorded_at >= p_start_iso
AND a.recorded_at <= p_end_iso
UNION ALL
-- Synthetic Manual Samples (Floor rounding, 1-minute steps)
SELECT
NULL::bigint as id, s.session_id, 0 as activity_percent, 'Manual Time' as app_name,
s.started_at + (i || ' minutes')::interval as recorded_at, s.project_id, s.user_id
FROM current_sessions s
CROSS JOIN LATERAL generate_series(
1,
GREATEST(0, floor(EXTRACT(EPOCH FROM (s.ended_at - s.started_at))/60)::int)
) as i
WHERE s.manual = true
AND (s.started_at + (i || ' minutes')::interval) >= p_start_iso
AND (s.started_at + (i || ' minutes')::interval) <= p_end_iso
),
unified_prev AS (
SELECT DISTINCT ON (a.session_id, date_trunc('minute', a.recorded_at))
a.id, a.session_id, a.activity_percent
FROM activity_samples a
JOIN current_sessions s ON a.session_id = s.session_id
WHERE a.organization_id = p_org_id
AND a.recorded_at >= p_prev_start_iso
AND a.recorded_at <= p_prev_end_iso
UNION ALL
SELECT
NULL::bigint as id, s.session_id, 0 as activity_percent
FROM current_sessions s
CROSS JOIN LATERAL generate_series(
1,
GREATEST(0, floor(EXTRACT(EPOCH FROM (s.ended_at - s.started_at))/60)::int)
) as i
WHERE s.manual = true
AND (s.started_at + (i || ' minutes')::interval) >= p_prev_start_iso
AND (s.started_at + (i || ' minutes')::interval) <= p_prev_end_iso
)
SELECT
(SELECT COUNT(*) FROM unified_current),
(SELECT COALESCE(SUM(activity_percent), 0) FROM unified_current),
(SELECT COUNT(DISTINCT project_id) FROM unified_current),
(SELECT COUNT(DISTINCT user_id) FROM unified_current),
(SELECT COUNT(*) FROM unified_prev),
(SELECT COALESCE(SUM(activity_percent), 0) FROM unified_prev),
(SELECT COALESCE(jsonb_object_agg(COALESCE(app_name, 'Unknown'), cnt), '{}'::jsonb) FROM (
SELECT app_name, COUNT(*) as cnt FROM unified_current WHERE app_name IS NOT NULL AND TRIM(app_name) != '' AND LOWER(app_name) != 'program manager' GROUP BY app_name ORDER BY cnt DESC LIMIT 20
) t),
(SELECT COALESCE(jsonb_object_agg(user_id::text, json_build_object('mins', mins, 'activity_sum', act_sum, 'cnt', cnt)), '{}'::jsonb) FROM (
SELECT user_id, COUNT(*) as mins, COALESCE(SUM(activity_percent), 0) as act_sum, COUNT(*) as cnt FROM unified_current GROUP BY user_id
) t),
(SELECT COALESCE(jsonb_object_agg(project_id::text, json_build_object('mins', mins, 'activity_sum', act_sum, 'cnt', cnt)), '{}'::jsonb) FROM (
SELECT project_id, COUNT(*) as mins, COALESCE(SUM(activity_percent), 0) as act_sum, COUNT(*) as cnt FROM unified_current WHERE project_id IS NOT NULL GROUP BY project_id
) t),
(SELECT COALESCE(jsonb_object_agg(day_name, cnt), '{}'::jsonb) FROM (
SELECT TO_CHAR(recorded_at AT TIME ZONE 'UTC', 'Dy') as day_name, COUNT(*) as cnt FROM unified_current GROUP BY TO_CHAR(recorded_at AT TIME ZONE 'UTC', 'Dy')
) t)
INTO
v_total_mins,
v_activity_sum,
v_projects_worked,
v_active_members,
v_prev_total_mins,
v_prev_activity_sum,
v_app_usage,
v_user_stats,
v_proj_stats,
v_daily_stats;
v_activity_count := v_total_mins;
v_prev_activity_count := v_prev_total_mins;
-- 8. User screenshots (for recent activity preview)
SELECT COALESCE(jsonb_agg(to_jsonb(ss)), '[]'::jsonb)
INTO v_user_screenshots
FROM (
SELECT
ss.id,
ss.user_id,
ss.file_url as path,
ss.recorded_at as "recordedAt",
COALESCE((
SELECT activity_percent
FROM activity_samples ast
WHERE ast.session_id = ss.session_id
ORDER BY ABS(EXTRACT(EPOCH FROM ast.recorded_at - ss.recorded_at)) ASC
LIMIT 1
), 0) as "activityPercent"
FROM screenshots ss
JOIN sessions s ON ss.session_id = s.id
WHERE ss.organization_id = p_org_id
AND ss.recorded_at >= p_start_iso
AND ss.recorded_at <= p_end_iso
AND (
(p_member_ids IS NULL AND p_project_ids IS NULL)
OR (p_member_ids IS NOT NULL AND s.user_id = ANY(p_member_ids::uuid[]))
OR (p_project_ids IS NOT NULL AND s.project_id = ANY(p_project_ids))
)
ORDER BY ss.recorded_at DESC
) ss;
RETURN json_build_object(
'total_mins', v_total_mins,
'activity_sum', v_activity_sum,
'activity_count', v_activity_count,
'prev_total_mins', v_prev_total_mins,
'prev_activity_sum', v_prev_activity_sum,
'prev_activity_count', v_prev_activity_count,
'screenshot_count', v_screenshot_count,
'projects_worked', v_projects_worked,
'active_members', v_active_members,
'app_usage', v_app_usage,
'user_stats', v_user_stats,
'proj_stats', v_proj_stats,
'daily_stats', v_daily_stats,
'screenshots', v_user_screenshots
);
END;
$$ LANGUAGE plpgsql;