Skip to content

Commit

Permalink
- ADD: Added a data statistics page with overview stats per trial.
Browse files Browse the repository at this point in the history
  • Loading branch information
sebastian-raubach committed Apr 23, 2024
1 parent 9e84ba3 commit 35525d3
Show file tree
Hide file tree
Showing 8 changed files with 786 additions and 6 deletions.
4 changes: 3 additions & 1 deletion src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
<b-dropdown-item :disabled="menuItemsDisabled" :to="{ name: 'visualization-map' }"><BIconPinMapFill /> {{ $t('menuVisualizationMap') }}</b-dropdown-item>
</b-nav-item-dropdown>
<b-nav-item :disabled="menuItemsDisabled" :active="$route.name === 'trial-export'" :to="{ name: 'trial-export' }"><BIconCloudDownload /> {{ $t('menuDataExport') }}</b-nav-item>
<b-nav-item :active="$route.name === 'data-statistics'" :to="{ name: 'data-statistics' }"><BIconClipboardData /> {{ $t('menuDataStatistics') }}</b-nav-item>
<b-nav-item :active="$route.name === 'settings'" :to="{ name: 'settings' }"><BIconGear /> {{ $t('menuSettings') }}</b-nav-item>
</b-navbar-nav>

Expand Down Expand Up @@ -124,7 +125,7 @@ import { VuePlausible } from 'vue-plausible'
import Vue from 'vue'
import { axiosCall, getServerSettings } from '@/plugins/api'
import { BIconInfoCircle, BIconFlag, BIconHouse, BIconGear, BIconGithub, BIconGlobe2, BIconTwitter, BIconUiChecksGrid, BIconGraphUp, BIconPinMapFill, BIconGridFill, BIconBarChartSteps, BIconEasel, BIconMoon, BIconSun, BIconCloudDownload } from 'bootstrap-vue'
import { BIconInfoCircle, BIconFlag, BIconHouse, BIconGear, BIconGithub, BIconGlobe2, BIconClipboardData, BIconTwitter, BIconUiChecksGrid, BIconGraphUp, BIconPinMapFill, BIconGridFill, BIconBarChartSteps, BIconEasel, BIconMoon, BIconSun, BIconCloudDownload } from 'bootstrap-vue'
import { getId } from '@/plugins/id'
import { gridScoreVersion } from '@/plugins/constants'
import { isOffline } from '@/plugins/misc'
Expand All @@ -150,6 +151,7 @@ export default {
BIconPinMapFill,
BIconSun,
BIconCloudDownload,
BIconClipboardData,
BIconGithub,
BIconGlobe2,
BIconTwitter,
Expand Down
179 changes: 179 additions & 0 deletions src/components/charts/DataCalendarHeatmapChart.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
<template>
<div ref="wrapper">
<div ref="chart" />
</div>
</template>

<script>
import { mapGetters } from 'vuex'
const Plotly = require('plotly.js/lib/core')
// Only register the chart types we're actually using to reduce the final bundle size
Plotly.register([
require('plotly.js/lib/heatmap')
])
export default {
props: {
chartData: {
type: Map,
default: () => new Map()
}
},
data: function () {
return {
width: 1000
}
},
computed: {
...mapGetters([
'storeLocale',
'storeDarkMode'
]),
dateFormat: function () {
return new Intl.DateTimeFormat(this.storeLocale || 'en', { month: 'short' })
},
months: function () {
if (this.dateFormat) {
return [...Array(12).keys()].map(m => this.dateFormat.format(new Date(Date.UTC(2000, m, 1, 0, 0, 0)))).reverse()
} else {
return []
}
},
isHorizontal: function () {
return this.width < 720
}
},
watch: {
isHorizontal: function () {
this.update()
},
storeDarkMode: function () {
this.update()
},
chartData: function () {
this.update()
}
},
methods: {
update: function () {
try {
Plotly.purge(this.$refs.chart)
} catch {
}
let z = [
new Array(31).fill(0),
new Array(30).fill(0),
new Array(31).fill(0),
new Array(30).fill(0),
new Array(31).fill(0),
new Array(31).fill(0),
new Array(30).fill(0),
new Array(31).fill(0),
new Array(30).fill(0),
new Array(31).fill(0),
new Array(29).fill(0),
new Array(31).fill(0)
]
let max = 0
this.chartData.forEach((value, key) => {
const d = new Date(key)
z[11 - d.getMonth()][d.getDate() - 1] = value
max = Math.max(max, value)
})
const x = Array.from(Array(31).keys()).map(i => i + 1)
const y = this.months
if (this.isHorizontal) {
z = z[0].map((col, i) => z.reverse().map(row => row[i]))
}
const data = [{
z: z,
x: this.isHorizontal ? y.reverse() : x,
y: this.isHorizontal ? x : y,
name: '',
type: 'heatmap',
hoverongaps: false,
xgap: 1,
ygap: 1,
colorscale: [
[0, this.storeDarkMode ? '#2f2f2f' : '#e4e4e4'],
[1, '#00a0f1']
],
colorbar: {
title: {
side: 'right',
font: { color: this.storeDarkMode ? 'white' : 'black' }
},
tickfont: { color: this.storeDarkMode ? 'white' : 'black' },
orientation: this.isHorizontal ? 'h' : 'v'
},
hovertemplate: '%{x}. %{y}: %{z}'
}]
let xAxis = {
showgrid: false,
tickmode: 'array',
// nticks: 31,
tickvals: Array.from(Array(31).keys()).map(i => i + 1),
title: { text: this.$t('widgetChartHeatmapAxisTitleDay'), font: { color: this.storeDarkMode ? 'white' : 'black' } },
tickfont: { color: this.storeDarkMode ? 'white' : 'black' }
}
let yAxis = {
showgrid: false,
title: { text: this.$t('widgetChartHeatmapAxisTitleMonth'), font: { color: this.storeDarkMode ? 'white' : 'black' } },
tickfont: { color: this.storeDarkMode ? 'white' : 'black' }
}
if (this.isHorizontal) {
[xAxis, yAxis] = [yAxis, xAxis]
}
const layout = {
height: this.isHorizontal ? 800 : 500,
margin: {
t: 0
},
paper_bgcolor: 'transparent',
plot_bgcolor: 'transparent',
xaxis: xAxis,
yaxis: yAxis
}
const config = {
responsive: true,
displaylogo: false
}
Plotly.newPlot(this.$refs.chart, data, layout, config)
},
onResize: function () {
this.width = this.$refs.wrapper.clientWidth
}
},
beforeDestroy: function () {
try {
Plotly.purge(this.$refs.chart)
} catch {
}
window.removeEventListener('resize', this.onResize)
},
mounted: function () {
this.onResize()
window.addEventListener('resize', this.onResize)
this.update()
}
}
</script>

<style>
</style>
52 changes: 52 additions & 0 deletions src/plugins/color.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/**
* Converts a HEX value into an RGB object
* @param {String} hex The hex color
*/
const hexToRgb = (hex) => {
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
return result ? { r: parseInt(result[1], 16), g: parseInt(result[2], 16), b: parseInt(result[3], 16) } : null
}

const rgbToHex = (c) => `#${((1 << 24) + (c.r << 16) + (c.g << 8) + c.b).toString(16).slice(1)}`

/**
* Determines the best text color (either white or black) given the background color
* @param {String} backgroundColor The background color in HEX
*/
const getHighContrastTextColor = (backgroundColor) => {
if (backgroundColor) {
const rgb = hexToRgb(backgroundColor)
if (!rgb) {
return 'black'
}
const o = Math.round(((rgb.r * 299) + (rgb.g * 587) + (rgb.b * 114)) / 1000)
return (o > 125) ? 'black' : 'white'
} else {
return 'black'
}
}

const shadeColor = (hex, percent) => {
const rgb = hexToRgb(hex)

let r = parseInt(rgb.r * (100 + percent) / 100)
let g = parseInt(rgb.g * (100 + percent) / 100)
let b = parseInt(rgb.b * (100 + percent) / 100)

r = (r < 255) ? r : 255
g = (g < 255) ? g : 255
b = (b < 255) ? b : 255

r = Math.round(r)
g = Math.round(g)
b = Math.round(b)

return rgbToHex({ r, g, b })
}

export {
getHighContrastTextColor,
shadeColor,
hexToRgb,
rgbToHex
}
23 changes: 22 additions & 1 deletion src/plugins/i18n/de_DE.json
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,7 @@
"menuDataEntry": "Dateneingabe",
"menuDataExport": "Exportieren",
"menuDataVisualization": "Datenvisualisierung",
"menuDataStatistics": "Datenstatistiken",
"menuToggleDarkMode": "Dark-Mode umschalten",
"menuLocale": "Sprache",
"menuAbout": "Über",
Expand Down Expand Up @@ -792,5 +793,25 @@
"widgetGuideOrderTabZigzag": "Zickzack",
"widgetGuideOrderTabSnake": "Schlangenförmig",
"widgetTraitInputPreviousMeasures": "Vorheriger Wert ({date}): <strong><span style='color: {color}'>{values}</span></strong>",
"widgetTraitInputCurrentMeasures": "Aktueller Wert ({date}): <strong><span style='color: {color}'>{values}</span></strong>"
"widgetTraitInputCurrentMeasures": "Aktueller Wert ({date}): <strong><span style='color: {color}'>{values}</span></strong>",
"widgetTrialDataStatsPlots": "{count} Beete bewertet",
"widgetTrialDataStatsTraits": "{count} Merkmale bewertet",
"widgetTrialDataStatsComments": "{count} Kommentare aufgezeichnet",
"widgetTrialDataStatsEvents": "{count} Ereignisse aufgezeichnet",
"widgetTrialDataStatsMeasurements": "{count} Messungen aufgezeichnet",
"widgetTrialDataStatsArea": "{area} {unit} abgedeckt",
"widgetTrialDataStatsAreaUnknown": "Unbekannte Fläche",
"areaUnitMeter": "Quadratmeter",
"areaUnitKilometer": "Quadratkilometer",
"areaUnitHectare": "Hektar",
"areaUnitAcre": "Acker",
"areaUnitMiles": "Quadratmeilen",
"areaUnitFoot": "Quadratfuß",
"areaUnitYard": "Quadratyard",
"pageDataStatisticsTitle": "Übersichtsstatistiken",
"pageDataStatisticsText": "Diese Seite zeigt eine statistische Sicht auf die Versuche auf diesem Gerät. Wähle die Versuche zum darstellen unten aus.",
"formLabelDataStatsTrial": "Versuch auswählen",
"formLabelDataStatsAreaUnit": "Flächeneinheit auswählen",
"widgetChartHeatmapAxisTitleMonth": "Monat",
"widgetChartHeatmapAxisTitleDay": "Tag"
}
23 changes: 22 additions & 1 deletion src/plugins/i18n/en_GB.json
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,7 @@
"menuDataEntry": "Data entry",
"menuDataExport": "Export",
"menuDataVisualization": "Data visualizations",
"menuDataStatistics": "Data statistics",
"menuToggleDarkMode": "Toggle dark mode",
"menuLocale": "Locale",
"menuAbout": "About",
Expand Down Expand Up @@ -803,5 +804,25 @@
"widgetGuideOrderTabZigzag": "Zig-zag",
"widgetGuideOrderTabSnake": "Snake",
"widgetTraitInputPreviousMeasures": "Previous measurement ({date}): <strong><span style='color: {color}'>{values}</span></strong>",
"widgetTraitInputCurrentMeasures": "Current measurement ({date}): <strong><span style='color: {color}'>{values}</strong>"
"widgetTraitInputCurrentMeasures": "Current measurement ({date}): <strong><span style='color: {color}'>{values}</strong>",
"widgetTrialDataStatsPlots": "{count} plots scored",
"widgetTrialDataStatsTraits": "{count} traits scored",
"widgetTrialDataStatsComments": "{count} comments recorded",
"widgetTrialDataStatsEvents": "{count} events recorded",
"widgetTrialDataStatsMeasurements": "{count} measurements taken",
"widgetTrialDataStatsArea": "{area} {unit} covered",
"widgetTrialDataStatsAreaUnknown": "Unknown area",
"areaUnitMeter": "Square metres",
"areaUnitKilometer": "Square kilometres",
"areaUnitHectare": "Hectare",
"areaUnitAcre": "Acres",
"areaUnitMiles": "Square miles",
"areaUnitFoot": "Square feet",
"areaUnitYard": "Square yards",
"pageDataStatisticsTitle": "Overview statistics",
"pageDataStatisticsText": "This page gives you a statistical view onto the trials on your device. Select the trials to visualize below.",
"formLabelDataStatsTrial": "Select trial",
"formLabelDataStatsAreaUnit": "Select area unit",
"widgetChartHeatmapAxisTitleMonth": "Month",
"widgetChartHeatmapAxisTitleDay": "Day"
}
Loading

0 comments on commit 35525d3

Please sign in to comment.