sotlas-frontend/src/components/ActivationCharts.vue

248 wiersze
7.1 KiB
Vue
Czysty Wina Historia

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

<template>
<div>
<div class="columns is-5 is-variable">
<div class="column is-half">
<h4 class="title is-4">Activations per year</h4>
<BarChart v-if="activationsPerYear" :data="activationsPerYear" labelField="year" valueField="activations" valueFieldB="bonusActivations" name="Activations" nameB="with Bonus" :xIsSeries="true" />
</div>
<div class="column is-half">
<h4 class="title is-4">Activations per association</h4>
<PieChart v-if="activationsPerAssociation" :data="activationsPerAssociation" labelField="association" valueField="activations" name="Activations" />
</div>
</div>
<div v-if="moreStats" class="columns is-5 is-variable">
<div class="column is-half">
<h4 class="title is-4">Activations by altitude</h4>
<BarChart v-if="activationsPerAltitude" :data="activationsPerAltitude" labelField="altitude" valueField="activations" name="Activations" :xIsSeries="true" />
</div>
<div class="column is-half">
<h4 class="title is-4 no-margin-bottom">Number of QSOs per activation</h4>
<PercentageChart v-if="qsosPerActivation" :data="qsosPerActivation" labelField="qsos" valueField="activations" name="Activations" :maxSlices="11" />
<template v-if="modes">
<h4 class="title is-4 no-margin-bottom">Number of QSOs by mode</h4>
<PercentageChart :data="modes" labelField="mode" valueField="qsos" name="QSOs" />
</template>
<template v-if="bands">
<h4 class="title is-4 no-margin-bottom">Number of QSOs by band</h4>
<PercentageChart :data="bands" labelField="band" valueField="qsos" name="QSOs" />
</template>
<div v-if="!authenticated" class="stats-teaser">
<div>
<font-awesome-icon :icon="['far', 'chart-bar']" /> Log in for more statistics
</div>
</div>
</div>
</div>
<div class="more-button" v-else>
<b-button @click="moreStats = true" type="is-info" icon-left="chart-bar">More stats</b-button>
</div>
</div>
</template>
<script>
import moment from 'moment'
import utils from '../mixins/utils.js'
import BarChart from '../components/BarChart.vue'
import PieChart from '../components/PieChart.vue'
import PercentageChart from '../components/PercentageChart.vue'
export default {
props: {
activations: Array
},
mixins: [utils],
components: {
BarChart, PieChart, PercentageChart
},
computed: {
activationsPerYear () {
if (this.activations.length === 0) {
return null
}
let years = {}
let yearsBonus = {}
this.activations.forEach(activation => {
let year = moment.utc(activation.date).year()
if (!years[year]) {
years[year] = 0
yearsBonus[year] = 0
}
years[year]++
if (activation.bonus > 0) {
yearsBonus[year]++
}
})
return Object.keys(years).sort().map(year => {
return { year, activations: years[year], bonusActivations: yearsBonus[year] }
})
},
activationsPerAssociation () {
if (this.activations.length === 0) {
return null
}
let associations = {}
this.activations.forEach(activation => {
let association = activation.summit.code.substring(0, activation.summit.code.indexOf('/'))
if (!associations[association]) {
associations[association] = 0
}
associations[association]++
})
return Object.keys(associations).sort().map(association => {
return { association, activations: associations[association] }
})
},
activationsPerAltitude () {
return this.makeBands(this.activations.map(activation => { return activation.summit.altitude }), 500, ' m', 'altitude', 'activations')
},
qsosPerActivation () {
return this.makeBands(this.activations.map(activation => { return activation.qsos }), 10, '', 'qsos', 'activations', true, 110)
},
modes () {
if (this.activations.length === 0) {
return null
}
let modes = {}
let totalQsos = 0
this.activations.forEach(activation => {
if (activation.modeQsos) {
Object.keys(activation.modeQsos).forEach(mode => {
if (!modes[mode]) {
modes[mode] = 0
}
modes[mode] += activation.modeQsos[mode]
})
}
totalQsos += activation.qsos
})
let modesArr = []
let modeQsosSum = 0
Object.keys(modes).forEach(mode => {
modesArr.push({ mode: mode.toUpperCase(), qsos: modes[mode] })
modeQsosSum += modes[mode]
})
if (modesArr.length > 0) {
if (modeQsosSum < totalQsos) {
modesArr.push({ mode: 'Other', qsos: (totalQsos - modeQsosSum) })
}
return modesArr
} else {
return null
}
},
bands () {
if (this.activations.length === 0) {
return null
}
let bands = {}
this.activations.forEach(activation => {
if (activation.bandQsos) {
Object.keys(activation.bandQsos).forEach(band => {
if (!bands[band]) {
bands[band] = 0
}
bands[band] += activation.bandQsos[band]
})
}
})
let bandsArr = []
Object.keys(bands).forEach(band => {
bandsArr.push({ band, qsos: bands[band] })
})
if (bandsArr.length > 0) {
bandsArr.sort((a, b) => {
return b.qsos - a.qsos
})
return bandsArr
} else {
return null
}
}
},
methods: {
makeBands (data, interval, suffix, bandKey, valueKey, ranges = false, maxBand) {
if (!data) {
return null
}
let bands = {}
let maxValue = 0
data.forEach(value => {
let band = Math.ceil(value / interval) * interval
if (maxBand && band > maxBand) {
band = maxBand
}
if (!bands[band]) {
bands[band] = 0
}
bands[band]++
if (maxValue < band) {
maxValue = band
}
})
for (let value = interval; value < maxValue; value += interval) {
if (bands[value] === undefined) {
bands[value] = 0
}
}
return Object.keys(bands).sort((a, b) => { return parseInt(a) - parseInt(b) }).map(value => {
value = parseInt(value)
let label = '< ' + value + suffix
if (ranges) {
if (value === maxBand) {
label = '> ' + (value - interval) + suffix
} else {
label = (value - interval + 1) + '-' + value + suffix
}
}
return { [bandKey]: label, [valueKey]: bands[value] }
})
}
},
data () {
return {
moreStats: false
}
}
}
</script>
<style scoped>
.more-button {
text-align: center;
}
.no-margin-bottom {
margin-bottom: 0;
}
.stats-teaser {
text-align: center;
margin-top: 1em;
}
.stats-teaser div {
font-size: 1.5rem;
color: #ccc;
border: 2px dashed #ccc;
display: inline-block;
padding: 0.5em 0.75em;
border-radius: 0.75em;
}
.stats-teaser .fa-chart-bar {
font-size: 2rem;
vertical-align: middle;
margin-right: 0.3em;
}
</style>