Skip to content

Commit 08a7e35

Browse files
authored
Merge pull request #74 condition_trigger
Implements controls on triggering conditions.
2 parents 9817b9f + e1282b2 commit 08a7e35

4 files changed

Lines changed: 79 additions & 7 deletions

File tree

.Rbuildignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,5 @@
1111
^docs$
1212
^pkgdown$
1313
^\.nojekyll$
14-
^\.copilot-tracking$
14+
^\.copilot-tracking$
15+
^vignettes/.*_cache$

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
1+
# user added
12
.Rproj.user
23
.Rhistory
34
.RData
45
.Ruserdata
56
*.Rproj
6-
.DS_Store
77
inst/doc
88
docs
99
renv/profile
1010
/*.R
1111
.copilot-tracking/
1212
.github/agents/
1313
vignettes/.build.timestamp
14+
/vignettes/*_cache/
1415

1516
# Created by https://www.toptal.com/developers/gitignore/api/r,windows,macos,linux
1617
# Edit at https://www.toptal.com/developers/gitignore?templates=r,windows,macos,linux

R/Timer.R

Lines changed: 62 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ Timer <- R6::R6Class(
7979
#' - `where` `expr` filter conditions in [dplyr::filter()] style
8080
#' - `analysis` `function` or `NULL` analysis applied to filtered data
8181
#' - `name` `character` or `NULL` unique key for the condition
82+
#' - `cooldown` `numeric` minimum time between consecutive triggers
83+
#' - `max_triggers` `integer` maximum number of times this condition can trigger
8284
conditions = NULL,
8385

8486
# --- constructor ---
@@ -134,6 +136,8 @@ Timer <- R6::R6Class(
134136
#' @param ... `expression` Boolean expression(s) for `dplyr::filter()`.
135137
#' @param analysis `function` or `NULL` Optional function to apply.
136138
#' @param name `character` Unique condition identifier.
139+
#' @param cooldown `numeric` Minimum time between consecutive triggers (default: 0, no cooldown).
140+
#' @param max_triggers `integer` Maximum number of times this condition can trigger (default: 1, single trigger).
137141
#'
138142
#' @examples
139143
#' #' t <- Timer$new(name = "Timer")
@@ -166,15 +170,35 @@ Timer <- R6::R6Class(
166170
add_condition = function(
167171
...,
168172
analysis = NULL,
169-
name = NULL
173+
name = NULL,
174+
cooldown = 0,
175+
max_triggers = 1L
176+
170177
) {
171178
# Capture filter predicates as quosures (with caller env)
172179
where_quos <- rlang::enquos(..., .named = FALSE)
173180

181+
# Variable Checks and Error catching
182+
cooldown <- as.numeric(cooldown)
183+
if (length(cooldown) != 1L || is.na(cooldown) || cooldown < 0) {
184+
stop("`cooldown` must be a single non-negative number.")
185+
}
186+
187+
max_triggers <- as.integer(max_triggers)
188+
if (length(max_triggers) != 1L || is.na(max_triggers) || max_triggers < 0) {
189+
stop("`max_triggers` must be a single non-negative integer (use Inf for unlimited).")
190+
}
191+
192+
174193
cond <- list(
175194
where = where_quos,
176195
analysis = analysis,
177-
name = name
196+
name = name,
197+
cooldown = cooldown,
198+
max_triggers = max_triggers,
199+
trigger_count = 0L,
200+
last_trigger_time = NA_real_
201+
178202
)
179203
self$conditions <- append(self$conditions, list(cond))
180204
invisible(self)
@@ -322,17 +346,51 @@ Timer <- R6::R6Class(
322346
locked_data
323347
}
324348

325-
if (nrow(df_i) == 0L) {
326-
# warning(sprintf("Skipping condition '%s' at time %s: filtered data is empty", key, as.character(current_time)), call. = FALSE)
349+
350+
if (is.null(cond$trigger_count)) cond$trigger_count <- 0L
351+
if (is.null(cond$max_triggers)) cond$max_triggers <- 1L
352+
if (is.null(cond$last_trigger_time)) cond$last_trigger_time <- NA_real_
353+
if (is.null(cond$cooldown)) cond$cooldown <- 0
354+
355+
match_now <- nrow(df_i) > 0L
356+
357+
358+
# If no match, skip
359+
if (!match_now) {
360+
self$conditions[[i]] <- cond
327361
next
328362
}
329363

364+
365+
# Hard cap on number of triggers
366+
if (is.finite(cond$max_triggers) && cond$trigger_count >= cond$max_triggers) {
367+
self$conditions[[i]] <- cond
368+
next
369+
}
370+
371+
# Check cooldown
372+
if (is.finite(cond$last_trigger_time)) {
373+
if ((current_time - cond$last_trigger_time) < cond$cooldown) {
374+
self$conditions[[i]] <- cond
375+
next
376+
}
377+
}
378+
379+
330380
if (is.function(cond$analysis)) {
331381
results[[key]] <- cond$analysis(df_i, current_time)
332382
} else {
333383
results[[key]] <- df_i
334384
warning(sprintf(" returning filtered data as is because condition '%s' has no applicable analysis \n", key), call. = FALSE)
335385
}
386+
387+
# Update trigger info after a successful trigger
388+
cond$trigger_count <- cond$trigger_count + 1L
389+
cond$last_trigger_time <- current_time
390+
391+
# Persist state back into Timer
392+
self$conditions[[i]] <- cond
393+
336394
}
337395

338396
results

man/Timer.Rd

Lines changed: 13 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)