From cc7a1808ecc6874af9cc96f8ed09e62b3f6b4e39 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sun, 6 Feb 2022 06:24:06 +0900
Subject: [PATCH] imprpve active users chart

---
 .../migration/1644095659741-chart-v11.js      | 91 +++++++++++++++++++
 packages/backend/src/server/api/define.ts     |  1 +
 .../src/services/chart/charts/active-users.ts | 22 ++++-
 .../chart/charts/entities/active-users.ts     | 10 +-
 packages/backend/src/services/note/create.ts  |  5 +-
 packages/client/src/components/chart.vue      | 36 ++++++--
 6 files changed, 149 insertions(+), 16 deletions(-)
 create mode 100644 packages/backend/migration/1644095659741-chart-v11.js

diff --git a/packages/backend/migration/1644095659741-chart-v11.js b/packages/backend/migration/1644095659741-chart-v11.js
new file mode 100644
index 0000000000..40b1c3072b
--- /dev/null
+++ b/packages/backend/migration/1644095659741-chart-v11.js
@@ -0,0 +1,91 @@
+const { MigrationInterface, QueryRunner } = require("typeorm");
+
+module.exports = class chartV111644095659741 {
+    name = 'chartV111644095659741'
+
+    async up(queryRunner) {
+        await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "___local_users"`);
+        await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "___remote_users"`);
+        await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "unique_temp___local_users"`);
+        await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "unique_temp___remote_users"`);
+        await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "___local_users"`);
+        await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "___remote_users"`);
+        await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "unique_temp___local_users"`);
+        await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "unique_temp___remote_users"`);
+        await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "unique_temp___users" character varying array NOT NULL DEFAULT '{}'`);
+        await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "___users" integer NOT NULL DEFAULT '0'`);
+        await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "unique_temp___notedUsers" character varying array NOT NULL DEFAULT '{}'`);
+        await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "___notedUsers" smallint NOT NULL DEFAULT '0'`);
+        await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "unique_temp___registeredWithinWeek" character varying array NOT NULL DEFAULT '{}'`);
+        await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "___registeredWithinWeek" smallint NOT NULL DEFAULT '0'`);
+        await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "unique_temp___registeredWithinMonth" character varying array NOT NULL DEFAULT '{}'`);
+        await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "___registeredWithinMonth" smallint NOT NULL DEFAULT '0'`);
+        await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "unique_temp___registeredWithinYear" character varying array NOT NULL DEFAULT '{}'`);
+        await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "___registeredWithinYear" smallint NOT NULL DEFAULT '0'`);
+        await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "unique_temp___registeredOutsideWeek" character varying array NOT NULL DEFAULT '{}'`);
+        await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "___registeredOutsideWeek" smallint NOT NULL DEFAULT '0'`);
+        await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "unique_temp___registeredOutsideMonth" character varying array NOT NULL DEFAULT '{}'`);
+        await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "___registeredOutsideMonth" smallint NOT NULL DEFAULT '0'`);
+        await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "unique_temp___registeredOutsideYear" character varying array NOT NULL DEFAULT '{}'`);
+        await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "___registeredOutsideYear" smallint NOT NULL DEFAULT '0'`);
+        await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "unique_temp___users" character varying array NOT NULL DEFAULT '{}'`);
+        await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "___users" integer NOT NULL DEFAULT '0'`);
+        await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "unique_temp___notedUsers" character varying array NOT NULL DEFAULT '{}'`);
+        await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "___notedUsers" smallint NOT NULL DEFAULT '0'`);
+        await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "unique_temp___registeredWithinWeek" character varying array NOT NULL DEFAULT '{}'`);
+        await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "___registeredWithinWeek" smallint NOT NULL DEFAULT '0'`);
+        await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "unique_temp___registeredWithinMonth" character varying array NOT NULL DEFAULT '{}'`);
+        await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "___registeredWithinMonth" smallint NOT NULL DEFAULT '0'`);
+        await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "unique_temp___registeredWithinYear" character varying array NOT NULL DEFAULT '{}'`);
+        await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "___registeredWithinYear" smallint NOT NULL DEFAULT '0'`);
+        await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "unique_temp___registeredOutsideWeek" character varying array NOT NULL DEFAULT '{}'`);
+        await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "___registeredOutsideWeek" smallint NOT NULL DEFAULT '0'`);
+        await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "unique_temp___registeredOutsideMonth" character varying array NOT NULL DEFAULT '{}'`);
+        await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "___registeredOutsideMonth" smallint NOT NULL DEFAULT '0'`);
+        await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "unique_temp___registeredOutsideYear" character varying array NOT NULL DEFAULT '{}'`);
+        await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "___registeredOutsideYear" smallint NOT NULL DEFAULT '0'`);
+    }
+
+    async down(queryRunner) {
+        await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "___registeredOutsideYear"`);
+        await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "unique_temp___registeredOutsideYear"`);
+        await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "___registeredOutsideMonth"`);
+        await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "unique_temp___registeredOutsideMonth"`);
+        await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "___registeredOutsideWeek"`);
+        await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "unique_temp___registeredOutsideWeek"`);
+        await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "___registeredWithinYear"`);
+        await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "unique_temp___registeredWithinYear"`);
+        await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "___registeredWithinMonth"`);
+        await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "unique_temp___registeredWithinMonth"`);
+        await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "___registeredWithinWeek"`);
+        await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "unique_temp___registeredWithinWeek"`);
+        await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "___notedUsers"`);
+        await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "unique_temp___notedUsers"`);
+        await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "___users"`);
+        await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "unique_temp___users"`);
+        await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "___registeredOutsideYear"`);
+        await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "unique_temp___registeredOutsideYear"`);
+        await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "___registeredOutsideMonth"`);
+        await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "unique_temp___registeredOutsideMonth"`);
+        await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "___registeredOutsideWeek"`);
+        await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "unique_temp___registeredOutsideWeek"`);
+        await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "___registeredWithinYear"`);
+        await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "unique_temp___registeredWithinYear"`);
+        await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "___registeredWithinMonth"`);
+        await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "unique_temp___registeredWithinMonth"`);
+        await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "___registeredWithinWeek"`);
+        await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "unique_temp___registeredWithinWeek"`);
+        await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "___notedUsers"`);
+        await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "unique_temp___notedUsers"`);
+        await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "___users"`);
+        await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "unique_temp___users"`);
+        await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "unique_temp___remote_users" character varying array NOT NULL DEFAULT '{}'`);
+        await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "unique_temp___local_users" character varying array NOT NULL DEFAULT '{}'`);
+        await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "___remote_users" integer NOT NULL DEFAULT '0'`);
+        await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "___local_users" integer NOT NULL DEFAULT '0'`);
+        await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "unique_temp___remote_users" character varying array NOT NULL DEFAULT '{}'`);
+        await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "unique_temp___local_users" character varying array NOT NULL DEFAULT '{}'`);
+        await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "___remote_users" integer NOT NULL DEFAULT '0'`);
+        await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "___local_users" integer NOT NULL DEFAULT '0'`);
+    }
+}
diff --git a/packages/backend/src/server/api/define.ts b/packages/backend/src/server/api/define.ts
index 71e5fadde0..e0720e2adb 100644
--- a/packages/backend/src/server/api/define.ts
+++ b/packages/backend/src/server/api/define.ts
@@ -9,6 +9,7 @@ type NonOptional<T> = T extends undefined ? never : T;
 
 type SimpleUserInfo = {
 	id: ILocalUser['id'];
+	createdAt: ILocalUser['createdAt'];
 	host: ILocalUser['host'];
 	username: ILocalUser['username'];
 	uri: ILocalUser['uri'];
diff --git a/packages/backend/src/services/chart/charts/active-users.ts b/packages/backend/src/services/chart/charts/active-users.ts
index 9e5c332d3b..7fef99d3a9 100644
--- a/packages/backend/src/services/chart/charts/active-users.ts
+++ b/packages/backend/src/services/chart/charts/active-users.ts
@@ -4,6 +4,10 @@ import { User } from '@/models/entities/user';
 import { Users } from '@/models/index';
 import { name, schema } from './entities/active-users';
 
+const week = 1000 * 60 * 60 * 24 * 7;
+const month = 1000 * 60 * 60 * 24 * 30;
+const year = 1000 * 60 * 60 * 24 * 365;
+
 /**
  * アクティブユーザーに関するチャート
  */
@@ -19,10 +23,22 @@ export default class ActiveUsersChart extends Chart<typeof schema> {
 	}
 
 	@autobind
-	public async update(user: { id: User['id'], host: User['host'] }): Promise<void> {
+	public async update(user: { id: User['id'], host: null, createdAt: User['createdAt'] }): Promise<void> {
+		await this.commit({
+			'users': [user.id],
+			'registeredWithinWeek': (Date.now() - user.createdAt.getTime() < week) ? [user.id] : [],
+			'registeredWithinMonth': (Date.now() - user.createdAt.getTime() < month) ? [user.id] : [],
+			'registeredWithinYear': (Date.now() - user.createdAt.getTime() < year) ? [user.id] : [],
+			'registeredOutsideWeek': (Date.now() - user.createdAt.getTime() > week) ? [user.id] : [],
+			'registeredOutsideMonth': (Date.now() - user.createdAt.getTime() > month) ? [user.id] : [],
+			'registeredOutsideYear': (Date.now() - user.createdAt.getTime() > year) ? [user.id] : [],
+		});
+	}
+
+	@autobind
+	public async noted(user: { id: User['id'], host: null, createdAt: User['createdAt'] }): Promise<void> {
 		await this.commit({
-			'local.users': Users.isLocalUser(user) ? [user.id] : [],
-			'remote.users': Users.isLocalUser(user) ? [] : [user.id],
+			'notedUsers': [user.id],
 		});
 	}
 }
diff --git a/packages/backend/src/services/chart/charts/entities/active-users.ts b/packages/backend/src/services/chart/charts/entities/active-users.ts
index 3caa938d35..ee16ef1278 100644
--- a/packages/backend/src/services/chart/charts/entities/active-users.ts
+++ b/packages/backend/src/services/chart/charts/entities/active-users.ts
@@ -3,8 +3,14 @@ import Chart from '../../core';
 export const name = 'activeUsers';
 
 export const schema = {
-	'local.users': { uniqueIncrement: true },
-	'remote.users': { uniqueIncrement: true },
+	'users': { uniqueIncrement: true },
+	'notedUsers': { uniqueIncrement: true, range: 'small' },
+	'registeredWithinWeek': { uniqueIncrement: true, range: 'small' },
+	'registeredWithinMonth': { uniqueIncrement: true, range: 'small' },
+	'registeredWithinYear': { uniqueIncrement: true, range: 'small' },
+	'registeredOutsideWeek': { uniqueIncrement: true, range: 'small' },
+	'registeredOutsideMonth': { uniqueIncrement: true, range: 'small' },
+	'registeredOutsideYear': { uniqueIncrement: true, range: 'small' },
 } as const;
 
 export const entity = Chart.schemaToEntity(name, schema);
diff --git a/packages/backend/src/services/note/create.ts b/packages/backend/src/services/note/create.ts
index cfb1f8c1e5..53a86fb773 100644
--- a/packages/backend/src/services/note/create.ts
+++ b/packages/backend/src/services/note/create.ts
@@ -111,7 +111,7 @@ type Option = {
 	app?: App | null;
 };
 
-export default async (user: { id: User['id']; username: User['username']; host: User['host']; isSilenced: User['isSilenced']; }, data: Option, silent = false) => new Promise<Note>(async (res, rej) => {
+export default async (user: { id: User['id']; username: User['username']; host: User['host']; isSilenced: User['isSilenced']; createdAt: User['createdAt']; }, data: Option, silent = false) => new Promise<Note>(async (res, rej) => {
 	// チャンネル外にリプライしたら対象のスコープに合わせる
 	// (クライアントサイドでやっても良い処理だと思うけどとりあえずサーバーサイドで)
 	if (data.reply && data.channel && data.reply.channelId !== data.channel.id) {
@@ -297,8 +297,7 @@ export default async (user: { id: User['id']; username: User['username']; host:
 	}
 
 	if (!silent) {
-		// ローカルユーザーのチャートはタイムライン取得時に更新しているのでリモートユーザーの場合だけでよい
-		if (Users.isRemoteUser(user)) activeUsersChart.update(user);
+		if (Users.isLocalUser(user)) activeUsersChart.noted(user);
 
 		// 未読通知を作成
 		if (data.visibility === 'specified') {
diff --git a/packages/client/src/components/chart.vue b/packages/client/src/components/chart.vue
index d1b5ad9d1b..6d6f57188f 100644
--- a/packages/client/src/components/chart.vue
+++ b/packages/client/src/components/chart.vue
@@ -61,7 +61,7 @@ const alpha = (hex, a) => {
 	return `rgba(${r}, ${g}, ${b}, ${a})`;
 };
 
-const colors = ['#008FFB', '#00E396', '#FEB019', '#FF4560'];
+const colors = ['#008FFB', '#00E396', '#FEB019', '#FF4560', '#e300db'];
 const getColor = (i) => {
 	return colors[i % colors.length];
 };
@@ -471,17 +471,37 @@ export default defineComponent({
 			const raw = await os.api('charts/active-users', { limit: props.limit, span: props.span });
 			return {
 				series: [{
-					name: 'Combined',
-					type: 'line',
-					data: format(sum(raw.local.users, raw.remote.users)),
+					name: 'Users',
+					type: 'area',
+					data: format(raw.users),
 				}, {
-					name: 'Local',
+					name: 'Noted',
 					type: 'area',
-					data: format(raw.local.users),
+					data: format(raw.notedUsers),
 				}, {
-					name: 'Remote',
+					name: '< Week',
+					type: 'area',
+					data: format(raw.registeredWithinWeek),
+				}, {
+					name: '< Month',
+					type: 'area',
+					data: format(raw.registeredWithinMonth),
+				}, {
+					name: '< Year',
+					type: 'area',
+					data: format(raw.registeredWithinYear),
+				}, {
+					name: '> Week',
+					type: 'area',
+					data: format(raw.registeredOutsideWeek),
+				}, {
+					name: '> Month',
+					type: 'area',
+					data: format(raw.registeredOutsideMonth),
+				}, {
+					name: '> Year',
 					type: 'area',
-					data: format(raw.remote.users),
+					data: format(raw.registeredOutsideYear),
 				}],
 			};
 		};
-- 
GitLab