@@ -182,57 +182,93 @@ GROUP BY template_id;
182
182
-- GetTemplateAppInsights returns the aggregate usage of each app in a given
183
183
-- timeframe. The result can be filtered on template_ids, meaning only user data
184
184
-- from workspaces based on those templates will be included.
185
- WITH app_stats_by_user_and_agent AS (
186
- SELECT
187
- s .start_time ,
188
- 60 as seconds,
189
- w .template_id ,
190
- was .user_id ,
191
- was .agent_id ,
192
- was .access_method ,
193
- was .slug_or_port ,
194
- wa .display_name ,
195
- wa .icon ,
196
- (wa .slug IS NOT NULL )::boolean AS is_app
197
- FROM workspace_app_stats was
198
- JOIN workspaces w ON (
199
- w .id = was .workspace_id
200
- AND CASE WHEN COALESCE(array_length(@template_ids::uuid[], 1 ), 0 ) > 0 THEN w .template_id = ANY(@template_ids::uuid[]) ELSE TRUE END
201
- )
202
- -- We do a left join here because we want to include user IDs that have used
203
- -- e.g. ports when counting active users.
204
- LEFT JOIN workspace_apps wa ON (
205
- wa .agent_id = was .agent_id
206
- AND wa .slug = was .slug_or_port
185
+ WITH
186
+ app_insights AS (
187
+ SELECT
188
+ tus .user_id ,
189
+ array_agg(DISTINCT tus .template_id )::uuid[] AS template_ids,
190
+ app_usage .key ::text AS app_name,
191
+ COALESCE(wa .display_name , ' ' ) AS display_name,
192
+ COALESCE(wa .icon , ' ' ) AS icon,
193
+ (wa .slug IS NOT NULL )::boolean AS is_app,
194
+ LEAST(SUM (app_usage .value ::int ), 30 ) AS app_usage_mins
195
+ FROM
196
+ template_usage_stats AS tus, jsonb_each(app_usage_mins) AS app_usage
197
+ LEFT JOIN LATERAL (
198
+ -- Fetch the latest app info for each app based on slug and template.
199
+ SELECT
200
+ app .display_name ,
201
+ app .icon ,
202
+ app .slug
203
+ FROM
204
+ workspace_apps AS app
205
+ JOIN
206
+ workspace_agents AS agent
207
+ ON
208
+ agent .id = app .agent_id
209
+ JOIN
210
+ workspace_resources AS resource
211
+ ON
212
+ resource .id = agent .resource_id
213
+ JOIN
214
+ workspace_builds AS build
215
+ ON
216
+ build .job_id = resource .job_id
217
+ JOIN
218
+ workspaces AS workspace
219
+ ON
220
+ workspace .id = build .workspace_id
221
+ WHERE
222
+ -- Requires lateral join.
223
+ app .slug = app_usage .key
224
+ AND workspace .owner_id = tus .user_id
225
+ AND workspace .template_id = tus .template_id
226
+ ORDER BY
227
+ app .created_at DESC
228
+ LIMIT 1
229
+ ) AS wa
230
+ ON
231
+ true
232
+ WHERE
233
+ tus .start_time >= @start_time::timestamptz
234
+ AND tus .end_time <= @end_time::timestamptz
235
+ AND CASE WHEN COALESCE(array_length(@template_ids::uuid[], 1 ), 0 ) > 0 THEN tus .template_id = ANY(@template_ids::uuid[]) ELSE TRUE END
236
+ GROUP BY
237
+ tus .start_time , tus .user_id , app_usage .key ::text , wa .display_name , wa .icon , wa .slug
238
+ ),
239
+ templates AS (
240
+ SELECT
241
+ app_name,
242
+ display_name,
243
+ icon,
244
+ is_app,
245
+ array_agg(DISTINCT template_id)::uuid[] AS template_ids
246
+ FROM
247
+ app_insights, unnest(template_ids) AS template_id
248
+ GROUP BY
249
+ app_name, display_name, icon, is_app
207
250
)
208
- -- This table contains both 1 minute entries and >1 minute entries,
209
- -- to calculate this with our uniqueness constraints, we generate series
210
- -- for the longer intervals.
211
- CROSS JOIN LATERAL generate_series(
212
- date_trunc(' minute' , was .session_started_at ),
213
- -- Subtract 1 microsecond to avoid creating an extra series.
214
- date_trunc(' minute' , was .session_ended_at - ' 1 microsecond' ::interval),
215
- ' 1 minute' ::interval
216
- ) s(start_time)
217
- WHERE
218
- s .start_time >= @start_time::timestamptz
219
- -- Subtract one minute because the series only contains the start time.
220
- AND s .start_time < (@end_time::timestamptz ) - ' 1 minute' ::interval
221
- GROUP BY s .start_time , w .template_id , was .user_id , was .agent_id , was .access_method , was .slug_or_port , wa .display_name , wa .icon , wa .slug
222
- )
223
251
224
252
SELECT
225
- array_agg(DISTINCT template_id)::uuid[] AS template_ids,
226
- -- Return IDs so we can combine this with GetTemplateInsights.
227
- array_agg(DISTINCT user_id)::uuid[] AS active_user_ids,
228
- access_method,
229
- slug_or_port,
230
- display_name,
231
- icon,
232
- is_app,
233
- SUM (seconds) AS usage_seconds
234
- FROM app_stats_by_user_and_agent
235
- GROUP BY access_method, slug_or_port, display_name, icon, is_app;
253
+ t .template_ids ,
254
+ array_agg(DISTINCT ai .user_id )::uuid[] AS active_user_ids,
255
+ ' ' ::text AS access_method, -- TODO(mafredri): Remove.
256
+ ai .app_name AS slug_or_port,
257
+ ai .display_name ,
258
+ ai .icon ,
259
+ ai .is_app ,
260
+ (SUM (ai .app_usage_mins ) * 60 )::bigint AS usage_seconds
261
+ FROM
262
+ app_insights AS ai
263
+ JOIN
264
+ templates AS t
265
+ ON
266
+ ai .app_name = t .app_name
267
+ AND ai .display_name = t .display_name
268
+ AND ai .icon = t .icon
269
+ AND ai .is_app = t .is_app
270
+ GROUP BY
271
+ t .template_ids , ai .app_name , ai .display_name , ai .icon , ai .is_app ;
236
272
237
273
-- name: GetTemplateAppInsightsByTemplate :many
238
274
WITH app_stats_by_user_and_agent AS (
0 commit comments