Skip to content

Commit

Permalink
add statistic endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
t0lia committed May 17, 2024
1 parent bf6ef28 commit 2f8dcd7
Show file tree
Hide file tree
Showing 6 changed files with 176 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import com.retypeme.project.messaging.GameEventPublisher
import com.retypeme.project.racing.model.Race
import com.retypeme.project.racing.controller.DriverMetrics
import com.retypeme.project.racing.service.DateTimeProvider
import com.retypeme.project.statistic.service.StatisticService
import org.springframework.stereotype.Component
import java.time.LocalDateTime
import java.time.ZoneOffset.UTC
Expand All @@ -16,13 +17,13 @@ const val REGISTERED = "registered"
class RaceRepository(
private val dateTimeProvider: DateTimeProvider,
private val gameEventPublisher: GameEventPublisher,
private val chainService: ChainService

private val chainService: ChainService,
private val userStatisticService: StatisticService
) {

private val openRaces: MutableMap<String, Race> = mutableMapOf()

fun createRace(id: String, chain:Int, users: Int): Race {
fun createRace(id: String, chain: Int, users: Int): Race {
val now: LocalDateTime = dateTimeProvider.now()
val usersList: MutableList<DriverMetrics> = mutableListOf()
val session = Race(id, chain, "", users, null, now, usersList)
Expand All @@ -34,19 +35,19 @@ class RaceRepository(
return openRaces[id] ?: throw Exception("Session not found")
}

fun start(sessionId: String, text: String): Unit {
fun start(sessionId: String, text: String) {
val session: Race = getSessionById(sessionId)
session.startedAt = dateTimeProvider.now()
session.text = text
}

fun updateRegistration(sessionId: String, chain:Int, userId: String, walletId: String, state: String): List<String> {
fun updateRegistration(sessionId: String, chain: Int, userId: String, walletId: String, state: String): List<String> {
val race: Race = getSessionById(sessionId)
if(race.chain != chain) {
if (race.chain != chain) {
val expected = chainService.getChainById(race.chain).name
val actual = chainService.getChainById(chain).name
val msg = "Session is created for different chain, expected: $expected, got: $actual"
return mutableListOf(msg);
return mutableListOf(msg)
}
if (state == JOINED) {
join(sessionId, userId, walletId)
Expand All @@ -60,17 +61,16 @@ class RaceRepository(
private fun register(race: Race, userId: String, sessionId: String) {
val user: DriverMetrics =
race.users.find { u -> u.userId == userId && u.state == JOINED } ?: throw Exception("User not found")
user.state = REGISTERED;
user.state = REGISTERED
if (race.isReady()) {
gameEventPublisher.publishRaceReady(
sessionId, race.users.map { u -> u.userId }.toMutableList()
)
}
}

fun join(sessionId: String, userId: String, walletId: String): Unit {
fun join(sessionId: String, userId: String, walletId: String) {
val session = getSessionById(sessionId)

if (session.users.map { u -> u.userId }.contains(userId)) {
val user: DriverMetrics = session.users.find { u -> u.userId == userId } ?: throw Exception("User not found")
user.walletId = walletId
Expand All @@ -79,7 +79,7 @@ class RaceRepository(
}
}

fun updateProgress(sessionId: String, userId: String, progress: Int): Unit {
fun updateProgress(sessionId: String, userId: String, progress: Int) {
val race: Race = getSessionById(sessionId)
val user: DriverMetrics = race.users.find { u -> u.userId == userId } ?: throw Exception("User not found")
if (user.progress < 100) {
Expand All @@ -97,6 +97,12 @@ class RaceRepository(
if (user.place == 1 && user.walletId.isNotEmpty() && user.walletId.startsWith("0x")) {
gameEventPublisher.publishWinnerFinished(session.id, user.walletId)
}

val duelDate = session.startedAt?.toLocalDate()
if (duelDate != null) {
val won = user.place == 1
userStatisticService.updateUserStatistic(user.walletId, user.cpm.toDouble(), won, duelDate)
}
}
}

Expand All @@ -105,4 +111,4 @@ class RaceRepository(
val time: Long = session.updatedAt.toEpochSecond(UTC) - session.startedAt!!.toEpochSecond(UTC)
return ((typedChars * 60) / time).toInt()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.retypeme.project.racing.controller

import com.retypeme.project.statistic.model.Statistic
import com.retypeme.project.statistic.service.StatisticService
import org.springframework.web.bind.annotation.*
import java.time.LocalDate

@RestController
@RequestMapping("/statistics")
class StatisticController(private val userStatisticService: StatisticService) {

@GetMapping("/{userId}")
fun getStatistic(@PathVariable userId: String): Statistic? {
return userStatisticService.getUserStatistic(userId)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.retypeme.project.statistic.model

import java.time.LocalDate

class Statistic (
val userId: String,
var completedDuels: Int = 0,
var averageSpeed: Double = 0.0,
var bestSpeed: Double = 0.0,
var wins: Int = 0,
var maxSpeed: Double = 0.0,
var topSpeeds: MutableList<Double> = mutableListOf(),
var firstDuelDate: LocalDate? = null )

Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.retypeme.project.statistic.repository

import com.retypeme.project.statistic.model.Statistic
import org.springframework.stereotype.Repository
import java.util.concurrent.ConcurrentHashMap


interface StatisticRepository {
fun save(userStatistic: Statistic)
fun findById(userId: String): Statistic?
fun findAll(): List<Statistic>
}

@Repository
class UserStatisticRepositoryImpl : StatisticRepository {
private val userStatistics = ConcurrentHashMap<String, Statistic>()

override fun save(userStatistic: Statistic) {
userStatistics[userStatistic.userId] = userStatistic
}

override fun findById(userId: String): Statistic? {
return userStatistics[userId]
}

override fun findAll(): List<Statistic> {
return userStatistics.values.toList()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.retypeme.project.statistic.service

import com.retypeme.project.statistic.model.Statistic
import com.retypeme.project.statistic.repository.StatisticRepository
import org.springframework.stereotype.Service
import java.time.LocalDate

@Service
class StatisticService(private val userStatisticRepository: StatisticRepository) {

fun updateUserStatistic(userId: String, speed: Double, won: Boolean, duelDate: LocalDate) {
val userStatistic = userStatisticRepository.findById(userId) ?: Statistic(userId, firstDuelDate = duelDate)

userStatistic.completedDuels++
userStatistic.averageSpeed =
((userStatistic.averageSpeed * (userStatistic.completedDuels - 1)) + speed) / userStatistic.completedDuels
if (speed > userStatistic.bestSpeed) {
userStatistic.bestSpeed = speed
}
if (speed > userStatistic.maxSpeed) {
userStatistic.maxSpeed = speed
}
if (won) {
userStatistic.wins++
}
userStatistic.topSpeeds.add(speed)
userStatistic.topSpeeds.sortDescending()
if (userStatistic.topSpeeds.size > 10) {
userStatistic.topSpeeds = userStatistic.topSpeeds.take(10).toMutableList()
}

userStatisticRepository.save(userStatistic)
}

fun getUserStatistic(userId: String): Statistic? {
return userStatisticRepository.findById(userId)
}
}
62 changes: 61 additions & 1 deletion backend/src/main/resources/api-spec.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ paths:
schema:
$ref: '#/components/schemas/SessionResponse'
/sessions/{id}:
parameters: # Add parameters at the path level
parameters:
- name: id
in: path
required: true
Expand All @@ -52,6 +52,26 @@ paths:
$ref: '#/components/schemas/SessionResponse'
'404':
description: Not Found
/statistics/{userId}:
get:
summary: Get user statistics by ID
operationId: getUserStatistics
parameters:
- name: userId
in: path
required: true
description: User ID
schema:
type: string
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/UserStatistic'
'404':
description: Not Found

components:
schemas:
Expand Down Expand Up @@ -95,3 +115,43 @@ components:
properties:
userId:
type: string
ProgressUpdateRequest:
type: object
properties:
userId:
type: string
progress:
type: integer
ProgressUpdateResponse:
type: object
properties:
userId:
type: string
progress:
type: integer
UserStatistic:
type: object
properties:
userId:
type: string
completedDuels:
type: integer
averageSpeed:
type: number
format: double
bestSpeed:
type: number
format: double
wins:
type: integer
maxSpeed:
type: number
format: double
topSpeeds:
type: array
items:
type: number
format: double
firstDuelDate:
type: string
format: date

0 comments on commit 2f8dcd7

Please sign in to comment.