Added pie chart showing counts of categories with a rating of 3 or below
This commit is contained in:
parent
cbfddfe8c1
commit
39de14caf6
@ -49,3 +49,17 @@ data class ChartDataset(
|
|||||||
) {
|
) {
|
||||||
constructor(label: String, color: String, data: List<Double>) : this(label, color, color, data)
|
constructor(label: String, color: String, data: List<Double>) : this(label, color, color, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class AfiPieChart(
|
||||||
|
val labels: List<String> = listOf("Monitoring", "Knowledge", "Control", "Conservatism", "Teamwork"),
|
||||||
|
val datasets: List<AfiPieChartDataset>
|
||||||
|
) {
|
||||||
|
constructor(afiPieChartDataset: AfiPieChartDataset) : this(datasets = listOf(afiPieChartDataset))
|
||||||
|
}
|
||||||
|
|
||||||
|
data class AfiPieChartDataset(
|
||||||
|
val data: List<Int>,
|
||||||
|
val backgroundColor: List<String> = listOf("#F90", "#000", "#00FF00", "#33F", "#FF0")
|
||||||
|
) {
|
||||||
|
constructor(monitoring: Int, knowledge: Int, control: Int, conservatism: Int, teamwork: Int) : this(data = listOf(monitoring, knowledge, control, conservatism, teamwork))
|
||||||
|
}
|
||||||
|
@ -190,6 +190,39 @@ class Controller {
|
|||||||
}
|
}
|
||||||
return builder.toString()
|
return builder.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val afiPieThreshold = 3
|
||||||
|
|
||||||
|
@PostMapping("/observations/afipie")
|
||||||
|
fun getObservationsAfiPieChartData(@Valid @RequestBody observationsRequest: ObservationsRequest): AfiPieChart? {
|
||||||
|
val data = getObservations(observationsRequest)
|
||||||
|
var monitoring = 0
|
||||||
|
var knowledge = 0
|
||||||
|
var control = 0
|
||||||
|
var conservatism = 0
|
||||||
|
var teamwork = 0
|
||||||
|
for (x in data) {
|
||||||
|
for (y in x.scenarios) {
|
||||||
|
if (y.monitoring.rating <= afiPieThreshold)
|
||||||
|
monitoring++
|
||||||
|
if (y.knowledge.rating <= afiPieThreshold)
|
||||||
|
knowledge++
|
||||||
|
if (y.control.rating <= afiPieThreshold)
|
||||||
|
control++
|
||||||
|
if (y.controlProcedural.rating <= afiPieThreshold)
|
||||||
|
control++
|
||||||
|
if (y.conservatism.rating <= afiPieThreshold)
|
||||||
|
conservatism++
|
||||||
|
if (y.teamworkCommunications.rating <= afiPieThreshold)
|
||||||
|
teamwork++
|
||||||
|
if (y.teamworkLeadership.rating <= afiPieThreshold)
|
||||||
|
teamwork++
|
||||||
|
if (y.teamworkWorkload.rating <= afiPieThreshold)
|
||||||
|
teamwork++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return AfiPieChart(AfiPieChartDataset(monitoring, knowledge, control, conservatism, teamwork))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class AverageData(
|
data class AverageData(
|
||||||
|
12
frontend/src/components/AfiPie.vue
Normal file
12
frontend/src/components/AfiPie.vue
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<script>
|
||||||
|
import { Pie, mixins } from "vue-chartjs";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
extends: Pie,
|
||||||
|
mixins: [mixins.reactiveProp],
|
||||||
|
props: ["chartData", "options"],
|
||||||
|
mounted() {
|
||||||
|
this.renderChart(this.chartData, this.options);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
@ -13,6 +13,10 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</b-modal>
|
</b-modal>
|
||||||
|
<b-row>
|
||||||
|
<b-col><b-button @click="chartMode = 0; getFilteredAverage()">Trends</b-button></b-col>
|
||||||
|
<b-col><b-button @click="chartMode = 1; getFilteredAverage()">AFIs</b-button></b-col>
|
||||||
|
</b-row>
|
||||||
<b-row>
|
<b-row>
|
||||||
<b-col>
|
<b-col>
|
||||||
<b-form-group label="Site">
|
<b-form-group label="Site">
|
||||||
@ -49,22 +53,31 @@
|
|||||||
<b-button v-on:click="getFilteredAverage()">Refresh</b-button>
|
<b-button v-on:click="getFilteredAverage()">Refresh</b-button>
|
||||||
</b-col>
|
</b-col>
|
||||||
</b-row>
|
</b-row>
|
||||||
<b-row>
|
<b-row v-if="chartData != null && chartData.labels.length > 0 && !loading">
|
||||||
<b-col v-if="chartData != null && chartData.labels.length > 0">
|
<b-col v-if="chartMode == 0">
|
||||||
<stats-view v-bind:chartData="chartData"
|
<stats-view v-bind:chartData="chartData"
|
||||||
style="position: relative; height:80vh" :options="options"/>
|
style="position: relative; height:80vh" :options="options"/>
|
||||||
</b-col>
|
</b-col>
|
||||||
<b-col v-else>
|
<b-col v-else-if="chartMode == 1">
|
||||||
<v-icon name="search"></v-icon>
|
<afi-pie :chartData="chartData" :options="pieOptions"/>
|
||||||
<p>No data found with the given search parameters.</p>
|
|
||||||
</b-col>
|
</b-col>
|
||||||
</b-row>
|
</b-row>
|
||||||
|
<b-row v-else-if="loading">
|
||||||
|
Loading...
|
||||||
|
</b-row>
|
||||||
|
<b-row v-else>
|
||||||
|
<b-col>
|
||||||
|
<v-icon name="search"></v-icon>
|
||||||
|
<p>No data found with the given search parameters.</p>
|
||||||
|
</b-col>
|
||||||
|
</b-row>
|
||||||
</b-container>
|
</b-container>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Vue from "vue";
|
import Vue from "vue";
|
||||||
import StatsView from "../components/StatsView";
|
import StatsView from "../components/StatsView";
|
||||||
|
import AfiPie from "../components/AfiPie"
|
||||||
import "vue-awesome/icons/exclamation-circle";
|
import "vue-awesome/icons/exclamation-circle";
|
||||||
import "vue-awesome/icons/search";
|
import "vue-awesome/icons/search";
|
||||||
|
|
||||||
@ -73,9 +86,12 @@ var moment = require("moment");
|
|||||||
export default {
|
export default {
|
||||||
name: "stats",
|
name: "stats",
|
||||||
title: "Statistics",
|
title: "Statistics",
|
||||||
components: { StatsView },
|
components: { StatsView, AfiPie },
|
||||||
data: function() {
|
data: function() {
|
||||||
return {
|
return {
|
||||||
|
chartMode: 0,
|
||||||
|
endpoints: ["/observations/chartdata", "/observations/afipie"],
|
||||||
|
loading: false,
|
||||||
chartData: null,
|
chartData: null,
|
||||||
options: {
|
options: {
|
||||||
responsive: true,
|
responsive: true,
|
||||||
@ -110,6 +126,9 @@ export default {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
pieOptions:{
|
||||||
|
maintainAspectRatio: false
|
||||||
|
},
|
||||||
errorStatus: null,
|
errorStatus: null,
|
||||||
errorMessage: null,
|
errorMessage: null,
|
||||||
dateOptions: {
|
dateOptions: {
|
||||||
@ -172,8 +191,9 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getFilteredAverage: function() {
|
getFilteredAverage: function() {
|
||||||
|
this.loading = true;
|
||||||
Vue.axios
|
Vue.axios
|
||||||
.post("/observations/chartdata", {
|
.post(this.endpoints[this.chartMode], {
|
||||||
site: this.siteSelection,
|
site: this.siteSelection,
|
||||||
tutor: this.tutorSelection,
|
tutor: this.tutorSelection,
|
||||||
startDate: moment(this.startDate).format("YYYY-MM-DD"),
|
startDate: moment(this.startDate).format("YYYY-MM-DD"),
|
||||||
@ -183,11 +203,13 @@ export default {
|
|||||||
})
|
})
|
||||||
.then(response => {
|
.then(response => {
|
||||||
this.chartData = response.data;
|
this.chartData = response.data;
|
||||||
|
this.loading = false;
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
this.errorStatus = error.response.status;
|
this.errorStatus = error.response.status;
|
||||||
this.errorMessage = error.response.data;
|
this.errorMessage = error.response.data;
|
||||||
this.$refs.errorModal.show();
|
this.$refs.errorModal.show();
|
||||||
|
this.loading = false;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
getTutors: function() {
|
getTutors: function() {
|
||||||
@ -216,7 +238,6 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.getFilteredAverage();
|
|
||||||
Vue.axios
|
Vue.axios
|
||||||
.get("/site")
|
.get("/site")
|
||||||
.then(response => {
|
.then(response => {
|
||||||
|
Loading…
Reference in New Issue
Block a user