add stats

This commit is contained in:
Mylloon 2024-01-04 20:07:10 +01:00
parent b347bf01f1
commit 6a7080766c
Signed by: Anri
GPG key ID: A82D63DFF8D1317F
8 changed files with 188 additions and 13 deletions

View file

@ -2,27 +2,27 @@
## A faire ?
- Jeu de question
- [x] Jeu de question
- [x] Ajouter / Importer
- [x] Fichier local
- [x] Lien HTTP
- [x] Supprimer
- Modifier
- [x] Modifier
- [x] Créer des questions
- [x] Supprimer des questions
- [x] Choisir / Sélection
- [x] Commencer un jeu
- [ ] Reprendre la progression d'un jeu
- [ ] Afficher les statistiques de tous les jeux
- Dans un jeu
- [x] Afficher les statistiques de tous les jeux
- [x] Dans un jeu
- [x] Consulter la réponse à une question trop difficile
- [ ] Choisir le statut d'une question
- [x] Modifier la question
- [x] Supprimer la question
- [ ] Afficher les statistiques du jeu de question en cours
- [ ] Notification
- une fois par jour
- [ ] Paramètres
- [ ] une fois par jour
- [x] Paramètres
- [ ] Temps de réponse aux questions
- [ ] Thème
- [ ] Taille police

View file

@ -72,6 +72,8 @@ dependencies {
implementation("androidx.navigation:navigation-compose:$navVersion")
implementation("androidx.compose.material:material:1.3.1")
implementation("androidx.datastore:datastore-preferences:1.0.0")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")

View file

@ -0,0 +1,25 @@
package fr.uparis.diamantkennel.memorisationapplication
/** DataStore nom */
const val STATS = "stats"
/** DataStore clef lié au stockage temporaire
* du nombre de bonne réponse pour la session en cours */
// const val STATS_GOOD_ANSWER = "current_good"
/** DataStore clef lié au stockage temporaire
* du nombre de mauvaise réponse pour la session en cours */
// const val STATS_BAD_ANSWER = "current_bad"
/** DataStore clef lié au nombre de parties lancés */
const val STATS_TOTAL_TRIED = "total"
/** DataStore clef lié au nombre de partie gagnés (terminés) */
const val STATS_TOTAL_DONE = "total_done"
/** DataStore clef lié au nombre de questions bien répondu */
const val STATS_TOTAL_GOOD = "total_good"
/** DataStore clef lié au nombre de questions mal répondu */
const val STATS_TOTAL_BAD = "total_bad"

View file

@ -1,5 +1,6 @@
package fr.uparis.diamantkennel.memorisationapplication
import android.content.Context
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
@ -25,6 +26,9 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.Preview
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.preferencesDataStore
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
@ -32,6 +36,8 @@ import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import fr.uparis.diamantkennel.memorisationapplication.ui.theme.MemorisationApplicationTheme
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = STATS)
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

View file

