Skip to content

Commit

Permalink
fix: added multidevice support to query_active_history (#451)
Browse files Browse the repository at this point in the history
  • Loading branch information
ErikBjare committed May 29, 2023
1 parent 6468b39 commit 9b387d2
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 41 deletions.
32 changes: 17 additions & 15 deletions src/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,12 +116,12 @@ export function canonicalEvents(params: DesktopQueryParams | AndroidQueryParams)

return [
// Fetch window/app events
`events = flood(query_bucket("${bid_window}"));`,
`events = flood(query_bucket(find_bucket("${bid_window}")));`,
// On Android, merge events to avoid overload of events
isAndroidParams(params) ? 'events = merge_events_by_keys(events, ["app"]);' : '',
// Fetch not-afk events
isDesktopParams(params)
? `not_afk = flood(query_bucket("${params.bid_afk}"));
? `not_afk = flood(query_bucket(find_bucket("${params.bid_afk}")));
not_afk = filter_keyvals(not_afk, "status", ["not-afk"]);` +
(always_active_pattern_str
? `not_treat_as_afk = filter_keyvals_regex(events, "app", "${always_active_pattern_str}");
Expand Down Expand Up @@ -382,16 +382,15 @@ export function multideviceQuery(params: MultiQueryParams): string[] {
"cat_events": cat_events,
"active_events": not_afk,
"duration": duration
},
}
};`
);
}

export function editorActivityQuery(editorbuckets: string[]): string[] {
let q = ['events = [];'];
for (let editorbucket of editorbuckets) {
editorbucket = escape_doublequote(editorbucket);
q.push('events = concat(events, flood(query_bucket("' + editorbucket + '")));');
for (const editorbucket of editorbuckets) {
q.push(`events = concat(events, flood(query_bucket("${escape_doublequote(editorbucket)}")));`);
}
q = q.concat([
'files = sort_by_duration(merge_events_by_keys(events, ["file", "language"]));',
Expand All @@ -408,15 +407,18 @@ export function editorActivityQuery(editorbuckets: string[]): string[] {

// Returns a query that yields a single event with the duration set to
// the sum of all non-afk time in the queried period
export function activityQuery(afkbucket: string): string[] {
afkbucket = escape_doublequote(afkbucket);
return [
'afkbucket = "' + afkbucket + '";',
'not_afk = flood(query_bucket(afkbucket));',
'not_afk = filter_keyvals(not_afk, "status", ["not-afk"]);',
'not_afk = merge_events_by_keys(not_afk, ["status"]);',
'RETURN = not_afk;',
];
// TODO: Would ideally account for `filter_afk` and `always_active_pattern`
export function activityQuery(afkbuckets: string[]): string[] {
let q = ['not_afk = [];'];
for (const afkbucket of afkbuckets) {
q = q.concat([
`not_afk_curr = query_bucket("${escape_doublequote(afkbucket)}");`,
`not_afk_curr = filter_keyvals(not_afk_curr, "status", ["not-afk"]);`,
`not_afk = union_no_overlap(not_afk, not_afk_curr);`,
]);
}
q = q.concat(['not_afk = merge_events_by_keys(not_afk, ["status"]);', 'RETURN = not_afk;']);
return q;
}

// Equivalent function to activityQuery, but for Android (which doesn't have an afk bucket)
Expand Down
23 changes: 21 additions & 2 deletions src/stores/activity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -394,11 +394,30 @@ export const useActivityStore = defineStore('activity', {
this.query_editor_completed(data[0]);
},

async query_active_history({ timeperiod }: QueryOptions) {
async query_active_history({ timeperiod, ...query_options }: QueryOptions) {
const settingsStore = useSettingsStore();
const bucketsStore = useBucketsStore();
const periods = timeperiodStrsAroundTimeperiod(timeperiod).filter(tp_str => {
return !_.includes(this.active.history, tp_str);
});
const data = await getClient().query(periods, queries.activityQuery(this.buckets.afk[0]));
let afk_buckets: string[] = [];
if (settingsStore.useMultidevice) {
// get all hostnames that qualify for the multidevice query
const hostnames = bucketsStore.hosts.filter(
// require that the host has afk buckets,
// and that the host is not a fakedata host,
// unless we're explicitly querying fakedata
host =>
host &&
bucketsStore.bucketsAFK(host).length > 0 &&
(!host.startsWith('fakedata') || query_options.host.startsWith('fakedata'))
);
// get all afk buckets for all hosts
afk_buckets = _.flatten(hostnames.map(bucketsStore.bucketsAFK));
} else {
afk_buckets = [this.buckets.afk[0]];
}
const data = await getClient().query(periods, queries.activityQuery(afk_buckets));
const active_history = _.zipObject(
periods,
_.map(data, pair => _.filter(pair, e => e.data.status == 'not-afk'))
Expand Down
13 changes: 11 additions & 2 deletions test/unit/__snapshots__/queries.test.node.js.snap
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`generate fullDesktopQuery 1`] = `
"events = flood(query_bucket(""));
not_afk = flood(query_bucket(""));
"events = flood(query_bucket(find_bucket("aw-watcher-window_testhost")));
not_afk = flood(query_bucket(find_bucket("aw-watcher-afk_testhost")));
not_afk = filter_keyvals(not_afk, "status", ["not-afk"]);
not_treat_as_afk = filter_keyvals_regex(events, "app", "meow|nyaan|specials: \\w(\\\\)");
not_afk = period_union(not_afk, not_treat_as_afk);
Expand Down Expand Up @@ -43,3 +43,12 @@ RETURN = {
}
};"
`;

exports[`generate fullDesktopQuery 2`] = `
"not_afk = [];
not_afk_curr = query_bucket("aw-watcher-afk_testhost");
not_afk_curr = filter_keyvals(not_afk_curr, "status", ["not-afk"]);
not_afk = union_no_overlap(not_afk, not_afk_curr);
not_afk = merge_events_by_keys(not_afk, ["status"]);
RETURN = not_afk;"
`;
65 changes: 43 additions & 22 deletions test/unit/queries.test.node.js
Original file line number Diff line number Diff line change
@@ -1,28 +1,49 @@
const queries = require('~/queries');

test('generate fullDesktopQuery', () => {
const bid_window = '';
const bid_afk = '';
const bid_browsers = [];
const filter_afk = true;
const categories = [];
const filter_categories = true;
const include_audible = true;
const always_active_pattern = /meow|nyaan|specials: \w(\\)/.toString().substring(1).slice(0, -1);
const query_lines = queries.fullDesktopQuery({
bid_window,
bid_afk,
bid_browsers,
filter_afk,
categories,
filter_categories,
include_audible,
always_active_pattern,
});
// test data
const hostname = 'testhost';
const bid_window = 'aw-watcher-window_' + hostname;
const bid_afk = 'aw-watcher-afk_' + hostname;
const bid_browsers = [];
const filter_afk = true;
const always_active_pattern = /meow|nyaan|specials: \w(\\)/.toString().substring(1).slice(0, -1);
const queryParams = {
bid_window,
bid_afk,
bid_browsers,
filter_afk,
categories: [],
filter_categories: true,
include_audible: true,
always_active_pattern,
};

function expectBracketsClosed(query) {
// Checks that there are matching parens, brackets, braces, etc
// Doesn't actually check placement, just matching open/closed count.

// parens
const openParens = query.match(/\(/g);
const closeParens = query.match(/\)/g);
expect(openParens && openParens.length).toEqual(closeParens && closeParens.length);

// brackets
const openBrackets = query.match(/\[/g);
const closeBrackets = query.match(/\]/g);
expect(openBrackets && openBrackets.length).toEqual(closeBrackets && closeBrackets.length);

// join query lines into a single string
const query = query_lines.join('\n');
// braces
const openBraces = query.match(/\{/g);
const closeBraces = query.match(/\}/g);
expect(openBraces && openBraces.length).toEqual(closeBraces && closeBraces.length);
}

test('generate fullDesktopQuery', () => {
let query = queries.fullDesktopQuery(queryParams).join('\n');
expect(query).toMatchSnapshot();
expectBracketsClosed(query);

// check that query_str is well formatted
query = queries.activityQuery([bid_afk]).join('\n');
expect(query).toMatchSnapshot();
expectBracketsClosed(query);
});

1 comment on commit 9b387d2

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here are screenshots of this commit:

Screenshots using aw-server v0.12.1 (click to expand)

Screenshots using aw-server-rust master (click to expand)

Screenshots using aw-server-rust v0.12.1 (click to expand)

Please sign in to comment.