Merge friend route
continuous-integration/drone/push Build is failing Details

pull/12/head
luevard 11 months ago
commit d200bef2c3

@ -9,12 +9,13 @@ import java.time.ZonedDateTime
interface BetDataSource {
fun getAllBets(): List<Bet>
fun getBetById(id: String): Bet?
fun getBetDetailById(id: String, username: String): BetDetail?
fun getBetsNotFinished(): List<Bet>
fun addBet(bet: Bet)
fun removeBet(id: String): Boolean
fun updateBet(data: UpdatedBetData): Boolean
fun updateBetStatuses(date: ZonedDateTime)
fun getToConfirm(username: String): List<Bet>
fun getToConfirm(username: String): List<BetDetail>
fun confirmBet(betId: String, result: String)
fun getWonNotifications(username: String): List<BetResultDetail>
fun getHistory(username: String): List<BetResultDetail>

@ -4,20 +4,29 @@ import allin.data.BetDataSource
import allin.model.*
import allin.model.BetStatus.*
import java.time.ZonedDateTime
import kotlin.math.roundToInt
class MockBetDataSource(mockData: MockDataSource.MockData) : BetDataSource {
private val bets by lazy { mockData.bets }
private val results by lazy { mockData.results }
private val users by lazy { mockData.users }
private val participations by lazy { mockData.participations }
private val resultNotifications by lazy { mockData.resultNotifications }
class MockBetDataSource(private val mockData: MockDataSource.MockData) : BetDataSource {
private val bets get() = mockData.bets
private val results get() = mockData.results
private val users get() = mockData.users
private val participations get() = mockData.participations
private val resultNotifications get() = mockData.resultNotifications
private val betInfos get() = mockData.betInfos
private val answerInfos get() = mockData.answerInfos
override fun getAllBets(): List<Bet> = bets
override fun getBetById(id: String): Bet? =
bets.find { it.id == id }
override fun removeBet(id: String): Boolean =
bets.removeIf { it.id == id }
override fun getBetDetailById(id: String, username: String): BetDetail? =
bets.find { it.id == id }?.toBetDetail(username)
override fun removeBet(id: String): Boolean {
betInfos.removeIf { it.id == id }
answerInfos.removeIf { it.betId == id }
return bets.removeIf { it.id == id }
}
override fun updateBet(data: UpdatedBetData): Boolean {
return bets.find { it.id == data.id }?.let {
@ -30,6 +39,15 @@ class MockBetDataSource(mockData: MockDataSource.MockData) : BetDataSource {
override fun addBet(bet: Bet) {
bets += bet
betInfos += BetInfo(id = bet.id, totalStakes = 0)
bet.response.forEach {
answerInfos += BetAnswerInfo(
betId = bet.id,
response = it,
totalStakes = 0,
odds = 1f
)
}
}
override fun updateBetStatuses(date: ZonedDateTime) {
@ -46,8 +64,9 @@ class MockBetDataSource(mockData: MockDataSource.MockData) : BetDataSource {
}
}
override fun getToConfirm(username: String): List<Bet> =
override fun getToConfirm(username: String): List<BetDetail> =
bets.filter { it.createdBy == username && it.status == CLOSING }
.map { it.toBetDetail(username) }
override fun confirmBet(betId: String, result: String) {
results.add(
@ -82,11 +101,12 @@ class MockBetDataSource(mockData: MockDataSource.MockData) : BetDataSource {
if (participation.answer == result.result) {
resultNotifications.remove(notification)
val answerInfo = answerInfos.find { it.betId == bet.id && it.response == participation.answer }
BetResultDetail(
betResult = result,
bet = bet,
participation = participation,
amount = participation.stake,
amount = (participation.stake * (answerInfo?.odds ?: 1f)).roundToInt(),
won = true
)
} else null
@ -99,35 +119,49 @@ class MockBetDataSource(mockData: MockDataSource.MockData) : BetDataSource {
val participation = participations.find { it.username == username && it.betId == bet.id }
?: return@map null
val won = participation.answer == result.result
val answerInfo = answerInfos.find {
it.betId == bet.id && it.response == participation.answer
}
BetResultDetail(
betResult = result,
bet = bet,
participation = participation,
amount = participation.stake,
won = participation.answer == result.result
amount = if (won) {
(participation.stake * (answerInfo?.odds ?: 1f)).roundToInt()
} else participation.stake,
won = won
)
}.mapNotNull { it }
}
override fun getCurrent(username: String): List<BetDetail> {
return bets.map { bet ->
return bets.mapNotNull { bet ->
when (bet.status) {
CANCELLED, FINISHED -> return@map null
CANCELLED, FINISHED -> return@mapNotNull null
else -> {
val participation = participations.find { it.username == username && it.betId == bet.id }
?: return@map null
val participations = participations.filter { it.betId == bet.id }
BetDetail(
bet = bet,
answers = getBetAnswerDetail(bet, participations),
participations = participations,
userParticipation = participation
)
val userParticipation = participations.find { it.username == username && it.betId == bet.id }
if (userParticipation == null) return@mapNotNull null
return@mapNotNull bet.toBetDetail(username)
}
}
}.mapNotNull { it }
}
}
private fun Bet.toBetDetail(username: String): BetDetail {
val participation = participations.find { it.username == username && it.betId == this.id }
val participations = participations.filter { it.betId == this.id }
return BetDetail(
bet = this,
answers = getBetAnswerDetail(
bet = this,
participations = participations,
infos = answerInfos.filter { it.betId == this.id }
),
participations = participations,
userParticipation = participation
)
}
}

@ -1,10 +1,10 @@
package allin.data.mock
import allin.data.*
import allin.model.Bet
import allin.model.BetResult
import allin.model.Participation
import allin.model.User
import allin.data.AllInDataSource
import allin.data.BetDataSource
import allin.data.ParticipationDataSource
import allin.data.UserDataSource
import allin.model.*
import java.time.ZonedDateTime
class MockDataSource : AllInDataSource() {
@ -15,6 +15,8 @@ class MockDataSource : AllInDataSource() {
class MockData {
val bets by lazy { mutableListOf<Bet>() }
val betInfos by lazy { mutableListOf<BetInfo>() }
val answerInfos by lazy { mutableListOf<BetAnswerInfo>() }
val results by lazy { mutableListOf<BetResult>() }
val resultNotifications by lazy { mutableListOf<Pair<String, String>>() }
val users by lazy { mutableListOf<User>() }
@ -27,5 +29,4 @@ class MockDataSource : AllInDataSource() {
override val userDataSource: UserDataSource by lazy { MockUserDataSource(mockData) }
override val betDataSource: BetDataSource by lazy { MockBetDataSource(mockData) }
override val participationDataSource: ParticipationDataSource by lazy { MockParticipationDataSource(mockData) }
override val friendDataSource: FriendDataSource by lazy { MockFriendDataSource(mockData) }
}

@ -3,11 +3,41 @@ package allin.data.mock
import allin.data.ParticipationDataSource
import allin.model.Participation
class MockParticipationDataSource(mockData: MockDataSource.MockData) : ParticipationDataSource {
private val participations by lazy { mockData.participations }
class MockParticipationDataSource(private val mockData: MockDataSource.MockData) : ParticipationDataSource {
private val participations get() = mockData.participations
private val betInfos get() = mockData.betInfos
private val answerInfos get() = mockData.answerInfos
override fun addParticipation(participation: Participation) {
participations += participation
var betTotalStakes = 0
betInfos.replaceAll {
if (participation.betId == it.id) {
betTotalStakes = it.totalStakes + participation.stake
it.copy(totalStakes = betTotalStakes)
} else {
it
}
}
answerInfos.replaceAll {
if (participation.betId == it.betId) {
if (participation.answer == it.response) {
val answerTotalStakes = it.totalStakes + participation.stake
val probability = answerTotalStakes / betTotalStakes.toFloat()
it.copy(
totalStakes = answerTotalStakes,
odds = 1 / probability
)
} else {
val probability = it.totalStakes / betTotalStakes.toFloat()
it.copy(odds = 1 / probability)
}
} else {
it
}
}
}
override fun getParticipationFromBetId(betid: String): List<Participation> =
@ -16,6 +46,39 @@ class MockParticipationDataSource(mockData: MockDataSource.MockData) : Participa
override fun getParticipationFromUserId(username: String, betid: String): List<Participation> =
participations.filter { it.betId == betid && it.username == username }
override fun deleteParticipation(id: String): Boolean =
participations.removeIf { it.id == id }
override fun deleteParticipation(id: String): Boolean {
val participation = participations.find { it.id == id }
val result = participations.remove(participation)
var betTotalStakes = 0
betInfos.replaceAll {
if (participation?.betId == it.id) {
betTotalStakes = it.totalStakes - participation.stake
it.copy(totalStakes = betTotalStakes)
} else {
it
}
}
answerInfos.replaceAll {
if (participation?.betId == it.betId) {
if (participation.answer == it.response) {
val answerTotalStakes = it.totalStakes - participation.stake
val probability = answerTotalStakes / betTotalStakes.toFloat()
it.copy(
totalStakes = answerTotalStakes,
odds = 1 / probability
)
} else {
val probability = it.totalStakes / betTotalStakes.toFloat()
it.copy(odds = 1 / probability)
}
} else {
it
}
}
return result
}
}

@ -5,9 +5,9 @@ import allin.dto.UserDTO
import allin.model.User
import java.time.ZonedDateTime
class MockUserDataSource(mockData: MockDataSource.MockData) : UserDataSource {
private val users by lazy { mockData.users }
private val lastGifts by lazy { mockData.lastGifts }
class MockUserDataSource(private val mockData: MockDataSource.MockData) : UserDataSource {
private val users get() = mockData.users
private val lastGifts get() = mockData.lastGifts
override fun getUserByUsername(username: String): Pair<UserDTO?, String?> =
users.find { it.username == username }?.let {

@ -2,193 +2,146 @@ package allin.data.postgres
import allin.data.BetDataSource
import allin.data.postgres.entities.*
import allin.data.postgres.entities.ResponsesEntity.response
import allin.model.*
import org.ktorm.database.Database
import org.ktorm.dsl.*
import org.ktorm.entity.*
import java.time.ZoneId
import java.time.ZonedDateTime
class PostgresBetDataSource(private val database: Database) : BetDataSource {
private fun QueryRowSet.toBet() =
Bet(
id = this[BetsEntity.id].toString(),
theme = this[BetsEntity.theme].toString(),
sentenceBet = this[BetsEntity.sentenceBet].toString(),
endRegistration = this[BetsEntity.endRegistration]!!.atZone(ZoneId.of("Europe/Paris")),
endBet = this[BetsEntity.endBet]!!.atZone(ZoneId.of("Europe/Paris")),
isPrivate = this[BetsEntity.isPrivate] ?: false,
status = this[BetsEntity.status] ?: BetStatus.IN_PROGRESS,
type = this[BetsEntity.type] ?: BetType.CUSTOM,
createdBy = this[BetsEntity.createdBy].toString(),
response = let {
val idBet = this[BetsEntity.id].toString()
val type = this[BetsEntity.type] ?: BetType.CUSTOM
if (type == BetType.CUSTOM) {
database.from(ResponsesEntity)
.select(response)
.where { ResponsesEntity.id eq idBet }
.map { it[response].toString() }
} else {
listOf(YES_VALUE, NO_VALUE)
}
}
)
private fun QueryRowSet.toParticipation() =
Participation(
id = this[ParticipationsEntity.id]?.toString() ?: "",
betId = this[ParticipationsEntity.betId]?.toString() ?: "",
username = this[ParticipationsEntity.username] ?: "",
answer = this[ParticipationsEntity.answer] ?: "",
stake = this[ParticipationsEntity.stake] ?: 0
)
private fun QueryRowSet.toBetResultDetail() =
BetResultDetail(
betResult = BetResult(
betId = this[BetResultsEntity.betId]?.toString() ?: "",
result = this[BetResultsEntity.result] ?: ""
),
bet = this.toBet(),
participation = this.toParticipation(),
amount = this[ParticipationsEntity.stake] ?: 0,
won = this[ParticipationsEntity.answer] == this[BetResultsEntity.result]
)
private fun Query.mapToBet() = this.map { it.toBet() }
private fun Query.mapToBetResultDetail() = this.map { it.toBetResultDetail() }
override fun getAllBets(): List<Bet> =
database.from(BetsEntity).select().mapToBet()
database.bets.map { it.toBet(database) }
override fun getBetById(id: String): Bet? =
database.from(BetsEntity).select().where {
BetsEntity.id eq id
}.mapToBet().firstOrNull()
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("Europe/Paris"))
return database.from(BetsEntity)
.select()
.where { BetsEntity.endBet greaterEq currentTime.toInstant() }
.mapToBet()
return database.bets
.filter { it.endBet greaterEq currentTime.toInstant() }
.map { it.toBet(database) }
}
override fun getToConfirm(username: String): List<Bet> {
return database.from(BetsEntity)
.select()
.where {
(BetsEntity.createdBy eq username) and
(BetsEntity.status eq BetStatus.CLOSING)
}.mapToBet()
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.insert(BetResultsEntity) {
set(it.betId, betId)
set(it.result, result)
}
database.update(BetsEntity) {
where { BetsEntity.id eq betId }
set(BetsEntity.status, BetStatus.FINISHED)
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
}
)
}
database.from(ParticipationsEntity)
.select()
.where {
(ParticipationsEntity.betId eq betId) and
(ParticipationsEntity.answer eq result)
}
.forEach { participation ->
database.insert(BetResultNotificationsEntity) {
set(it.betId, betId)
set(it.username, participation[ParticipationsEntity.username])
database.participations.filter {
(ParticipationsEntity.betId eq betId) and
(ParticipationsEntity.answer eq result)
}.forEach {
database.betResultNotifications.add(
BetResultNotificationEntity {
this.betId = betId
this.username = it.username
}
}
)
}
}
override fun getWonNotifications(username: String): List<BetResultDetail> {
return database.from(BetsEntity)
.innerJoin(ParticipationsEntity, on = BetsEntity.id eq ParticipationsEntity.betId)
.innerJoin(BetResultsEntity, on = BetsEntity.id eq BetResultsEntity.betId)
.innerJoin(BetResultNotificationsEntity, on = BetsEntity.id eq BetResultNotificationsEntity.betId)
.select()
.where {
(BetResultsEntity.result eq ParticipationsEntity.answer) and
(ParticipationsEntity.username eq username)
}.let {
it.forEach { row ->
row[BetsEntity.id]?.let { betId ->
database.delete(BetResultNotificationsEntity) {
(it.betId eq betId) and (it.username eq username)
}
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)
}
}
it
}.mapToBetResultDetail()
.mapNotNull { participation ->
database.betResults
.find { it.betId eq participation.bet.id }
?.toBetResultDetail(
database,
participation
)
}
}
}
override fun getHistory(username: String): List<BetResultDetail> {
return database.from(BetsEntity)
.innerJoin(ParticipationsEntity, on = BetsEntity.id eq ParticipationsEntity.betId)
.innerJoin(BetResultsEntity, on = BetsEntity.id eq BetResultsEntity.betId)
.select()
.where { ParticipationsEntity.username eq username }.mapToBetResultDetail()
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.from(BetsEntity)
.innerJoin(ParticipationsEntity, on = BetsEntity.id eq ParticipationsEntity.betId)
.select()
.where {
(BetsEntity.status notEq BetStatus.FINISHED) and
(BetsEntity.status notEq BetStatus.CANCELLED) and
(ParticipationsEntity.username eq username)
}.map {
val participations = it[BetsEntity.id]?.let { betId ->
database.from(ParticipationsEntity)
.select().where { ParticipationsEntity.betId eq betId }.map { it.toParticipation() }
} ?: emptyList()
val bet = it.toBet()
BetDetail(
bet = bet,
answers = getBetAnswerDetail(bet, participations),
participations = participations,
userParticipation = it.toParticipation()
)
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 addBet(bet: Bet) {
database.insert(BetsEntity) {
set(it.id, bet.id)
set(it.endBet, bet.endBet.toInstant())
set(it.endRegistration, bet.endRegistration.toInstant())
set(it.sentenceBet, bet.sentenceBet)
set(it.theme, bet.theme)
set(it.isPrivate, bet.isPrivate)
set(it.createdBy, bet.createdBy)
set(it.status, bet.status)
set(it.type, bet.type)
}
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
}
)
if (bet.type == BetType.CUSTOM) {
bet.response.forEach { selected ->
database.insert(ResponsesEntity) {
set(it.id, bet.id)
set(it.response, selected)
}
database.responses.add(
ResponseEntity {
this.betId = bet.id
this.response = selected
}
)
}
}
}
override fun removeBet(id: String): Boolean {
return database.delete(BetsEntity) { it.id eq id } > 0
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 {

@ -25,7 +25,7 @@ class PostgresDataSource : AllInDataSource() {
database.execute(
"""
CREATE TABLE IF not exists utilisateur (
CREATE TABLE IF not exists users (
id VARCHAR(255) PRIMARY KEY,
username VARCHAR(255),
password VARCHAR(255),
@ -43,6 +43,7 @@ class PostgresDataSource : AllInDataSource() {
theme VARCHAR(255),
endregistration timestamp,
endbet timestamp,
zoneid varchar(500),
sentencebet varchar(500),
isprivate boolean,
createdby varchar(250),
@ -86,9 +87,30 @@ class PostgresDataSource : AllInDataSource() {
database.execute(
"""
CREATE TABLE IF NOT EXISTS response (
id VARCHAR(255),
betId VARCHAR(255),
response VARCHAR(250),
CONSTRAINT pk_response_id PRIMARY KEY (id, response)
CONSTRAINT pk_response_id PRIMARY KEY (betId, response)
)
""".trimIndent()
)
database.execute(
"""
CREATE TABLE IF not exists betInfo (
id VARCHAR(255) PRIMARY KEY,
totalStakes int
)
""".trimIndent()
)
database.execute(
"""
CREATE TABLE IF not exists betAnswerInfo (
betId VARCHAR(255),
response VARCHAR(255),
totalStakes int,
odds float,
CONSTRAINT pk_bet_answer_info_id PRIMARY KEY (betId, response)
)
""".trimIndent()
)

@ -1,24 +1,16 @@
package allin.data.postgres
import allin.data.ParticipationDataSource
import allin.data.postgres.entities.ParticipationsEntity
import allin.data.postgres.entities.*
import allin.model.Participation
import org.ktorm.database.Database
import org.ktorm.dsl.*
import org.ktorm.dsl.and
import org.ktorm.dsl.eq
import org.ktorm.dsl.insert
import org.ktorm.entity.*
class PostgresParticipationDataSource(private val database: Database) : ParticipationDataSource {
private fun QueryRowSet.toParticipation() =
Participation(
id = this[ParticipationsEntity.id].toString(),
betId = this[ParticipationsEntity.betId].toString(),
username = this[ParticipationsEntity.username].toString(),
answer = this[ParticipationsEntity.answer].toString(),
stake = this[ParticipationsEntity.stake] ?: 0,
)
private fun Query.mapToParticipation() = this.map { it.toParticipation() }
override fun addParticipation(participation: Participation) {
database.insert(ParticipationsEntity) {
set(it.id, participation.id)
@ -27,25 +19,50 @@ class PostgresParticipationDataSource(private val database: Database) : Particip
set(it.answer, participation.answer)
set(it.stake, participation.stake)
}
val betInfo = database.betInfos.find { it.id eq participation.betId } ?: BetInfoEntity {
this.id = participation.betId
this.totalStakes = 0
}
betInfo.totalStakes += participation.stake
database.betInfos.update(betInfo)
database.betAnswerInfos.filter { it.betId eq participation.betId }.forEach {
if (it.response == participation.answer) {
it.totalStakes += participation.stake
}
val probability = it.totalStakes / betInfo.totalStakes.toFloat()
it.odds = 1 / probability
it.flushChanges()
}
}
override fun getParticipationFromBetId(betid: String): List<Participation> =
database.from(ParticipationsEntity)
.select()
.where { ParticipationsEntity.betId eq betid }
.mapToParticipation()
database.participations.filter { it.betId eq betid }.map { it.toParticipation() }
override fun getParticipationFromUserId(username: String, betid: String): List<Participation> =
database.from(ParticipationsEntity)
.select()
.where { (ParticipationsEntity.betId eq betid) and (ParticipationsEntity.username eq username) }
.mapToParticipation()
fun getParticipationEntity(): List<Participation> =
database.from(ParticipationsEntity).select().mapToParticipation()
override fun deleteParticipation(id: String): Boolean =
database.delete(ParticipationsEntity) {
it.id eq id
} > 0
}
database.participations.filter {
(ParticipationsEntity.betId eq betid) and (ParticipationsEntity.username eq username)
}.map { it.toParticipation() }
override fun deleteParticipation(id: String): Boolean {
val participation = database.participations.find { it.id eq id } ?: return false
database.betInfos.find { it.id eq participation.bet.id }?.let { betInfo ->
betInfo.totalStakes -= participation.stake
database.betAnswerInfos.filter { it.betId eq participation.bet.id }.forEach {
if (it.response == participation.answer) {
it.totalStakes -= participation.stake
}
val probability = it.totalStakes / betInfo.totalStakes.toFloat()
it.odds = 1 / probability
it.flushChanges()
}
betInfo.flushChanges()
}
return participation.delete() > 0
}
}

@ -1,70 +1,60 @@
package allin.data.postgres
import allin.data.UserDataSource
import allin.data.postgres.entities.UserEntity
import allin.data.postgres.entities.UsersEntity
import allin.data.postgres.entities.users
import allin.dto.UserDTO
import allin.ext.executeWithResult
import allin.model.User
import org.ktorm.database.Database
import org.ktorm.database.use
import org.ktorm.dsl.*
import org.ktorm.entity.add
import org.ktorm.entity.filter
import org.ktorm.entity.find
import org.ktorm.entity.removeIf
import java.time.Instant.now
class PostgresUserDataSource(private val database: Database) : UserDataSource {
override fun getUserByUsername(username: String): Pair<UserDTO?, String?> =
database.from(UsersEntity)
.select()
.where { UsersEntity.username eq username }
.map { row ->
Pair(
UserDTO(
row[UsersEntity.id].toString(),
row[UsersEntity.username].toString(),
row[UsersEntity.email].toString(),
row[UsersEntity.nbCoins] ?: 0,
null
),
row[UsersEntity.password].toString()
)
}
.firstOrNull() ?: Pair(null, null)
database.users
.find { it.username eq username }
?.let { it.toUserDTO() to it.password }
?: (null to null)
override fun addUser(user: User) {
database.insert(UsersEntity) {
set(it.id, user.id)
set(it.nbCoins, user.nbCoins)
set(it.username, user.username)
set(it.password, user.password)
set(it.email, user.email)
set(it.lastGift, now())
}
database.users.add(
UserEntity {
this.id = user.id
this.nbCoins = user.nbCoins
this.username = user.username
this.password = user.password
this.email = user.email
this.lastGift = now()
}
)
}
override fun deleteUser(username: String): Boolean {
val deletedCount = database.delete(UsersEntity) {
it.username eq username
}
return deletedCount > 0
}
override fun deleteUser(username: String): Boolean =
database.users.removeIf { it.username eq username } > 0
override fun userExists(username: String, email: String): Boolean {
return database.from(UsersEntity).select(UsersEntity.username, UsersEntity.email).where {
(UsersEntity.username eq username) and (UsersEntity.email eq email)
return database.users.filter {
(it.username eq username) and (it.email eq email)
}.totalRecords > 0
}
override fun addCoins(username: String, amount: Int) {
database.update(UsersEntity) {
set(UsersEntity.nbCoins, UsersEntity.nbCoins + amount)
where { UsersEntity.username eq username }
}
database.users
.find { it.username eq username }
?.set(UsersEntity.nbCoins.name, UsersEntity.nbCoins + amount)
}
override fun removeCoins(username: String, amount: Int) {
database.update(UsersEntity) {
set(UsersEntity.nbCoins, UsersEntity.nbCoins - amount)
where { UsersEntity.username eq username }
}
database.users
.find { it.username eq username }
?.set(UsersEntity.nbCoins.name, UsersEntity.nbCoins - amount)
}
override fun canHaveDailyGift(username: String): Boolean {

@ -0,0 +1,36 @@
package allin.data.postgres.entities
import allin.model.BetAnswerInfo
import org.ktorm.database.Database
import org.ktorm.entity.Entity
import org.ktorm.entity.sequenceOf
import org.ktorm.schema.Table
import org.ktorm.schema.float
import org.ktorm.schema.int
import org.ktorm.schema.varchar
interface BetAnswerInfoEntity : Entity<BetAnswerInfoEntity> {
companion object : Entity.Factory<BetAnswerInfoEntity>()
var betId: String
var response: String
var totalStakes: Int
var odds: Float
fun toBetAnswerInfo() =
BetAnswerInfo(
betId = betId,
response = response,
totalStakes = totalStakes,
odds = odds
)
}
object BetAnswerInfosEntity : Table<BetAnswerInfoEntity>("betanswerinfo") {
val betId = varchar("betid").primaryKey().bindTo { it.betId }
val response = varchar("response").primaryKey().bindTo { it.response }
val totalStakes = int("totalstakes").bindTo { it.totalStakes }
val odds = float("odds").bindTo { it.odds }
}
val Database.betAnswerInfos get() = this.sequenceOf(BetAnswerInfosEntity)

@ -1,31 +1,78 @@
package allin.data.postgres.entities
import allin.model.BetStatus
import allin.model.BetType
import org.ktorm.entity.Entity
import allin.model.*
import org.ktorm.database.Database
import org.ktorm.dsl.eq
import org.ktorm.entity.*
import org.ktorm.schema.*
import java.time.Instant
import java.time.ZoneId
import java.time.ZonedDateTime
interface BetEntity : Entity<BetEntity> {
val theme: String
val sentenceBet: String
val endRegistration: ZonedDateTime
val endBet: ZonedDateTime
val isPrivate: Boolean
val status: BetStatus
val type: BetType
val createdBy: String
companion object : Entity.Factory<BetEntity>()
var id: String
var theme: String
var sentenceBet: String
var endRegistration: Instant
var endBet: Instant
var zoneId: String
var isPrivate: Boolean
var status: BetStatus
var type: BetType
var createdBy: String
fun toBet(database: Database) =
Bet(
id = id,
theme = theme,
sentenceBet = sentenceBet,
status = status,
type = type,
endRegistration = ZonedDateTime.ofInstant(endRegistration, ZoneId.of(zoneId)),
endBet = ZonedDateTime.ofInstant(endBet, ZoneId.of(zoneId)),
isPrivate = isPrivate,
response = if (type == BetType.BINARY) {
listOf(YES_VALUE, NO_VALUE)
} else {
database.responses.filter { it.betId eq id }.map { it.response }
},
createdBy = createdBy
)
fun toBetDetail(database: Database, username: String): BetDetail {
val bet = this.toBet(database)
val participations = database.participations.filter { it.betId eq bet.id }
val userParticipation = participations.find { it.username eq username }
val participationEntities = participations.map { it.toParticipation() }
val answerInfos = database.betAnswerInfos
.filter { it.betId eq bet.id }
.map { it.toBetAnswerInfo() }
return BetDetail(
bet = bet,
answers = getBetAnswerDetail(bet, participationEntities, answerInfos),
participations = participationEntities,
userParticipation = userParticipation?.toParticipation()
)
}
}
object BetsEntity : Table<BetEntity>("bet") {
val id = varchar("id").primaryKey()
val id = varchar("id").primaryKey().bindTo { it.id }
val theme = varchar("theme").bindTo { it.theme }
val sentenceBet = varchar("sentencebet").bindTo { it.sentenceBet }
val endRegistration = timestamp("endregistration")
val endBet = timestamp("endbet")
val endRegistration = timestamp("endregistration").bindTo { it.endRegistration }
val endBet = timestamp("endbet").bindTo { it.endBet }
val zoneId = varchar("zoneid").bindTo { it.zoneId }
val isPrivate = boolean("isprivate").bindTo { it.isPrivate }
val status = enum<BetStatus>("status").bindTo { it.status }
val type = enum<BetType>("type").bindTo { it.type }
val createdBy = varchar("createdby").bindTo { it.createdBy }
}
}
val Database.bets get() = this.sequenceOf(BetsEntity)

@ -0,0 +1,23 @@
package allin.data.postgres.entities
import org.ktorm.database.Database
import org.ktorm.entity.Entity
import org.ktorm.entity.sequenceOf
import org.ktorm.schema.Table
import org.ktorm.schema.int
import org.ktorm.schema.varchar
interface BetInfoEntity : Entity<BetInfoEntity> {
companion object : Entity.Factory<BetInfoEntity>()
var id: String
var totalStakes: Int
}
object BetInfosEntity : Table<BetInfoEntity>("betinfo") {
val id = varchar("id").primaryKey().bindTo { it.id }
val totalStakes = int("totalstakes").bindTo { it.totalStakes }
}
val Database.betInfos get() = this.sequenceOf(BetInfosEntity)

@ -1,13 +1,37 @@
package allin.data.postgres.entities
import allin.model.BetResult
import allin.model.BetResultDetail
import org.ktorm.database.Database
import org.ktorm.entity.Entity
import org.ktorm.entity.sequenceOf
import org.ktorm.schema.Table
import org.ktorm.schema.varchar
interface BetResultEntity : Entity<BetResultEntity> {
val bet: BetEntity
val result: String
companion object : Entity.Factory<BetResultEntity>()
var bet: BetEntity
var result: String
fun toBetResult() =
BetResult(
betId = bet.id,
result = result
)
fun toBetResultDetail(
database: Database,
participationEntity: ParticipationEntity
) =
BetResultDetail(
betResult = this.toBetResult(),
bet = bet.toBet(database),
participation = participationEntity.toParticipation(),
amount = participationEntity.stake,
won = participationEntity.answer == result
)
}
object BetResultsEntity : Table<BetResultEntity>("betresult") {
@ -15,12 +39,4 @@ object BetResultsEntity : Table<BetResultEntity>("betresult") {
val result = varchar("result").bindTo { it.result }
}
interface BetResultNotificationEntity : Entity<BetResultNotificationEntity> {
val betId: String
val username: String
}
object BetResultNotificationsEntity : Table<BetResultNotificationEntity>("betresult") {
val betId = varchar("betid").primaryKey()
val username = varchar("username").primaryKey()
}
val Database.betResults get() = this.sequenceOf(BetResultsEntity)

@ -0,0 +1,21 @@
package allin.data.postgres.entities
import org.ktorm.database.Database
import org.ktorm.entity.Entity
import org.ktorm.entity.sequenceOf
import org.ktorm.schema.Table
import org.ktorm.schema.varchar
interface BetResultNotificationEntity : Entity<BetResultNotificationEntity> {
companion object : Entity.Factory<BetResultNotificationEntity>()
var betId: String
var username: String
}
object BetResultNotificationsEntity : Table<BetResultNotificationEntity>("betresultnotification") {
val betId = varchar("betid").primaryKey()
val username = varchar("username").primaryKey()
}
val Database.betResultNotifications get() = this.sequenceOf(BetResultNotificationsEntity)

@ -1,23 +1,38 @@
package allin.data.postgres.entities
import allin.model.Participation
import org.ktorm.database.Database
import org.ktorm.entity.Entity
import org.ktorm.entity.sequenceOf
import org.ktorm.schema.Table
import org.ktorm.schema.int
import org.ktorm.schema.varchar
interface ParticipationEntity : Entity<ParticipationEntity> {
val id: String
val bet: BetEntity
val username: String
val answer: String
val stake: Int
}
companion object : Entity.Factory<ParticipationEntity>()
var id: String
var bet: BetEntity
var username: String
var answer: String
var stake: Int
fun toParticipation() =
Participation(
id = id,
betId = bet.id,
username = username,
answer = answer,
stake = stake
)
}
object ParticipationsEntity : Table<ParticipationEntity>("participation") {
val id = varchar("id").primaryKey()
val betId = varchar("bet").references(BetsEntity) { it.bet }
val username = varchar("username")
val answer = varchar("answer")
val stake = int("stake")
val username = varchar("username").bindTo { it.username }
val answer = varchar("answer").bindTo { it.answer }
val stake = int("stake").bindTo { it.stake }
}
val Database.participations get() = this.sequenceOf(ParticipationsEntity)

@ -1,19 +1,21 @@
package allin.data.postgres.entities
import org.ktorm.database.Database
import org.ktorm.entity.Entity
import org.ktorm.entity.sequenceOf
import org.ktorm.schema.Table
import org.ktorm.schema.varchar
const val YES_VALUE = "Yes"
const val NO_VALUE = "No"
interface ResponseEntity : Entity<ResponseEntity> {
val betId: String
val response: String
companion object : Entity.Factory<ResponseEntity>()
var betId: String
var response: String
}
object ResponsesEntity : Table<ResponseEntity>("response") {
val id = varchar("id").primaryKey()
val response = varchar("response").primaryKey()
val betId = varchar("betid").primaryKey().bindTo { it.betId }
val response = varchar("response").primaryKey().bindTo { it.response }
}
val Database.responses get() = this.sequenceOf(ResponsesEntity)

@ -1,24 +1,43 @@
package allin.data.postgres.entities
import allin.dto.UserDTO
import org.ktorm.database.Database
import org.ktorm.entity.Entity
import org.ktorm.entity.sequenceOf
import org.ktorm.schema.Table
import org.ktorm.schema.int
import org.ktorm.schema.timestamp
import org.ktorm.schema.varchar
import java.time.Instant
interface UserEntity : Entity<UserEntity> {
val username: String
companion object : Entity.Factory<UserEntity>()
var id: String
var username: String
var email: String
var password: String
var nbCoins: Int
var lastGift: Instant
fun toUserDTO() =
UserDTO(
id = id,
username = username,
email = email,
nbCoins = nbCoins,
token = null
)
}
object UsersEntity : Table<UserEntity>("utilisateur") {
val id = varchar("id").primaryKey()
object UsersEntity : Table<UserEntity>("users") {
val id = varchar("id").primaryKey().bindTo { it.id }
val username = varchar("username").bindTo { it.username }
val password = varchar("password").bindTo { it.password }
val nbCoins = int("coins").bindTo { it.nbCoins }
val email = varchar("email").bindTo { it.email }
val lastGift = timestamp("lastgift")
val lastGift = timestamp("lastgift").bindTo { it.lastGift }
}
val Database.users get() = this.sequenceOf(UsersEntity)

@ -20,7 +20,7 @@ suspend fun PipelineContext<*, ApplicationCall>.verifyUserFromToken(
content: suspend (user: UserDTO, password: String) -> Unit
) {
val username = principal.payload.getClaim(USERNAME).asString()
val userPassword = userDataSource.getUserByUsername(username)
userPassword.first?.let { content(it, userPassword.second ?: "") }
val user = userDataSource.getUserByUsername(username)
user.first?.let { content(it, user.second ?: "") }
?: call.respond(HttpStatusCode.NotFound, ApiMessage.TOKEN_USER_NOT_FOUND)
}

@ -5,6 +5,9 @@ import allin.serializer.ZonedDateTimeSerializer
import kotlinx.serialization.Serializable
import java.time.ZonedDateTime
const val YES_VALUE = "Yes"
const val NO_VALUE = "No"
@Serializable
data class Bet(
val id: String = "",

@ -19,16 +19,21 @@ data class BetDetail(
val userParticipation: Participation? // La participation du User current
)
fun getBetAnswerDetail(bet: Bet, participations: List<Participation>): List<BetAnswerDetail> {
fun getBetAnswerDetail(
bet: Bet,
participations: List<Participation>,
infos: List<BetAnswerInfo>
): List<BetAnswerDetail> {
return bet.response.map { response ->
val responseParticipations = participations.filter { it.answer == response }
val answerInfo = infos.find { it.response == response }
BetAnswerDetail(
response = response,
totalStakes = responseParticipations.sumOf { it.stake },
totalStakes = answerInfo?.totalStakes ?: 0,
totalParticipants = responseParticipations.size,
highestStake = responseParticipations.maxOfOrNull { it.stake } ?: 0,
odds = if (participations.isEmpty()) 1f else responseParticipations.size / participations.size.toFloat()
odds = answerInfo?.odds ?: 1f
)
}
}

@ -0,0 +1,17 @@
package allin.model
import kotlinx.serialization.Serializable
@Serializable
data class BetInfo(
val id: String,
val totalStakes: Int,
)
@Serializable
data class BetAnswerInfo(
val betId: String,
val response: String,
val totalStakes: Int,
val odds: Float
)

@ -5,7 +5,6 @@ import allin.ext.hasToken
import allin.ext.verifyUserFromToken
import allin.model.ApiMessage
import allin.model.BetDetail
import allin.model.getBetAnswerDetail
import io.github.smiley4.ktorswaggerui.dsl.get
import io.ktor.http.*
import io.ktor.server.application.*
@ -19,7 +18,6 @@ import java.util.*
fun Application.betDetailRouter() {
val userDataSource = this.dataSource.userDataSource
val betDataSource = this.dataSource.betDataSource
val participationDataSource = this.dataSource.participationDataSource
routing {
authenticate {
@ -43,17 +41,11 @@ fun Application.betDetailRouter() {
hasToken { principal ->
verifyUserFromToken(userDataSource, principal) { user, _ ->
val id = call.parameters["id"].toString()
val participations = participationDataSource.getParticipationFromBetId(id)
val selectedBet = betDataSource.getBetById(id)
if (selectedBet != null) {
val result = betDataSource.getBetDetailById(id, user.username)
if (result != null) {
call.respond(
HttpStatusCode.Accepted,
BetDetail(
selectedBet,
getBetAnswerDetail(selectedBet, participations),
participations.toList(),
participationDataSource.getParticipationFromUserId(user.username, id).lastOrNull()
)
result
)
} else {
call.respond(HttpStatusCode.NotFound, ApiMessage.BET_NOT_FOUND)

@ -21,7 +21,6 @@ val tokenManagerBet = AppConfig.tokenManager
fun Application.betRouter() {
val userDataSource = this.dataSource.userDataSource
val betDataSource = this.dataSource.betDataSource
val participationDataSource = this.dataSource.participationDataSource
routing {
authenticate {
@ -36,7 +35,7 @@ fun Application.betRouter() {
response {
HttpStatusCode.Created to {
description = "the bet has been added"
body<Bet>() {
body<Bet> {
description = "Bet with assigned id"
}
}
@ -54,7 +53,7 @@ fun Application.betRouter() {
betDataSource.getBetById(id)?.let {
call.respond(HttpStatusCode.Conflict, ApiMessage.BET_ALREADY_EXIST)
} ?: run {
val betWithId = bet.copy(id = id, createdBy = user.first?.id.toString())
val betWithId = bet.copy(id = id, createdBy = user.first?.username.toString())
betDataSource.addBet(betWithId)
call.respond(HttpStatusCode.Created, betWithId)
}
@ -70,7 +69,7 @@ fun Application.betRouter() {
response {
HttpStatusCode.Accepted to {
description = "The list of bets is available"
body<List<Bet>>() {
body<List<Bet>> {
description = "List of all bet in the selected source"
}
}
@ -167,7 +166,7 @@ fun Application.betRouter() {
response {
HttpStatusCode.Accepted to {
description = "The list of bets that can be validated is available"
body<List<BetDetail>>() {
body<List<BetDetail>> {
description = "list of bets that can be validated"
}
}
@ -175,15 +174,7 @@ fun Application.betRouter() {
}) {
hasToken { principal ->
verifyUserFromToken(userDataSource, principal) { user, _ ->
val response = betDataSource.getToConfirm(user.username).map {
val participations = participationDataSource.getParticipationFromBetId(it.id)
BetDetail(
it,
getBetAnswerDetail(it, participations),
participations,
participations.find { it.username == user.username }
)
}
val response = betDataSource.getToConfirm(user.username)
call.respond(HttpStatusCode.Accepted, response)
}
}
@ -199,7 +190,7 @@ fun Application.betRouter() {
response {
HttpStatusCode.Accepted to {
description = "The list of won bets is available"
body<List<BetResultDetail>>() {
body<List<BetResultDetail>> {
description = "List of won bets"
}
}
@ -222,7 +213,7 @@ fun Application.betRouter() {
response {
HttpStatusCode.Accepted to {
description = "Bet history is available"
body<List<BetResultDetail>>() {
body<List<BetResultDetail>> {
description = "Betting history list"
}
}

@ -52,7 +52,7 @@ fun Application.participationRouter() {
Participation(
id = UUID.randomUUID().toString(),
betId = participation.betId,
username = user.id,
username = user.username,
answer = participation.answer,
stake = participation.stake
)

Loading…
Cancel
Save