@ -4,19 +4,25 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Divider
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.lifecycle.viewmodel.compose.viewModel
import fr.uparis.diamantkennel.memorisationapplication.ui.SettingsViewModel
@ -25,23 +31,44 @@ fun SettingsScreen(padding: PaddingValues, model: SettingsViewModel = viewModel(
val context = LocalContext.current
var deletionDBRequest by model.deletionDB
var cleanStatRequest by model.deletionStat
if (deletionDBRequest) {
DeletionDBDialog(model::deleteDb) { deletionDBRequest = false }
}
if (cleanStatRequest) {
CleanStatDialog(model::cleanStats) { cleanStatRequest = false }
}
Column(
modifier = Modifier.padding(padding),
horizontalAlignment = Alignment.CenterHorizontally
) {
Stats(model)
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) {
Spacer(modifier = Modifier.padding(top = 10.dp))
Divider(color = Color.Gray)
Spacer(modifier = Modifier.padding(top = 10.dp))
Text(text = "Gestion", fontSize = 30.sp)
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceEvenly
) {
Button(
onClick = { deletionDBRequest = true },
colors = ButtonDefaults.buttonColors(containerColor = colorResource(id = R.color.red))
) {
Text(text = context.getString(R.string.main_button_deletebase))
}
Button(
onClick = { cleanStatRequest = true },
colors = ButtonDefaults.buttonColors(containerColor = colorResource(id = R.color.red))
) {
Text(text = context.getString(R.string.clean_stat_button))
}
}
}
}
@ -55,3 +82,41 @@ fun DeletionDBDialog(confirm: () -> Unit, dismiss: () -> Unit) =
Button(onClick = confirm) { Text(text = LocalContext.current.getString(R.string.yes)) }
},
dismissButton = { Button(onClick = dismiss) { Text(text = LocalContext.current.getString(R.string.no)) } })
@Composable
fun Stats(model: SettingsViewModel) {
val context = LocalContext.current
val games by model.statTotal.collectAsState(0)
val gamesDone by model.statTotalDone.collectAsState(0)
val goodAnswers by model.statTotalGood.collectAsState(0)
val badAnswers by model.statTotalBad.collectAsState(0)
Text(text = context.getString(R.string.stats), fontSize = 30.sp)
Column {
Text(text = "${context.getString(R.string.stats_all_games)} : $games")
Text(text = "${context.getString(R.string.stats_games_done)} : $gamesDone")
Text(text = "${context.getString(R.string.stats_good_answer)} : $goodAnswers")
Text(text = "${context.getString(R.string.stats_bad_answer)} : $badAnswers")
Text(
text = "${context.getString(R.string.stats_winrate)} : ${
model.winrate(
goodAnswers,
badAnswers
)
}%"
)
}
}
@Composable
fun CleanStatDialog(confirm: () -> Unit, dismiss: () -> Unit) =
AlertDialog(onDismissRequest = dismiss,
title = { Text(text = LocalContext.current.getString(R.string.clean_stat)) },
text = { Text(text = LocalContext.current.getString(R.string.clean_stat_desc)) },
confirmButton = {
Button(onClick = confirm) { Text(text = LocalContext.current.getString(R.string.yes)) }
},
dismissButton = { Button(onClick = dismiss) { Text(text = LocalContext.current.getString(R.string.no)) } })

View file

