@@ -235,5 +235,60 @@ def duration_seconds(scope = all)
235
235
connection . select_value ( "SELECT COALESCE(SUM(diff), 0)::integer FROM (#{ capped_diffs . to_sql } ) AS diffs" ) . to_i
236
236
end
237
237
end
238
+
239
+ def duration_seconds_boundary_aware ( scope , start_time , end_time )
240
+ scope = scope . with_valid_timestamps
241
+
242
+ model_class = scope . model
243
+ base_scope = model_class . all . with_valid_timestamps
244
+
245
+ if scope . where_values_hash [ "user_id" ]
246
+ base_scope = base_scope . where ( user_id : scope . where_values_hash [ "user_id" ] )
247
+ end
248
+
249
+ if scope . where_values_hash [ "category" ]
250
+ base_scope = base_scope . where ( category : scope . where_values_hash [ "category" ] )
251
+ end
252
+
253
+ if scope . where_values_hash [ "project" ]
254
+ base_scope = base_scope . where ( project : scope . where_values_hash [ "project" ] )
255
+ end
256
+
257
+ if scope . where_values_hash [ "deleted_at" ]
258
+ base_scope = base_scope . where ( deleted_at : scope . where_values_hash [ "deleted_at" ] )
259
+ end
260
+
261
+ # get the heartbeat before the start_time
262
+ boundary_heartbeat = base_scope
263
+ . where ( "time < ?" , start_time )
264
+ . order ( time : :desc )
265
+ . limit ( 1 )
266
+ . first
267
+
268
+ # if it's not NULL, we'll use it
269
+ if boundary_heartbeat
270
+ combined_scope = base_scope
271
+ . where ( "time >= ? OR time = ?" , start_time , boundary_heartbeat . time )
272
+ . where ( "time <= ?" , end_time )
273
+ else
274
+ combined_scope = base_scope
275
+ . where ( time : start_time ..end_time )
276
+ end
277
+
278
+ # we calc w/ the boundary heartbeat, but we only sum within the orignal constraint
279
+ capped_diffs = combined_scope
280
+ . select ( "time, CASE
281
+ WHEN LAG(time) OVER (ORDER BY time) IS NULL THEN 0
282
+ ELSE LEAST(EXTRACT(EPOCH FROM (to_timestamp(time) - to_timestamp(LAG(time) OVER (ORDER BY time)))), #{ heartbeat_timeout_duration . to_i } )
283
+ END as diff" )
284
+ . where . not ( time : nil )
285
+ . order ( time : :asc )
286
+
287
+ connection . select_value (
288
+ "SELECT COALESCE(SUM(diff), 0)::integer
289
+ FROM (#{ capped_diffs . to_sql } ) AS diffs
290
+ WHERE time >= #{ start_time } "
291
+ ) . to_i
292
+ end
238
293
end
239
294
end
0 commit comments