You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

266 lines
9.6 KiB

package allin.data.postgres
import allin.data.BetDataSource
import allin.data.postgres.entities.*
import allin.model.*
import org.ktorm.database.Database
import org.ktorm.dsl.*
import org.ktorm.entity.*
import java.time.ZoneId
import java.time.ZonedDateTime
import java.time.temporal.ChronoUnit
import kotlin.math.roundToInt
class PostgresBetDataSource(private val database: Database) : BetDataSource {
override fun getAllBets(filters: List<BetFilter>): List<Bet> {
return when {
filters.isEmpty() -> database.bets.filter { it.isPrivate eq false }.map { it.toBet(database) }
filters.size == 1 -> {
val filter = filters.first()
when (filter) {
BetFilter.PUBLIC -> database.bets.filter { !it.isPrivate }
BetFilter.INVITATION -> database.bets.filter { it.isPrivate }
BetFilter.FINISHED -> database.bets.filter { it.status eq BetStatus.FINISHED }
BetFilter.IN_PROGRESS -> database.bets.filter {
it.status inList listOf(BetStatus.IN_PROGRESS, BetStatus.WAITING, BetStatus.CLOSING)
}
}.map { it.toBet(database) }
}
else -> {
database.bets.filter { bet ->
val public = (BetFilter.PUBLIC in filters) and !bet.isPrivate
val invitation = (BetFilter.INVITATION in filters) and bet.isPrivate
val finished =
(BetFilter.FINISHED in filters) and ((bet.status eq BetStatus.FINISHED) or (bet.status eq BetStatus.CANCELLED))
val inProgress = (BetFilter.IN_PROGRESS in filters) and (bet.status inList listOf(
BetStatus.IN_PROGRESS,
BetStatus.WAITING,
BetStatus.CLOSING
))
(public or invitation) and (finished or inProgress)
}.map { it.toBet(database) }
}
}
}
override fun getBetById(id: String): Bet? =
database.bets.find { it.id eq id }?.toBet(database)
override fun getBetDetailById(id: String, username: String): BetDetail? =
database.bets.find { it.id eq id }?.toBetDetail(database, username)
override fun getBetsNotFinished(): List<Bet> {
val currentTime = ZonedDateTime.now(ZoneId.of("+02:00"))
return database.bets
.filter { it.endBet greaterEq currentTime.toInstant() }
.map { it.toBet(database) }
}
override fun getToConfirm(username: String): List<BetDetail> {
return database.bets
.filter {
(it.createdBy eq username) and (BetsEntity.status eq BetStatus.CLOSING)
}
.map { it.toBetDetail(database, username) }
}
override fun confirmBet(betId: String, result: String) {
database.bets.find { it.id eq betId }?.let { bet ->
bet.status = BetStatus.FINISHED
bet.flushChanges()
database.betResults.add(
BetResultEntity {
this.bet = bet
this.result = result
}
)
}
val resultAnswerInfo = database.betAnswerInfos
.find { (it.betId eq betId) and (it.response eq result) }
?.toBetAnswerInfo()
database.participations.filter {
(ParticipationsEntity.betId eq betId) and
(ParticipationsEntity.answer eq result)
}.forEach { participation ->
database.betResultNotifications.add(
BetResultNotificationEntity {
this.betId = betId
this.username = participation.username
}
)
val amount = (participation.stake * (resultAnswerInfo?.odds ?: 1f)).roundToInt()
database.update(UsersEntity) { usr ->
set(usr.nbCoins, usr.nbCoins + amount)
where { usr.username eq participation.username }
}
}
}
override fun getWonNotifications(username: String): List<BetResultDetail> {
return database.betResultNotifications
.filter { it.username eq username }
.flatMap { notif ->
notif.delete()
database.participations
.filter {
(it.username eq username) and
(it.betId eq notif.betId)
}
.mapNotNull { participation ->
database.betResults
.find { it.betId eq participation.bet.id }
?.toBetResultDetail(
database,
participation
)
}
}
}
override fun getHistory(username: String): List<BetResultDetail> {
return database.participations
.filter { it.username eq username }
.mapNotNull { participation ->
database.betResults
.find { it.betId eq participation.bet.id }
?.toBetResultDetail(
database,
participation
)
}
}
override fun getCurrent(username: String): List<BetDetail> {
return database.participations
.filter { it.username eq username }
.mapNotNull {
if (it.bet.status !in listOf(BetStatus.FINISHED, BetStatus.CANCELLED)) {
it.bet.toBetDetail(
database = database,
username = username
)
} else null
}
}
override fun getMostPopularBet(): Bet? {
return database.bets
.filter { (it.isPrivate eq false) and (it.status eq BetStatus.IN_PROGRESS) }
.sortedByDescending { it.popularityscore }
.firstOrNull()
?.toBet(database)
}
override fun updatePopularityScore(betId: String) {
database.bets.filter { it.id eq betId }.firstOrNull() ?: return
val participations = database.participations.filter { it.betId eq betId }
val score = (participations.count() * participations.count()) + participations.map { it.stake }.sum()
database.update(BetsEntity) {
set(it.popularityscore, score)
where { it.id eq betId }
}
}
override fun addBet(bet: Bet) {
database.bets.add(
BetEntity {
this.id = bet.id
this.endBet = bet.endBet.toInstant()
this.endRegistration = bet.endRegistration.toInstant()
this.zoneId = bet.endBet.zone.id
this.sentenceBet = bet.sentenceBet
this.theme = bet.theme
this.isPrivate = bet.isPrivate
this.createdBy = bet.createdBy
this.status = bet.status
this.type = bet.type
}
)
database.betInfos.add(
BetInfoEntity {
this.id = bet.id
this.totalStakes = 0
this.totalParticipants = 0
}
)
if (bet.type == BetType.CUSTOM) {
bet.response.forEach { selected ->
database.responses.add(
ResponseEntity {
this.betId = bet.id
this.response = selected
}
)
}
}
}
override fun removeBet(id: String): Boolean {
database.betInfos.removeIf { it.id eq id }
database.betAnswerInfos.removeIf { it.betId eq id }
return database.bets.removeIf { it.id eq id } > 0
}
override fun updateBet(data: UpdatedBetData): Boolean {
return database.update(BetsEntity) {
set(BetsEntity.isPrivate, data.isPrivate)
where { BetsEntity.id eq data.id }
} > 0
}
override fun updateBetStatuses(date: ZonedDateTime) {
database.bets
.filter {
(date.toInstant() greaterEq BetsEntity.endRegistration) and
(BetsEntity.status notEq BetStatus.FINISHED) and
(BetsEntity.status notEq BetStatus.CANCELLED)
}.let {
it.filter { date.toInstant() less BetsEntity.endBet }.forEach { bet ->
bet.status = BetStatus.WAITING
bet.flushChanges()
}
it.filter { date.toInstant() greaterEq BetsEntity.endBet }.forEach { bet ->
if (date.toInstant() >= bet.endBet.plus(7, ChronoUnit.DAYS)) {
database.participations
.filter { it.betId eq bet.id }
.forEach { participation ->
database.users.find { it.username eq participation.username }?.let { user ->
user.nbCoins += participation.stake
user.flushChanges()
}
}
bet.status = BetStatus.CANCELLED
bet.flushChanges()
} else {
bet.status = BetStatus.CLOSING
bet.flushChanges()
}
}
}
}
override fun addPrivateBet(bet: Bet) {
addBet(bet)
bet.userInvited?.forEach{
database.privatebets.add(PrivateBetEntity{
betId=bet.id
userId=it
})
}
}
}