Skip to content

Commit

Permalink
Merge pull request #4 from bitkid/sascha/show-search-names
Browse files Browse the repository at this point in the history
Sascha/show search names
  • Loading branch information
bitkid committed Feb 3, 2024
2 parents 0b7c3d0 + 4f989b1 commit 75d4da4
Show file tree
Hide file tree
Showing 6 changed files with 269 additions and 180 deletions.
93 changes: 5 additions & 88 deletions src/main/kotlin/com/bitkid/itsfranking/ITSFData.kt
Original file line number Diff line number Diff line change
@@ -1,102 +1,19 @@
package com.bitkid.itsfranking

import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.fasterxml.jackson.module.kotlin.readValue
import org.apache.commons.codec.language.bm.NameType
import org.apache.commons.codec.language.bm.PhoneticEngine
import org.apache.commons.codec.language.bm.RuleType
import java.io.File

data class ITSFRank(val rank: Int, val points: Int)

data class ITSFPlayer(
val licenseNumber: String,
val name: String,
val country: String,
val rankings: Map<Category, ITSFRank>
)
) {
fun hasFemaleRanking(): Boolean {
return rankings[Categories.womenDoubles] != null || rankings[Categories.womenSingles] != null
}
}

data class PlayerNameWithResults(val playerName: String, val results: List<ITSFPlayer>)
data class TwoPlayersWithResults(val player1: PlayerNameWithResults, val player2: PlayerNameWithResults)


class ITSFPlayers(rankings: List<Ranking>) {

companion object {
fun readFromFile(file: File): ITSFPlayers {
val ranking = jacksonObjectMapper().readValue<List<Ranking>>(file)
return ITSFPlayers(ranking)
}
}

private val engine = PhoneticEngine(NameType.GENERIC, RuleType.APPROX, true)

data class CategoriesAndPlayer(val category: Category, val player: RankedPlayer)

val players: Map<String, ITSFPlayer>

private val phoneticNames: Map<String, String>

init {
val playerPerLicense = rankings.flatMap { r -> r.database.values.map { CategoriesAndPlayer(r.category, it) } }
.groupBy { it.player.licenseNumber }
players = playerPerLicense.mapValues { playerEntry ->
val licenseNumber = playerEntry.key
val rankingsForPlayer = playerEntry.value
val name = rankingsForPlayer.first().player.name
val country = rankingsForPlayer.first().player.country
val r = rankingsForPlayer.associate { it.category to ITSFRank(it.player.rank, it.player.points) }
ITSFPlayer(licenseNumber, name, country, r)
}
phoneticNames = players.map {
it
}.associate {
val encode = engine.encode(it.value.name)
it.key to encode
}
}

fun getRanking(category: Category): List<ITSFPlayer> {
return players.values.filter {
it.rankings[category] != null
}.sortedBy { it.rankings.getValue(category).rank }
}

fun find(search: String, searchForNameParts: Boolean = false): List<ITSFPlayer> {
val res = findPlayer(search)
val newRes = res.ifEmpty { findPlayer(search.split(" ").reversed().joinToString(" ")) }
if (newRes.isEmpty() && searchForNameParts) {
search.split(" ").forEach {
val splitRes = findPlayer(it)
if (splitRes.isNotEmpty())
return splitRes
}
}
return newRes
}

private fun findPlayer(search: String): List<ITSFPlayer> {
val normalSearch = players.filter { it.value.name.contains(search, true) }.map { it.value }
if (normalSearch.isEmpty()) {
val enc = engine.encode(search).split("|")
return phoneticNames.filter {
matchesName(it.value, enc)
}.map {
players.getValue(it.key)
}
} else {
return normalSearch
}
}

fun matchesName(name: String, enc: List<String>): Boolean {
name.split("|").forEach { encName ->
enc.forEach { encSearch ->
if (encName.contains(encSearch, true)) {
return true
}
}
}
return false
}
}
103 changes: 103 additions & 0 deletions src/main/kotlin/com/bitkid/itsfranking/ITSFPlayers.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package com.bitkid.itsfranking

import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.fasterxml.jackson.module.kotlin.readValue
import org.apache.commons.codec.language.bm.NameType
import org.apache.commons.codec.language.bm.PhoneticEngine
import org.apache.commons.codec.language.bm.RuleType
import java.io.File

class ITSFPlayers(rankings: List<Ranking>) {

companion object {
fun readFromFile(file: File): ITSFPlayers {
val ranking = jacksonObjectMapper().readValue<List<Ranking>>(file)
return ITSFPlayers(ranking)
}
}

private val engine = PhoneticEngine(NameType.GENERIC, RuleType.APPROX, true)

private data class CategoriesAndPlayer(val category: Category, val player: RankedPlayer)

private val players: Map<String, ITSFPlayer>

private val phoneticNames: Map<String, String>

init {
val playerPerLicense = rankings.flatMap { r -> r.database.values.map { CategoriesAndPlayer(r.category, it) } }
.groupBy { it.player.licenseNumber }
players = playerPerLicense.mapValues { playerEntry ->
val licenseNumber = playerEntry.key
val rankingsForPlayer = playerEntry.value
val name = rankingsForPlayer.first().player.name
val country = rankingsForPlayer.first().player.country
val r = rankingsForPlayer.associate { it.category to ITSFRank(it.player.rank, it.player.points) }
ITSFPlayer(licenseNumber, name, country, r)
}
phoneticNames = players.map {
it
}.associate {
val encode = engine.encode(it.value.name)
it.key to encode
}
}

fun getSortedRanking(category: Category): List<ITSFPlayer> {
return players.values.filter {
it.rankings[category] != null
}.sortedBy { it.rankings.getValue(category).rank }
}

fun getPlayer(licenseNumber: String): ITSFPlayer? {
return players[licenseNumber]
}

fun find(search: String, searchForNameParts: Boolean = false): List<ITSFPlayer> {
val res = findPlayer(search)
if (res.isEmpty()) {
val phoneticRes = phoneticSearch(search)
if (phoneticRes.isEmpty()) {
val reversedName = search.split(" ").reversed().joinToString(" ")
val reversedRes = if (reversedName != search) findPlayer(reversedName) else emptyList()
if (reversedRes.isEmpty() && searchForNameParts) {
return findPlayerWithNameParts(search)
}
return reversedRes
}
return phoneticRes
}
return res
}

private fun findPlayerWithNameParts(search: String): List<ITSFPlayer> {
search.split(" ").forEach {
val splitRes = findPlayer(it)
if (splitRes.isNotEmpty())
return splitRes
}
return emptyList()
}

private fun findPlayer(search: String): List<ITSFPlayer> = players.filter { it.value.name.contains(search, true) }.map { it.value }

private fun phoneticSearch(search: String): List<ITSFPlayer> {
val enc = engine.encode(search).split("|")
return phoneticNames.filter {
matchesName(it.value, enc)
}.map {
players.getValue(it.key)
}
}

fun matchesName(phoneticName: String, listWithPhoneticSubString: List<String>): Boolean {
phoneticName.split("|").forEach { encName ->
listWithPhoneticSubString.forEach { encSearch ->
if (encName.contains(encSearch, true)) {
return true
}
}
}
return false
}
}
Loading

0 comments on commit 75d4da4

Please sign in to comment.