Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sascha/more fun #7

Merged
merged 4 commits into from
Feb 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading