@@ -292,51 +292,56 @@ GROUP BY
292
292
t .template_ids , ai .app_name , ai .display_name , ai .icon , ai .is_app ;
293
293
294
294
-- name: GetTemplateAppInsightsByTemplate :many
295
- WITH app_stats_by_user_and_agent AS (
295
+ SELECT
296
+ tus .template_id ,
297
+ COUNT (DISTINCT tus .user_id ) AS active_users,
298
+ app_usage .key ::text AS slug_or_port,
299
+ COALESCE(wa .display_name , ' ' ) AS display_name,
300
+ (SUM (app_usage .value ::int ) * 60 )::bigint AS usage_seconds
301
+ FROM
302
+ template_usage_stats AS tus, jsonb_each(app_usage_mins) AS app_usage
303
+ LEFT JOIN LATERAL (
304
+ -- The joins in this query are necessary to associate an app with a
305
+ -- template, we use this to get the app metadata like display name
306
+ -- and icon.
296
307
SELECT
297
- s .start_time ,
298
- 60 as seconds,
299
- w .template_id ,
300
- was .user_id ,
301
- was .agent_id ,
302
- was .slug_or_port ,
303
- wa .display_name ,
304
- (wa .slug IS NOT NULL )::boolean AS is_app
305
- FROM workspace_app_stats was
306
- JOIN workspaces w ON (
307
- w .id = was .workspace_id
308
- )
309
- -- We do a left join here because we want to include user IDs that have used
310
- -- e.g. ports when counting active users.
311
- LEFT JOIN workspace_apps wa ON (
312
- wa .agent_id = was .agent_id
313
- AND wa .slug = was .slug_or_port
314
- )
315
- -- This table contains both 1 minute entries and >1 minute entries,
316
- -- to calculate this with our uniqueness constraints, we generate series
317
- -- for the longer intervals.
318
- CROSS JOIN LATERAL generate_series(
319
- date_trunc(' minute' , was .session_started_at ),
320
- -- Subtract 1 microsecond to avoid creating an extra series.
321
- date_trunc(' minute' , was .session_ended_at - ' 1 microsecond' ::interval),
322
- ' 1 minute' ::interval
323
- ) s(start_time)
308
+ app .display_name ,
309
+ app .slug
310
+ FROM
311
+ workspace_apps AS app
312
+ JOIN
313
+ workspace_agents AS agent
314
+ ON
315
+ agent .id = app .agent_id
316
+ JOIN
317
+ workspace_resources AS resource
318
+ ON
319
+ resource .id = agent .resource_id
320
+ JOIN
321
+ workspace_builds AS build
322
+ ON
323
+ build .job_id = resource .job_id
324
+ JOIN
325
+ workspaces AS workspace
326
+ ON
327
+ workspace .id = build .workspace_id
324
328
WHERE
325
- s .start_time >= @start_time::timestamptz
326
- -- Subtract one minute because the series only contains the start time.
327
- AND s .start_time < (@end_time::timestamptz ) - ' 1 minute' ::interval
328
- GROUP BY s .start_time , w .template_id , was .user_id , was .agent_id , was .slug_or_port , wa .display_name , wa .slug
329
- )
330
-
331
- SELECT
332
- template_id,
333
- display_name,
334
- slug_or_port,
335
- COALESCE(COUNT (DISTINCT user_id))::bigint AS active_users,
336
- SUM (seconds) AS usage_seconds
337
- FROM app_stats_by_user_and_agent
338
- WHERE is_app IS TRUE
339
- GROUP BY template_id, display_name, slug_or_port;
329
+ -- Requires lateral join.
330
+ app .slug = app_usage .key
331
+ AND workspace .owner_id = tus .user_id
332
+ AND workspace .template_id = tus .template_id
333
+ ORDER BY
334
+ app .created_at DESC
335
+ LIMIT 1
336
+ ) wa
337
+ ON
338
+ true
339
+ WHERE
340
+ tus .start_time >= @start_time::timestamptz
341
+ AND tus .end_time <= @end_time::timestamptz
342
+ AND wa .slug IS NOT NULL -- Check is_app.
343
+ GROUP BY
344
+ tus .template_id , app_usage .key ::text , wa .display_name ;
340
345
341
346
-- name: GetTemplateInsightsByInterval :many
342
347
-- GetTemplateInsightsByInterval returns all intervals between start and end
0 commit comments