一時間ごとのグラフも見れるように

This commit is contained in:
syuilo 2018-08-23 16:36:23 +09:00
parent 8fc1e07136
commit 71a5662195
4 changed files with 251 additions and 148 deletions

View File

@ -1,11 +1,14 @@
<template> <template>
<div>
<a @click="span = 'day'">Per day</a> | <a @click="span = 'hour'">Per hour</a>
<svg :viewBox="`0 0 ${ viewBoxX } ${ viewBoxY }`"> <svg :viewBox="`0 0 ${ viewBoxX } ${ viewBoxY }`">
<polyline <polyline
:points="points" :points="points"
fill="none" fill="none"
stroke-width="1" stroke-width="0.3"
stroke="#555"/> stroke="#555"/>
</svg> </svg>
</div>
</template> </template>
<script lang="ts"> <script lang="ts">
@ -23,20 +26,40 @@ export default Vue.extend({
}, },
data() { data() {
return { return {
viewBoxX: 365, viewBoxX: 100,
viewBoxY: 70, viewBoxY: 30,
points: null points: null,
span: 'day'
}; };
}, },
created() { computed: {
const peak = Math.max.apply(null, this.chart.map(d => this.type == 'local' ? d.drive.local.totalSize : d.drive.remote.totalSize)); stats(): any[] {
return (
this.span == 'day' ? this.chart.perDay :
this.span == 'hour' ? this.chart.perHour :
null
);
}
},
watch: {
stats() {
this.render();
}
},
mounted() {
this.render();
},
methods: {
render() {
const peak = Math.max.apply(null, this.stats.map(d => this.type == 'local' ? d.drive.local.totalSize : d.drive.remote.totalSize));
if (peak != 0) { if (peak != 0) {
const data = this.chart.slice().reverse().map(x => ({ const data = this.stats.slice().reverse().map(x => ({
size: this.type == 'local' ? x.drive.local.totalSize : x.drive.remote.totalSize size: this.type == 'local' ? x.drive.local.totalSize : x.drive.remote.totalSize
})); }));
this.points = data.map((d, i) => `${i},${(1 - (d.size / peak)) * this.viewBoxY}`).join(' '); this.points = data.map((d, i) => `${(this.viewBoxX / data.length) * i},${(1 - (d.size / peak)) * this.viewBoxY}`).join(' ');
}
} }
} }
}); });

View File

@ -1,27 +1,30 @@
<template> <template>
<div>
<a @click="span = 'day'">Per day</a> | <a @click="span = 'hour'">Per hour</a>
<svg :viewBox="`0 0 ${ viewBoxX } ${ viewBoxY }`"> <svg :viewBox="`0 0 ${ viewBoxX } ${ viewBoxY }`">
<polyline <polyline
:points="pointsNote" :points="pointsNote"
fill="none" fill="none"
stroke-width="1" stroke-width="0.3"
stroke="#41ddde"/> stroke="#41ddde"/>
<polyline <polyline
:points="pointsReply" :points="pointsReply"
fill="none" fill="none"
stroke-width="1" stroke-width="0.3"
stroke="#f7796c"/> stroke="#f7796c"/>
<polyline <polyline
:points="pointsRenote" :points="pointsRenote"
fill="none" fill="none"
stroke-width="1" stroke-width="0.3"
stroke="#a1de41"/> stroke="#a1de41"/>
<polyline <polyline
:points="pointsTotal" :points="pointsTotal"
fill="none" fill="none"
stroke-width="1" stroke-width="0.3"
stroke="#555" stroke="#555"
stroke-dasharray="2 2"/> stroke-dasharray="1 1"/>
</svg> </svg>
</div>
</template> </template>
<script lang="ts"> <script lang="ts">
@ -39,29 +42,49 @@ export default Vue.extend({
}, },
data() { data() {
return { return {
viewBoxX: 365, viewBoxX: 100,
viewBoxY: 70, viewBoxY: 30,
pointsNote: null, pointsNote: null,
pointsReply: null, pointsReply: null,
pointsRenote: null, pointsRenote: null,
pointsTotal: null pointsTotal: null,
span: 'day'
}; };
}, },
created() { computed: {
const peak = Math.max.apply(null, this.chart.map(d => this.type == 'local' ? d.notes.local.diff : d.notes.remote.diff)); stats(): any[] {
return (
this.span == 'day' ? this.chart.perDay :
this.span == 'hour' ? this.chart.perHour :
null
);
}
},
watch: {
stats() {
this.render();
}
},
mounted() {
this.render();
},
methods: {
render() {
const peak = Math.max.apply(null, this.stats.map(d => this.type == 'local' ? d.notes.local.diff : d.notes.remote.diff));
if (peak != 0) { if (peak != 0) {
const data = this.chart.slice().reverse().map(x => ({ const data = this.stats.slice().reverse().map(x => ({
normal: this.type == 'local' ? x.notes.local.diffs.normal : x.notes.remote.diffs.normal, normal: this.type == 'local' ? x.notes.local.diffs.normal : x.notes.remote.diffs.normal,
reply: this.type == 'local' ? x.notes.local.diffs.reply : x.notes.remote.diffs.reply, reply: this.type == 'local' ? x.notes.local.diffs.reply : x.notes.remote.diffs.reply,
renote: this.type == 'local' ? x.notes.local.diffs.renote : x.notes.remote.diffs.renote, renote: this.type == 'local' ? x.notes.local.diffs.renote : x.notes.remote.diffs.renote,
total: this.type == 'local' ? x.notes.local.diff : x.notes.remote.diff total: this.type == 'local' ? x.notes.local.diff : x.notes.remote.diff
})); }));
this.pointsNote = data.map((d, i) => `${i},${(1 - (d.normal / peak)) * this.viewBoxY}`).join(' '); this.pointsNote = data.map((d, i) => `${(this.viewBoxX / data.length) * i},${(1 - (d.normal / peak)) * this.viewBoxY}`).join(' ');
this.pointsReply = data.map((d, i) => `${i},${(1 - (d.reply / peak)) * this.viewBoxY}`).join(' '); this.pointsReply = data.map((d, i) => `${(this.viewBoxX / data.length) * i},${(1 - (d.reply / peak)) * this.viewBoxY}`).join(' ');
this.pointsRenote = data.map((d, i) => `${i},${(1 - (d.renote / peak)) * this.viewBoxY}`).join(' '); this.pointsRenote = data.map((d, i) => `${(this.viewBoxX / data.length) * i},${(1 - (d.renote / peak)) * this.viewBoxY}`).join(' ');
this.pointsTotal = data.map((d, i) => `${i},${(1 - (d.total / peak)) * this.viewBoxY}`).join(' '); this.pointsTotal = data.map((d, i) => `${(this.viewBoxX / data.length) * i},${(1 - (d.total / peak)) * this.viewBoxY}`).join(' ');
}
} }
} }
}); });

View File

