mirror of
https://github.com/InsanusMokrassar/PlaguPoster.git
synced 2024-12-22 14:07:14 +00:00
add selector
This commit is contained in:
parent
cf5a4c0f61
commit
f3f7761bf9
22
ratings/selector/build.gradle
Normal file
22
ratings/selector/build.gradle
Normal file
@ -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 {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
21
ratings/selector/src/commonMain/kotlin/DefaultSelector.kt
Normal file
21
ratings/selector/src/commonMain/kotlin/DefaultSelector.kt
Normal file
@ -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<PostId> {
|
||||
val result = mutableListOf<PostId>()
|
||||
|
||||
do {
|
||||
val selected = config.active ?.ratings ?.select(repo, result) ?: break
|
||||
result.add(selected)
|
||||
} while (result.size < n)
|
||||
|
||||
return result.toList()
|
||||
}
|
||||
}
|
1
ratings/selector/src/commonMain/kotlin/PackageInfo.kt
Normal file
1
ratings/selector/src/commonMain/kotlin/PackageInfo.kt
Normal file
@ -0,0 +1 @@
|
||||
package dev.inmo.plaguposter.ratings.selector
|
7
ratings/selector/src/commonMain/kotlin/Selector.kt
Normal file
7
ratings/selector/src/commonMain/kotlin/Selector.kt
Normal file
@ -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<PostId>
|
||||
}
|
104
ratings/selector/src/commonMain/kotlin/models/RatingConfig.kt
Normal file
104
ratings/selector/src/commonMain/kotlin/models/RatingConfig.kt
Normal file
@ -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>): 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<Prefer> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package dev.inmo.plaguposter.ratings.selector.models
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class SelectorConfig(
|
||||
val items: List<SelectorConfigItem>
|
||||
) {
|
||||
val active: SelectorConfigItem?
|
||||
get() = items.firstOrNull { it.time.isActive }
|
||||
}
|
@ -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
|
||||
)
|
36
ratings/selector/src/commonMain/kotlin/models/TimeConfig.kt
Normal file
36
ratings/selector/src/commonMain/kotlin/models/TimeConfig.kt
Normal file
@ -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<Time> {
|
||||
val format = TimeFormat("HH:mm")
|
||||
override val descriptor: SerialDescriptor = String.serializer().descriptor
|
||||
|
||||
override fun deserialize(decoder: Decoder): Time {
|
||||
return format.parseTime(decoder.decodeString())
|
||||
}
|
||||
|
||||
override fun serialize(encoder: Encoder, value: Time) {
|
||||
encoder.encodeString(format.format(value))
|
||||
}
|
||||
}
|
||||
}
|
14
ratings/selector/src/jvmMain/kotlin/Plugin.kt
Normal file
14
ratings/selector/src/jvmMain/kotlin/Plugin.kt
Normal file
@ -0,0 +1,14 @@
|
||||
package dev.inmo.plaguposter.ratings.selector
|
||||
|
||||
import dev.inmo.plagubot.Plugin
|
||||
import dev.inmo.plaguposter.ratings.selector.models.SelectorConfig
|
||||
import kotlinx.serialization.json.*
|
||||
import org.jetbrains.exposed.sql.Database
|
||||
import org.koin.core.module.Module
|
||||
|
||||
object Plugin : Plugin {
|
||||
override fun Module.setupDI(database: Database, params: JsonObject) {
|
||||
single { get<Json>().decodeFromJsonElement(SelectorConfig.serializer(), params["selector"] ?: return@single null) }
|
||||
single<Selector> { DefaultSelector(get(), get()) }
|
||||
}
|
||||
}
|
1
ratings/selector/src/main/AndroidManifest.xml
Normal file
1
ratings/selector/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1 @@
|
||||
<manifest package="dev.inmo.plaguposter.ratings.selector"/>
|
@ -7,4 +7,6 @@ import kotlin.jvm.JvmInline
|
||||
@JvmInline
|
||||
value class Rating(
|
||||
val double: Double
|
||||
)
|
||||
) : Comparable<Rating> {
|
||||
override fun compareTo(other: Rating): Int = double.compareTo(other.double)
|
||||
}
|
||||
|
@ -4,4 +4,24 @@ import dev.inmo.micro_utils.repos.ReadKeyValueRepo
|
||||
import dev.inmo.plaguposter.posts.models.PostId
|
||||
import dev.inmo.plaguposter.ratings.models.Rating
|
||||
|
||||
interface ReadRatingsRepo : ReadKeyValueRepo<PostId, Rating>
|
||||
interface ReadRatingsRepo : ReadKeyValueRepo<PostId, Rating> {
|
||||
suspend fun getPosts(
|
||||
range: ClosedRange<Rating>,
|
||||
reversed: Boolean = false,
|
||||
count: Int? = null,
|
||||
exclude: List<PostId> = emptyList()
|
||||
): Map<PostId, Rating>
|
||||
|
||||
suspend fun getPostsWithRatingGreaterEq(
|
||||
then: Rating,
|
||||
reversed: Boolean = false,
|
||||
count: Int? = null,
|
||||
exclude: List<PostId> = emptyList()
|
||||
): Map<PostId, Rating>
|
||||
suspend fun getPostsWithRatingLessEq(
|
||||
then: Rating,
|
||||
reversed: Boolean = false,
|
||||
count: Int? = null,
|
||||
exclude: List<PostId> = emptyList()
|
||||
): Map<PostId, Rating>
|
||||
}
|
||||
|
@ -1,24 +1,81 @@
|
||||
package dev.inmo.plaguposter.ratings.exposed
|
||||
|
||||
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.micro_utils.pagination.utils.optionallyReverse
|
||||
import dev.inmo.micro_utils.repos.exposed.keyvalue.AbstractExposedKeyValueRepo
|
||||
import dev.inmo.plaguposter.posts.models.PostId
|
||||
import dev.inmo.plaguposter.ratings.models.Rating
|
||||
import dev.inmo.plaguposter.ratings.repo.RatingsRepo
|
||||
import org.jetbrains.exposed.sql.Column
|
||||
import org.jetbrains.exposed.sql.Database
|
||||
import org.jetbrains.exposed.sql.*
|
||||
import org.jetbrains.exposed.sql.statements.InsertStatement
|
||||
import org.jetbrains.exposed.sql.statements.UpdateStatement
|
||||
import org.jetbrains.exposed.sql.transactions.transaction
|
||||
|
||||
class ExposedRatingsRepo(
|
||||
class ExposedRatingsRepo (
|
||||
database: Database
|
||||
) : RatingsRepo, KeyValueRepo<PostId, Rating> by ExposedKeyValueRepo(
|
||||
) : RatingsRepo, AbstractExposedKeyValueRepo<PostId, Rating>(
|
||||
database,
|
||||
{ text("post_id") },
|
||||
{ double("rating") },
|
||||
"ratings"
|
||||
).withMapper(
|
||||
{ string },
|
||||
{ double },
|
||||
{ PostId(this) },
|
||||
{ Rating(this) }
|
||||
)
|
||||
) {
|
||||
override val keyColumn = text("post_id")
|
||||
val ratingsColumn = double("rating")
|
||||
override val selectById: SqlExpressionBuilder.(PostId) -> Op<Boolean> = { keyColumn.eq(it.string) }
|
||||
override val selectByValue: SqlExpressionBuilder.(Rating) -> Op<Boolean> = { ratingsColumn.eq(it.double) }
|
||||
override val ResultRow.asKey: PostId
|
||||
get() = get(keyColumn).let(::PostId)
|
||||
override val ResultRow.asObject: Rating
|
||||
get() = get(ratingsColumn).let(::Rating)
|
||||
|
||||
override fun update(k: PostId, v: Rating, it: UpdateStatement) {
|
||||
it[ratingsColumn] = v.double
|
||||
}
|
||||
|
||||
override fun insert(k: PostId, v: Rating, it: InsertStatement<Number>) {
|
||||
it[keyColumn] = k.string
|
||||
it[ratingsColumn] = v.double
|
||||
}
|
||||
|
||||
private fun Query.optionallyLimit(limit: Int?) = if (limit == null) {
|
||||
this
|
||||
} else {
|
||||
limit(limit)
|
||||
}
|
||||
|
||||
override suspend fun getPosts(
|
||||
range: ClosedRange<Rating>,
|
||||
reversed: Boolean,
|
||||
count: Int?,
|
||||
exclude: List<PostId>
|
||||
): Map<PostId, Rating> = transaction(database) {
|
||||
select {
|
||||
ratingsColumn.greaterEq(range.start.double).and(
|
||||
ratingsColumn.lessEq(range.endInclusive.double)
|
||||
).and(
|
||||
keyColumn.notInList(exclude.map { it.string })
|
||||
)
|
||||
}.optionallyLimit(count).optionallyReverse(reversed).map {
|
||||
it.asKey to it.asObject
|
||||
}
|
||||
}.toMap()
|
||||
|
||||
override suspend fun getPostsWithRatingGreaterEq(
|
||||
then: Rating,
|
||||
reversed: Boolean,
|
||||
count: Int?,
|
||||
exclude: List<PostId>
|
||||
) = transaction(database) {
|
||||
select { ratingsColumn.greaterEq(then.double).and(keyColumn.notInList(exclude.map { it.string })) }.optionallyLimit(count).optionallyReverse(reversed).map {
|
||||
it.asKey to it.asObject
|
||||
}
|
||||
}.toMap()
|
||||
|
||||
override suspend fun getPostsWithRatingLessEq(
|
||||
then: Rating,
|
||||
reversed: Boolean,
|
||||
count: Int?,
|
||||
exclude: List<PostId>
|
||||
): Map<PostId, Rating> = transaction(database) {
|
||||
select { ratingsColumn.lessEq(then.double).and(keyColumn.notInList(exclude.map { it.string })) }.optionallyLimit(count).optionallyReverse(reversed).map {
|
||||
it.asKey to it.asObject
|
||||
}
|
||||
}.toMap()
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ dependencies {
|
||||
api project(":plaguposter.triggers.command")
|
||||
api project(":plaguposter.ratings")
|
||||
api project(":plaguposter.ratings.source")
|
||||
api project(":plaguposter.ratings.selector")
|
||||
|
||||
api libs.psql
|
||||
}
|
||||
|
@ -24,5 +24,42 @@
|
||||
},
|
||||
"autoAttach": true,
|
||||
"ratingOfferText": "What do you think about it?"
|
||||
},
|
||||
"selector": {
|
||||
"items": [
|
||||
{
|
||||
"time": {
|
||||
"from": "00:00",
|
||||
"to": "00:00"
|
||||
},
|
||||
"rating": {
|
||||
"min": 0.0,
|
||||
"max": 1.0,
|
||||
"prefer": "random"
|
||||
}
|
||||
},
|
||||
{
|
||||
"time": {
|
||||
"from": "00:00",
|
||||
"to": "00:00"
|
||||
},
|
||||
"rating": {
|
||||
"min": 0.0,
|
||||
"max": 1.0,
|
||||
"prefer": "min"
|
||||
}
|
||||
},
|
||||
{
|
||||
"time": {
|
||||
"from": "00:00",
|
||||
"to": "00:00"
|
||||
},
|
||||
"rating": {
|
||||
"min": 0.0,
|
||||
"max": 1.0,
|
||||
"prefer": "max"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ String[] includes = [
|
||||
":posts_registrar",
|
||||
":ratings",
|
||||
":ratings:source",
|
||||
":ratings:selector",
|
||||
":triggers:command",
|
||||
// ":settings",
|
||||
":runner"
|
||||
|
Loading…
Reference in New Issue
Block a user