Skip to content

Commit

Permalink
New algorithm (#78)
Browse files Browse the repository at this point in the history
* Simplify choice algorithm

* Removed unused models

* Fixed bugs on multiple choices and failed tests
  • Loading branch information
JackEblan authored Oct 9, 2024
1 parent b697af2 commit bf80a4a
Show file tree
Hide file tree
Showing 19 changed files with 218 additions and 650 deletions.
1 change: 0 additions & 1 deletion .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -19,111 +19,54 @@ package com.eblan.socialworkreviewer.core.cache

import com.eblan.socialworkreviewer.core.common.Dispatcher
import com.eblan.socialworkreviewer.core.common.SwrDispatchers.Default
import com.eblan.socialworkreviewer.core.model.Choice
import com.eblan.socialworkreviewer.core.model.Question
import com.eblan.socialworkreviewer.core.model.QuestionData
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.withContext
import javax.inject.Inject

internal class DefaultInMemoryChoiceDataSource @Inject constructor(
@Dispatcher(Default) private val defaultDispatcher: CoroutineDispatcher,
) : InMemoryChoiceDataSource {
private val _questions = mutableListOf<Question>()
internal class DefaultInMemoryChoiceDataSource @Inject constructor(@Dispatcher(Default) private val defaultDispatcher: CoroutineDispatcher) :
InMemoryChoiceDataSource {
private val _answeredQuestions = mutableMapOf<Question, List<String>>()

private val _selectedChoices = mutableListOf<Choice>()

private val _currentQuestionData = MutableSharedFlow<QuestionData>(
private val _answeredQuestionsFlow = MutableSharedFlow<Map<Question, List<String>>>(
replay = 1,
onBufferOverflow = BufferOverflow.DROP_OLDEST,
)

override val questions get() = _questions.toList()

override val selectedChoices get() = _selectedChoices.toList()

override val currentQuestionData get() = _currentQuestionData.asSharedFlow()

override fun addQuestions(questions: List<Question>) {
_questions.addAll(questions)
}
override val answeredQuestionsFlow get() = _answeredQuestionsFlow.asSharedFlow()

override suspend fun addChoice(choice: Choice) {
_selectedChoices.add(choice)
override fun multipleChoices(question: Question, choice: String) {
_answeredQuestions[question] = _answeredQuestions[question]?.let { selectedChoices ->
if (choice in selectedChoices) {
selectedChoices.minus(choice)
} else {
selectedChoices.plus(choice).distinct()
}
} ?: listOf(choice)

_currentQuestionData.emit(
QuestionData(
question = choice.question,
selectedChoices = getQuestionsWithSelectedChoices().getOrDefault(
key = choice.question,
defaultValue = emptyList(),
),
questionsWithSelectedChoices = getQuestionsWithSelectedChoices(),
),
)
_answeredQuestionsFlow.tryEmit(_answeredQuestions.toMap())
}

override suspend fun deleteChoice(choice: Choice) {
_selectedChoices.remove(choice)
override fun singleChoice(question: Question, choice: String) {
_answeredQuestions[question] = listOf(choice)

_currentQuestionData.emit(
QuestionData(
question = choice.question,
selectedChoices = getQuestionsWithSelectedChoices().getOrDefault(
key = choice.question,
defaultValue = emptyList(),
),
questionsWithSelectedChoices = getQuestionsWithSelectedChoices(),
),
)
_answeredQuestionsFlow.tryEmit(_answeredQuestions.toMap())
}

override suspend fun replaceChoice(oldChoice: Choice, newChoice: Choice) {
val oldChoiceIndex = _selectedChoices.indexOf(oldChoice)

if (oldChoiceIndex != -1) {
_selectedChoices[oldChoiceIndex] = newChoice

_currentQuestionData.emit(
QuestionData(
question = newChoice.question,
selectedChoices = getQuestionsWithSelectedChoices().getOrDefault(
key = newChoice.question,
defaultValue = emptyList(),
),
questionsWithSelectedChoices = getQuestionsWithSelectedChoices(),
),
)
override suspend fun getScore(): Int {
return withContext(defaultDispatcher) {
_answeredQuestions.count {
it.key.correctChoices == it.value
}
}
}

override fun clearCache() {
_questions.clear()
_answeredQuestions.clear()

_selectedChoices.clear()

_currentQuestionData.resetReplayCache()
}

override suspend fun addCurrentQuestion(question: Question) {
_currentQuestionData.emit(
QuestionData(
question = question,
selectedChoices = getQuestionsWithSelectedChoices().getOrDefault(
key = question,
defaultValue = emptyList(),
),
questionsWithSelectedChoices = getQuestionsWithSelectedChoices(),
),
)
}

private suspend fun getQuestionsWithSelectedChoices(): Map<Question, List<String>> {
return withContext(defaultDispatcher) {
_selectedChoices.groupBy({ it.question }, { it.choice })
}
_answeredQuestionsFlow.resetReplayCache()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,27 +17,17 @@
*/
package com.eblan.socialworkreviewer.core.cache

import com.eblan.socialworkreviewer.core.model.Choice
import com.eblan.socialworkreviewer.core.model.Question
import com.eblan.socialworkreviewer.core.model.QuestionData
import kotlinx.coroutines.flow.SharedFlow

interface InMemoryChoiceDataSource {
val questions: List<Question>
val answeredQuestionsFlow: SharedFlow<Map<Question, List<String>>>

val selectedChoices: List<Choice>
fun multipleChoices(question: Question, choice: String)

val currentQuestionData: SharedFlow<QuestionData>
fun singleChoice(question: Question, choice: String)

fun addQuestions(questions: List<Question>)

suspend fun addChoice(choice: Choice)

suspend fun deleteChoice(choice: Choice)

suspend fun replaceChoice(oldChoice: Choice, newChoice: Choice)
suspend fun getScore(): Int

fun clearCache()

suspend fun addCurrentQuestion(question: Question)
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,11 @@
*/
package com.eblan.socialworkreviewer.core.cache

import com.eblan.socialworkreviewer.core.model.Choice
import com.eblan.socialworkreviewer.core.model.Question
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import kotlin.test.assertNotNull
import kotlin.test.assertTrue

class InMemoryChoiceDataSourceTest {
Expand All @@ -38,144 +36,79 @@ class InMemoryChoiceDataSourceTest {
}

@Test
fun addChoice() = runTest {
val choice = Choice(
question = Question(
question = "",
correctChoices = listOf(),
wrongChoices = listOf(),
choices = listOf(""),
),
choice = "",
fun multipleChoices() = runTest {
val question = Question(
question = "",
correctChoices = listOf(),
wrongChoices = listOf(),
choices = listOf(""),
)

inMemoryChoiceDataSource.addChoice(choice = choice)
repeat(2) { index ->
inMemoryChoiceDataSource.multipleChoices(question = question, choice = "$index")
}

val answeredQuestion = inMemoryChoiceDataSource.answeredQuestionsFlow.replayCache.first()

assertTrue {
inMemoryChoiceDataSource.selectedChoices.contains(choice)
answeredQuestion[question]?.size == 2
}

assertNotNull(inMemoryChoiceDataSource.currentQuestionData.replayCache.firstOrNull())
}

@Test
fun deleteChoice() = runTest {
val choice = Choice(
question = Question(
question = "",
correctChoices = listOf(),
wrongChoices = listOf(),
choices = listOf(""),
),
choice = "",
fun singleChoice() = runTest {
val question = Question(
question = "",
correctChoices = listOf(),
wrongChoices = listOf(),
choices = listOf(""),
)

inMemoryChoiceDataSource.addChoice(choice = choice)
inMemoryChoiceDataSource.singleChoice(question = question, choice = "")

inMemoryChoiceDataSource.deleteChoice(choice = choice)
val answeredQuestion = inMemoryChoiceDataSource.answeredQuestionsFlow.replayCache.first()

assertTrue {
inMemoryChoiceDataSource.selectedChoices.contains(choice).not()
answeredQuestion[question]?.size == 1
}

assertNotNull(inMemoryChoiceDataSource.currentQuestionData.replayCache.firstOrNull())
}

@Test
fun replaceChoice() = runTest {
val oldChoice = Choice(
question = Question(
question = "0",
correctChoices = listOf(),
wrongChoices = listOf(),
choices = listOf(""),
),
choice = "",
)

val newChoice = Choice(
question = Question(
question = "1",
correctChoices = listOf(),
wrongChoices = listOf(),
choices = listOf(""),
),
choice = "",
)

inMemoryChoiceDataSource.addChoice(choice = oldChoice)

inMemoryChoiceDataSource.replaceChoice(oldChoice = oldChoice, newChoice = newChoice)

assertTrue {
inMemoryChoiceDataSource.selectedChoices.contains(newChoice)
fun getScore() = runTest {
repeat(10) { index ->
inMemoryChoiceDataSource.singleChoice(
question = Question(
question = "$index",
correctChoices = listOf(""),
wrongChoices = listOf(),
choices = listOf(""),
),
choice = "",
)
}

assertTrue {
inMemoryChoiceDataSource.selectedChoices.contains(oldChoice).not()
inMemoryChoiceDataSource.getScore() == 10
}

assertNotNull(inMemoryChoiceDataSource.currentQuestionData.replayCache.firstOrNull())
}

@Test
fun clearCache() = runTest {
val questions = List(10) { index ->
Question(
question = "",
correctChoices = listOf(),
wrongChoices = listOf(),
choices = listOf(),
)
}

val choice = Choice(
question = Question(
question = "",
repeat(10) { index ->
val question = Question(
question = "$index",
correctChoices = listOf(),
wrongChoices = listOf(),
choices = listOf(""),
),
choice = "",
)

inMemoryChoiceDataSource.addQuestions(questions = questions)

inMemoryChoiceDataSource.addChoice(choice = choice)

inMemoryChoiceDataSource.clearCache()
)

assertTrue {
inMemoryChoiceDataSource.questions.isEmpty()
inMemoryChoiceDataSource.singleChoice(question = question, choice = "$index")
}

assertTrue {
inMemoryChoiceDataSource.selectedChoices.isEmpty()
}
inMemoryChoiceDataSource.clearCache()

assertTrue {
inMemoryChoiceDataSource.currentQuestionData.replayCache.isEmpty()
inMemoryChoiceDataSource.answeredQuestionsFlow.replayCache.isEmpty()
}
}

@Test
fun addQuestion() = runTest {
val question = Question(
question = "",
correctChoices = listOf(),
wrongChoices = listOf(),
choices = listOf(""),
)

val choice = Choice(
question = question,
choice = "",
)

inMemoryChoiceDataSource.addChoice(choice = choice)

inMemoryChoiceDataSource.addCurrentQuestion(question = question)

assertNotNull(inMemoryChoiceDataSource.currentQuestionData.replayCache.firstOrNull())
}
}
Loading

0 comments on commit bf80a4a

Please sign in to comment.