From 3bebf82501695ec4372eaadafaf42b845c387dcf Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 22 Oct 2018 17:36:36 +0900 Subject: [PATCH] Implement #2980 --- .../api/endpoints/charts/user/reactions.ts | 41 ++++++++++++++++++ src/services/note/reaction/create.ts | 3 ++ src/services/stats.ts | 43 +++++++++++++++++++ 3 files changed, 87 insertions(+) create mode 100644 src/server/api/endpoints/charts/user/reactions.ts diff --git a/src/server/api/endpoints/charts/user/reactions.ts b/src/server/api/endpoints/charts/user/reactions.ts new file mode 100644 index 000000000..1d1e7d5a4 --- /dev/null +++ b/src/server/api/endpoints/charts/user/reactions.ts @@ -0,0 +1,41 @@ +import $ from 'cafy'; +import getParams from '../../../get-params'; +import { perUserReactionsStats } from '../../../../../services/stats'; +import ID from '../../../../../misc/cafy-id'; + +export const meta = { + desc: { + 'ja-JP': 'ユーザーごとの被リアクション数の統計を取得します。' + }, + + params: { + span: $.str.or(['day', 'hour']).note({ + desc: { + 'ja-JP': '集計のスパン (day または hour)' + } + }), + + limit: $.num.optional.range(1, 100).note({ + default: 30, + desc: { + 'ja-JP': '最大数。例えば 30 を指定したとすると、スパンが"day"の場合は30日分のデータが、スパンが"hour"の場合は30時間分のデータが返ります。' + } + }), + + userId: $.type(ID).note({ + desc: { + 'ja-JP': '対象のユーザーのID', + 'en-US': 'Target user ID' + } + }) + } +}; + +export default (params: any) => new Promise(async (res, rej) => { + const [ps, psErr] = getParams(meta, params); + if (psErr) throw psErr; + + const stats = await perUserReactionsStats.getChart(ps.span as any, ps.limit, ps.userId); + + res(stats); +}); diff --git a/src/services/note/reaction/create.ts b/src/services/note/reaction/create.ts index 8fa0b52e7..13bb44ff3 100644 --- a/src/services/note/reaction/create.ts +++ b/src/services/note/reaction/create.ts @@ -8,6 +8,7 @@ import watch from '../watch'; import renderLike from '../../../remote/activitypub/renderer/like'; import { deliver } from '../../../queue'; import pack from '../../../remote/activitypub/renderer'; +import { perUserReactionsStats } from '../../stats'; export default async (user: IUser, note: INote, reaction: string) => new Promise(async (res, rej) => { // Myself @@ -43,6 +44,8 @@ export default async (user: IUser, note: INote, reaction: string) => new Promise $inc: inc }); + perUserReactionsStats.update(user, note); + publishNoteStream(note._id, 'reacted', { reaction: reaction, userId: user._id diff --git a/src/services/stats.ts b/src/services/stats.ts index 086420bb4..a7b584f4d 100644 --- a/src/services/stats.ts +++ b/src/services/stats.ts @@ -912,6 +912,49 @@ class PerUserNotesStats extends Stats { export const perUserNotesStats = new PerUserNotesStats(); //#endregion +//#region Per user reactions stats +/** + * ユーザーごとのリアクションに関する統計 + */ +type PerUserReactionsLog = { + local: { + /** + * リアクションされた数 + */ + count: number; + }; + + remote: PerUserReactionsLog['local']; +}; + +class PerUserReactionsStats extends Stats { + constructor() { + super('perUserReaction', true); + } + + @autobind + protected async getTemplate(init: boolean, latest?: PerUserReactionsLog, group?: any): Promise { + return { + local: { + count: 0 + }, + remote: { + count: 0 + } + }; + } + + @autobind + public async update(user: IUser, note: INote) { + this.inc({ + [isLocalUser(user) ? 'local' : 'remote']: { count: 1 } + }, note.userId); + } +} + +export const perUserReactionsStats = new PerUserReactionsStats(); +//#endregion + //#region Per user drive stats /** * ユーザーごとのドライブに関する統計