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