Various packaging tweaks, documentation, etc.
This commit is contained in:
@@ -53,7 +53,7 @@ api-webapp/src/main/kotlin/org/jeudego/pairgoth
|
||||
└── web .................................... Web interface
|
||||
```
|
||||
|
||||
Tests are located in `webapp/src/test/kotlin`
|
||||
Tests are located in `api-webapp/src/test/kotlin`
|
||||
|
||||
## Building and running
|
||||
|
||||
@@ -82,5 +82,3 @@ The `./server.sh` will launch the server in debugging mode, with a remote debugg
|
||||
The `./client.sh` will launch the web client in debugging mode, with a remote debugger socket on port 5006.
|
||||
|
||||
The corresponding `./debug-...` scripts will do the same with additional debugging features like automatic re-compilation of CSS files, automatic reloading of template files, etc.
|
||||
|
||||
|
||||
|
@@ -27,5 +27,5 @@ smtp.user =
|
||||
smtp.password =
|
||||
|
||||
# logging
|
||||
logger.level = trace
|
||||
logger.level = info
|
||||
logger.format = [%level] %ip [%logger] %message
|
||||
|
153
doc/API.md
Normal file
153
doc/API.md
Normal file
@@ -0,0 +1,153 @@
|
||||
# Pairgoth API specification
|
||||
|
||||
## General remarks
|
||||
|
||||
The API expects an `Accept` header of `application/json`, with no encoding or an `UTF-8` encoding. Exceptions are some export operations which can have different MIME types to specify the expected format.
|
||||
|
||||
GET requests return either an array or an object, as specified below.
|
||||
|
||||
POST, PUT and DELETE requests return either the 200 HTTP code with `{ "success": true }` (with an optional `"id"` field for some POST requests), or and invalid HTTP code and (for some errors) the body `{ "success": false, "error": <error message> }`.
|
||||
|
||||
## Synopsis
|
||||
|
||||
+ /api/tour GET POST Tournaments handling
|
||||
+ /api/tour/#tid GET PUT DELETE Tournaments handling
|
||||
+ /api/tour/#tid/part GET POST Registration handling
|
||||
+ /api/tour/#tid/part/#pid GET PUT DELETE Registration handling
|
||||
+ /api/tour/#tid/team GET POST Team handling
|
||||
+ /api/tour/#tid/team/#tid GET PUT DELETE Team handling
|
||||
+ /api/tour/#tid/pair/#rn GET POST PUT DELETE Pairing
|
||||
+ /api/tour/#tid/res/#rn GET PUT Results
|
||||
+ /api/tour/#tid/standings GET Standings
|
||||
+ /api/tour/#tid/stand/#rn GET Standings
|
||||
|
||||
## Tournament handling
|
||||
|
||||
+ `GET /api/tour` Get a list of known tournaments ids
|
||||
|
||||
*output* json map (id towards shortName) of known tournaments (subject to change)
|
||||
|
||||
+ `GET /api/tour/#tid` Get the details of tournament #tid
|
||||
|
||||
*output* json object for tournament #tid
|
||||
|
||||
+ `POST /api/tour` Create a new tournament
|
||||
|
||||
*input* json object for new tournament (see `Tournament.fromJson` in the sources)
|
||||
|
||||
*output* `{ "success": true, "id": #tid }`
|
||||
|
||||
+ `PUT /api/tour/#tid` Modify a tournament
|
||||
|
||||
*input* json object for updated tournament (only id and updated fields required)
|
||||
|
||||
*output* `{ "success": true }`
|
||||
|
||||
## Players handling
|
||||
|
||||
+ `GET /api/tour/#tid/part` Get a list of registered players
|
||||
|
||||
*output* json array of known players
|
||||
|
||||
+ `GET /api/tour/#tid/part/#pid` Get regitration details for player #pid
|
||||
|
||||
*output* json object for player #pid
|
||||
|
||||
+ `POST /api/tour/#tid/part` Register a new player
|
||||
|
||||
*input* `{ "name":"..." , "firstname":"..." , "rating":<rating> , "rank":<rank> , "country":"XX" [ , "club":"Xxxx" ] [ , "final":true/false ] [ , "mmsCorrection":0 ] }`
|
||||
|
||||
*output* `{ "success": true, "id": #pid }`
|
||||
|
||||
+ `PUT /api/tour/#tid/part/#pid` Modify a player registration
|
||||
|
||||
*input* json object for updated registration (only id and updated fields required)
|
||||
|
||||
*output* `{ "success": true }`
|
||||
|
||||
+ `DELETE /api/tour/#tid/part/#pid` Delete a player registration
|
||||
|
||||
*input* `{ "id": #pid }`
|
||||
|
||||
*output* `{ "success": true }`
|
||||
|
||||
## Teams handling
|
||||
|
||||
+ `GET /api/tour/#tid/team` Get a list of registered teams
|
||||
|
||||
*output* json array of known teams
|
||||
|
||||
+ `GET /api/tour/#tid/team/#tid` Get regitration details for team #tid
|
||||
|
||||
*output* json object for team #tid
|
||||
|
||||
+ `POST /api/tour/#tid/team` Register a new team
|
||||
|
||||
*input* json object for new team
|
||||
|
||||
*output* `{ "success": true, "id": #tid }`
|
||||
|
||||
+ `PUT /api/tour/#tid/team/#tid` Modify a team registration
|
||||
|
||||
*input* json object for updated registration (only id and updated fields required)
|
||||
|
||||
*output* `{ "success": true }`
|
||||
|
||||
+ `DELETE /api/tour/#tid/team/#tid` Delete a team registration
|
||||
|
||||
*input* `{ "id": #tid }`
|
||||
|
||||
*output* `{ "success": true }`
|
||||
|
||||
|
||||
## Pairing
|
||||
|
||||
+ `GET /api/tour/#tid/pair/#rn` Get pairable players for round #rn
|
||||
|
||||
*output* `{ "games": [ games... ], "pairables:" [ #pid, ... of players not skipping and not playing the round ], "unpairables": [ #pid, ... of players skipping the round ] }`
|
||||
|
||||
+ `POST /api/tour/#tip/pair/#n` Generate pairing for round #n and given players (or string "all") ; error if already generated for provided players
|
||||
|
||||
*input* `[ "all" ]` or `[ #pid, ... ]`
|
||||
|
||||
*output* `[ { "id": #gid, "t": table, "w": #wpid, "b": #bpid, "h": handicap }, ... ]`
|
||||
|
||||
+ `PUT /api/tour/#tip/pair/#n` Manual pairing (with optional handicap)
|
||||
|
||||
*input* `{ "id": #gid, "w": #wpid, "b": #bpid, "h": <handicap> }`
|
||||
|
||||
*output* `{ "success": true }`
|
||||
|
||||
+ `DELETE /api/tour/#tip/pair/#n` Delete pairing for round #n and given players (or string "all") ; games with results entered are skipped
|
||||
|
||||
*input* `[ "all" ]` or `[ #gid, ... ]`
|
||||
|
||||
*output* `{ "success": true }`
|
||||
|
||||
## Results
|
||||
|
||||
+ `GET /api/tour/#tip/res/#rn` Get results for round #rn
|
||||
|
||||
*output* `[ { "id": #gid, "res": <result> } ]` with `res` being one of: `"w"`, `"b"`, `"="` (jigo), `"x"` (cancelled),`"?"` (unknown), `"#"` (both win), or `"0"` (both loose).
|
||||
|
||||
+ `PUT /api/tour/#tip/res/#rn` Save a result (or put it back to unknown)
|
||||
|
||||
*input* `{ "id": #gid, "res": <result> }` with `res` being one of: `"w"`, `"b"`, `"="` (jigo), `"x"` (cancelled)
|
||||
|
||||
*output* `{ "success": true }`
|
||||
|
||||
## Standings
|
||||
|
||||
+ `GET /api/tour/#tid/stand/#rn` Get standings after round #rn (or initial standings for round '0')
|
||||
|
||||
*output* `[ { "id": #pid, "place": place, "<crit>": double }, ... ]`
|
||||
where `<crit>` is the name of a criterium, among "score", "nbw", "mms", "sosm", "sososm", ...
|
||||
|
||||
## Authentication
|
||||
|
||||
+ `GET /api/token` Get the token of the currently logged user, or give an error.
|
||||
|
||||
+ `POST /api/token` Create an access token. Expects an authentication json object.
|
||||
|
||||
+ `DELETE /api/token` Delete the token of the currently logged user.
|
||||
|
81
doc/configuration.md
Normal file
81
doc/configuration.md
Normal file
@@ -0,0 +1,81 @@
|
||||
# Configuration
|
||||
|
||||
Pairgoth general configuration is done using the `pairgoth.properties` file in the installation folder.
|
||||
|
||||
## environment
|
||||
|
||||
Controls the running environment: `dev` for development, `prod` for distributed instances.
|
||||
|
||||
```
|
||||
env = prod
|
||||
```
|
||||
|
||||
## mode
|
||||
|
||||
Running mode: `standalone`, `client` or `server`.
|
||||
|
||||
```
|
||||
mode = standalone
|
||||
```
|
||||
|
||||
## authentication
|
||||
|
||||
Authentication: `none`, `sesame` for a shared unique password, `oauth` for email and/or oauth accounts.
|
||||
|
||||
```
|
||||
auth = none
|
||||
```
|
||||
|
||||
## webapp connector
|
||||
|
||||
Pairgoth webapp connector configuration.
|
||||
|
||||
```
|
||||
webapp.protocol = http
|
||||
webapp.interface = localhost
|
||||
webapp.port = 8080
|
||||
webapp.context = /
|
||||
webapp.external.url = http://localhost:8080
|
||||
```
|
||||
|
||||
## api connector
|
||||
|
||||
Pairgoth API connector configuration.
|
||||
|
||||
```
|
||||
api.protocol = http
|
||||
api.interface = localhost
|
||||
api.port = 8085
|
||||
api.context = /api
|
||||
api.external.url = http://localhost:8085/api
|
||||
```
|
||||
|
||||
## store
|
||||
|
||||
Persistent storage for tournaments, `memory` (mainly used for tests) or `file`.
|
||||
|
||||
```
|
||||
store = file
|
||||
store.file.path = tournamentfiles
|
||||
```
|
||||
|
||||
## smtp
|
||||
|
||||
SMTP configuration. Not yet functional.
|
||||
|
||||
```
|
||||
smtp.sender =
|
||||
smtp.host =
|
||||
smtp.port = 587
|
||||
smtp.user =
|
||||
smtp.password =
|
||||
```
|
||||
|
||||
## logging
|
||||
|
||||
Logging configuration.
|
||||
|
||||
```
|
||||
logger.level = info
|
||||
logger.format = [%level] %ip [%logger] %message
|
||||
```
|
17
doc/md2pdf
Executable file
17
doc/md2pdf
Executable file
@@ -0,0 +1,17 @@
|
||||
#!/bin/bash
|
||||
|
||||
# HTML doc generation. Needs 'pandoc'.
|
||||
|
||||
INPUT=$1
|
||||
BASE=$(basename -s .md $INPUT)
|
||||
|
||||
if test -f "$BASE.css"
|
||||
then
|
||||
pandoc --pdf-engine-opt=--enable-local-file-access -t html -F mermaid-filter --css $BASE.css $INPUT -o $BASE.pdf
|
||||
else
|
||||
pandoc --pdf-engine=xelatex -F mermaid-filter $INPUT -o $BASE.pdf
|
||||
# pandoc --pdf-engine-opt=--enable-local-file-access -t html -F mermaid-filter $INPUT -o $BASE.pdf
|
||||
fi
|
||||
|
||||
## See also pandoc --number-sections
|
||||
|
80
doc/model.md
Normal file
80
doc/model.md
Normal file
@@ -0,0 +1,80 @@
|
||||
# PairGoth model
|
||||
|
||||
## Entity Relationship Diagram
|
||||
|
||||
For simplicity, teams (pairgo, rengo) and teams of individuals (clubs championships) are not included.
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
|
||||
%% entities
|
||||
|
||||
Tournament {
|
||||
int id
|
||||
string type
|
||||
string name
|
||||
string shortName
|
||||
date startDate
|
||||
date endDate
|
||||
string country
|
||||
string location
|
||||
bool isOnline
|
||||
int rounds
|
||||
int gobanSize
|
||||
string rules
|
||||
int komi
|
||||
}
|
||||
|
||||
TimeSystem {
|
||||
string type
|
||||
int mainTime
|
||||
int increment
|
||||
int maxTime
|
||||
int byoyomi
|
||||
int periods
|
||||
int stones
|
||||
}
|
||||
|
||||
Pairing {
|
||||
PairingType type
|
||||
BaseParams base
|
||||
MainParams main
|
||||
SecondaryParams secondary
|
||||
GeographicalParams geo
|
||||
HandicapParams handicap
|
||||
PlacementParams place
|
||||
}
|
||||
|
||||
Game {
|
||||
int table
|
||||
int handicap
|
||||
string result
|
||||
}
|
||||
|
||||
Player {
|
||||
int id
|
||||
string name
|
||||
string firstname
|
||||
string country
|
||||
string club
|
||||
int rating
|
||||
string rank
|
||||
bool final
|
||||
array skip
|
||||
}
|
||||
|
||||
Standings {
|
||||
array criteria
|
||||
}
|
||||
|
||||
%% relationships
|
||||
|
||||
Tournament ||--|{ TimeSystem: "time system"
|
||||
Tournament ||--|{ Pairing: "pairing"
|
||||
Tournament ||--|{ Game: "round"
|
||||
Tournament }o--|{ Player: "participate(round)"
|
||||
Game ||--|| Player: "black"
|
||||
Game ||--|| Player: "white"
|
||||
Player }|--|| Standings: "position"
|
||||
|
||||
```
|
@@ -15,9 +15,9 @@
|
||||
!define LICENSE_TXT "resources/LICENSE.txt"
|
||||
|
||||
!define INSTALL_DIR "$PROGRAMFILES64\${APP_NAME}"
|
||||
!define INSTALL_TYPE "SetShellVarContext all"
|
||||
!define INSTALL_TYPE "SetShellVarContext current"
|
||||
!define DATA_DIR "$LocalAppdata\Pairgoth"
|
||||
!define REG_ROOT "HKLM"
|
||||
!define REG_ROOT "HKCU"
|
||||
!define REG_APP_PATH "Software\Microsoft\Windows\CurrentVersion\App Paths\${MAIN_APP_EXE}"
|
||||
!define UNINSTALL_PATH "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APP_NAME}"
|
||||
!define REG_START_MENU "Start Menu Folder"
|
||||
|
@@ -19,7 +19,7 @@ object EGFRatingsHandler: RatingsHandler(RatingsManager.Ratings.EGF) {
|
||||
}.mapNotNullTo(Json.MutableArray()) {
|
||||
val match = linePattern.matchEntire(it)
|
||||
if (match == null) {
|
||||
logger.error("could not parse line: $it")
|
||||
logger.debug("could not parse line: $it")
|
||||
null
|
||||
} else {
|
||||
val pairs = groups.map {
|
||||
|
@@ -17,7 +17,7 @@ object FFGRatingsHandler: RatingsHandler(RatingsManager.Ratings.FFG) {
|
||||
payload.lines().mapNotNullTo(Json.MutableArray()) { line ->
|
||||
val match = linePattern.matchEntire(line)
|
||||
if (match == null) {
|
||||
logger.error("could not parse line: $line")
|
||||
logger.debug("could not parse line: $line")
|
||||
null
|
||||
} else {
|
||||
val pairs = groups.map {
|
||||
|
@@ -110,7 +110,8 @@ abstract class RatingsHandler(val origin: RatingsManager.Ratings) {
|
||||
return response.body!!.source().readString(contentType?.charset() ?: defaultCharset())
|
||||
}
|
||||
} catch (ioe: IOException) {
|
||||
logger.error("Could not refresh ${origin.name} ratings from ${url}", ioe)
|
||||
logger.error("Could not refresh ${origin.name} ratings from ${url}: ${ioe.javaClass.name} ${ioe.message}")
|
||||
logger.debug("Could not refresh ${origin.name} ratings from ${url}", ioe)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
@@ -95,7 +95,8 @@ object RatingsManager: Runnable {
|
||||
}
|
||||
|
||||
} catch (e: Exception) {
|
||||
logger.error("could not build or refresh index", e)
|
||||
logger.error("could not build or refresh index: ${e.javaClass.name} ${e.message}")
|
||||
logger.debug("could not build or refresh index", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -41,6 +41,16 @@ class WebappManager : BaseWebappManager("View Webapp", "view") {
|
||||
else -> throw Error("Unhandled auth: $auth")
|
||||
}
|
||||
|
||||
logger.info("")
|
||||
logger.info("*****************************************************************")
|
||||
logger.info("* *")
|
||||
logger.info("* Web server is ready. *");
|
||||
logger.info("* Open a browser on http://localhost:8080 to access Pairgoth. *")
|
||||
logger.info("* Press control-c to stop the server when you are done. *")
|
||||
logger.info("* *")
|
||||
logger.info("*****************************************************************")
|
||||
logger.info("")
|
||||
|
||||
registerService("ratings", RatingsManager)
|
||||
startService("ratings")
|
||||
}
|
||||
|
Reference in New Issue
Block a user