Daily and monthly stats

This commit is contained in:
Maximilian Giller 2022-02-11 21:34:38 +01:00
parent 9cfba93f65
commit aa706bfcaf
5 changed files with 410 additions and 12 deletions

View file

@ -0,0 +1,151 @@
<template>
<b-table
:items="aggregatedStatistics"
primary-key="day"
hover
:busy="isLoading"
:fields="statistic_fields"
sort-by="day"
sort-desc
>
<template #table-busy>
<div class="text-center">
<b-spinner></b-spinner>
</div>
</template>
<!-- Custom data -->
<template #cell(duration)="data">
{{ getDurationTimestamp(data.item.duration) }}
</template>
<template #cell(distribution)="data">
<JugglProjectDistribution :projects="data.item.projects" />
</template>
<template #cell(details)="data">
<b-button
size="sm"
@click="data.toggleDetails"
variant="outline-secondary"
>
<b-icon class="icon-btn" icon="bar-chart-steps" />
</b-button>
</template>
<template #row-details="data">
<b-card>
<JugglProjectDistribution
:projects="data.item.projects"
names
class="mb-3"
/>
<JugglProjectStatisticsList :statistics="data.item.statistics" />
</b-card>
</template>
</b-table>
</template>
<script>
import { helperService } from "@/services/helper.service.js";
import JugglProjectStatisticsList from "./JugglProjectStatisticsList.vue";
import JugglProjectDistribution from "./JugglProjectDistribution.vue";
export default {
name: "JugglDailyStatisticsList",
components: { JugglProjectStatisticsList, JugglProjectDistribution },
props: {
statistics: {
required: true,
type: Array
}
},
data: () => {
return {
statistic_fields: [
{
key: "day",
label: "Day"
},
{
key: "duration",
label: "Duration"
},
{
key: "distribution",
lavel: "Distribution"
},
{
key: "record_count",
label: "Records"
},
{
key: "details",
label: "Details"
}
]
};
},
computed: {
isLoading: function() {
return this.statistics === undefined;
},
aggregatedStatistics: function() {
if (this.statistics === undefined) {
return [];
}
var aggregated = {};
this.statistics.forEach(stat => {
var dayName = stat.date;
if (aggregated[dayName] === undefined) {
aggregated[dayName] = {
day: dayName,
duration: 0,
record_count: 0,
statistics: [],
projects: {}
};
}
if (aggregated[dayName].projects[stat.project_id] === undefined) {
aggregated[dayName].projects[stat.project_id] = {
project_id: stat.project_id,
duration: 0,
record_count: 0,
color: stat.color,
name: stat.name,
visible: stat.visible,
statistics: []
};
}
aggregated[dayName].duration += Number(stat.duration);
aggregated[dayName].record_count += Number(stat.record_count);
aggregated[dayName].statistics.push(stat);
aggregated[dayName].projects[stat.project_id].duration += Number(
stat.duration
);
aggregated[dayName].projects[stat.project_id].record_count += Number(
stat.record_count
);
aggregated[dayName].projects[stat.project_id].statistics.push(stat);
});
// Simplyfying object to lists
aggregated = Object.values(aggregated);
aggregated.forEach(stat => {
stat.projects = Object.values(stat.projects);
});
return aggregated;
}
},
methods: {
getDurationTimestamp: helperService.getDurationTimestamp
}
};
</script>
<style lang="sass"></style>

View file

@ -0,0 +1,151 @@
<template>
<b-table
:items="aggregatedStatistics"
primary-key="month"
hover
:busy="isLoading"
:fields="statistic_fields"
sort-by="month"
sort-desc
>
<template #table-busy>
<div class="text-center">
<b-spinner></b-spinner>
</div>
</template>
<!-- Custom data -->
<template #cell(duration)="data">
{{ getDurationTimestamp(data.item.duration) }}
</template>
<template #cell(distribution)="data">
<JugglProjectDistribution :projects="data.item.projects" class="mb-3" />
</template>
<template #cell(details)="data">
<b-button
size="sm"
@click="data.toggleDetails"
variant="outline-secondary"
>
<b-icon class="icon-btn" icon="bar-chart-steps" />
</b-button>
</template>
<template #row-details="data">
<b-card>
<JugglProjectDistribution
:projects="data.item.projects"
names
class="mb-3"
/>
<JugglProjectStatisticsList :statistics="data.item.statistics" />
</b-card>
</template>
</b-table>
</template>
<script>
import { helperService } from "@/services/helper.service.js";
import JugglProjectStatisticsList from "./JugglProjectStatisticsList.vue";
import JugglProjectDistribution from "./JugglProjectDistribution.vue";
export default {
name: "JugglMonthlyStatisticsList",
components: { JugglProjectStatisticsList, JugglProjectDistribution },
props: {
statistics: {
required: true,
type: Array
}
},
data: () => {
return {
statistic_fields: [
{
key: "month",
label: "Month"
},
{
key: "duration",
label: "Duration"
},
{
key: "distribution",
lavel: "Distribution"
},
{
key: "record_count",
label: "Records"
},
{
key: "details",
label: "Details"
}
]
};
},
computed: {
isLoading: function() {
return this.statistics === undefined;
},
aggregatedStatistics: function() {
if (this.statistics === undefined) {
return [];
}
var aggregated = {};
this.statistics.forEach(stat => {
var monthName = stat.date.substring(0, 7); // date is in the format "YYYY-MM-DD", so simply cutting of the day
if (aggregated[monthName] === undefined) {
aggregated[monthName] = {
month: monthName,
duration: 0,
record_count: 0,
statistics: [],
projects: {}
};
}
if (aggregated[monthName].projects[stat.project_id] === undefined) {
aggregated[monthName].projects[stat.project_id] = {
project_id: stat.project_id,
duration: 0,
record_count: 0,
color: stat.color,
name: stat.name,
visible: stat.visible,
statistics: []
};
}
aggregated[monthName].duration += Number(stat.duration);
aggregated[monthName].record_count += Number(stat.record_count);
aggregated[monthName].statistics.push(stat);
aggregated[monthName].projects[stat.project_id].duration += Number(
stat.duration
);
aggregated[monthName].projects[stat.project_id].record_count += Number(
stat.record_count
);
aggregated[monthName].projects[stat.project_id].statistics.push(stat);
});
// Simplyfying object to lists
aggregated = Object.values(aggregated);
aggregated.forEach(stat => {
stat.projects = Object.values(stat.projects);
});
return aggregated;
}
},
methods: {
getDurationTimestamp: helperService.getDurationTimestamp
}
};
</script>
<style lang="sass"></style>

