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

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>
<svg :viewBox="`0 0 ${ viewBoxX } ${ viewBoxY }`"> <div>
<polyline <a @click="span = 'day'">Per day</a> | <a @click="span = 'hour'">Per hour</a>
:points="points" <svg :viewBox="`0 0 ${ viewBoxX } ${ viewBoxY }`">
fill="none" <polyline
stroke-width="1" :points="points"
stroke="#555"/> fill="none"
</svg> stroke-width="0.3"
stroke="#555"/>
</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>
<svg :viewBox="`0 0 ${ viewBoxX } ${ viewBoxY }`"> <div>
<polyline <a @click="span = 'day'">Per day</a> | <a @click="span = 'hour'">Per hour</a>
:points="pointsNote" <svg :viewBox="`0 0 ${ viewBoxX } ${ viewBoxY }`">
fill="none" <polyline
stroke-width="1" :points="pointsNote"
stroke="#41ddde"/> fill="none"
<polyline stroke-width="0.3"
:points="pointsReply" stroke="#41ddde"/>
fill="none" <polyline
stroke-width="1" :points="pointsReply"
stroke="#f7796c"/> fill="none"
<polyline stroke-width="0.3"
:points="pointsRenote" stroke="#f7796c"/>
fill="none" <polyline
stroke-width="1" :points="pointsRenote"
stroke="#a1de41"/> fill="none"
<polyline stroke-width="0.3"
:points="pointsTotal" stroke="#a1de41"/>
fill="none" <polyline
stroke-width="1" :points="pointsTotal"
stroke="#555" fill="none"
stroke-dasharray="2 2"/> stroke-width="0.3"
</svg> stroke="#555"
stroke-dasharray="1 1"/>
</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>
<svg :viewBox="`0 0 ${ viewBoxX } ${ viewBoxY }`"> <div>
<polyline <a @click="span = 'day'">Per day</a> | <a @click="span = 'hour'">Per hour</a>
:points="points" <svg :viewBox="`0 0 ${ viewBoxX } ${ viewBoxY }`">
fill="none" <polyline
stroke-width="1" :points="points"
stroke="#555"/> fill="none"
</svg> stroke-width="0.3"
stroke="#555"/>
</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,96 +8,130 @@ 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([
span: 'day', Stats.find({
date: { span: 'day',
$gt: new Date(y - 1, m, d) date: {
} $gt: new Date(y, m, d - daysRange)
}, { }
sort: { }, {
date: -1 sort: {
}, date: -1
fields: { },
_id: 0 fields: {
} _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;
if (stat) { const stat = src.find(s => s.date.getTime() == current.getTime());
chart.unshift(stat);
} else { // 隙間埋め if (stat) {
const mostRecent = stats.find(s => s.date.getTime() < day.getTime()); chart.unshift(stat);
if (mostRecent) { } else { // 隙間埋め
chart.unshift(Object.assign({}, mostRecent, { const mostRecent = src.find(s => s.date.getTime() < current.getTime());
date: day if (mostRecent) {
})); chart.unshift(Object.assign({}, mostRecent, {
} else { date: current
chart.unshift({ }));
date: day, } else {
span: 'day', chart.unshift({
users: { date: current,
local: { users: {
total: 0, local: {
diff: 0 total: 0,
}, diff: 0
remote: { },
total: 0, remote: {
diff: 0 total: 0,
} diff: 0
},
notes: {
local: {
total: 0,
diff: 0,
diffs: {
normal: 0,
reply: 0,
renote: 0
} }
}, },
remote: { notes: {
total: 0, local: {
diff: 0, total: 0,
diffs: { diff: 0,
normal: 0, diffs: {
reply: 0, normal: 0,
renote: 0 reply: 0,
renote: 0
}
},
remote: {
total: 0,
diff: 0,
diffs: {
normal: 0,
reply: 0,
renote: 0
}
}
},
drive: {
local: {
totalCount: 0,
totalSize: 0,
diffCount: 0,
diffSize: 0
},
remote: {
totalCount: 0,
totalSize: 0,
diffCount: 0,
diffSize: 0
} }
} }
}, });
drive: { }
local: {
totalCount: 0,
totalSize: 0,
diffCount: 0,
diffSize: 0
},
remote: {
totalCount: 0,
totalSize: 0,
diffCount: 0,
diffSize: 0
}
}
});
} }
} }
}
chart.forEach(x => { chart.forEach(x => {
delete x.date; delete x.date;
delete (x as any).span;
});
return chart;
};
res({
perDay: format(statsPerDay, 'day'),
perHour: format(statsPerHour, 'hour')
}); });
res(chart);
}); });