@ -1,11 +1,14 @@
<template> <template>
<div>
<a @click="span = 'day'">Per day</a> | <a @click="span = 'hour'">Per hour</a>
<svg :viewBox="`0 0 ${ viewBoxX } ${ viewBoxY }`"> <svg :viewBox="`0 0 ${ viewBoxX } ${ viewBoxY }`">
<polyline <polyline
:points="points" :points="points"
fill="none" fill="none"
stroke-width="1" stroke-width="0.3"
stroke="#555"/> stroke="#555"/>
</svg> </svg>
</div>
</template> </template>
<script lang="ts"> <script lang="ts">
@ -23,20 +26,40 @@ export default Vue.extend({
}, },
data() { data() {
return { return {
viewBoxX: 365, viewBoxX: 100,
viewBoxY: 70, viewBoxY: 30,
points: null points: null,
span: 'day'
}; };
}, },
created() { computed: {
const peak = Math.max.apply(null, this.chart.map(d => this.type == 'local' ? d.users.local.diff : d.users.remote.diff)); stats(): any[] {
return (
this.span == 'day' ? this.chart.perDay :
this.span == 'hour' ? this.chart.perHour :
null
);
}
},
watch: {
stats() {
this.render();
}
},
mounted() {
this.render();
},
methods: {
render() {
const peak = Math.max.apply(null, this.stats.map(d => this.type == 'local' ? d.users.local.diff : d.users.remote.diff));
if (peak != 0) { if (peak != 0) {
const data = this.chart.slice().reverse().map(x => ({ const data = this.stats.slice().reverse().map(x => ({
count: this.type == 'local' ? x.users.local.diff : x.users.remote.diff count: this.type == 'local' ? x.users.local.diff : x.users.remote.diff
})); }));
this.points = data.map((d, i) => `${i},${(1 - (d.count / peak)) * this.viewBoxY}`).join(' '); this.points = data.map((d, i) => `${(this.viewBoxX / data.length) * i},${(1 - (d.count / peak)) * this.viewBoxY}`).join(' ');
}
} }
} }
}); });

View File

@ -8,15 +8,20 @@ export const meta = {
}; };
export default (params: any) => new Promise(async (res, rej) => { export default (params: any) => new Promise(async (res, rej) => {
const daysRange = 365;
const hoursRange = 24;
const now = new Date(); const now = new Date();
const y = now.getFullYear(); const y = now.getFullYear();
const m = now.getMonth(); const m = now.getMonth();
const d = now.getDate(); const d = now.getDate();
const h = now.getHours();
const stats = await Stats.find({ const [statsPerDay, statsPerHour] = await Promise.all([
Stats.find({
span: 'day', span: 'day',
date: { date: {
$gt: new Date(y - 1, m, d) $gt: new Date(y, m, d - daysRange)
} }
}, { }, {
sort: { sort: {
@ -25,27 +30,49 @@ export default (params: any) => new Promise(async (res, rej) => {
fields: { fields: {
_id: 0 _id: 0
} }
}); }),
Stats.find({
span: 'hour',
date: {
$gt: new Date(y, m, d, h - hoursRange)
}
}, {
sort: {
date: -1
},
fields: {
_id: 0
}
}),
]);
const chart: Array<Omit<IStats, '_id'>> = []; const format = (src: IStats[], span: 'day' | 'hour') => {
const chart: Array<Omit<Omit<IStats, '_id'>, 'span'>> = [];
for (let i = 364; i >= 0; i--) { const range =
const day = new Date(y, m, d - i); span == 'day' ? daysRange :
span == 'hour' ? hoursRange :
null;
const stat = stats.find(s => s.date.getTime() == day.getTime()); for (let i = (range - 1); i >= 0; i--) {
const current =
span == 'day' ? new Date(y, m, d - i) :
span == 'hour' ? new Date(y, m, d, h - i) :
null;
const stat = src.find(s => s.date.getTime() == current.getTime());
if (stat) { if (stat) {
chart.unshift(stat); chart.unshift(stat);
} else { // 隙間埋め } else { // 隙間埋め
const mostRecent = stats.find(s => s.date.getTime() < day.getTime()); const mostRecent = src.find(s => s.date.getTime() < current.getTime());
if (mostRecent) { if (mostRecent) {
chart.unshift(Object.assign({}, mostRecent, { chart.unshift(Object.assign({}, mostRecent, {
date: day date: current
})); }));
} else { } else {
chart.unshift({ chart.unshift({
date: day, date: current,
span: 'day',
users: { users: {
local: { local: {
total: 0, total: 0,
@ -97,7 +124,14 @@ export default (params: any) => new Promise(async (res, rej) => {
chart.forEach(x => { chart.forEach(x => {
delete x.date; delete x.date;
delete (x as any).span;
}); });
res(chart); return chart;
};
res({
perDay: format(statsPerDay, 'day'),
perHour: format(statsPerHour, 'hour')
});
}); });