feat(queries): add multidevice canonical query and host discovery - #121
Conversation
Add canonicalMultideviceEvents(), which queries each host with its own AFK filtering and combines the results with union_no_overlap in priority order, following canonicalMultideviceEvents in aw-webui. Add multideviceHostParams(), which builds per-host params (desktop window+afk, or Android/ScreenTime) from bucket metadata, including buckets synced via aw-sync. Unlike aw-webui, Android events are not merged by app before the union: merged events keep only their first timestamp, so they would claim time the device was not in use and break the overlap removal. canonicalEvents() gains keyword-only options (return_variable_suffix, merge_android, exact_bucket_ids) and now defines not_afk on Android.
|
@greptileai review |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 674725199a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
- attach host-attributed browser buckets to desktop hosts - order hosts by the buckets actually selected, not rejected ones - treat hostname "unknown" as missing before falling back to data.hostname - never pass bid_browsers to Android params - format with black
|
@greptileai review |
browsersWithBuckets matched a browser name anywhere in the bucket ID, so a hostname containing a browser name (e.g. host "chrome-box") could map a Firefox bucket to Chrome.
|
@greptileai review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b429ff419f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
) * feat(screentime): discover ActivityWatch hosts from bucket metadata Hosts are now discovered from bucket metadata, including buckets synced with aw-sync, instead of requiring every hostname in the config. The config hostnames list becomes an optional override (selection + priority order), with new optional exclude_hostnames and include_mobile. With aw-client that has multidevice support (ActivityWatch/aw-client#121), all hosts, including Android devices, are loaded with one canonicalMultideviceEvents query per week, cached with the host/bucket spec as part of the key so the cache is invalidated when hosts change. With the currently released aw-client, it falls back to the previous per-host aw-research query for the discovered desktop hosts. * fix(screentime): typecheck against aw-client without multidevice helpers * fix(screentime): keep $hostname by querying hosts individually Load each discovered host with the aw-client per-host canonical query and combine them client-side in priority order, instead of one server-side multidevice union, so events keep $hostname (used by the Dashboard notebook to show time per device). Caching is now per host and week. Also add a mocked test for combining hosts.
Adds a multidevice version of the canonical events query, so Python users (e.g. quantifiedme, aw-research) can get combined screen time across desktop hosts, hosts synced with aw-sync, and Android devices, the way the aw-webui multidevice view does.
What
canonicalMultideviceEvents(host_params): builds a query that runscanonicalEventsper host (with that host's own AFK filtering) into host-suffixed variables, then combines them withunion_no_overlapin the given order. Earlier hosts win where hosts overlap, so time is never double counted. Leaveseventsandnot_afkdefined, likecanonicalEvents.multideviceHostParams(buckets, hosts=None, **common): discovers per-host params fromget_buckets()metadata:hostnamefield (falls back todata.hostname, then the-synced-from-<host>suffix),unknownis skippedDesktopQueryParams; only an Android/ScreenTime bucket:AndroidQueryParams(no AFK filter, since mobile devices have no afk bucket); otherwise skippedaw-watcher-android-test-...oraw-watcher-window2_...are not picked), then local over synced copies, then most recently updatedhosts=[...]selects and orders explicitlycanonicalEventsgains keyword-only options used by the above (defaults keep the current behaviour):return_variable_suffix,merge_android,exact_bucket_ids. The multidevice path uses exactquery_bucket("<id>")instead offind_bucketprefix matching, since discovered IDs are exact and prefix matching can pick the wrong host when names overlap (e.g.host.localvshost.localdomain, see Unnecessary call tofind_bucketat activity page aw-webui#590).canonicalEventsnow setsnot_afk = eventson Android (previouslynot_afkwas undefined there), matching aw-webui.Difference from aw-webui
aw-webui's
canonicalEventsrunsmerge_events_by_keys(events, ["app"])on Android hosts, also insidecanonicalMultideviceEvents. Merged events keep only the first event's timestamp with the summed duration, so before aunion_no_overlapthey claim time the phone was not used (and are no longer sorted). Here Android events are not merged in the multidevice path.test_android_events_not_merged_before_unioncovers this and fails if the merge is re-enabled.Tests
tests/test_queries.pyexecutes the generated queries with aw-core's query2 engine against an in-memory datastore (no server needed): union and AFK filtering across a desktop and a synced phone, the no-merge case, host priority, combinednot_afk, and discovery from bucket metadata. Added tomake test.Also smoke-tested against a real aw-server-rust instance with 8 desktop hosts (local and synced) plus Android and imported ScreenTime buckets.
Example: