Skip to content

Commit

Permalink
Merge pull request #7 from bitkid/sascha/more-fun
Browse files Browse the repository at this point in the history
Sascha/more fun
  • Loading branch information
bitkid authored Feb 6, 2024
2 parents e7ece9e + 2d8d31b commit 21c3af6
Show file tree
Hide file tree
Showing 7 changed files with 144 additions and 67 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Publish package to the Maven Central Repository
name: Publish packages to github
on:
release:
types: [ created ]
Expand Down
16 changes: 15 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,25 @@
## ITSF Ranking App

To run the app you need to have a Java JRE installed (version 8 or higher).
### Installation

To run the app you need to have a Java JRE installed (version 8 or higher) and you need internet access.

Download the executable jar file and launch it (probably by double-clicking on it)

The latest release can always be found here https://github.com/bitkid/itsf-ranking/packages/2052592 and is named itsf-ranking-{version}-all.jar

### Features

- downloads the ranking for the chosen tour from the ITSF page (top 2000 player in every category)
- you can look at the rankings and copy them in an excel friendly way
- you can search by ITSF license number
- you can search for player names (phonetic, string part matching)
- you can upload text files (for singles 1 player per line for doubles 2 players per line separated by `;`) and the app tries to match it with the current ranking

### Support

There is no support whatsoever. You can write issues and if i have time i might fix them. But i always accept PRs!

### Development

Needs at least JDK 8 and IntelliJ Idea (for Kotlin development)
Expand Down
30 changes: 19 additions & 11 deletions src/main/kotlin/com/bitkid/itsfranking/ITSFRankingApp.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,16 @@ import javax.swing.*


@OptIn(DelicateCoroutinesApi::class)
fun showLoadingDialog(jFrame: JFrame): JDialog {
val jDialog = JDialog(jFrame, "loading", true)
fun showLoadingDialog(): JDialog {
val jDialog = JDialog(ITSFRankingApp.jFrame, "loading", true)
jDialog.contentPane.layout = MigLayout()
jDialog.isUndecorated = true
jDialog.contentPane.add(JLabel(" LOADING ...").apply {
border = BorderFactory.createMatteBorder(1, 1, 1, 1, Color.black)
}, "width :100:")
jDialog.defaultCloseOperation = JDialog.DO_NOTHING_ON_CLOSE
jDialog.pack()
jDialog.setLocationRelativeTo(jFrame)
jDialog.setLocationRelativeTo(ITSFRankingApp.jFrame)
GlobalScope.launch(Dispatchers.Swing) {
jDialog.isVisible = true
}
Expand All @@ -34,7 +34,9 @@ fun showLoadingDialog(jFrame: JFrame): JDialog {
@DelicateCoroutinesApi
object ITSFRankingApp {
private lateinit var itsfPlayers: ITSFPlayers
private lateinit var jFrame: JFrame
lateinit var jFrame: JFrame

val version = getVersionFromJarFile()


private val playerNameField = JTextField(50)
Expand All @@ -55,7 +57,7 @@ object ITSFRankingApp {
private val tabbedPane = JTabbedPane()


private fun emptyModel() = ResultTableModel(listOf("itsf no.", "name") + Categories.all.map { it.targetAudience })
private fun emptyModel() = ResultTableModel(listOf("itsf no.", "name", "country") + Categories.all.map { it.targetAudience })

private fun emptyRankingModel() = ResultTableModel(listOf("itsf no.", "name", "country", "rank", "points"))

Expand Down Expand Up @@ -93,7 +95,7 @@ object ITSFRankingApp {
}

private suspend fun loadRanking(s: String) {
//val rankings = ITSFPlayerDatabaseReader(topXPlayers = 2000).readTestRankings()
// val rankings = ITSFPlayerDatabaseReader(topXPlayers = 2000).readTestRankings()
val rankings = ITSFPlayerDatabaseReader(topXPlayers = 2000, tour = s).readRankings()
itsfPlayers = ITSFPlayers(rankings)
}
Expand Down Expand Up @@ -181,9 +183,9 @@ object ITSFRankingApp {
if (r == null)
""
else
"${r.rank} (${r.points})"
"${r.points} (rank ${r.rank})"
}
val all = listOf(it.licenseNumber, it.name) + ranks
val all = listOf(it.licenseNumber, it.name, it.country) + ranks
model.addRow(all)
}

Expand All @@ -193,7 +195,7 @@ object ITSFRankingApp {
val panel = JPanel(MigLayout("wrap 2"))

panel.add(JLabel("Load ITSF Rankings"))
panel.add(LoadPanel(jFrame) { loadRanking(it) }, "growx")
panel.add(LoadITSFDataPanel { loadRanking(it) }, "growx")

val searchAction: Action = object : AbstractAction() {
override fun actionPerformed(e: ActionEvent) {
Expand Down Expand Up @@ -223,7 +225,7 @@ object ITSFRankingApp {
panel.add(JButton("Search").apply { addActionListener(searchLicenseAction) }, "growx")

panel.add(JLabel("Upload player list"))
panel.add(LoadCsvPanel(jFrame, ::loadFile, ::checkRankingLoaded), "growx")
panel.add(LoadCsvPanel(::loadFile, ::checkRankingLoaded), "growx")

panel.add(JLabel("Results"))

Expand All @@ -243,12 +245,18 @@ object ITSFRankingApp {
} catch (ex: Exception) {
ex.printStackTrace()
}
val frame = JFrame("ITSF Rankings")
val frame = JFrame("ITSF Rankings ($version)")
frame.contentPane.add(createPanel(frame))
frame.pack()
frame.isResizable = false
frame.defaultCloseOperation = WindowConstants.EXIT_ON_CLOSE
frame.isVisible = true
}
}

private fun getVersionFromJarFile(): String {
val c = ITSFRankingApp.javaClass
val input = c.getResource('/' + c.getName().replace('.', '/') + ".class")
return "itsf-ranking-.*all\\.jar".toRegex().find(input!!.toString())?.groupValues?.first()?.split("-")?.get(2) ?: "DEV"
}
}
9 changes: 5 additions & 4 deletions src/main/kotlin/com/bitkid/itsfranking/ui/LoadCsvPanel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.bitkid.itsfranking.ui

import com.bitkid.itsfranking.Categories
import com.bitkid.itsfranking.Category
import com.bitkid.itsfranking.ITSFRankingApp
import com.bitkid.itsfranking.showLoadingDialog
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
Expand All @@ -13,7 +14,7 @@ import java.nio.charset.Charset
import javax.swing.*

@DelicateCoroutinesApi
class LoadCsvPanel(private val jFrame: JFrame, private val load: (File, Charset, Category) -> Unit, private val isLoaded: () -> Boolean) : JPanel(MigLayout("insets 0 0 0 0")) {
class LoadCsvPanel(private val load: (File, Charset, Category) -> Unit, private val isLoaded: () -> Boolean) : JPanel(MigLayout("insets 0 0 0 0")) {

private var currentDirectory = File(System.getProperty("user.home"))

Expand All @@ -30,9 +31,9 @@ class LoadCsvPanel(private val jFrame: JFrame, private val load: (File, Charset,
val fileChooser = JFileChooser(currentDirectory)
fileChooser.isMultiSelectionEnabled = false
fileChooser.fileSelectionMode = JFileChooser.FILES_ONLY
val r = fileChooser.showOpenDialog(jFrame)
val r = fileChooser.showOpenDialog(ITSFRankingApp.jFrame)
if (r == JFileChooser.APPROVE_OPTION) {
val dialog = showLoadingDialog(jFrame)
val dialog = showLoadingDialog()
currentDirectory = fileChooser.selectedFile.parentFile
GlobalScope.launch(Dispatchers.IO) {
try {
Expand All @@ -41,7 +42,7 @@ class LoadCsvPanel(private val jFrame: JFrame, private val load: (File, Charset,
} catch (e: Exception) {
dialog.dispose()
JOptionPane.showMessageDialog(
jFrame,
ITSFRankingApp.jFrame,
"Loading CSV list failed! \n ${e.stackTraceToString()}",
"Error",
JOptionPane.ERROR_MESSAGE
Expand Down
103 changes: 103 additions & 0 deletions src/main/kotlin/com/bitkid/itsfranking/ui/LoadITSFDataPanel.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package com.bitkid.itsfranking.ui

import com.bitkid.itsfranking.ITSFRankingApp
import com.bitkid.itsfranking.showLoadingDialog
import io.ktor.client.*
import io.ktor.client.engine.cio.*
import io.ktor.client.request.*
import io.ktor.client.statement.*
import kotlinx.coroutines.*
import kotlinx.coroutines.swing.Swing
import net.miginfocom.swing.MigLayout
import java.awt.Color
import java.awt.Cursor
import java.awt.Desktop
import java.awt.event.MouseAdapter
import java.awt.event.MouseEvent
import java.net.URI
import java.time.LocalDateTime
import javax.swing.*


@OptIn(DelicateCoroutinesApi::class)
class LoadITSFDataPanel(private val load: suspend (String) -> Unit) : JPanel(MigLayout("insets 0 0 0 0, wrap 4")) {
init {
val label = JLabel("Tour")
val currentYear = LocalDateTime.now().year
val tourField = JComboBox(listOf(currentYear, currentYear - 1).toTypedArray())
tourField.selectedIndex = 0
val button = JButton("Load").apply {
addActionListener {
val jDialog = showLoadingDialog()
isEnabled = false
tourField.isEnabled = false

val job = GlobalScope.async(Dispatchers.IO) {
load(tourField.selectedItem!!.toString())
}
GlobalScope.launch(Dispatchers.Swing) {
try {
job.await()
background = Color.GREEN
jDialog.dispose()
} catch (e: Exception) {
jDialog.dispose()
background = Color.RED
isEnabled = true
tourField.isEnabled = true
JOptionPane.showMessageDialog(
ITSFRankingApp.jFrame,
"Fetching data failed! \n ${e.stackTraceToString()}",
"Error",
JOptionPane.ERROR_MESSAGE
)
}
}
}
}
add(label)
add(tourField)
add(button, "growx")
val versionLabel = JLabel("Checking for new version ..")
add(versionLabel, "growx")
checkForVersionUpdate(versionLabel)
}

private fun checkForVersionUpdate(versionLabel: JLabel) {
GlobalScope.launch(Dispatchers.IO) {
val ghv = getGithubLatestVersion()
launch(Dispatchers.Swing) {
if (ghv != null) {
if (ghv == ITSFRankingApp.version) {
versionLabel.text = "Version ${ITSFRankingApp.version} is up to date"
} else {
versionLabel.text = """<html>Update $ghv available <a href="/">https://github.com/bitkid/itsf-ranking/packages/2052592</a></html>"""
versionLabel.setCursor(Cursor(Cursor.HAND_CURSOR))
versionLabel.addMouseListener(object : MouseAdapter() {
override fun mouseClicked(e: MouseEvent) {
try {
Desktop.getDesktop().browse(URI("https://github.com/bitkid/itsf-ranking/packages/2052592"))
} catch (_: Exception) {
}
}
})
}
} else {
versionLabel.text = "Version data not available at the moment"
}
}
}
}

private suspend fun getGithubLatestVersion(): String? {
try {
HttpClient(CIO).use {
val githubPage = it.get("https://github.com/bitkid/itsf-ranking/packages/2052592").bodyAsText()
val match = "&lt;version&gt;.*&lt;/version&gt;".toRegex().find(githubPage)!!.groupValues.first()
return match.replace("&lt;version&gt;", "").replace("&lt;/version&gt;", "")
}
} catch (e: Exception) {
return null
}
}
}
49 changes: 0 additions & 49 deletions src/main/kotlin/com/bitkid/itsfranking/ui/LoadPanel.kt

This file was deleted.

2 changes: 1 addition & 1 deletion src/test/kotlin/com/bitkid/itsfranking/ListMatcherTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import strikt.assertions.isEqualTo
import java.io.File
import java.nio.charset.Charset

val players = ITSFPlayers.readFromFile(File(ITSFPlayerDatabaseReaderTest::class.java.getResource("/itsfFullRankings_2023.json")!!.toURI()))
val players by lazy { ITSFPlayers.readFromFile(File(ITSFPlayerDatabaseReaderTest::class.java.getResource("/itsfFullRankings_2023.json")!!.toURI())) }

class ListMatcherTest {

Expand Down

0 comments on commit 21c3af6

Please sign in to comment.