View file

@ -0,0 +1,45 @@
<template>
<b-progress :max="totalDuration">
<b-progress-bar
v-for="(project, index) in projects"
v-bind:key="project.project_id"
:value="project.duration"
:style="'background-color: ' + project.color + ' !important'"
:variant="index % 2 == 0 ? 'primary' : 'dark'"
v-b-tooltip.hover
:title="project.name + ' · ' + getDurationTimestamp(project.duration)"
><span v-if="names">{{ project.name }}</span></b-progress-bar
>
</b-progress>
</template>
<script>
import { helperService } from "@/services/helper.service.js";
export default {
name: "JugglProjectDistribution",
props: {
projects: {
required: true,
type: Array
},
names: {
required: false,
type: Boolean,
default: false
}
},
computed: {
totalDuration: function() {
var duration = 0;
this.projects.forEach(stat => (duration += Number(stat.duration)));
return duration;
}
},
methods: {
getDurationTimestamp: helperService.getDurationTimestamp
}
};
</script>
<style></style>

View file

@ -1,11 +1,12 @@
<template>
<b-table
:items="statistics"
:items="projectStatistics"
primary-key="project_id"
hover
:busy="isLoading"
:fields="statistic_fields"
sort-by="name"
sort-by="duration"
sort-desc
>
<template #table-busy>
<div class="text-center">
@ -21,6 +22,10 @@
<template #cell(duration)="data">
{{ getDurationTimestamp(data.item.duration) }}
</template>
<template #cell(distribution)="data">
{{ ((data.item.duration / totalDuration) * 100).toFixed(0) }}%
</template>
</b-table>
</template>
@ -53,6 +58,10 @@ export default {
{
key: "record_count",
label: "Records"
},
{
key: "distribution",
label: "Distribution"
}
]
};
@ -60,6 +69,38 @@ export default {
computed: {
isLoading: function() {
return this.statistics === undefined;
},
totalDuration: function() {
var duration = 0;
this.statistics.forEach(stat => (duration += Number(stat.duration)));
return duration;
},
projectStatistics: function() {
if (this.statistics === undefined) {
return [];
}
var projects = {};
this.statistics.forEach(stat => {
if (projects[stat.project_id] === undefined) {
projects[stat.project_id] = {
project_id: stat.project_id,
duration: 0,
record_count: 0,
color: stat.color,
name: stat.name,
visible: stat.visible,
statistics: []
};
}
projects[stat.project_id].duration += Number(stat.duration);
projects[stat.project_id].record_count += Number(stat.record_count);
projects[stat.project_id].statistics.push(stat);
});
// Simplyfying object to lists
return Object.values(projects);
}
},
methods: {

View file

@ -88,15 +88,23 @@
</b-form>
</BaseContainer>
<BaseSection id="projects" title="Total">
<JugglProjectStatisticsList :statistics="visibleStatistics" />
<BaseSection id="projects" :title="modeTitle">
<JugglDailyStatisticsList
:statistics="visibleStatistics"
v-if="mode == 'daily'"
/>
<JugglMonthlyStatisticsList
:statistics="visibleStatistics"
v-if="mode == 'monthly'"
/>
</BaseSection>
</LayoutNavbarPrivate>
</template>
<script>
import LayoutNavbarPrivate from "@/components/layout/LayoutNavbarPrivate";
import JugglProjectStatisticsList from "@/components/juggl/JugglProjectStatisticsList";
import JugglDailyStatisticsList from "@/components/juggl/JugglDailyStatisticsList";
import JugglMonthlyStatisticsList from "@/components/juggl/JugglMonthlyStatisticsList";
import { helperService } from "@/services/helper.service.js";
import BaseSection from "@/components/base/BaseSection";
import BaseContainer from "@/components/base/BaseContainer";
@ -106,7 +114,8 @@ export default {
name: "Statistics",
components: {
LayoutNavbarPrivate,
JugglProjectStatisticsList,
JugglMonthlyStatisticsList,
JugglDailyStatisticsList,
BaseSection,
BaseContainer
},
@ -138,18 +147,19 @@ export default {
return store.getters.getFilteredStatistics({
projectVisible: true
});
},
modeTitle: function() {
return this.mode[0].toUpperCase() + this.mode.substring(1);
}
},
methods: {
getDurationTimestamp: helperService.getDurationTimestamp,
updateStatistics: function() {
if (this.mode == "daily") {
store.dispatch("loadStatistics", [
{
from: new Date(this.daily.startDate),
until: new Date(this.daily.endDate)
}
]);
store.dispatch("loadStatistics", {
from: new Date(this.daily.startDate),
until: new Date(this.daily.endDate)
});
} else if (this.mode == "monthly") {
store.dispatch("loadMonthlyStatistics", this.monthly);
}