From 9858f85a5ab1d51df5e7f9cd831bb14cd3ad2ca7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Bj=C3=A4reholt?= Date: Thu, 8 Sep 2022 15:41:47 +0200 Subject: [PATCH] feat: started working on including stopwatch activity in Activity view --- src/queries.ts | 5 +++ src/stores/activity.ts | 23 ++++++++++++ src/stores/buckets.ts | 66 +++++++++++++++++---------------- src/util/interfaces.ts | 1 + src/views/activity/Activity.vue | 9 +++++ 5 files changed, 73 insertions(+), 31 deletions(-) diff --git a/src/queries.ts b/src/queries.ts index 502c39e9..f8d5c8ab 100644 --- a/src/queries.ts +++ b/src/queries.ts @@ -31,6 +31,7 @@ interface BaseQueryParams { categories: Category[]; filter_categories: string[][]; bid_browsers?: string[]; + bid_stopwatch?: string; return_variable_suffix?: string; } @@ -129,6 +130,10 @@ export function canonicalEvents(params: DesktopQueryParams | AndroidQueryParams) isDesktopParams(params) && params.filter_afk ? 'events = filter_period_intersect(events, not_afk);' : '', + params.bid_stopwatch + ? `stopwatch_events = query_bucket("${params.bid_stopwatch}"); + events = period_union(events, stopwatch_events);` + : '', // Categorize params.categories ? `events = categorize(events, ${categories_str});` : '', // Filter out selected categories diff --git a/src/stores/activity.ts b/src/stores/activity.ts index 56a8d66f..d8078e59 100644 --- a/src/stores/activity.ts +++ b/src/stores/activity.ts @@ -54,6 +54,7 @@ export interface QueryOptions { timeperiod?: TimePeriod; filter_afk?: boolean; include_audible?: boolean; + include_stopwatch?: boolean; filter_categories?: string[][]; dont_query_inactive?: boolean; force?: boolean; @@ -102,6 +103,10 @@ interface State { available: boolean; }; + stopwatch: { + available: boolean; + }; + query_options?: QueryOptions; // Can't this be handled in bucketStore? @@ -112,6 +117,7 @@ interface State { editor: string[]; browser: string[]; android: string[]; + stopwatch: string[]; }; } @@ -161,6 +167,10 @@ export const useActivityStore = defineStore('activity', { available: false, }, + stopwatch: { + available: false, + }, + query_options: null, buckets: { @@ -170,6 +180,7 @@ export const useActivityStore = defineStore('activity', { editor: [], browser: [], android: [], + stopwatch: [], }, }), @@ -317,6 +328,7 @@ export const useActivityStore = defineStore('activity', { filter_categories, filter_afk, include_audible, + include_stopwatch, }: QueryOptions) { const periods = [timeperiodToStr(timeperiod)]; const categories = useCategoryStore().classes_for_query; @@ -325,6 +337,10 @@ export const useActivityStore = defineStore('activity', { bid_window: this.buckets.window[0], bid_afk: this.buckets.afk[0], bid_browsers: this.buckets.browser, + bid_stopwatch: + include_stopwatch && this.buckets.stopwatch.length > 0 + ? this.buckets.stopwatch[0] + : undefined, filter_afk, categories, filter_categories, @@ -364,6 +380,7 @@ export const useActivityStore = defineStore('activity', { timeperiod, filter_categories, filter_afk, + include_stopwatch, dontQueryInactive, }: QueryOptions & { dontQueryInactive: boolean }) { // TODO: Needs to be adapted for Android @@ -429,6 +446,10 @@ export const useActivityStore = defineStore('activity', { bid_afk: this.buckets.afk[0], bid_window: this.buckets.window[0], bid_browsers: this.buckets.browser, + bid_stopwatch: + include_stopwatch && this.buckets.stopwatch.length > 0 + ? this.buckets.stopwatch[0] + : undefined, // bid_android: this.buckets.android, categories, filter_categories, @@ -475,6 +496,7 @@ export const useActivityStore = defineStore('activity', { this.editor.available = this.buckets.editor.length > 0; this.android.available = this.buckets.android.length > 0; this.category.available = this.window.available || this.android.available; + this.stopwatch.available = this.buckets.stopwatch.length > 0; }, async get_buckets(this: State, { host }) { @@ -485,6 +507,7 @@ export const useActivityStore = defineStore('activity', { this.buckets.android = bucketsStore.bucketsAndroid(host); this.buckets.browser = bucketsStore.bucketsBrowser(host); this.buckets.editor = bucketsStore.bucketsEditor(host); + this.buckets.stopwatch = bucketsStore.bucketsStopwatch(host); console.log('Available buckets: ', this.buckets); this.buckets.loaded = true; diff --git a/src/stores/buckets.ts b/src/stores/buckets.ts index 57889879..c4f0e0b5 100644 --- a/src/stores/buckets.ts +++ b/src/stores/buckets.ts @@ -11,7 +11,7 @@ function select_buckets( return _.map( _.filter( buckets, - bucket => (!type || bucket['type'] === type) && (!host || bucket['hostname'] == host) + bucket => (!type || bucket.type === type) && (!host || bucket.hostname == host) ), bucket => bucket['id'] ); @@ -29,12 +29,12 @@ export const useBucketsStore = defineStore('buckets', { getters: { hosts(this: State): string[] { // TODO: Include consideration of device_id UUID - return _.uniq(_.map(this.buckets, bucket => bucket['hostname'])); + return _.uniq(_.map(this.buckets, bucket => bucket.hostname)); }, // Uses device_id instead of hostname devices(this: State): string[] { // TODO: Include consideration of device_id UUID - return _.uniq(_.map(this.buckets, bucket => bucket['device_id'])); + return _.uniq(_.map(this.buckets, bucket => bucket.device_id)); }, available(): (hostname: string) => { @@ -43,6 +43,7 @@ export const useBucketsStore = defineStore('buckets', { editor: boolean; android: boolean; category: boolean; + stopwatch: boolean; } { // Returns a map of which kinds of buckets are available // @@ -60,51 +61,54 @@ export const useBucketsStore = defineStore('buckets', { editor: this.bucketsEditor(hostname).length > 0, android: androidAvail, category: windowAvail || androidAvail, + stopwatch: this.bucketsStopwatch(hostname).length > 0, }; }; }, - // These should be considered low-level, and should be used sparingly. - bucketsAFK(this: State): (host: string) => string[] { - return host => select_buckets(this.buckets, { host, type: 'afkstatus' }); + bucketsByType( + this: State + ): (host: string, type: string, fallback_unknown_host?: boolean) => string[] { + return (host, type, fallback_unknown_host) => { + let buckets = select_buckets(this.buckets, { host, type }); + if (fallback_unknown_host && buckets.length == 0) { + buckets = select_buckets(this.buckets, { host: 'unknown', type }); + //console.log('fallback: ', buckets); + } + return buckets; + }; }, - bucketsWindow(this: State): (host: string) => string[] { + + // Convenience getters for bucketsByType + bucketsAFK(): (host: string) => string[] { + return host => this.bucketsByType(host, 'afkstatus'); + }, + bucketsWindow(): (host: string) => string[] { return host => - _.filter( - select_buckets(this.buckets, { host, type: 'currentwindow' }), - id => !id.startsWith('aw-watcher-android') + this.bucketsByType(host, 'currentwindow').filter( + (id: string) => !id.startsWith('aw-watcher-android') ); }, - bucketsAndroid(this: State): (host: string) => string[] { + bucketsAndroid(): (host: string) => string[] { return host => - _.filter(select_buckets(this.buckets, { host, type: 'currentwindow' }), id => + this.bucketsByType(host, 'currentwindow').filter((id: string) => id.startsWith('aw-watcher-android') ); }, - bucketsEditor(this: State): (host: string) => string[] { + bucketsEditor(): (host: string) => string[] { // fallback to a bucket with 'unknown' host, if one exists. // TODO: This needs a fix so we can get rid of this workaround. - const type = 'app.editor.activity'; - return (host: string) => { - const buckets = select_buckets(this.buckets, { host, type }); - return buckets.length == 0 - ? select_buckets(this.buckets, { host: 'unknown', type }) - : buckets; - }; + return host => this.bucketsByType(host, 'app.editor.activity', true); }, - bucketsBrowser(this: State): (host: string) => string[] { + bucketsBrowser(): (host: string) => string[] { // fallback to a bucket with 'unknown' host, if one exists. // TODO: This needs a fix so we can get rid of this workaround. - const type = 'web.tab.current'; - return (host: string) => { - const buckets = select_buckets(this.buckets, { host, type }); - if (buckets.length > 0) { - return buckets; - } else { - console.log('fallback: ', select_buckets(this.buckets, { host: 'unknown', type })); - return select_buckets(this.buckets, { host: 'unknown', type }); - } - }; + return host => this.bucketsByType(host, 'web.tab.current', true); + }, + bucketsStopwatch(): (host: string) => string[] { + // fallback to a bucket with 'unknown' host, if one exists. + // TODO: This needs a fix so we can get rid of this workaround. + return (host: string) => this.bucketsByType(host, 'general.stopwatch', true); }, getBucket(this: State): (id: string) => IBucket { diff --git a/src/util/interfaces.ts b/src/util/interfaces.ts index e0d4a51d..51470689 100644 --- a/src/util/interfaces.ts +++ b/src/util/interfaces.ts @@ -7,6 +7,7 @@ export interface IEvent { export interface IBucket { id: string; hostname: string; + device_id: string; type: string; data: Record; } diff --git a/src/views/activity/Activity.vue b/src/views/activity/Activity.vue index 079dc1c5..2b9d361a 100644 --- a/src/views/activity/Activity.vue +++ b/src/views/activity/Activity.vue @@ -61,6 +61,12 @@ div icon#includeAudibleHelp(name="question-circle" style="opacity: 0.4") b-tooltip(target="includeAudibleHelp" v-b-tooltip.hover title="If the active window is an audible browser tab, count as active. Requires a browser watcher.") + b-form-checkbox(v-if="devmode" v-model="include_stopwatch" size="sm") + // WIP: https://github.com/ActivityWatch/aw-webui/pull/368 + | Include manually logged events (stopwatch) + br + | #[b Note:] WIP, breaks aw-server-rust badly. Only shown in devmode. + div.col-md-6.mt-2.mt-md-0 b-form-group(label="Show category" label-cols="5" label-cols-lg="4" style="font-size: 0.88em") b-form-select(v-model="filter_category", :options="categoryStore.category_select(true)" size="sm") @@ -189,12 +195,14 @@ export default { showOptions: false, include_audible: true, + include_stopwatch: false, filter_afk: true, new_view: {}, }; }, computed: { ...mapState(useViewsStore, ['views']), + ...mapState(useSettingsStore, ['devmode']), // number of filters currently set (different from defaults) filters_set() { @@ -396,6 +404,7 @@ export default { force: force, filter_afk: this.filter_afk, include_audible: this.include_audible, + include_stopwatch: this.include_stopwatch, filter_categories: this.filter_categories, }; await this.activityStore.ensure_loaded(queryOptions);