Add user-specific quiz statistics
This commit is contained in:
@@ -1,13 +1,27 @@
|
|||||||
<template>
|
<template>
|
||||||
<v-container fluid>
|
<v-card>
|
||||||
<v-subheader class="text-h5 text--primary">Challenge</v-subheader>
|
<v-card-title>
|
||||||
|
Spiele {{ games.won + games.lost + games.tie }}
|
||||||
|
</v-card-title>
|
||||||
|
<v-card-subtitle>
|
||||||
|
<div>Siege: {{ games.won }}</div>
|
||||||
|
<div>Unentschieden: {{ games.lost }}</div>
|
||||||
|
<div>Niederlagen: {{ games.tie }}</div>
|
||||||
|
<div>Fragen: {{ games.questionsCorrect + games.questionsIncorrect }}</div>
|
||||||
|
<div>Richtig: {{ questionsCorrectPercent }}%</div>
|
||||||
|
</v-card-subtitle>
|
||||||
|
<v-card-text>
|
||||||
|
{{ text }}
|
||||||
|
</v-card-text>
|
||||||
|
<v-card-text>
|
||||||
|
<v-btn depressed color="success" class="my-3" @click="playVersus">
|
||||||
|
<v-icon left>mdi-play</v-icon>
|
||||||
|
Spielen
|
||||||
|
</v-btn>
|
||||||
|
</v-card-text>
|
||||||
<v-divider></v-divider>
|
<v-divider></v-divider>
|
||||||
<v-btn depressed color="success" class="my-3" @click="playVersus">
|
|
||||||
<v-icon left>mdi-play</v-icon>
|
|
||||||
Quiz Spielen
|
|
||||||
</v-btn>
|
|
||||||
<AddClosedEndedQuestion />
|
<AddClosedEndedQuestion />
|
||||||
</v-container>
|
</v-card>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@@ -23,7 +37,22 @@ export default {
|
|||||||
return {
|
return {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created () {
|
computed: {
|
||||||
|
games () {
|
||||||
|
return this.$store.state.user.games[this.courseId]
|
||||||
|
},
|
||||||
|
questionsCorrectPercent () {
|
||||||
|
const percent = (this.games.questionsCorrect / (this.games.questionsCorrect + this.games.questionsIncorrect)) * 100
|
||||||
|
// percent will be NaN if the student hasn't played any games yet
|
||||||
|
return percent ? Math.round(parseFloat(percent)) : 100 // Convert to float, then round to closest int
|
||||||
|
},
|
||||||
|
text () {
|
||||||
|
if (this.questionsAnswered >= 20 && this.questionsCorrectPercent >= 90) {
|
||||||
|
return 'Du scheinst gut auf die Klausur vorbereitet zu sein. Viel Glück!'
|
||||||
|
} else {
|
||||||
|
return 'Wir empfehlen Dir noch ein wenig zu üben, bevor du zur Klausur antrittst.'
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
playVersus () {
|
playVersus () {
|
||||||
|
|||||||
58
components/GameResultDialog.vue
Normal file
58
components/GameResultDialog.vue
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
<template>
|
||||||
|
<v-dialog v-model="show" persistent width="500">
|
||||||
|
<v-card class="mx-auto">
|
||||||
|
<v-card-title class="justify-center">{{ title }}</v-card-title>
|
||||||
|
<v-card-text>
|
||||||
|
<v-row no-gutters class="text-h4 text--primary">
|
||||||
|
<v-col cols="5" class="d-flex justify-end">{{ result.correctAnswersPl1 }}</v-col>
|
||||||
|
<v-col cols="2" class="d-flex justify-center">:</v-col>
|
||||||
|
<v-col cols="5" class="d-flex justify-start">{{ result.correctAnswersPl2 }}</v-col>
|
||||||
|
</v-row>
|
||||||
|
<v-row no-gutters class="text-subtitle-1">
|
||||||
|
<v-col cols="5" class="d-flex justify-end">{{ game.player1name }}</v-col>
|
||||||
|
<v-col cols="2" class="d-flex justify-center"></v-col>
|
||||||
|
<v-col cols="5" class="d-flex justify-start">{{ game.player2name }}</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-card-text>
|
||||||
|
<v-card-actions>
|
||||||
|
<v-spacer></v-spacer>
|
||||||
|
<v-btn depressed block color="primary" :to="`/courses/${courseId}`">Abschließen</v-btn>
|
||||||
|
<v-spacer></v-spacer>
|
||||||
|
</v-card-actions>
|
||||||
|
</v-card>
|
||||||
|
</v-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { Game } from '~/plugins/game'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
game: {
|
||||||
|
type: Game,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
courseId: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
show: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
result () {
|
||||||
|
return this.game.getResult()
|
||||||
|
},
|
||||||
|
title () {
|
||||||
|
if (this.result.tie) return 'Unentschieden!'
|
||||||
|
else return this.result.winner === this.playerNumber ? 'Gewonnen!' : 'Verloren :('
|
||||||
|
},
|
||||||
|
playerNumber () {
|
||||||
|
return this.game.player1id === this.$auth.currentUser.uid ? 1 : 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
export class Game {
|
export class Game {
|
||||||
constructor (id, questions, created, player1id, player1name, player1answers, player2id, player2name, player2answers) {
|
constructor (id, questions, created, player1id, player1name, player1answers, player2id, player2name, player2answers) {
|
||||||
this.id = id
|
this.id = id
|
||||||
this.questions = questions
|
this.questionIds = questions
|
||||||
this.created = created
|
this.created = created
|
||||||
this.player1id = player1id
|
this.player1id = player1id
|
||||||
this.player1name = player1name
|
this.player1name = player1name
|
||||||
@@ -10,13 +10,38 @@ export class Game {
|
|||||||
this.player2name = player2name
|
this.player2name = player2name
|
||||||
this.player2answers = player2answers
|
this.player2answers = player2answers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getResult () {
|
||||||
|
if (!this.questions) return {}
|
||||||
|
const qs = this.questions
|
||||||
|
|
||||||
|
function countCorrectAnswers(answersGiven) {
|
||||||
|
let i = 0
|
||||||
|
answersGiven.forEach(a => {
|
||||||
|
const q = qs.find(e => e.id === a.frage)
|
||||||
|
if (q && a.antwort === q.correctAnswer) i++
|
||||||
|
})
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
const correctAnswersPl1 = countCorrectAnswers(this.player1answers)
|
||||||
|
const correctAnswersPl2 = countCorrectAnswers(this.player2answers)
|
||||||
|
|
||||||
|
return {
|
||||||
|
winner: correctAnswersPl1 > correctAnswersPl2 ? 1 : 2,
|
||||||
|
loser: correctAnswersPl1 > correctAnswersPl2 ? 2 : 1,
|
||||||
|
tie: correctAnswersPl1 === correctAnswersPl2,
|
||||||
|
correctAnswersPl1,
|
||||||
|
correctAnswersPl2
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Firestore data converter
|
// Firestore data converter
|
||||||
export const GameConverter = {
|
export const GameConverter = {
|
||||||
toFirestore: (game) => {
|
toFirestore: (game) => {
|
||||||
return {
|
return {
|
||||||
fragen: game.questions,
|
fragen: game.questionIds,
|
||||||
erstellt: game.created,
|
erstellt: game.created,
|
||||||
spieler1id: game.player1id,
|
spieler1id: game.player1id,
|
||||||
spieler1name: game.player1name,
|
spieler1name: game.player1name,
|
||||||
|
|||||||
@@ -1,20 +1,48 @@
|
|||||||
export class User {
|
export class User {
|
||||||
constructor (displayName, courses, gamesStarted) {
|
constructor (displayName, courses, games, gamesStarted) {
|
||||||
this.displayName = displayName
|
this.displayName = displayName
|
||||||
this.courses = courses
|
this.courses = courses
|
||||||
|
this.games = {}
|
||||||
this.gamesStarted = []
|
this.gamesStarted = []
|
||||||
|
|
||||||
|
if (games) this.setGames(games)
|
||||||
|
|
||||||
if (gamesStarted && gamesStarted.length > 0) {
|
if (gamesStarted && gamesStarted.length > 0) {
|
||||||
gamesStarted.forEach(e => {
|
gamesStarted.forEach(e => {
|
||||||
this.gamesStarted.push({ course: e.kurs, game: e.spiel })
|
this.gamesStarted.push({ course: e.kurs, game: e.spiel })
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setGames (gamesObj) {
|
||||||
|
for (const [key, value] of Object.entries(gamesObj)) {
|
||||||
|
const obj = {
|
||||||
|
won: value.gewonnen || 0,
|
||||||
|
lost: value.verloren || 0,
|
||||||
|
tie: value.unentschieden || 0,
|
||||||
|
questionsCorrect: value.fragenRichtig || 0,
|
||||||
|
questionsIncorrect: value.fragenFalsch || 0
|
||||||
|
}
|
||||||
|
this.games[key] = obj
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Firestore data converter
|
// Firestore data converter
|
||||||
export const UserConverter = {
|
export const UserConverter = {
|
||||||
toFirestore: (user) => {
|
toFirestore: (user) => {
|
||||||
|
const spiele = {}
|
||||||
|
for (const [key, value] of Object.entries(user.games)) {
|
||||||
|
const obj = {
|
||||||
|
gewonnen: value.won || 0,
|
||||||
|
verloren: value.lost || 0,
|
||||||
|
unentschieden: value.tie || 0,
|
||||||
|
fragenRichtig: value.questionsCorrect || 0,
|
||||||
|
fragenFalsch: value.questionsIncorrect || 0
|
||||||
|
}
|
||||||
|
spiele[key] = obj
|
||||||
|
}
|
||||||
|
|
||||||
const spieleBegonnen = []
|
const spieleBegonnen = []
|
||||||
user.gamesStarted.forEach(e => {
|
user.gamesStarted.forEach(e => {
|
||||||
spieleBegonnen.push({ kurs: e.course, spiel: e.game })
|
spieleBegonnen.push({ kurs: e.course, spiel: e.game })
|
||||||
@@ -23,11 +51,12 @@ export const UserConverter = {
|
|||||||
return {
|
return {
|
||||||
anzeigename: user.displayName,
|
anzeigename: user.displayName,
|
||||||
kurse: user.courses,
|
kurse: user.courses,
|
||||||
|
spiele,
|
||||||
spieleBegonnen
|
spieleBegonnen
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
fromFirestore: (snapshot, options) => {
|
fromFirestore: (snapshot, options) => {
|
||||||
const data = snapshot.data(options)
|
const data = snapshot.data(options)
|
||||||
return new User(data.anzeigename, data.kurse, data.spieleBegonnen)
|
return new User(data.anzeigename, data.kurse, data.spiele, data.spieleBegonnen)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -37,6 +37,9 @@ export const mutations = {
|
|||||||
setCourses (state, courses) {
|
setCourses (state, courses) {
|
||||||
state.courses = courses
|
state.courses = courses
|
||||||
},
|
},
|
||||||
|
initCourse (state, courseID) {
|
||||||
|
state.user.setGames({ [courseID]: {} })
|
||||||
|
},
|
||||||
addFavoriteCourse (state, courseID) {
|
addFavoriteCourse (state, courseID) {
|
||||||
state.user.courses.push(courseID)
|
state.user.courses.push(courseID)
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user