@ -2,10 +2,17 @@ package fr.uparis.diamantkennel.memorisationapplication.ui
import android.app.Application
import androidx.compose.runtime.mutableStateOf
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.intPreferencesKey
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import fr.uparis.diamantkennel.memorisationapplication.MemoApplication
import fr.uparis.diamantkennel.memorisationapplication.STATS_TOTAL_BAD
import fr.uparis.diamantkennel.memorisationapplication.STATS_TOTAL_DONE
import fr.uparis.diamantkennel.memorisationapplication.STATS_TOTAL_GOOD
import fr.uparis.diamantkennel.memorisationapplication.STATS_TOTAL_TRIED
import fr.uparis.diamantkennel.memorisationapplication.data.Question
import fr.uparis.diamantkennel.memorisationapplication.dataStore
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@ -13,6 +20,12 @@ class PlayViewModel(application: Application) : AndroidViewModel(application) {
private val dao = (application as MemoApplication).database.memoDao()
private var questions = mutableStateOf<List<Question>>(listOf())
private val stats = application.dataStore
private val statsKeyTotal = intPreferencesKey(STATS_TOTAL_TRIED)
private val statsKeyTotalDone = intPreferencesKey(STATS_TOTAL_DONE)
private val statsKeyTotalGood = intPreferencesKey(STATS_TOTAL_GOOD)
private val statsKeyTotalBad = intPreferencesKey(STATS_TOTAL_BAD)
var currentQuestion = mutableStateOf<Question?>(null)
private var index = mutableStateOf(0)
var proposedAnswer = mutableStateOf("")
@ -28,6 +41,9 @@ class PlayViewModel(application: Application) : AndroidViewModel(application) {
viewModelScope.launch(Dispatchers.Main) {
dao.loadQuestions(setId).collect { questionList ->
questions.value = questionList.shuffled()
if (questions.value.isNotEmpty()) {
stats.edit { it[statsKeyTotal] = (it[statsKeyTotal] ?: 0) + 1 }
}
updateQuestion()
}
}
@ -41,6 +57,11 @@ class PlayViewModel(application: Application) : AndroidViewModel(application) {
if (index.value >= questions.value.size) {
/* Fin des questions */
end.value = true
viewModelScope.launch {
stats.edit {
it[statsKeyTotalDone] = (it[statsKeyTotalDone] ?: 0) + 1
}
}
} else {
currentQuestion.value = questions.value[index.value]
}
@ -81,7 +102,7 @@ class PlayViewModel(application: Application) : AndroidViewModel(application) {
fun checkAnswer() {
val probaReponse = calcSimilarite(currentQuestion.value!!.reponse, proposedAnswer.value)
if (probaReponse >= .60f) {
if (probaReponse >= .70f) {
evaluatedAnswer.value = AnswerType.GOOD
} else {
evaluatedAnswer.value = AnswerType.BAD
@ -89,17 +110,23 @@ class PlayViewModel(application: Application) : AndroidViewModel(application) {
}
fun sbUpdate() {
/* TODO: Statistiques à sauvegarder :
* - temps de réponse
* - taux réussite (ratio bonne/mauvaise réponse
*
* Tout ça va être récupérer depuis ici */
/* TODO: Statistiques à sauvegarder : temps de réponse */
when (evaluatedAnswer.value!!) {
AnswerType.GOOD -> {
viewModelScope.launch {
stats.edit {
it[statsKeyTotalGood] = (it[statsKeyTotalGood] ?: 0) + 1
}
}
newQuestion()
}
AnswerType.BAD -> {
viewModelScope.launch {
stats.edit {
it[statsKeyTotalBad] = (it[statsKeyTotalBad] ?: 0) + 1
}
}
reset()
}
}

View file

@ -2,15 +2,36 @@ package fr.uparis.diamantkennel.memorisationapplication.ui
import android.app.Application
import androidx.compose.runtime.mutableStateOf
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.intPreferencesKey
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import fr.uparis.diamantkennel.memorisationapplication.MemoApplication
import fr.uparis.diamantkennel.memorisationapplication.STATS_TOTAL_BAD
import fr.uparis.diamantkennel.memorisationapplication.STATS_TOTAL_DONE
import fr.uparis.diamantkennel.memorisationapplication.STATS_TOTAL_GOOD
import fr.uparis.diamantkennel.memorisationapplication.STATS_TOTAL_TRIED
import fr.uparis.diamantkennel.memorisationapplication.dataStore
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
class SettingsViewModel(application: Application) : AndroidViewModel(application) {
private val dao = (application as MemoApplication).database.memoDao()
private val stats = application.dataStore
private val statsKeyTotal = intPreferencesKey(STATS_TOTAL_TRIED)
private val statsKeyTotalDone = intPreferencesKey(STATS_TOTAL_DONE)
private val statsKeyTotalGood = intPreferencesKey(STATS_TOTAL_GOOD)
private val statsKeyTotalBad = intPreferencesKey(STATS_TOTAL_BAD)
val statTotal = stats.data.map { it[statsKeyTotal] ?: 0 }
val statTotalDone = stats.data.map { it[statsKeyTotalDone] ?: 0 }
val statTotalGood = stats.data.map { it[statsKeyTotalGood] ?: 0 }
val statTotalBad = stats.data.map { it[statsKeyTotalBad] ?: 0 }
val deletionDB = mutableStateOf(false)
val deletionStat = mutableStateOf(false)
fun deleteDb() {
deletionDB.value = false
@ -18,4 +39,24 @@ class SettingsViewModel(application: Application) : AndroidViewModel(application
dao.deleteTable()
}
}
fun cleanStats() {
deletionStat.value = false
viewModelScope.launch {
stats.edit {
it[statsKeyTotal] = 0
it[statsKeyTotalDone] = 0
it[statsKeyTotalGood] = 0
it[statsKeyTotalBad] = 0
}
}
}
fun winrate(good: Int, bad: Int): Int {
val total = good + bad
if (total == 0) {
return 0
}
return ((good.toFloat() / total.toFloat()) * 100).toInt()
}
}

View file

@ -41,4 +41,13 @@
<string name="no">Non</string>
<string name="bravo">Bravo</string>
<string name="set_ended">Le set de questions est terminé !</string>
<string name="stats">Statistiques</string>
<string name="stats_all_games">Set lancés</string>
<string name="stats_games_done">Set terminés</string>
<string name="stats_good_answer">Réponses bonnes</string>
<string name="stats_bad_answer">Réponses mauvaises</string>
<string name="stats_winrate">Taux de victoire</string>
<string name="clean_stat_button">Réinitialiser stats</string>
<string name="clean_stat">Réinitialiser les statistiques</string>
<string name="clean_stat_desc">Voulez-vous réinitialiser les statistiques ?</string>
</resources>