fix(query): expose pulsetime parameter in flood() query function#139
fix(query): expose pulsetime parameter in flood() query function#139behdadmansouri wants to merge 1 commit into
Conversation
Previously q2_flood accepted no arguments, always using the default 5s pulsetime from aw_transform.flood(). This means users who set a polling interval above 5s (e.g. 30s) would see unexplained gaps in their activity timeline because flood() would not fill the inter-poll gaps. - Add optional pulsetime parameter to q2_flood (default 5.0s, backward compatible) - Add two regression tests: one verifying large gaps are NOT filled at default pulsetime, one verifying they ARE filled with a custom pulsetime The web UI can now pass the appropriate pulsetime to flood() based on the user's polling interval setting. Fixes ActivityWatch/activitywatch#1177 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
| def test_flood_large_gap_not_filled_with_default_pulsetime(): | ||
| """A gap larger than the default pulsetime (5s) should not be filled.""" | ||
| events = [ | ||
| Event(timestamp=now, duration=10, data={"a": 0}), | ||
| Event(timestamp=now + 25 * td1s, duration=10, data={"b": 1}), | ||
| ] | ||
| flooded = flood(events) | ||
| # Gap is 25s - 10s = 15s, larger than default pulsetime=5; stays as a gap | ||
| assert len(flooded) == 2 | ||
| gap = flooded[1].timestamp - (flooded[0].timestamp + flooded[0].duration) | ||
| assert gap > timedelta(0) | ||
|
|
||
|
|
||
| def test_flood_large_gap_filled_with_custom_pulsetime(): | ||
| """A gap larger than default pulsetime should be filled when pulsetime is increased. | ||
|
|
||
| This is the fix for ActivityWatch/activitywatch#1177: users with a high | ||
| poll interval (e.g. 30s) need to call flood with pulsetime=poll_time+1. | ||
| """ | ||
| events = [ | ||
| Event(timestamp=now, duration=10, data={"a": 0}), | ||
| Event(timestamp=now + 25 * td1s, duration=10, data={"b": 1}), | ||
| ] | ||
| flooded = flood(events, pulsetime=20) | ||
| # Gap is 15s, within pulsetime=20; should be filled | ||
| assert len(flooded) == 2 | ||
| gap = flooded[1].timestamp - (flooded[0].timestamp + flooded[0].duration) | ||
| assert gap == timedelta(0) |
There was a problem hiding this comment.
New tests exercise
aw_transform.flood directly, not q2_flood
Both new tests import and call flood from aw_transform, bypassing the q2_flood wrapper in aw_query/functions.py that was actually changed. A bug in how pulsetime is threaded through q2_flood (e.g. a copy-paste that keeps the old return flood(events)) would leave these tests green while the query API remains broken. Adding a test (or even a smoke-test) that calls q2_flood or exercises it through the query engine would close this gap.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| def q2_flood(events: list, pulsetime: float = 5.0) -> List[Event]: | ||
| """Fill gaps between events up to pulsetime seconds. | ||
|
|
||
| The default pulsetime of 5s works well for the default 1s poll interval. | ||
| If you have set a higher polling interval (e.g. 30s), set pulsetime to | ||
| at least poll_time + 1 to avoid gaps in the activity timeline. | ||
|
|
||
| See ActivityWatch/activitywatch#1177. | ||
| """ | ||
| return flood(events, pulsetime) |
There was a problem hiding this comment.
pulsetime type is not validated by q2_typecheck
The q2_typecheck decorator only enforces type annotations on required parameters (param.default == param.empty). Because pulsetime has a default value of 5.0, it is silently skipped. If a user calls flood(events, "30") in a query, Python will pass a string into aw_transform.flood, which then calls timedelta(seconds="30"), producing a cryptic TypeError instead of a QueryFunctionException. A manual guard (if not isinstance(pulsetime, (int, float)): raise QueryFunctionException(...)) before the return would give users a clear error message.
…h high poll intervals Users who raise the watcher polling interval above 5s lose trackable time because the default 5s pulsetime in flood() no longer fills the inter-poll gaps. A user working 8h could see only 7.5h in the dashboard. Changes: - queries.ts: add optional floodPulsetime to BaseQueryParams (default 5); pass it as the explicit second argument to all flood() calls in canonicalEvents() and browserEvents() - settings.ts: add floodPulsetime: number (default 5, persisted like other settings) - stores/activity.ts: thread useSettingsStore().floodPulsetime into both fullDesktopQuery and multideviceQuery param objects - DeveloperSettings.vue: add a number input for floodPulsetime with guidance text explaining the poll_interval + 1 rule The default of 5 is fully backward-compatible. Users who increased their polling interval can now set this to polling_interval + 1 in Developer Settings and immediately recover the missing time. Pairs with aw-core fix: ActivityWatch/aw-core#139 Fixes ActivityWatch/activitywatch#1177 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Summary
Fixes ActivityWatch/activitywatch#1177
The
flood()query function previously accepted no arguments, always using the hardcoded default pulsetime of 5 seconds fromaw_transform.flood(). Users who raised their watcher polling interval above 5s (e.g. to 30s) would see unexplained gaps — the inter-poll gap exceeds the pulsetime so flood never fills it. A user working 8h could see only 7.5h reported.Changes:
aw_query/functions.py: Add optionalpulsetime: float = 5.0toq2_floodso queries can callflood(events, 30)for a 30s polling interval. Default of 5.0 is fully backward-compatible.tests/test_flood.py: Add two regression tests — one confirming large gaps are not filled at default pulsetime, one confirming they are filled when pulsetime is raised.Test plan
pytest tests/test_flood.py— all tests pass including the two new onesflood(events)continue to work unchanged (default pulsetime=5)flood(events, 31)correctly fills 30s gaps