diff --git a/ratings/selector/build.gradle b/ratings/selector/build.gradle new file mode 100644 index 0000000..ea7c956 --- /dev/null +++ b/ratings/selector/build.gradle @@ -0,0 +1,22 @@ +plugins { + id "org.jetbrains.kotlin.multiplatform" + id "org.jetbrains.kotlin.plugin.serialization" + id "com.android.library" +} + +apply from: "$mppProjectWithSerializationPresetPath" + +kotlin { + sourceSets { + commonMain { + dependencies { + api project(":plaguposter.common") + api project(":plaguposter.ratings") + } + } + jvmMain { + dependencies { + } + } + } +} diff --git a/ratings/selector/src/commonMain/kotlin/DefaultSelector.kt b/ratings/selector/src/commonMain/kotlin/DefaultSelector.kt new file mode 100644 index 0000000..482f74d --- /dev/null +++ b/ratings/selector/src/commonMain/kotlin/DefaultSelector.kt @@ -0,0 +1,21 @@ +package dev.inmo.plaguposter.ratings.selector + +import dev.inmo.plaguposter.posts.models.PostId +import dev.inmo.plaguposter.ratings.repo.RatingsRepo +import dev.inmo.plaguposter.ratings.selector.models.SelectorConfig + +class DefaultSelector ( + private val config: SelectorConfig, + private val repo: RatingsRepo +) : Selector { + override suspend fun take(n: Int): List { + val result = mutableListOf() + + do { + val selected = config.active ?.ratings ?.select(repo, result) ?: break + result.add(selected) + } while (result.size < n) + + return result.toList() + } +} diff --git a/ratings/selector/src/commonMain/kotlin/PackageInfo.kt b/ratings/selector/src/commonMain/kotlin/PackageInfo.kt new file mode 100644 index 0000000..15f49c3 --- /dev/null +++ b/ratings/selector/src/commonMain/kotlin/PackageInfo.kt @@ -0,0 +1 @@ +package dev.inmo.plaguposter.ratings.selector diff --git a/ratings/selector/src/commonMain/kotlin/Selector.kt b/ratings/selector/src/commonMain/kotlin/Selector.kt new file mode 100644 index 0000000..c35d18e --- /dev/null +++ b/ratings/selector/src/commonMain/kotlin/Selector.kt @@ -0,0 +1,7 @@ +package dev.inmo.plaguposter.ratings.selector + +import dev.inmo.plaguposter.posts.models.PostId + +interface Selector { + suspend fun take(n: Int = 1): List +} diff --git a/ratings/selector/src/commonMain/kotlin/models/RatingConfig.kt b/ratings/selector/src/commonMain/kotlin/models/RatingConfig.kt new file mode 100644 index 0000000..0d38475 --- /dev/null +++ b/ratings/selector/src/commonMain/kotlin/models/RatingConfig.kt @@ -0,0 +1,104 @@ +package dev.inmo.plaguposter.ratings.selector.models + +import dev.inmo.micro_utils.pagination.FirstPagePagination +import dev.inmo.micro_utils.pagination.Pagination +import dev.inmo.plaguposter.posts.models.PostId +import dev.inmo.plaguposter.ratings.models.Rating +import dev.inmo.plaguposter.ratings.repo.RatingsRepo +import kotlinx.serialization.KSerializer +import kotlinx.serialization.Serializable +import kotlinx.serialization.builtins.serializer +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import kotlin.random.Random + +@Serializable +data class RatingConfig( + val min: Rating?, + val max: Rating?, + val prefer: Prefer +) { + suspend fun select(repo: RatingsRepo, exclude: List): PostId? { + var reversed: Boolean = false + var count: Int? = null + + when (prefer) { + Prefer.Max -> { + reversed = true + count = 1 + } + Prefer.Min -> { + reversed = false + count = 1 + } + Prefer.Random -> { + reversed = false + count = null + } + } + + val posts = when(min) { + null -> { + when (max) { + null -> { + repo.keys( + count ?.let { Pagination(0, it) } ?: FirstPagePagination(repo.count().toInt()), + reversed + ).results.filterNot { + it in exclude + } + } + else -> { + repo.getPostsWithRatingLessEq(max, exclude = exclude).keys + } + } + } + else -> { + when (max) { + null -> { + repo.getPostsWithRatingGreaterEq(min, exclude = exclude).keys + } + else -> { + repo.getPosts(min .. max, reversed, count, exclude = exclude).keys + } + } + } + } + + return when (prefer) { + Prefer.Max, + Prefer.Min -> posts.firstOrNull() + Prefer.Random -> posts.randomOrNull() + } + } + + @Serializable(Prefer.Serializer::class) + sealed interface Prefer { + val type: String + @Serializable(Serializer::class) + object Max : Prefer { override val type: String = "max" } + @Serializable(Serializer::class) + object Min : Prefer { override val type: String = "min" } + @Serializable(Serializer::class) + object Random : Prefer { override val type: String = "random" } + + object Serializer : KSerializer { + override val descriptor: SerialDescriptor = String.serializer().descriptor + + override fun deserialize(decoder: Decoder): Prefer { + val identifier = decoder.decodeString().lowercase() + return values.first { it.type.lowercase() == identifier } + } + + override fun serialize(encoder: Encoder, value: Prefer) { + encoder.encodeString(value.type.lowercase()) + } + + } + + companion object { + val values = arrayOf(Max, Min, Random) + } + } +} diff --git a/ratings/selector/src/commonMain/kotlin/models/SelectorConfig.kt b/ratings/selector/src/commonMain/kotlin/models/SelectorConfig.kt new file mode 100644 index 0000000..c7909c0 --- /dev/null +++ b/ratings/selector/src/commonMain/kotlin/models/SelectorConfig.kt @@ -0,0 +1,11 @@ +package dev.inmo.plaguposter.ratings.selector.models + +import kotlinx.serialization.Serializable + +@Serializable +data class SelectorConfig( + val items: List +) { + val active: SelectorConfigItem? + get() = items.firstOrNull { it.time.isActive } +} diff --git a/ratings/selector/src/commonMain/kotlin/models/SelectorConfigItem.kt b/ratings/selector/src/commonMain/kotlin/models/SelectorConfigItem.kt new file mode 100644 index 0000000..fc95497 --- /dev/null +++ b/ratings/selector/src/commonMain/kotlin/models/SelectorConfigItem.kt @@ -0,0 +1,9 @@ +package dev.inmo.plaguposter.ratings.selector.models + +import kotlinx.serialization.Serializable + +@Serializable +data class SelectorConfigItem( + val time: TimeConfig, + val ratings: RatingConfig +) diff --git a/ratings/selector/src/commonMain/kotlin/models/TimeConfig.kt b/ratings/selector/src/commonMain/kotlin/models/TimeConfig.kt new file mode 100644 index 0000000..6937834 --- /dev/null +++ b/ratings/selector/src/commonMain/kotlin/models/TimeConfig.kt @@ -0,0 +1,36 @@ +package dev.inmo.plaguposter.ratings.selector.models + +import com.soywiz.klock.* +import kotlinx.serialization.* +import kotlinx.serialization.builtins.serializer +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder + +@Serializable +class TimeConfig( + @Serializable(TimeSerializer::class) + val from: Time, + @Serializable(TimeSerializer::class) + val to: Time +) { + @Transient + val range = from .. to + + val isActive: Boolean + get() = DateTime.now().time in range + + + object TimeSerializer : KSerializer