Skip to content

Commit

Permalink
Merge pull request #190 from ActivityWatch/dev/selectable-activity-views
Browse files Browse the repository at this point in the history
Selectable activity views
  • Loading branch information
johan-bjareholt committed Apr 26, 2020
2 parents a5a9e70 + bc4e950 commit 353634b
Show file tree
Hide file tree
Showing 11 changed files with 286 additions and 75 deletions.
196 changes: 196 additions & 0 deletions src/components/SelectableVisualization.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
<template lang="pug">
div
h5 {{ type_title }}
div(v-if="type == 'top_apps'")
aw-summary(:fields="top_apps",
:namefunc="e => e.data.app",
:colorfunc="e => e.data.app",
with_limit)
div(v-if="type == 'top_titles'")
aw-summary(:fields="top_titles",
:namefunc="e => e.data.title",
:colorfunc="e => e.data.app",
with_limit)
div(v-if="type == 'top_domains'")
aw-summary(:fields="top_domains",
:namefunc="e => e.data.$domain",
:colorfunc="e => e.data.$domain",
with_limit)
div(v-if="type == 'top_urls'")
aw-summary(:fields="top_urls",
:namefunc="e => e.data.url",
:colorfunc="e => e.data.$domain",
with_limit)
div(v-if="type == 'top_editor_files'")
aw-summary(:fields="$store.state.activity.editor.top_files",
:namefunc="top_editor_files_namefunc",
:colorfunc="e => e.data.language",
with_limit)
div(v-if="type == 'top_editor_languages'")
aw-summary(:fields="$store.state.activity.editor.top_languages",
:namefunc="e => e.data.language",
:colorfunc="e => e.data.language",
with_limit)
div(v-if="type == 'top_editor_projects'")
aw-summary(:fields="$store.state.activity.editor.top_projects",
:namefunc="top_editor_projects_namefunc",
:colorfunc="e => e.data.language",
with_limit)
div(v-if="type == 'top_categories'")
aw-summary(:fields="top_categories",
:namefunc="e => e.data['$category'].join(' > ')",
:colorfunc="e => e.data['$category'].join(' > ')",
with_limit)
div(v-if="type == 'category_tree'")
aw-categorytree(:events="top_categories")
div(v-if="type == 'category_sunburst'")
aw-sunburst-categories(:data="top_categories_hierarchy", style="height: 20em")

b-dropdown.vis-style-dropdown-btn(size="sm" variant="outline-secondary")
template(v-slot:button-content)
icon(name="cog")
b-dropdown-item(v-for="t in types" :key="t" variant="outline-secondary" @click="$emit('onTypeChange', id, t)" v-bind:disabled="!get_type_available(t)")
| {{ get_type_title(t) }}
</template>

<style scoped lang="scss">
.vis-style-dropdown-btn {
position: absolute;
bottom: 0;
right: 0.5em;
background-color: #fff;
}
</style>

<script>
// TODO: Move this somewhere else
import { build_category_hierarchy } from '~/util/classes';
function pick_subname_as_name(c) {
c.name = c.subname;
c.children = c.children.map(pick_subname_as_name);
return c;
}
export default {
name: 'aw-selectable-vis',
props: {
id: Number,
type: String,
},
data: function() {
return {
types: [
'top_apps',
'top_titles',
'top_domains',
'top_urls',
'top_categories',
'category_tree',
'category_sunburst',
'top_editor_files',
'top_editor_languages',
'top_editor_projects',
],
// TODO: Move this function somewhere else
top_editor_files_namefunc: e => {
let f = e.data.file || '';
f = f.split('/');
f = f[f.length - 1];
return f;
},
// TODO: Move this function somewhere else
top_editor_projects_namefunc: e => {
let f = e.data.project || '';
f = f.split('/');
f = f[f.length - 1];
return f;
},
};
},
computed: {
top_apps: function() {
return this.$store.state.activity.window.top_apps;
},
top_titles: function() {
return this.$store.state.activity.window.top_titles;
},
top_domains: function() {
return this.$store.state.activity.browser.top_domains;
},
top_urls: function() {
return this.$store.state.activity.browser.top_urls;
},
top_categories: function() {
return this.$store.state.activity.category.top;
},
top_categories_hierarchy: function() {
if (this.top_categories) {
const categories = this.top_categories.map(c => {
return { name: c.data.$category, size: c.duration };
});
return {
name: 'All',
children: build_category_hierarchy(categories).map(c => pick_subname_as_name(c)),
};
} else {
return null;
}
},
type_title: function() {
return this.get_type_title(this.type);
},
},
methods: {
get_type_available: function(type) {
if (type === 'top_apps' || type === 'top_titles') {
return this.$store.state.activity.window.available;
} else if (type === 'top_domains' || type === 'top_urls') {
return this.$store.state.activity.browser.available;
} else if (
type === 'top_editor_files' ||
type === 'top_editor_languages' ||
type === 'top_editor_projects'
) {
return this.$store.state.activity.editor.available;
} else if (
type === 'top_categories' ||
type === 'category_tree' ||
type === 'category_sunburst'
) {
return this.$store.state.activity.category.available;
} else {
console.error('Unknown type available: ', type);
return false;
}
},
get_type_title: function(type) {
if (type === 'top_apps') {
return 'Top Applications';
} else if (type === 'top_titles') {
return 'Top Window Titles';
} else if (type === 'top_domains') {
return 'Top Browser Domains';
} else if (type === 'top_urls') {
return 'Top Browser URLs';
} else if (type === 'top_editor_files') {
return 'Top Editor Files';
} else if (type === 'top_editor_languages') {
return 'Top Editor Languages';
} else if (type === 'top_editor_projects') {
return 'Top Editor Projects';
} else if (type === 'top_categories') {
return 'Top Categories';
} else if (type === 'category_tree') {
return 'Category Tree';
} else if (type === 'category_sunburst') {
return 'Category Sunburst';
} else {
console.error('Unknown type: ', type);
return 'Unknown';
}
},
},
};
</script>
1 change: 1 addition & 0 deletions src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ Vue.component('error-boundary', () => import('./components/ErrorBoundary.vue'));
Vue.component('input-timeinterval', () => import('./components/InputTimeInterval.vue'));
Vue.component('aw-header', () => import('./components/Header.vue'));
Vue.component('aw-devonly', () => import('./components/DevOnly.vue'));
Vue.component('aw-selectable-vis', () => import('./components/SelectableVisualization.vue'));

