2014-05-08 17:46:57 +04:00
|
|
|
#!/usr/bin/env bash
|
|
|
|
# Copyright 2014 Vladimir Ivanov <ivvl82@gmail.com>
|
|
|
|
# Distributed under the terms of the GNU General Public License v2
|
|
|
|
|
2014-09-05 14:41:52 +04:00
|
|
|
REPO_DIR=
|
|
|
|
|
2014-08-31 00:44:06 +04:00
|
|
|
# Specify the tournament here
|
|
|
|
TOURNAMENT=
|
2014-05-08 17:46:57 +04:00
|
|
|
|
2014-05-21 23:19:45 +04:00
|
|
|
# Version information
|
2014-09-05 14:41:52 +04:00
|
|
|
VERSION="0.3"
|
2014-05-21 23:19:45 +04:00
|
|
|
|
2014-11-22 12:07:18 +04:00
|
|
|
# Firefox User Agent
|
|
|
|
FIREFOX_UA="Mozilla/5.0 (X11; Linux x86_64; rv:24.0) Gecko/20100101 Firefox/24.0"
|
|
|
|
|
2014-08-31 00:44:06 +04:00
|
|
|
argv0=${0##*/}
|
2014-05-21 23:19:45 +04:00
|
|
|
|
2014-08-31 00:44:06 +04:00
|
|
|
function usage {
|
2014-05-21 23:19:45 +04:00
|
|
|
cat <<EOF
|
2014-09-29 13:18:47 +04:00
|
|
|
Store chess games played on lichess.org
|
2014-05-21 23:19:45 +04:00
|
|
|
|
|
|
|
Usage:
|
2014-09-29 13:18:47 +04:00
|
|
|
$argv0 -t <num> [-u] [<url>]
|
2014-05-21 23:19:45 +04:00
|
|
|
$argv0 -h
|
|
|
|
$argv0 -v
|
|
|
|
|
2014-09-05 14:41:52 +04:00
|
|
|
Put the script under the root directory of your repository or set
|
|
|
|
inner variable REPO_DIR to it. If the tournament is not the last one
|
|
|
|
(default), store its sub-directory in inner variable TOURNAMENT.
|
2014-05-21 23:19:45 +04:00
|
|
|
|
2014-09-29 13:18:47 +04:00
|
|
|
The first form fills the results of chess games and stores its PGN
|
|
|
|
files, assuming that all the games were played by the same pair of
|
|
|
|
players on tour <num> of the tournament. The PGN files are sorted by
|
|
|
|
their timestamps, unless '-u' is set. Each game should be available
|
|
|
|
at the corresponding <url> (lichess.org).
|
|
|
|
|
|
|
|
The second form shows this help output. The third form shows version
|
|
|
|
information.
|
|
|
|
|
|
|
|
Options:
|
|
|
|
-u Don't sort games by their timestamps
|
2014-05-21 23:19:45 +04:00
|
|
|
EOF
|
|
|
|
|
|
|
|
exit "${1:-0}"
|
|
|
|
}
|
|
|
|
|
2014-08-31 00:44:06 +04:00
|
|
|
function version {
|
2014-05-21 23:19:45 +04:00
|
|
|
exec echo "${argv0}-${VERSION}"
|
|
|
|
}
|
|
|
|
|
2014-08-31 00:44:06 +04:00
|
|
|
function game_setup {
|
2014-09-05 14:41:52 +04:00
|
|
|
[[ -z $REPO_DIR ]] && REPO_DIR=`dirname "$0"`
|
2014-08-31 20:00:00 +04:00
|
|
|
# Convert REPO_DIR to an absolute path
|
2014-09-05 14:41:52 +04:00
|
|
|
[[ ! $REPO_DIR =~ ^/ ]] && REPO_DIR=$(cd ${REPO_DIR}; pwd)
|
2014-08-31 20:00:00 +04:00
|
|
|
|
2014-08-31 00:44:06 +04:00
|
|
|
# If no tournament given, set it to the last one
|
|
|
|
if [[ -z $TOURNAMENT ]]; then
|
2014-08-31 20:00:00 +04:00
|
|
|
local year_dir=$(ls -1 -d ${REPO_DIR}/[0-9][0-9][0-9][0-9]/ | tail -1)
|
|
|
|
TOURNAMENT=$(ls -1 -d ${year_dir}[0-9]-*/ | tail -1 \
|
|
|
|
| sed -E "s|${REPO_DIR}/(.*)|\1|")
|
2014-08-31 00:44:06 +04:00
|
|
|
# Remove the trailing slash
|
|
|
|
TOURNAMENT=${TOURNAMENT%/}
|
|
|
|
fi
|
|
|
|
|
|
|
|
# Configuration file for players
|
2014-08-31 20:00:00 +04:00
|
|
|
ply_ini=${REPO_DIR}/${TOURNAMENT}/players.ini
|
2014-08-31 00:44:06 +04:00
|
|
|
}
|
|
|
|
|
2014-09-29 13:18:47 +04:00
|
|
|
function game_mktemp {
|
|
|
|
# Don't sort just one game
|
|
|
|
[[ $# == 1 ]] && SORT_GAMES=false
|
|
|
|
|
|
|
|
for url in $@; do
|
|
|
|
local pgn_template="pgn"
|
|
|
|
[[ $url =~ ^(http://[^/]*)/([^/]*) ]]
|
|
|
|
# Link to annotated game PGN
|
|
|
|
local game_url=${BASH_REMATCH[1]}/game/export/${BASH_REMATCH[2]::8}.pgn
|
|
|
|
|
|
|
|
# Append the timestamp of game for sorting
|
|
|
|
if $SORT_GAMES; then
|
|
|
|
local game_api=${BASH_REMATCH[1]}/api/game/${BASH_REMATCH[2]::8}
|
|
|
|
local api_response=$(curl -q --fail --location --silent "$game_api")
|
|
|
|
[[ -z $api_response ]] && die "Unreachable game API ${game_api}"
|
|
|
|
local game_time=$(sed -En "s/.*\"timestamp\":([0-9]+).*/\1/p" <<< "$api_response")
|
|
|
|
pgn_template+="-${game_time}"
|
|
|
|
fi
|
|
|
|
|
|
|
|
# Store PGN file in a temporal location
|
|
|
|
local tmp_pgn=$(mktemp -t ${pgn_template}.XXXXXX)
|
|
|
|
TMP_PGN_FILES+=" $tmp_pgn"
|
|
|
|
trap "rm $TMP_INI_FILES $TMP_PGN_FILES" EXIT
|
|
|
|
wget -q -U "$FIREFOX_UA" -O $tmp_pgn "$game_url" \
|
|
|
|
|| die "Unreachable game PGN ${game_url}"
|
|
|
|
done
|
|
|
|
|
|
|
|
$SORT_GAMES && TMP_PGN_FILES=$(echo "$TMP_PGN_FILES" | xargs -n1 | sort | xargs)
|
|
|
|
}
|
|
|
|
|
2014-08-31 00:44:06 +04:00
|
|
|
function game_get_players {
|
2014-05-08 17:46:57 +04:00
|
|
|
# Extract players on Lichess
|
2014-09-17 23:51:09 +04:00
|
|
|
local wt_lichess=$(sed -En "s/\[White \"([^\"]*)\"\]/\1/p" $tmp_pgn)
|
|
|
|
local bk_lichess=$(sed -En "s/\[Black \"([^\"]*)\"\]/\1/p" $tmp_pgn)
|
2014-05-08 17:46:57 +04:00
|
|
|
|
|
|
|
# Get names of white and black players
|
|
|
|
local counter=1 players=()
|
2014-05-09 21:24:14 +04:00
|
|
|
game_parse_config
|
2014-05-08 17:46:57 +04:00
|
|
|
while eval "config_section_player${counter}" 2>/dev/null; do
|
|
|
|
players+=("$name")
|
2014-09-05 14:41:52 +04:00
|
|
|
[[ $lichess == $wt_lichess ]] && white=$name
|
|
|
|
[[ $lichess == $bk_lichess ]] && black=$name
|
2014-09-06 14:12:25 +04:00
|
|
|
((counter += 1))
|
2014-05-08 17:46:57 +04:00
|
|
|
done
|
2014-08-31 00:44:06 +04:00
|
|
|
[[ -z $white ]] && fix_white_player
|
|
|
|
[[ -z $black ]] && fix_black_player
|
2014-05-08 17:46:57 +04:00
|
|
|
}
|
|
|
|
|
2014-08-31 00:44:06 +04:00
|
|
|
function game_parse_config {
|
2014-05-08 17:46:57 +04:00
|
|
|
# Copy player INI file to the temporary location
|
2014-05-08 20:06:57 +04:00
|
|
|
# NOTE: an empty line is added to the file beginning in order to
|
|
|
|
# match the only first occurrence for non-GNU sed
|
2014-08-31 00:44:06 +04:00
|
|
|
echo > $tmp_ini
|
|
|
|
cat "$ply_ini" >> $tmp_ini
|
2014-05-08 17:46:57 +04:00
|
|
|
|
|
|
|
# Remove tabs or spaces around the `='
|
2014-08-31 22:49:32 +04:00
|
|
|
sed -E -i.prev "s/[[:blank:]]*=[[:blank:]]*/=/" "$tmp_ini"
|
2014-05-08 17:46:57 +04:00
|
|
|
|
|
|
|
# Transform section labels into function declaration
|
2014-08-31 22:49:32 +04:00
|
|
|
sed -E -i.prev "1,/^\[.*\]/s/^\[([^]]*)\]/config_section_\1() {/" "$tmp_ini"
|
|
|
|
sed -E -i.prev "s/^\[([^]]*)\]/}\\"$'\n'"config_section_\1() {/" "$tmp_ini"
|
2014-08-31 00:44:06 +04:00
|
|
|
echo -e "\n}" >> $tmp_ini
|
2014-05-08 17:46:57 +04:00
|
|
|
|
2014-08-31 00:44:06 +04:00
|
|
|
# Source the file
|
2014-05-08 17:46:57 +04:00
|
|
|
source "$tmp_ini"
|
|
|
|
}
|
|
|
|
|
2014-08-31 00:44:06 +04:00
|
|
|
function fix_white_player {
|
2014-05-08 17:46:57 +04:00
|
|
|
local number
|
2014-09-05 14:41:52 +04:00
|
|
|
echo "Lichess player '${wt_lichess}' not found."
|
2014-05-08 17:46:57 +04:00
|
|
|
choose_player
|
2014-08-31 00:44:06 +04:00
|
|
|
white=${players[$number]}
|
2014-05-08 17:46:57 +04:00
|
|
|
}
|
|
|
|
|
2014-08-31 00:44:06 +04:00
|
|
|
function fix_black_player {
|
2014-05-08 17:46:57 +04:00
|
|
|
local number
|
2014-09-05 14:41:52 +04:00
|
|
|
echo "Lichess player '${bk_lichess}' not found."
|
2014-05-08 17:46:57 +04:00
|
|
|
choose_player
|
2014-08-31 00:44:06 +04:00
|
|
|
black=${players[$number]}
|
2014-05-08 17:46:57 +04:00
|
|
|
}
|
|
|
|
|
2014-08-31 00:44:06 +04:00
|
|
|
function choose_player {
|
2014-05-09 16:43:20 +04:00
|
|
|
echo "Please choose a proper name from the list below:"
|
2014-05-08 17:46:57 +04:00
|
|
|
for ((i=0; i<${#players[@]}; ++i)); do
|
|
|
|
echo "$((i+1)) ${players[$i]}"
|
|
|
|
done \
|
|
|
|
| column -t \
|
2014-08-31 22:49:32 +04:00
|
|
|
| sed -E "s/^([0-9]*)/$(tput setaf 6)\1$(tput sgr0)/" # highlight number
|
2014-05-08 17:46:57 +04:00
|
|
|
echo -n "Put number> "
|
|
|
|
read number
|
2014-05-08 20:06:57 +04:00
|
|
|
|
2014-08-31 00:44:06 +04:00
|
|
|
if (( $number < 1 || $number > ${#players[@]} )); then
|
2014-05-08 20:06:57 +04:00
|
|
|
die "Incorrect player number."
|
|
|
|
fi
|
2014-09-06 14:12:25 +04:00
|
|
|
((number += -1))
|
2014-05-08 17:46:57 +04:00
|
|
|
}
|
|
|
|
|
2014-08-31 00:44:06 +04:00
|
|
|
function game_add_to_repo {
|
2014-08-31 22:49:32 +04:00
|
|
|
local date_re="[0-9?]{2}\.[0-9?]{2}\.[0-9?]{4}"
|
2014-08-31 00:44:06 +04:00
|
|
|
local correct=false length_max=0
|
2014-09-05 14:41:52 +04:00
|
|
|
# Change field separator to read a file line by line
|
|
|
|
old_IFS=$IFS IFS=$'\n'
|
|
|
|
# Check if the tour number is correct
|
|
|
|
for line in $(cat "$tour_info"); do
|
2014-08-31 11:28:56 +04:00
|
|
|
if [[ $line =~ ^(${date_re}\ +([^\ ]+)\ +-\ +([^\ ]+)) ]]; then
|
2014-09-05 14:41:52 +04:00
|
|
|
local length=${#BASH_REMATCH[1]}
|
|
|
|
local fst_ply=${BASH_REMATCH[2]} snd_ply=${BASH_REMATCH[3]}
|
|
|
|
|
|
|
|
if [[ $fst_ply == $white && $snd_ply == $black ]]; then
|
|
|
|
local length_rec=${#BASH_REMATCH[1]}
|
2014-08-31 00:44:06 +04:00
|
|
|
correct=true
|
2014-09-05 14:41:52 +04:00
|
|
|
fi
|
|
|
|
|
|
|
|
if [[ $fst_ply == $black && $snd_ply == $white ]]; then
|
2014-08-31 00:44:06 +04:00
|
|
|
local length_rec=${#BASH_REMATCH[1]}
|
2014-09-05 14:41:52 +04:00
|
|
|
|
|
|
|
local answer
|
|
|
|
echo -n "Approve game with wrong players' sides? (Y/n)> "
|
|
|
|
read answer
|
|
|
|
[[ ! $answer =~ ^(Y|y|Yes|yes)$ ]] && exit 1
|
|
|
|
white=$fst_ply black=$snd_ply correct=true
|
2014-08-31 00:44:06 +04:00
|
|
|
fi
|
2014-09-05 14:41:52 +04:00
|
|
|
|
|
|
|
# Determine the maximal length of game records
|
2014-08-31 00:44:06 +04:00
|
|
|
(( $length > $length_max )) && length_max=$length
|
2014-05-09 16:43:20 +04:00
|
|
|
fi
|
2014-09-05 14:41:52 +04:00
|
|
|
done
|
|
|
|
IFS=$old_IFS
|
|
|
|
|
2014-05-09 16:43:20 +04:00
|
|
|
if ! $correct; then
|
|
|
|
die "Game '${white} vs. ${black}' not found in ${tour_info}."
|
|
|
|
fi
|
|
|
|
|
2014-09-17 23:51:09 +04:00
|
|
|
local pgn_date=$(sed -En "s/\[Date \"([^\"]*)\"\]/\1/p" "$tmp_pgn")
|
2014-09-05 14:41:52 +04:00
|
|
|
local pgn_date=$(tr "." "-" <<< "$pgn_date")
|
2014-09-06 14:30:14 +04:00
|
|
|
pgn_dir=${REPO_DIR}/${TOURNAMENT}/tours/${TOUR}/${pgn_date}-${white}-vs-${black}
|
2014-09-05 14:41:52 +04:00
|
|
|
|
2014-08-31 00:44:06 +04:00
|
|
|
[[ -d $pgn_dir ]] && die "Directory ${pgn_dir} already exist."
|
2014-05-09 16:43:20 +04:00
|
|
|
echo "Creating directory ${pgn_dir}..."
|
|
|
|
mkdir -p "$pgn_dir"
|
2014-05-09 15:38:59 +04:00
|
|
|
|
2014-05-09 16:43:20 +04:00
|
|
|
echo "Storing PGN file..."
|
|
|
|
cp "$tmp_pgn" "${pgn_dir}/1.pgn"
|
|
|
|
|
|
|
|
echo "Updating tour_info..."
|
2014-08-31 00:44:06 +04:00
|
|
|
local game_date=${pgn_date:8:2}.${pgn_date:5:2}.${pgn_date::4}
|
2014-09-17 23:51:09 +04:00
|
|
|
local result=$(sed -En "s/\[Result \"([^\"]*)\"\]/\1/p" $tmp_pgn)
|
2014-08-31 00:44:06 +04:00
|
|
|
local spaces=$((length_max - length_rec + 1))
|
|
|
|
local sep=$(printf "%${spaces}s" " ")
|
2014-05-09 15:38:59 +04:00
|
|
|
|
2014-09-05 14:41:52 +04:00
|
|
|
# Change the representation of draw
|
|
|
|
[[ $result == 1/2-1/2 ]] && result=1/2
|
|
|
|
|
2014-08-31 22:49:32 +04:00
|
|
|
sed -E -i.orig \
|
2014-10-26 18:43:35 +04:00
|
|
|
"s|${date_re}( +${white} +- +${black})|${game_date}\1${sep}${result}|" "$tour_info"
|
2014-05-09 15:38:59 +04:00
|
|
|
rm "${tour_info}.orig"
|
|
|
|
}
|
|
|
|
|
2014-08-31 00:44:06 +04:00
|
|
|
function game_git_commit {
|
2014-08-31 20:00:00 +04:00
|
|
|
cd $REPO_DIR
|
2014-05-09 21:24:14 +04:00
|
|
|
git add "${pgn_dir}/1.pgn" "$tour_info"
|
|
|
|
git commit -m "Tour ${TOUR#0}: ${white} vs. ${black}."
|
|
|
|
git push
|
|
|
|
}
|
|
|
|
|
2014-08-31 00:44:06 +04:00
|
|
|
function die {
|
2014-09-29 13:18:47 +04:00
|
|
|
echo "$@" >&2
|
2014-05-08 17:46:57 +04:00
|
|
|
exit 1
|
|
|
|
}
|
|
|
|
|
2014-08-31 00:44:06 +04:00
|
|
|
function checkargs {
|
2014-09-29 13:18:47 +04:00
|
|
|
if [[ $opt == t ]]; then
|
|
|
|
[[ ! $OPTARG =~ ^[0-9]+$ ]] && die "Incorrect tour number."
|
2014-08-31 00:44:06 +04:00
|
|
|
TOUR=$(printf "%02g" $OPTARG)
|
2014-05-08 17:46:57 +04:00
|
|
|
fi
|
|
|
|
}
|
|
|
|
|
2014-09-29 13:18:47 +04:00
|
|
|
SORT_GAMES=true
|
|
|
|
while getopts t:uhv opt; do
|
2014-08-31 00:44:06 +04:00
|
|
|
case $opt in
|
2014-09-29 13:18:47 +04:00
|
|
|
t) checkargs ;;
|
|
|
|
u) SORT_GAMES=false ;;
|
|
|
|
h) usage ;;
|
|
|
|
v) version ;;
|
|
|
|
*) usage 1 ;;
|
2014-05-08 17:46:57 +04:00
|
|
|
esac
|
|
|
|
done
|
|
|
|
|
|
|
|
shift $(($OPTIND - 1))
|
|
|
|
# For now, tour number should be given explicitly
|
2014-09-29 13:18:47 +04:00
|
|
|
[[ -z $TOUR || $# == 0 ]] && usage 1
|
2014-08-31 00:44:06 +04:00
|
|
|
|
|
|
|
game_setup
|
2014-08-31 20:00:00 +04:00
|
|
|
tour_info=${REPO_DIR}/${TOURNAMENT}/tours/${TOUR}/tour_info
|
2014-08-31 00:44:06 +04:00
|
|
|
[[ ! -f $tour_info ]] && die "File ${tour_info} not found."
|
2014-05-21 23:19:45 +04:00
|
|
|
|
2014-08-31 00:44:06 +04:00
|
|
|
# Ensure that the repository is up-to-date
|
|
|
|
git pull
|
2014-05-08 17:46:57 +04:00
|
|
|
|
|
|
|
# Temporary files
|
|
|
|
tmp_ini=$(mktemp -t `basename $ply_ini`.XXXXXX)
|
2014-09-29 13:18:47 +04:00
|
|
|
TMP_INI_FILES="${tmp_ini} ${tmp_ini}.prev"
|
|
|
|
TMP_PGN_FILES=
|
|
|
|
game_mktemp $@
|
2014-05-08 17:46:57 +04:00
|
|
|
|
2014-05-09 21:24:14 +04:00
|
|
|
game_get_players
|
|
|
|
game_add_to_repo
|
|
|
|
game_git_commit
|
2014-05-08 17:46:57 +04:00
|
|
|
|
|
|
|
exit 0
|