summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorsyuilo <syuilotan@yahoo.co.jp>2019-01-17 17:16:08 +0900
committersyuilo <syuilotan@yahoo.co.jp>2019-01-17 17:16:08 +0900
commit8d42e94e57b9426f122cee4ded52d9e640116502 (patch)
tree4304bff191272d816f1ee5f591ac9d05cba57ed2 /src
parent[Client] Add information (diff)
downloadsharkey-8d42e94e57b9426f122cee4ded52d9e640116502.tar.gz
sharkey-8d42e94e57b9426f122cee4ded52d9e640116502.tar.bz2
sharkey-8d42e94e57b9426f122cee4ded52d9e640116502.zip
Implement active users chart
Resolve #3904
Diffstat (limited to 'src')
-rw-r--r--src/chart/active-users.ts48
-rw-r--r--src/client/app/admin/views/charts.vue36
-rw-r--r--src/server/api/endpoints/charts/active-users.ts34
-rw-r--r--src/server/api/endpoints/notes/hybrid-timeline.ts3
-rw-r--r--src/server/api/endpoints/notes/local-timeline.ts5
-rw-r--r--src/server/api/endpoints/notes/timeline.ts3
-rw-r--r--src/services/note/create.ts3
7 files changed, 126 insertions, 6 deletions
diff --git a/src/chart/active-users.ts b/src/chart/active-users.ts
new file mode 100644
index 0000000000..06d9b8aa90
--- /dev/null
+++ b/src/chart/active-users.ts
@@ -0,0 +1,48 @@
+import autobind from 'autobind-decorator';
+import Chart, { Obj } from '.';
+import { IUser, isLocalUser } from '../models/user';
+
+/**
+ * アクティブユーザーに関するチャート
+ */
+type ActiveUsersLog = {
+ local: {
+ /**
+ * アクティブユーザー数
+ */
+ count: number;
+ };
+
+ remote: ActiveUsersLog['local'];
+};
+
+class ActiveUsersChart extends Chart<ActiveUsersLog> {
+ constructor() {
+ super('activeUsers');
+ }
+
+ @autobind
+ protected async getTemplate(init: boolean, latest?: ActiveUsersLog): Promise<ActiveUsersLog> {
+ return {
+ local: {
+ count: 0
+ },
+ remote: {
+ count: 0
+ }
+ };
+ }
+
+ @autobind
+ public async update(user: IUser) {
+ const update: Obj = {
+ count: 1
+ };
+
+ await this.incIfUnique({
+ [isLocalUser(user) ? 'local' : 'remote']: update
+ }, 'users', user._id.toHexString());
+ }
+}
+
+export default new ActiveUsersChart();
diff --git a/src/client/app/admin/views/charts.vue b/src/client/app/admin/views/charts.vue
index 13e8b3671e..04cf9512cf 100644
--- a/src/client/app/admin/views/charts.vue
+++ b/src/client/app/admin/views/charts.vue
@@ -10,6 +10,7 @@
<optgroup :label="$t('users')">
<option value="users">{{ $t('charts.users') }}</option>
<option value="users-total">{{ $t('charts.users-total') }}</option>
+ <option value="active-users">{{ $t('charts.active-users') }}</option>
</optgroup>
<optgroup :label="$t('notes')">
<option value="notes">{{ $t('charts.notes') }}</option>
@@ -67,6 +68,7 @@ export default Vue.extend({
case 'federation-instances-total': return this.federationInstancesChart(true);
case 'users': return this.usersChart(false);
case 'users-total': return this.usersChart(true);
+ case 'active-users': return this.activeUsersChart();
case 'notes': return this.notesChart('combined');
case 'local-notes': return this.notesChart('local');
case 'remote-notes': return this.notesChart('remote');
@@ -107,12 +109,14 @@ export default Vue.extend({
const [perHour, perDay] = await Promise.all([Promise.all([
this.$root.api('charts/federation', { limit: limit, span: 'hour' }),
this.$root.api('charts/users', { limit: limit, span: 'hour' }),
+ this.$root.api('charts/active-users', { limit: limit, span: 'hour' }),
this.$root.api('charts/notes', { limit: limit, span: 'hour' }),
this.$root.api('charts/drive', { limit: limit, span: 'hour' }),
this.$root.api('charts/network', { limit: limit, span: 'hour' })
]), Promise.all([
this.$root.api('charts/federation', { limit: limit, span: 'day' }),
this.$root.api('charts/users', { limit: limit, span: 'day' }),
+ this.$root.api('charts/active-users', { limit: limit, span: 'day' }),
this.$root.api('charts/notes', { limit: limit, span: 'day' }),
this.$root.api('charts/drive', { limit: limit, span: 'day' }),
this.$root.api('charts/network', { limit: limit, span: 'day' })
@@ -122,16 +126,18 @@ export default Vue.extend({
perHour: {
federation: perHour[0],
users: perHour[1],
- notes: perHour[2],
- drive: perHour[3],
- network: perHour[4]
+ activeUsers: perHour[2],
+ notes: perHour[3],
+ drive: perHour[4],
+ network: perHour[5]
},
perDay: {
federation: perDay[0],
users: perDay[1],
- notes: perDay[2],
- drive: perDay[3],
- network: perDay[4]
+ activeUsers: perDay[2],
+ notes: perDay[3],
+ drive: perDay[4],
+ network: perDay[5]
}
};
@@ -321,6 +327,24 @@ export default Vue.extend({
};
},
+ activeUsersChart(): any {
+ return {
+ series: [{
+ name: 'Combined',
+ type: 'line',
+ data: this.format(sum(this.stats.activeUsers.local.count, this.stats.activeUsers.remote.count))
+ }, {
+ name: 'Local',
+ type: 'area',
+ data: this.format(this.stats.activeUsers.local.count)
+ }, {
+ name: 'Remote',
+ type: 'area',
+ data: this.format(this.stats.activeUsers.remote.count)
+ }]
+ };
+ },
+
driveChart(): any {
return {
bytes: true,
diff --git a/src/server/api/endpoints/charts/active-users.ts b/src/server/api/endpoints/charts/active-users.ts
new file mode 100644
index 0000000000..5187e5b353
--- /dev/null
+++ b/src/server/api/endpoints/charts/active-users.ts
@@ -0,0 +1,34 @@
+import $ from 'cafy';
+import define from '../../define';
+import activeUsersChart from '../../../../chart/active-users';
+
+export const meta = {
+ stability: 'stable',
+
+ desc: {
+ 'ja-JP': 'アクティブユーザーのチャートを取得します。'
+ },
+
+ params: {
+ span: {
+ validator: $.str.or(['day', 'hour']),
+ desc: {
+ 'ja-JP': '集計のスパン (day または hour)'
+ }
+ },
+
+ limit: {
+ validator: $.num.optional.range(1, 500),
+ default: 30,
+ desc: {
+ 'ja-JP': '最大数。例えば 30 を指定したとすると、スパンが"day"の場合は30日分のデータが、スパンが"hour"の場合は30時間分のデータが返ります。'
+ }
+ },
+ }
+};
+
+export default define(meta, (ps) => new Promise(async (res, rej) => {
+ const stats = await activeUsersChart.getChart(ps.span as any, ps.limit);
+
+ res(stats);
+}));
diff --git a/src/server/api/endpoints/notes/hybrid-timeline.ts b/src/server/api/endpoints/notes/hybrid-timeline.ts
index 20855e5139..919b0662aa 100644
--- a/src/server/api/endpoints/notes/hybrid-timeline.ts
+++ b/src/server/api/endpoints/notes/hybrid-timeline.ts
@@ -6,6 +6,7 @@ import { packMany } from '../../../../models/note';
import define from '../../define';
import { countIf } from '../../../../prelude/array';
import fetchMeta from '../../../../misc/fetch-meta';
+import activeUsersChart from '../../../../chart/active-users';
export const meta = {
desc: {
@@ -272,4 +273,6 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => {
});
res(await packMany(timeline, user));
+
+ activeUsersChart.update(user);
}));
diff --git a/src/server/api/endpoints/notes/local-timeline.ts b/src/server/api/endpoints/notes/local-timeline.ts
index 30d2762ad5..fd624c10e6 100644
--- a/src/server/api/endpoints/notes/local-timeline.ts
+++ b/src/server/api/endpoints/notes/local-timeline.ts
@@ -5,6 +5,7 @@ import { packMany } from '../../../../models/note';
import define from '../../define';
import { countIf } from '../../../../prelude/array';
import fetchMeta from '../../../../misc/fetch-meta';
+import activeUsersChart from '../../../../chart/active-users';
export const meta = {
desc: {
@@ -161,4 +162,8 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => {
});
res(await packMany(timeline, user));
+
+ if (user) {
+ activeUsersChart.update(user);
+ }
}));
diff --git a/src/server/api/endpoints/notes/timeline.ts b/src/server/api/endpoints/notes/timeline.ts
index 5604cf291b..24ed222e9a 100644
--- a/src/server/api/endpoints/notes/timeline.ts
+++ b/src/server/api/endpoints/notes/timeline.ts
@@ -5,6 +5,7 @@ import { getFriends } from '../../common/get-friends';
import { packMany } from '../../../../models/note';
import define from '../../define';
import { countIf } from '../../../../prelude/array';
+import activeUsersChart from '../../../../chart/active-users';
export const meta = {
desc: {
@@ -266,4 +267,6 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => {
// Serialize
res(await packMany(timeline, user));
+
+ activeUsersChart.update(user);
}));
diff --git a/src/services/note/create.ts b/src/services/note/create.ts
index 18073f3aa6..8031746c89 100644
--- a/src/services/note/create.ts
+++ b/src/services/note/create.ts
@@ -23,6 +23,7 @@ import registerHashtag from '../register-hashtag';
import isQuote from '../../misc/is-quote';
import notesChart from '../../chart/notes';
import perUserNotesChart from '../../chart/per-user-notes';
+import activeUsersChart from '../../chart/active-users';
import { erase } from '../../prelude/array';
import insertNoteUnread from './unread';
@@ -196,6 +197,8 @@ export default async (user: IUser, data: Option, silent = false) => new Promise<
// 統計を更新
notesChart.update(note, true);
perUserNotesChart.update(user, note, true);
+ // ローカルユーザーのチャートはタイムライン取得時に更新しているのでリモートユーザーの場合だけでよい
+ if (isRemoteUser(user)) activeUsersChart.update(user);
// Register host
if (isRemoteUser(user)) {