diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c68758..ddbaa13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # PlaguPoster +## 0.5.1 + +* Add opportunity to set unique + ## 0.5.0 * Dependencies update diff --git a/gradle.properties b/gradle.properties index 366c342..21058bf 100644 --- a/gradle.properties +++ b/gradle.properties @@ -10,4 +10,4 @@ android.enableJetifier=true # Project data group=dev.inmo -version=0.5.0 +version=0.5.1 diff --git a/ratings/selector/src/commonMain/kotlin/DefaultSelector.kt b/ratings/selector/src/commonMain/kotlin/DefaultSelector.kt index a1195df..7838edb 100644 --- a/ratings/selector/src/commonMain/kotlin/DefaultSelector.kt +++ b/ratings/selector/src/commonMain/kotlin/DefaultSelector.kt @@ -1,5 +1,6 @@ package dev.inmo.plaguposter.ratings.selector +import dev.inmo.micro_utils.repos.KeyValueRepo import korlibs.time.DateTime import dev.inmo.plaguposter.posts.models.PostId import dev.inmo.plaguposter.posts.repo.PostsRepo @@ -9,13 +10,14 @@ import dev.inmo.plaguposter.ratings.selector.models.SelectorConfig class DefaultSelector ( private val config: SelectorConfig, private val ratingsRepo: RatingsRepo, - private val postsRepo: PostsRepo + private val postsRepo: PostsRepo, + private val latestChosenRepo: KeyValueRepo ) : Selector { override suspend fun take(n: Int, now: DateTime, exclude: List): List { val result = mutableListOf() do { - val selected = config.active(now.time) ?.rating ?.select(ratingsRepo, postsRepo, result + exclude, now) ?: break + val selected = config.active(now.time) ?.rating ?.select(ratingsRepo, postsRepo, result + exclude, now, latestChosenRepo) ?: break result.add(selected) } while (result.size < n) diff --git a/ratings/selector/src/commonMain/kotlin/models/RatingConfig.kt b/ratings/selector/src/commonMain/kotlin/models/RatingConfig.kt index 4c8046d..2b10031 100644 --- a/ratings/selector/src/commonMain/kotlin/models/RatingConfig.kt +++ b/ratings/selector/src/commonMain/kotlin/models/RatingConfig.kt @@ -2,11 +2,9 @@ package dev.inmo.plaguposter.ratings.selector.models import korlibs.time.DateTime import korlibs.time.seconds -import dev.inmo.micro_utils.pagination.FirstPagePagination -import dev.inmo.micro_utils.pagination.Pagination import dev.inmo.micro_utils.pagination.utils.getAllByWithNextPaging -import dev.inmo.micro_utils.repos.pagination.getAll -import dev.inmo.plaguposter.common.DateTimeSerializer +import dev.inmo.micro_utils.repos.KeyValueRepo +import dev.inmo.micro_utils.repos.unset import dev.inmo.plaguposter.posts.models.PostId import dev.inmo.plaguposter.posts.repo.PostsRepo import dev.inmo.plaguposter.ratings.models.Rating @@ -25,17 +23,23 @@ data class RatingConfig( val max: Rating? = null, val prefer: Prefer = Prefer.Random, val otherwise: RatingConfig? = null, - val postAge: Seconds? = null + val postAge: Seconds? = null, + val uniqueCount: Int? = null ) { suspend fun select( ratingsRepo: RatingsRepo, postsRepo: PostsRepo, exclude: List, - now: DateTime + now: DateTime, + latestChosenRepo: KeyValueRepo ): PostId? { var reversed: Boolean = false var count: Int? = null val allowedCreationTime = now - (postAge ?: 0).seconds + val excludedByRepo = uniqueCount ?.let { + latestChosenRepo.getAll().toList().sortedBy { it.second }.takeLast(uniqueCount).map { it.first } + } ?: emptyList() + val resultExcluded = exclude + excludedByRepo when (prefer) { Prefer.Max -> { @@ -59,40 +63,53 @@ data class RatingConfig( ratingsRepo.getAllByWithNextPaging { keys(it) } } else -> { - ratingsRepo.getPostsWithRatingLessEq(max, exclude = exclude).keys + ratingsRepo.getPostsWithRatingLessEq(max, exclude = resultExcluded).keys } } } else -> { when (max) { null -> { - ratingsRepo.getPostsWithRatingGreaterEq(min, exclude = exclude).keys + ratingsRepo.getPostsWithRatingGreaterEq(min, exclude = resultExcluded).keys } else -> { - ratingsRepo.getPosts(min .. max, reversed, count, exclude = exclude).keys + ratingsRepo.getPosts(min .. max, reversed, count, exclude = resultExcluded).keys } } } }.filter { - it !in exclude && (postsRepo.getPostCreationTime(it) ?.let { it < allowedCreationTime } ?: true) + it !in resultExcluded && (postsRepo.getPostCreationTime(it) ?.let { it < allowedCreationTime } ?: true) } - return when (prefer) { + val resultPosts: PostId = when (prefer) { Prefer.Max, Prefer.Min -> posts.firstOrNull() Prefer.Random -> posts.randomOrNull() - } ?: otherwise ?.select(ratingsRepo, postsRepo, exclude, now) + } ?: otherwise ?.select(ratingsRepo, postsRepo, resultExcluded, now, latestChosenRepo) ?: return null + + val postsToKeep = uniqueCount ?.let { + (excludedByRepo + resultPosts).takeLast(it) + } ?: return resultPosts + + val postsToRemoveFromKeep = excludedByRepo.filter { it !in postsToKeep } + latestChosenRepo.unset(postsToRemoveFromKeep) + val postsToAdd = postsToKeep.filter { it !in excludedByRepo } + latestChosenRepo.set( + postsToAdd.associateWith { DateTime.now() } + ) + + return resultPosts } @Serializable(Prefer.Serializer::class) sealed interface Prefer { val type: String @Serializable(Serializer::class) - object Max : Prefer { override val type: String = "max" } + data object Max : Prefer { override val type: String = "max" } @Serializable(Serializer::class) - object Min : Prefer { override val type: String = "min" } + data object Min : Prefer { override val type: String = "min" } @Serializable(Serializer::class) - object Random : Prefer { override val type: String = "random" } + data object Random : Prefer { override val type: String = "random" } object Serializer : KSerializer { override val descriptor: SerialDescriptor = String.serializer().descriptor diff --git a/ratings/selector/src/jvmMain/kotlin/Plugin.kt b/ratings/selector/src/jvmMain/kotlin/Plugin.kt index da75edb..f405210 100644 --- a/ratings/selector/src/jvmMain/kotlin/Plugin.kt +++ b/ratings/selector/src/jvmMain/kotlin/Plugin.kt @@ -1,14 +1,33 @@ package dev.inmo.plaguposter.ratings.selector +import dev.inmo.micro_utils.repos.KeyValueRepo +import dev.inmo.micro_utils.repos.exposed.keyvalue.ExposedKeyValueRepo +import dev.inmo.micro_utils.repos.mappers.withMapper import dev.inmo.plagubot.Plugin +import dev.inmo.plaguposter.posts.models.PostId import dev.inmo.plaguposter.ratings.selector.models.SelectorConfig +import korlibs.time.DateTime import kotlinx.serialization.json.* import org.jetbrains.exposed.sql.Database import org.koin.core.module.Module +import org.koin.core.qualifier.qualifier object Plugin : Plugin { override fun Module.setupDI(database: Database, params: JsonObject) { single { get().decodeFromJsonElement(SelectorConfig.serializer(), params["selector"] ?: return@single null) } - single { DefaultSelector(get(), get(), get()) } + single>(qualifier("latestChosenRepo")) { + ExposedKeyValueRepo( + get(), + { text("post_id") }, + { double("date_time") }, + "LatestChosenRepo" + ).withMapper( + { string }, + { unixMillis }, + { PostId(this) }, + { DateTime(this) } + ) + } + single { DefaultSelector(get(), get(), get(), get(qualifier("latestChosenRepo"))) } } } diff --git a/sample/config.json b/sample/config.json index 18f7014..294fabc 100644 --- a/sample/config.json +++ b/sample/config.json @@ -51,7 +51,8 @@ "to": "23:59" }, "rating": { - "prefer": "max" + "prefer": "max", + "uniqueCount": 1 } }, { @@ -60,7 +61,8 @@ "to": "00:00" }, "rating": { - "prefer": "max" + "prefer": "max", + "uniqueCount": 1 } } ]