// Visualization components
Vue.component('aw-summary', () => import('./visualizations/Summary.vue'));
Expand Down
32 changes: 16 additions & 16 deletions src/route.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ import VueRouter from 'vue-router';

const Home = () => import('./views/Home.vue');

// Daily activity views for desktop
const ActivityDaily = () => import('./views/activity/daily/ActivityDaily.vue');
const ActivityDailySummary = () => import('./views/activity/daily/ActivityDailySummary.vue');
const ActivityDailyWindow = () => import('./views/activity/daily/ActivityDailyWindow.vue');
const ActivityDailyBrowser = () => import('./views/activity/daily/ActivityDailyBrowser.vue');
const ActivityDailyEditor = () => import('./views/activity/daily/ActivityDailyEditor.vue');

// Activity views for desktop
const Activity = () => import('./views/activity/Activity.vue');
const ActivitySummary = () => import('./views/activity/ActivitySummary.vue');
const ActivityWindow = () => import('./views/activity/ActivityWindow.vue');
const ActivityBrowser = () => import('./views/activity/ActivityBrowser.vue');
const ActivityEditor = () => import('./views/activity/ActivityEditor.vue');
const ActivityAndroid = () => import('./views/activity/ActivityAndroid.vue');

const Buckets = () => import('./views/Buckets.vue');
const Bucket = () => import('./views/Bucket.vue');
const QueryExplorer = () => import('./views/QueryExplorer.vue');
Expand All @@ -29,34 +29,34 @@ const router = new VueRouter({
{ path: '/activity/android/:host/:date?', component: ActivityAndroid, props: true },
{
path: '/activity/:host/:periodLength?/:date?',
component: ActivityDaily,
component: Activity,
props: true,
children: [
{
path: 'summary',
meta: { subview: 'summary' },
name: 'activity-daily-summary',
component: ActivityDailySummary,
name: 'activity-summary',
component: ActivitySummary,
props: true,
},
{
path: 'window',
meta: { subview: 'window' },
name: 'activity-daily-window',
component: ActivityDailyWindow,
name: 'activity-window',
component: ActivityWindow,
props: true,
},
{
path: 'browser',
meta: { subview: 'browser' },
name: 'activity-daily-browser',
component: ActivityDailyBrowser,
name: 'activity-browser',
component: ActivityBrowser,
},
{
path: 'editor',
meta: { subview: 'editor' },
name: 'activity-daily-editor',
component: ActivityDailyEditor,
name: 'activity-editor',
component: ActivityEditor,
},
// Unspecified should redirect to summary view is the summary view
// (needs to be last since otherwise it'll always match first)
Expand Down
4 changes: 2 additions & 2 deletions src/store/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Vue from 'vue';
import Vuex from 'vuex';
import activity_daily from './modules/activity_daily';
import activity from './modules/activity';
import buckets from './modules/buckets';
import settings from './modules/settings';
//import createLogger from '../../../src/plugins/logger';
Expand All @@ -11,7 +11,7 @@ const debug = process.env.NODE_ENV !== 'production';

export default new Vuex.Store({
modules: {
activity_daily,
activity,
buckets,
settings,
},
Expand Down
File renamed without changes.
4 changes: 2 additions & 2 deletions src/views/Dev.vue
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ export default {
},
methods: {
query_window_timing: async function() {
await this.$store.dispatch('activity_daily/query_window', this.queryOptions);
await this.$store.dispatch('activity/query_window', this.queryOptions);
},
query_browser_timing: async function() {
await this.$store.dispatch('activity_daily/query_browser', this.queryOptions);
await this.$store.dispatch('activity/query_browser', this.queryOptions);
},
},
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ div
p
| Host: {{ host }}
br
| Active time: {{ $store.state.activity_daily.active.duration | friendlyduration }}
| Active time: {{ $store.state.activity.active.duration | friendlyduration }}

div.d-flex
div.p-1
Expand Down Expand Up @@ -38,16 +38,16 @@ div

ul.row.nav.nav-tabs.my-3.px-3
li.nav-item
router-link.nav-link(:to="{ name: 'activity-daily-summary', params: $route.params }")
router-link.nav-link(:to="{ name: 'activity-summary', params: $route.params }")
h5 Summary
li.nav-item
router-link.nav-link(:to="{ name: 'activity-daily-window', params: $route.params }")
router-link.nav-link(:to="{ name: 'activity-window', params: $route.params }")
h5 Window
li.nav-item
router-link.nav-link(:to="{ name: 'activity-daily-browser', params: $route.params }")
router-link.nav-link(:to="{ name: 'activity-browser', params: $route.params }")
h5 Browser
li.nav-item
router-link.nav-link(:to="{ name: 'activity-daily-editor', params: $route.params }")
router-link.nav-link(:to="{ name: 'activity-editor', params: $route.params }")
h5 Editor

div
Expand Down Expand Up @@ -157,9 +157,7 @@ export default {
return `/activity/${this.host}/${this.periodLength}`;
},
periodusage: function() {
return this.$store.getters['activity_daily/getActiveHistoryAroundTimeperiod'](
this.timeperiod
);
return this.$store.getters['activity/getActiveHistoryAroundTimeperiod'](this.timeperiod);
},
timeperiod: function() {
// TODO: Get start of day/week/month (depending on periodLength) with offset
Expand Down Expand Up @@ -230,7 +228,7 @@ export default {
},
refresh: async function(force) {
await this.$store.dispatch('activity_daily/ensure_loaded', {
await this.$store.dispatch('activity/ensure_loaded', {
timeperiod: this.timeperiod,
host: this.host,
force: force,
Expand All @@ -240,7 +238,7 @@ export default {
},
load_demo: async function() {
await this.$store.dispatch('activity_daily/load_demo');
await this.$store.dispatch('activity/load_demo');
},
},
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,25 @@ div
small Make sure you have a browser watcher installed to use this feature
div(v-if="browserBuckets.length > 0")

h6 Active browser time: {{ $store.state.activity_daily.browser.duration | friendlyduration }}
h6 Active browser time: {{ $store.state.activity.browser.duration | friendlyduration }}

div.row
div.col-md-6
h5 Top Browser Domains
div(v-if="browserBuckets")
aw-summary(:fields="$store.state.activity_daily.browser.top_domains", :namefunc="e => e.data.$domain", :colorfunc="e => e.data.$domain", with_limit)
aw-summary(:fields="$store.state.activity.browser.top_domains", :namefunc="e => e.data.$domain", :colorfunc="e => e.data.$domain", with_limit)

div.col-md-6
h5 Top Browser URLs
div(v-if="browserBuckets")
aw-summary(:fields="$store.state.activity_daily.browser.top_urls", :namefunc="e => e.data.url", :colorfunc="e => e.data.$domain", with_limit)
aw-summary(:fields="$store.state.activity.browser.top_urls", :namefunc="e => e.data.url", :colorfunc="e => e.data.$domain", with_limit)

//div(v-if="periodLength === 'day'")
br
hr
b-form-checkbox(v-model="timelineShowAFK")
| Show AFK time
aw-timeline-inspect(:chunks="$store.state.activity_daily.web_chunks", :show_afk='timelineShowAFK', :chunkfunc='e => e.data.$domain', :eventfunc='e => e.data.url')
aw-timeline-inspect(:chunks="$store.state.activity.web_chunks", :show_afk='timelineShowAFK', :chunkfunc='e => e.data.$domain', :eventfunc='e => e.data.url')
br
</template>

Expand All @@ -49,7 +49,7 @@ export default {
},
computed: {
browserBuckets: function() {
return this.$store.state.activity_daily.buckets.browser_buckets;
return this.$store.state.activity.buckets.browser_buckets;
},
},
};
Expand Down
Loading

0 comments on commit 353634b

Please sign in to comment.