Linux.org.ru chess tournament logs and tables
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

311 lines
9.5KB

  1. #!/usr/bin/env bash
  2. # Copyright 2014 Vladimir Ivanov <ivvl82@gmail.com>
  3. # Distributed under the terms of the GNU General Public License v2
  4. # REPO_DIR="https://github.com/fat0troll/lorchess"
  5. REPO_DIR=
  6. # Specify the tournament here
  7. TOURNAMENT=
  8. # Version information
  9. VERSION="0.6"
  10. argv0=${0##*/}
  11. function usage {
  12. cat <<EOF
  13. List games on tours of tournament
  14. Usage: $argv0 [options] [tours]
  15. Put the script under the root directory of your repository or set
  16. inner variable REPO_DIR to point to your local or remote (GitHub)
  17. repository. If the tournament is not the last one (default), store
  18. its sub-directory in inner variable TOURNAMENT.
  19. Tournament tours are restricted to sequence 'tours' given in the
  20. Bash-style syntax, i.e., 'tours' may include explicit numbers
  21. (tours=1 3), intervals (tours={1..5}), or any combination of them.
  22. If no tours are specified, display all started tours (use '-f' to
  23. see future tours too). By default, only unaccomplished games are
  24. listed.
  25. Options:
  26. -a List accomplished games too
  27. -f Allow to display future tours
  28. -p PLAYER Show game if PLAYER is a part of a player's name
  29. -o FORMAT Specify the output format; FORMAT is 'term' (default),
  30. 'html', or 'lor'
  31. -l Add the URL link to game (if accomplished)
  32. -h Show this help output
  33. -v Show version information
  34. EOF
  35. exit "${1:-0}"
  36. }
  37. function version {
  38. exec echo "${argv0}-${VERSION}"
  39. }
  40. function setup_repo {
  41. [[ -z $REPO_DIR ]] && REPO_DIR=`dirname "$0"`
  42. CURL_OPTS="-q --fail --location --silent"
  43. if [[ $REPO_DIR =~ ^https://github\.com/(.*) ]]; then
  44. REPO_API="https://api.github.com/repos/${BASH_REMATCH[1]}/contents"
  45. REPO_URL="${REPO_DIR}/raw/master"
  46. # If no tournament given, set it to the last one
  47. if [[ -z $TOURNAMENT ]]; then
  48. local year=$(curl $CURL_OPTS $REPO_API \
  49. | sed -En "/\"path\":/h;/\"type\": \"dir\"/ \
  50. {g;s|.*\"([0-9]{4})\".*|\1|p;}" | tail -1)
  51. TOURNAMENT=$(curl $CURL_OPTS ${REPO_API}/${year} \
  52. | sed -En "/\"path\":/h;/\"type\": \"dir\"/ \
  53. {g;s|.*\"(${year}/[0-9]-.*)\".*|\1|p;}" | tail -1)
  54. fi
  55. else
  56. # Convert REPO_DIR to an absolute path
  57. [[ ! $REPO_DIR =~ ^/ ]] && REPO_DIR=$(cd $REPO_DIR; pwd)
  58. REPO_URL="file://${REPO_DIR}"
  59. # If no tournament given, set it to the last one
  60. if [[ -z $TOURNAMENT ]]; then
  61. local year_dir=$(ls -1 -d ${REPO_DIR}/[0-9][0-9][0-9][0-9]/ | tail -1)
  62. TOURNAMENT=$(ls -1 -d ${year_dir}[0-9]-*/ | tail -1 \
  63. | sed -E "s|${REPO_DIR}/(.*)/|\1|")
  64. fi
  65. fi
  66. }
  67. function show_tour_sequence {
  68. local tour_seq=$@
  69. # If no tour sequence given, set it to all tours
  70. if [[ -z $tour_seq ]]; then
  71. if [[ $REPO_DIR =~ ^https://github\.com/ ]]; then
  72. tour_seq=$(curl $CURL_OPTS ${REPO_API}/${TOURNAMENT}/tours \
  73. | sed -En "/\"path\":/h;/\"type\": \"dir\"/ \
  74. {g;s|.*\"${TOURNAMENT}/tours/([0-9]{2})\".*|\1|p;}")
  75. else
  76. tour_seq=$(ls -1 -d ${REPO_DIR}/${TOURNAMENT}/tours/[0-9][0-9]/ \
  77. | sed -E "s|${REPO_DIR}/${TOURNAMENT}/tours/([0-9]{2})/|\1|")
  78. fi
  79. else
  80. # Don't care about the begging of an explicitly specified tour
  81. SHOW_FUTURE=true
  82. fi
  83. for tour in $tour_seq; do
  84. # Change tour numbers: '1' -> '01', '2' -> '02', and so on
  85. tour=$(printf "%02g" $tour)
  86. # *NOTE* The incorporation of a newline at the end of
  87. # 'tour_info' (--write-out '\n') is important and allows one
  88. # to read the last line with no trailing '\n'
  89. local tour_info=$(curl $CURL_OPTS --write-out '\n' \
  90. ${REPO_URL}/${TOURNAMENT}/tours/${tour}/tour_info)
  91. [[ -n $tour_info ]] && output_tour_info
  92. done
  93. }
  94. function output_tour_info {
  95. local date_re="([0-9?]{2})\.([0-9?]{2})\.([0-9?]{4})"
  96. local score_re="(1|½|0|\+|−)"
  97. local res_re="${score_re}-${score_re}"
  98. local tour_num= date_beg= date_end=
  99. local white= black= result= game_date=
  100. # Lines with game info
  101. local output_lines=
  102. while read line; do
  103. # Read the number of tour
  104. get_tour_num
  105. # Read the begging and end of tour
  106. get_tour_dates
  107. # Keep game info if needed, and store it in variables 'white',
  108. # 'black', 'result', and 'game_date'
  109. if keep_game_info; then
  110. output_lines+=$(sed -E "s/$date_re +//" <<< "$line")
  111. # Append the link of game to the end of output line
  112. if $SHOW_LINK; then
  113. [[ $result =~ ^(\+|-)*$ ]] || append_game_url
  114. fi
  115. output_lines+="\n"
  116. fi
  117. done <<< "$tour_info"
  118. [[ -n $output_lines ]] && eval info_output_$FORMAT
  119. }
  120. function get_tour_num {
  121. if [[ -z $tour_num ]]; then
  122. [[ $line =~ "Тур №"([0-9]+) ]] && tour_num=${BASH_REMATCH[1]}
  123. fi
  124. }
  125. function get_tour_dates {
  126. if [[ -z $date_beg || -z $date_end ]]; then
  127. if [[ $line =~ "Время проведения:"\ +($date_re)\ +-\ +($date_re) ]]; then
  128. date_beg=${BASH_REMATCH[1]} date_end=${BASH_REMATCH[5]}
  129. if ! $SHOW_FUTURE; then
  130. # Unknown beginning of tour
  131. [[ $date_beg =~ \? ]] && exit 0
  132. if date --version >/dev/null 2>&1; then
  133. local date_beg_gnu=$(sed -E "s/${date_re}/\3-\2-\1/" <<< $date_beg)
  134. local date_tour=$(date -d $date_beg_gnu +%s)
  135. else
  136. local date_beg_bsd=$(sed -E "s/${date_re}/-v\3y -v\2m -v\1d/" <<< $date_beg)
  137. local date_tour=$(date $date_beg_bsd +%s)
  138. fi
  139. local date_this=$(date +%s)
  140. # The date of tour later than today
  141. (( date_tour > date_this )) && exit 0
  142. fi
  143. # Use short dates
  144. date_beg=${date_beg::5} date_end=${date_end::5}
  145. fi
  146. fi
  147. }
  148. function keep_game_info {
  149. local keep=1
  150. if [[ $line =~ $date_re\ +([^\ ]+)\ +-\ +([^\ ]+)(\ +$res_re)? ]]; then
  151. white=${BASH_REMATCH[4]}
  152. black=${BASH_REMATCH[5]}
  153. result=${BASH_REMATCH[7]}-${BASH_REMATCH[8]}
  154. game_date=${BASH_REMATCH[3]}-${BASH_REMATCH[2]}-${BASH_REMATCH[1]}
  155. # If the player was passed as an argument, check if it is his game or not
  156. if [[ -z $PLAYER || $white =~ $PLAYER || $black =~ $PLAYER ]]; then
  157. if $SHOW_ALL; then
  158. keep=0
  159. else
  160. # Keep line if the game is not finished
  161. [[ $result == "-" ]] && keep=0
  162. fi
  163. fi
  164. fi
  165. return $keep
  166. }
  167. function append_game_url {
  168. local tour=$(printf "%02g" "$tour_num")
  169. local game_dir=${game_date}-${white}-vs-${black}
  170. local game_url=$(curl $CURL_OPTS \
  171. ${REPO_URL}/${TOURNAMENT}/tours/${tour}/${game_dir}/1.pgn \
  172. | sed -En "s/\[Site \"([a-z]+:[^\"]+)\"\]/\1/p")
  173. output_lines+=" $game_url"
  174. }
  175. function info_output_term {
  176. echo -n "$(tput setaf 2)${tour_num} тур "
  177. echo "$(tput setaf 6)(${date_beg} - ${date_end})$(tput sgr0)"
  178. for line in "$(echo -e "$output_lines")"; do
  179. # Highlight player's name
  180. if [[ -n $PLAYER ]]; then
  181. players=$(grep -o "[^ ]*${PLAYER}[^ ]*" <<< "$line")
  182. for name in $players; do
  183. # PLAYER is a part of 'game_url' or 'result'
  184. [[ $name =~ ^http:// || $name =~ ^${res_re}$ ]] && continue
  185. line=$(sed -E "s/(${name})/$(tput setaf 1)\1$(tput sgr0)/" <<< "$line")
  186. done
  187. fi
  188. # Highlight result
  189. line=$(sed -E "s/ ${res_re}( |$)/ $(tput setaf 6)\1-\2$(tput sgr0)\3/" <<< "$line")
  190. echo "$line"
  191. done
  192. echo
  193. }
  194. function info_output_lor {
  195. echo "[b]${tour_num} тур (${date_beg} - ${date_end})[/b]"
  196. echo "[list]"
  197. echo -en "$output_lines" | while read white hyphen black result url; do
  198. [[ -n $url ]] && result="[url=${url}]${result}[/url]"
  199. echo " [*] [user]${white}[/user] - [user]${black}[/user] ${result}"
  200. done
  201. echo "[/list]"
  202. echo
  203. }
  204. function info_output_html {
  205. echo "<div class=\"tour-info\">"
  206. echo " <table class=\"table table-condensed\">"
  207. echo " <caption>"
  208. echo " <strong>${tour_num} тур (${date_beg} - ${date_end})</strong>"
  209. echo " </caption>"
  210. echo " <tbody>"
  211. echo -en "$output_lines" | while read white hyphen black result url; do
  212. [[ -n $url ]] && result="<a href=\"${url}\">${result}</a>"
  213. echo " <tr>"
  214. echo " <td>${white} - ${black}</td><td class=\"result\">${result}</td>"
  215. echo " </tr>"
  216. done
  217. echo " </tbody>"
  218. echo " </table>"
  219. echo "</div>"
  220. echo
  221. }
  222. function die {
  223. echo "$@" 1>&2
  224. exit 1
  225. }
  226. function checkargs {
  227. [[ $OPTARG =~ ^-[afpolhv]$ ]] && die "Option -${opt}: argument not found"
  228. [[ $opt == o && ! $OPTARG =~ ^(term|html|lor)$ ]] && die "Incorrect FORMAT specified"
  229. }
  230. FORMAT=term
  231. SHOW_ALL=false
  232. SHOW_FUTURE=false
  233. SHOW_LINK=false
  234. while getopts afp:o:lhv opt; do
  235. case $opt in
  236. a) SHOW_ALL=true
  237. ;;
  238. f) SHOW_FUTURE=true
  239. ;;
  240. p) checkargs
  241. PLAYER=$OPTARG
  242. ;;
  243. o) checkargs
  244. FORMAT=$OPTARG
  245. ;;
  246. l) SHOW_LINK=true
  247. ;;
  248. h) usage
  249. ;;
  250. v) version
  251. ;;
  252. *) usage 1
  253. ;;
  254. esac
  255. done
  256. shift $(($OPTIND - 1))
  257. setup_repo
  258. show_tour_sequence $@
  259. exit 0