From ea87d1e18134621140bf4bfa70e00f77297db89d Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 24 Aug 2018 14:55:58 +0900 Subject: [PATCH] =?UTF-8?q?=E3=83=90=E3=82=B0=E4=BF=AE=E6=AD=A3=E3=81=AA?= =?UTF-8?q?=E3=81=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/ja-JP.yml | 27 +- .../desktop/views/pages/admin/admin.chart.vue | 280 +++++++++++++----- src/server/api/endpoints/chart.ts | 54 +++- 3 files changed, 268 insertions(+), 93 deletions(-) diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 3755f55f3..c44254439 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -946,22 +946,17 @@ desktop/views/pages/admin/admin.chart.vue: notes: "投稿" users: "ユーザー" drive: "ドライブ" - local-notes: "ローカルの投稿の増減" - remote-notes: "リモートの投稿の増減" - local-notes-total: "ローカルの投稿の累計" - remote-notes-total: "リモートの投稿の累計" - local-users: "ローカルのユーザーの増減" - remote-users: "リモートのユーザーの増減" - local-users-total: "ローカルのユーザーの累計" - remote-users-total: "リモートのユーザーの累計" - local-drive: "ローカルのドライブ使用量の増減" - remote-drive: "リモートのドライブ使用量の増減" - local-drive-total: "ローカルのドライブ使用量の累計" - remote-drive-total: "リモートのドライブ使用量の累計" - local-drive-files: "ローカルのドライブのファイル数の増減" - remote-drive-files: "リモートのドライブのファイル数の増減" - local-drive-files-total: "ローカルのドライブのファイル数の累計" - remote-drive-files-total: "リモートのドライブのファイル数の累計" + charts: + notes: "投稿の増減 (統合)" + local-notes: "投稿の増減 (ローカル)" + remote-notes: "投稿の増減 (リモート)" + notes-total: "投稿の累計" + users: "ユーザーの増減" + users-total: "ユーザーの累計" + drive: "ドライブ使用量の増減" + drive-total: "ドライブ使用量の累計" + drive-files: "ドライブのファイル数の増減" + drive-files-total: "ドライブのファイル数の累計" desktop/views/pages/deck/deck.tl-column.vue: is-media-only: "メディア投稿のみ" diff --git a/src/client/app/desktop/views/pages/admin/admin.chart.vue b/src/client/app/desktop/views/pages/admin/admin.chart.vue index 4301f9978..89e61d4c7 100644 --- a/src/client/app/desktop/views/pages/admin/admin.chart.vue +++ b/src/client/app/desktop/views/pages/admin/admin.chart.vue @@ -4,30 +4,24 @@ %i18n:@title%:
- %i18n:@per-day% | %i18n:@per-hour% + %i18n:@per-day% | %i18n:@per-hour%
@@ -47,7 +41,7 @@ export default Vue.extend({ data() { return { chart: null, - chartType: 'local-notes', + chartType: 'notes', span: 'hour' }; }, @@ -55,22 +49,16 @@ export default Vue.extend({ data(): any { if (this.chart == null) return null; switch (this.chartType) { - case 'local-users': return this.usersChart(true, false); - case 'remote-users': return this.usersChart(false, false); - case 'local-users-total': return this.usersChart(true, true); - case 'remote-users-total': return this.usersChart(false, true); - case 'local-notes': return this.notesChart(true); - case 'remote-notes': return this.notesChart(false); - case 'local-notes-total': return this.notesTotalChart(true); - case 'remote-notes-total': return this.notesTotalChart(false); - case 'local-drive': return this.driveChart(true, false); - case 'remote-drive': return this.driveChart(false, false); - case 'local-drive-total': return this.driveChart(true, true); - case 'remote-drive-total': return this.driveChart(false, true); - case 'local-drive-files': return this.driveFilesChart(true, false); - case 'remote-drive-files': return this.driveFilesChart(false, false); - case 'local-drive-files-total': return this.driveFilesChart(true, true); - case 'remote-drive-files-total': return this.driveFilesChart(false, true); + case 'users': return this.usersChart(false); + case 'users-total': return this.usersChart(true); + case 'notes': return this.notesChart('combined'); + case 'local-notes': return this.notesChart('local'); + case 'remote-notes': return this.notesChart('remote'); + case 'notes-total': return this.notesTotalChart(); + case 'drive': return this.driveChart(false); + case 'drive-total': return this.driveChart(true); + case 'drive-files': return this.driveFilesChart(false); + case 'drive-files-total': return this.driveFilesChart(true); } }, stats(): any[] { @@ -87,13 +75,13 @@ export default Vue.extend({ }); }, methods: { - notesChart(local: boolean): any { + notesChart(type: string): any { const data = this.stats.slice().reverse().map(x => ({ date: new Date(x.date), - normal: local ? x.notes.local.diffs.normal : x.notes.remote.diffs.normal, - reply: local ? x.notes.local.diffs.reply : x.notes.remote.diffs.reply, - renote: local ? x.notes.local.diffs.renote : x.notes.remote.diffs.renote, - all: local ? x.notes.local.diff : x.notes.remote.diff + normal: type == 'local' ? x.notes.local.diffs.normal : type == 'remote' ? x.notes.remote.diffs.normal : x.notes.local.diffs.normal + x.notes.remote.diffs.normal, + reply: type == 'local' ? x.notes.local.diffs.reply : type == 'remote' ? x.notes.remote.diffs.reply : x.notes.local.diffs.reply + x.notes.remote.diffs.reply, + renote: type == 'local' ? x.notes.local.diffs.renote : type == 'remote' ? x.notes.remote.diffs.renote : x.notes.local.diffs.renote + x.notes.remote.diffs.renote, + all: type == 'local' ? x.notes.local.diff : type == 'remote' ? x.notes.remote.diff : x.notes.local.diff + x.notes.remote.diff })); return [{ @@ -107,14 +95,14 @@ export default Vue.extend({ lineTension: 0, data: data.map(x => ({ t: x.date, y: x.all })) }, { - label: 'Normal', + label: 'Renotes', fill: true, - backgroundColor: 'rgba(65, 221, 222, 0.1)', - borderColor: '#41ddde', + backgroundColor: 'rgba(161, 222, 65, 0.1)', + borderColor: '#a1de41', borderWidth: 2, pointBackgroundColor: '#fff', lineTension: 0, - data: data.map(x => ({ t: x.date, y: x.normal })) + data: data.map(x => ({ t: x.date, y: x.renote })) }, { label: 'Replies', fill: true, @@ -125,78 +113,185 @@ export default Vue.extend({ lineTension: 0, data: data.map(x => ({ t: x.date, y: x.reply })) }, { - label: 'Renotes', + label: 'Normal', fill: true, - backgroundColor: 'rgba(161, 222, 65, 0.1)', - borderColor: '#a1de41', + backgroundColor: 'rgba(65, 221, 222, 0.1)', + borderColor: '#41ddde', borderWidth: 2, pointBackgroundColor: '#fff', lineTension: 0, - data: data.map(x => ({ t: x.date, y: x.renote })) + data: data.map(x => ({ t: x.date, y: x.normal })) }] + }, { + scales: { + yAxes: [{ + ticks: { + callback: value => { + return Vue.filter('number')(value); + } + } + }] + }, + tooltips: { + callbacks: { + label: (tooltipItem, data) => { + const label = data.datasets[tooltipItem.datasetIndex].label || ''; + return `${label}: ${Vue.filter('number')(tooltipItem.yLabel)}`; + } + } + } }]; }, - notesTotalChart(local: boolean): any { + notesTotalChart(): any { const data = this.stats.slice().reverse().map(x => ({ date: new Date(x.date), - count: local ? x.notes.local.total : x.notes.remote.total, + localCount: x.notes.local.total, + remoteCount: x.notes.remote.total })); return [{ datasets: [{ - label: local ? 'Local Notes' : 'Remote Notes', + label: 'Notes', + fill: false, + borderColor: '#555', + borderWidth: 2, + borderDash: [4, 4], + pointBackgroundColor: '#fff', + lineTension: 0, + data: data.map(x => ({ t: x.date, y: x.remoteCount + x.localCount })) + }, { + label: 'Remote Notes', + fill: true, + backgroundColor: 'rgba(65, 221, 222, 0.1)', + borderColor: '#41ddde', + borderWidth: 2, + pointBackgroundColor: '#fff', + lineTension: 0, + data: data.map(x => ({ t: x.date, y: x.remoteCount })) + }, { + label: 'Local Notes', fill: true, backgroundColor: 'rgba(246, 88, 79, 0.1)', borderColor: '#f6584f', borderWidth: 2, pointBackgroundColor: '#fff', lineTension: 0, - data: data.map(x => ({ t: x.date, y: x.count })) + data: data.map(x => ({ t: x.date, y: x.localCount })) }] + }, { + scales: { + yAxes: [{ + ticks: { + callback: value => { + return Vue.filter('number')(value); + } + } + }] + }, + tooltips: { + callbacks: { + label: (tooltipItem, data) => { + const label = data.datasets[tooltipItem.datasetIndex].label || ''; + return `${label}: ${Vue.filter('number')(tooltipItem.yLabel)}`; + } + } + } }]; }, - usersChart(local: boolean, total: boolean): any { + usersChart(total: boolean): any { const data = this.stats.slice().reverse().map(x => ({ date: new Date(x.date), - count: local ? - total ? x.users.local.total : x.users.local.diff : - total ? x.users.remote.total : x.users.remote.diff + localCount: total ? x.users.local.total : x.users.local.diff, + remoteCount: total ? x.users.remote.total : x.users.remote.diff })); return [{ datasets: [{ - label: local ? 'Local Users' : 'Remote Users', + label: 'Users', + fill: false, + borderColor: '#555', + borderWidth: 2, + borderDash: [4, 4], + pointBackgroundColor: '#fff', + lineTension: 0, + data: data.map(x => ({ t: x.date, y: x.remoteCount + x.localCount })) + }, { + label: 'Remote Users', + fill: true, + backgroundColor: 'rgba(65, 221, 222, 0.1)', + borderColor: '#41ddde', + borderWidth: 2, + pointBackgroundColor: '#fff', + lineTension: 0, + data: data.map(x => ({ t: x.date, y: x.remoteCount })) + }, { + label: 'Local Users', fill: true, backgroundColor: 'rgba(246, 88, 79, 0.1)', borderColor: '#f6584f', borderWidth: 2, pointBackgroundColor: '#fff', lineTension: 0, - data: data.map(x => ({ t: x.date, y: x.count })) + data: data.map(x => ({ t: x.date, y: x.localCount })) }] + }, { + scales: { + yAxes: [{ + ticks: { + callback: value => { + return Vue.filter('number')(value); + } + } + }] + }, + tooltips: { + callbacks: { + label: (tooltipItem, data) => { + const label = data.datasets[tooltipItem.datasetIndex].label || ''; + return `${label}: ${Vue.filter('number')(tooltipItem.yLabel)}`; + } + } + } }]; }, - driveChart(local: boolean, total: boolean): any { + driveChart(total: boolean): any { const data = this.stats.slice().reverse().map(x => ({ date: new Date(x.date), - size: local ? - total ? x.drive.local.totalSize : x.drive.local.diffSize : - total ? x.drive.remote.totalSize : x.drive.remote.diffSize + localSize: total ? x.drive.local.totalSize : x.drive.local.diffSize, + remoteSize: total ? x.drive.remote.totalSize : x.drive.remote.diffSize })); return [{ datasets: [{ - label: local ? 'Local Drive Usage' : 'Remote Drive Usage', + label: 'Drive Usage', + fill: false, + borderColor: '#555', + borderWidth: 2, + borderDash: [4, 4], + pointBackgroundColor: '#fff', + lineTension: 0, + data: data.map(x => ({ t: x.date, y: x.remoteSize + x.localSize })) + }, { + label: 'Remote Drive Usage', + fill: true, + backgroundColor: 'rgba(65, 221, 222, 0.1)', + borderColor: '#41ddde', + borderWidth: 2, + pointBackgroundColor: '#fff', + lineTension: 0, + data: data.map(x => ({ t: x.date, y: x.remoteSize })) + }, { + label: 'Local Drive Usage', fill: true, backgroundColor: 'rgba(246, 88, 79, 0.1)', borderColor: '#f6584f', borderWidth: 2, pointBackgroundColor: '#fff', lineTension: 0, - data: data.map(x => ({ t: x.date, y: x.size })) + data: data.map(x => ({ t: x.date, y: x.localSize })) }] }, { scales: { @@ -210,35 +305,71 @@ export default Vue.extend({ }, tooltips: { callbacks: { - label: tooltipItem => { - return Vue.filter('bytes')(tooltipItem.yLabel); + label: (tooltipItem, data) => { + const label = data.datasets[tooltipItem.datasetIndex].label || ''; + return `${label}: ${Vue.filter('bytes')(tooltipItem.yLabel)}`; } } } }]; }, - driveFilesChart(local: boolean, total: boolean): any { + driveFilesChart(total: boolean): any { const data = this.stats.slice().reverse().map(x => ({ date: new Date(x.date), - count: local ? - total ? x.drive.local.totalCount : x.drive.local.diffCount : - total ? x.drive.remote.totalCount : x.drive.remote.diffCount + localCount: total ? x.drive.local.totalCount : x.drive.local.diffCount, + remoteCount: total ? x.drive.remote.totalCount : x.drive.remote.diffCount })); return [{ datasets: [{ - label: local ? 'Local Drive Files' : 'Remote Drive Files', + label: 'Drive Files', + fill: false, + borderColor: '#555', + borderWidth: 2, + borderDash: [4, 4], + pointBackgroundColor: '#fff', + lineTension: 0, + data: data.map(x => ({ t: x.date, y: x.remoteCount + x.localCount })) + }, { + label: 'Remote Drive Files', + fill: true, + backgroundColor: 'rgba(65, 221, 222, 0.1)', + borderColor: '#41ddde', + borderWidth: 2, + pointBackgroundColor: '#fff', + lineTension: 0, + data: data.map(x => ({ t: x.date, y: x.remoteCount })) + }, { + label: 'Local Drive Files', fill: true, backgroundColor: 'rgba(246, 88, 79, 0.1)', borderColor: '#f6584f', borderWidth: 2, pointBackgroundColor: '#fff', lineTension: 0, - data: data.map(x => ({ t: x.date, y: x.count })) + data: data.map(x => ({ t: x.date, y: x.localCount })) }] + }, { + scales: { + yAxes: [{ + ticks: { + callback: value => { + return Vue.filter('number')(value); + } + } + }] + }, + tooltips: { + callbacks: { + label: (tooltipItem, data) => { + const label = data.datasets[tooltipItem.datasetIndex].label || ''; + return `${label}: ${Vue.filter('number')(tooltipItem.yLabel)}`; + } + } + } }]; - }, + } } }); @@ -259,6 +390,11 @@ export default Vue.extend({ > *:last-child margin-left auto + * + &:not(.active) + color $theme-color + cursor pointer + > div > * display block diff --git a/src/server/api/endpoints/chart.ts b/src/server/api/endpoints/chart.ts index a58f0b163..da3476f06 100644 --- a/src/server/api/endpoints/chart.ts +++ b/src/server/api/endpoints/chart.ts @@ -6,8 +6,8 @@ export const meta = { }; export default (params: any) => new Promise(async (res, rej) => { - const daysRange = 90; - const hoursRange = 24; + const daysRange = 30; + const hoursRange = 30; const now = new Date(); const y = now.getFullYear(); @@ -65,9 +65,53 @@ export default (params: any) => new Promise(async (res, rej) => { } else { // 隙間埋め const mostRecent = src.find(s => s.date.getTime() < current.getTime()); if (mostRecent) { - chart.unshift(Object.assign({}, mostRecent, { - date: current - })); + chart.unshift({ + date: current, + users: { + local: { + total: mostRecent.users.local.total, + diff: 0 + }, + remote: { + total: mostRecent.users.remote.total, + diff: 0 + } + }, + notes: { + local: { + total: mostRecent.notes.local.total, + diff: 0, + diffs: { + normal: 0, + reply: 0, + renote: 0 + } + }, + remote: { + total: mostRecent.notes.remote.total, + diff: 0, + diffs: { + normal: 0, + reply: 0, + renote: 0 + } + } + }, + drive: { + local: { + totalCount: mostRecent.drive.local.totalCount, + totalSize: mostRecent.drive.local.totalSize, + diffCount: 0, + diffSize: 0 + }, + remote: { + totalCount: mostRecent.drive.remote.totalCount, + totalSize: mostRecent.drive.remote.totalSize, + diffCount: 0, + diffSize: 0 + } + } + }); } else { chart.unshift({ date: current,