All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.insanusmokrassar.AutoPostTelegramBot.plugins.rating.database.PostsLikesTable.kt Maven / Gradle / Ivy

package com.github.insanusmokrassar.AutoPostTelegramBot.plugins.rating.database

import com.github.insanusmokrassar.AutoPostTelegramBot.base.database.tables.PostsTable
import com.github.insanusmokrassar.AutoPostTelegramBot.base.plugins.commonLogger
import com.github.insanusmokrassar.AutoPostTelegramBot.utils.extensions.subscribe
import kotlinx.coroutines.experimental.channels.BroadcastChannel
import kotlinx.coroutines.experimental.launch
import org.h2.jdbc.JdbcSQLException
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
import org.jetbrains.exposed.sql.transactions.transaction

typealias PostIdRatingPair = Pair
typealias PostIdUserId = Pair

private const val countOfSubscriptions = 256

private const val resultColumnName = "result"

class PostsLikesTable : Table() {
    val likesChannel = BroadcastChannel(countOfSubscriptions)
    val dislikesChannel = BroadcastChannel(countOfSubscriptions)
    val ratingsChannel = BroadcastChannel(countOfSubscriptions)

    private val userId = long("userId").primaryKey()
    private val postId = integer("postId").primaryKey()
    private val like = bool("like").default(false)

    internal lateinit var postsLikesMessagesTable: PostsLikesMessagesTable

    init {
        PostsTable.postRemovedChannel.subscribe(
            {
                commonLogger.throwing(
                    "PostsLikesTable",
                    "Clear likes",
                    it
                )
                true
            }
        ) {
            clearPostMarks(it)
        }
    }

    fun userLikePost(userId: Long, postId: Int) {
        userLike(userId, postId, true)

        launch {
            likesChannel.send(postId to userId)
        }
    }

    fun userDislikePost(userId: Long, postId: Int) {
        userLike(userId, postId, false)

        launch {
            dislikesChannel.send(postId to userId)
        }
    }

    fun postLikes(postId: Int): Int = postLikeCount(postId, true)

    fun postDislikes(postId: Int): Int = postLikeCount(postId, false)

    fun getPostRating(postId: Int): Int {
        return transaction {
            try {
                exec("SELECT (likes-dislikes) as $resultColumnName FROM " +
                    "(SELECT count(*) as likes FROM ${nameInDatabaseCase()} WHERE ${[email protected]}=$postId AND \"${like.name}\"=${like.columnType.valueToString(true)}), " +
                    "(SELECT count(*) as dislikes FROM ${nameInDatabaseCase()} WHERE ${[email protected]}=$postId AND \"${like.name}\"=${like.columnType.valueToString(false)});") {
                    if (it.first()) {
                        it.getInt(it.findColumn(resultColumnName))
                    } else {
                        0
                    }
                } ?: 0
            } catch (e: JdbcSQLException) {
                select {
                    createChooser(postId, like = true)
                }.count() - select {
                    createChooser(postId, like = false)
                }.count()
            }
        }
    }

    fun getMostRated(): List {
        return transaction {
            postsLikesMessagesTable.getEnabledPostsIdAndRatings().let {
                var maxRating = Int.MIN_VALUE
                ArrayList().apply {
                    it.forEach {
                        val currentRating = it.second
                        if (currentRating > maxRating) {
                            maxRating = currentRating
                            clear()
                        }
                        if (currentRating == maxRating) {
                            add(it.first)
                        }
                    }
                }
            }
        }
    }

    /**
     * @param min Included. If null - always true
     * @param max Included. If null - always true
     *
     * @return Pairs with postId to Rate
     */
    fun getRateRange(min: Int?, max: Int?): List {
        return postsLikesMessagesTable.getEnabledPostsIdAndRatings().sortedByDescending {
            it.second
        }.filter {
            pair ->
            min ?.let { it <= pair.second } != false && max ?.let { pair.second <= it } != false
        }
    }

    private fun postLikeCount(postId: Int, like: Boolean): Int = transaction {
        select {
            [email protected](postId).and([email protected](like))
        }.count()
    }

    private fun createChooser(postId: Int, userId: Long? = null, like: Boolean? = null): Op {
        return [email protected](postId).let {
            userId ?.let {
                userId ->
                it.and([email protected](userId))
            } ?: it
        }.let {
            like ?. let {
                like ->
                it.and([email protected](like))
            } ?: it
        }
    }

    private fun userLike(userId: Long, postId: Int, like: Boolean) {
        val chooser = createChooser(postId, userId)
        transaction {
            val record = select {
                chooser
            }.firstOrNull()
            record ?.let {
                if (it[[email protected]] == like) {
                    deleteWhere { chooser }
                } else {
                    update(
                        {
                            chooser
                        }
                    ) {
                        it[[email protected]] = like
                    }
                }
            } ?:let {
                addUser(userId, postId, like)
            }
            launch {
                ratingsChannel.send(
                    PostIdRatingPair(postId, getPostRating(postId))
                )
            }
        }
    }

    private fun addUser(userId: Long, postId: Int, like: Boolean) {
        transaction {
            insert {
                it[[email protected]] = postId
                it[[email protected]] = userId
                it[[email protected]] = like
            }
        }
    }

    internal fun clearPostMarks(postId: Int) {
        transaction {
            deleteWhere { [email protected](postId) }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy