Skip to content
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
8 changes: 8 additions & 0 deletions docs/source/cli.rst
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,13 @@ Command Line Reference
+--------------------+----------------+------------------------------------------------------------------+
| -d, --docx | DOCX file | Path to a Word document to export to |
+--------------------+----------------+------------------------------------------------------------------+
| -im, --image | PNG file | Path to an image file (PNG format) to export to |
+--------------------+----------------+------------------------------------------------------------------+
| -hm, --halfmoves | Halfmoves | Halfmove number to export an image at |
+--------------------+----------------+------------------------------------------------------------------+
| -mov, --movie | MP4 file | Path to a movie file (MP4 format) to export to |
+--------------------+----------------+------------------------------------------------------------------+
| -du, --duration | Seconds | Movie frame duration in seconds |
+--------------------+----------------+------------------------------------------------------------------+
| -vb, --verbose | | Write analysis details to the console during analysis |
+--------------------+----------------+------------------------------------------------------------------+
8 changes: 8 additions & 0 deletions docs/source/code/chess_analyser.reporting.rst
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,14 @@ chess\_analyser.reporting.images module
:show-inheritance:
:undoc-members:

chess\_analyser.reporting.movies module
---------------------------------------

.. automodule:: chess_analyser.reporting.movies
:members:
:show-inheritance:
:undoc-members:

chess\_analyser.reporting.search module
---------------------------------------

Expand Down
51 changes: 51 additions & 0 deletions docs/source/data_export.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
Data Export
===========


Exporting Analysis Results
--------------------------

To export the analysis for a game that's been loaded and analysed, use the following options:

.. code-block:: bash

run.sh --export --reference "<unique-game-reference>" --engine <engine-name> --xlsx <spreadsheet>
run.sh --export --reference "<unique-game-reference>" --engine <engine-name> --docx <document>
run.sh --export --reference "<unique-game-reference>" --engine <engine-name> --pgn <PGN>

The first form exports a report in XLSX format to the specified spreadsheet, the second exports a report in
DOCX format to the specified document file and the final form writes a PGN file for the game annotated with
the evaluation and annotations for each move.

If required, multiple outputs can be specified in a single export command:

.. code-block:: bash

run.sh --export --reference "<unique-game-reference>" --engine <engine-name> --xlsx <spreadsheet> --docx <document> --pgn <PGN>

This command exports the analysis in both XLSX and DOCX format and writes the annotated PGN file.

Exporting Images of the Board
-----------------------------

To export a PNG format image of the board position for a game that's been loaded, but not necessarily analysed, use the following options:

.. code-block:: bash

run.sh --export --reference "<unique-game-reference>" --image "<image-file-path>" --halfmoves <halfmoves>


"<halfmoves>" indicates the number of halfmoves to fast-forward by before exporting the image. It may also have the value "*" to fast-forward
to the end of the game and export the final position.

Exporting Movies of a Game
--------------------------

To export an MP4 format move of a game that's been loaded, but not necessarily analysed, use the following options:

.. code-block:: bash

run.sh --export --reference "<unique-game-reference>" --movie "<movie-file-path>" --duration <n>


"<n>" is the number of seconds for which each move in the game is displayed. The duration should be a positive number e.g. 0.5, 1.
1 change: 1 addition & 0 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ algorithms from Lichess [#2]_ and En Croissant [#3]_ to the per-move analysis re
engines
database
workflow
data_export
metadata_search
data_management
scoring
Expand Down
3 changes: 3 additions & 0 deletions docs/source/setup.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ The Chess Analysis application requires the following:
- Local copies of the chess engines (see the documentation on engine installation)
- A configured SQLite database (see the documentation on the database)

For movie export, then `ffmpeg <https://ffmpeg.org/>`_ must also be installed.

Creating a Virtual environment
------------------------------

Expand Down Expand Up @@ -40,3 +42,4 @@ cannot load library 'libcairo-2.dll': dlopen(libcairo-2.dll, 2): image not found
```

- This can be installed using Homebrew or MacPorts

37 changes: 0 additions & 37 deletions docs/source/workflow.rst
Original file line number Diff line number Diff line change
Expand Up @@ -68,41 +68,4 @@ Where:
- "--winchance" tabulates the data used to generate a "Win%" chart [#1]_
- "--info" tabulates the game headers, read from the PGN file

Exporting Analysis Results
--------------------------

To export the analysis for a game that's been loaded and analysed, use the following options:

.. code-block:: bash

run.sh --export --reference "<unique-game-reference>" --engine <engine-name> --xlsx <spreadsheet>
run.sh --export --reference "<unique-game-reference>" --engine <engine-name> --docx <document>
run.sh --export --reference "<unique-game-reference>" --engine <engine-name> --pgn <PGN>

The first form exports a report in XLSX format to the specified spreadsheet, the second exports a report in
DOCX format to the specified document file and the final form writes a PGN file for the game annotated with
the evaluation and annotations for each move.

If required, multiple outputs can be specified in a single export command:

.. code-block:: bash

run.sh --export --reference "<unique-game-reference>" --engine <engine-name> --xlsx <spreadsheet> --docx <document> --pgn <PGN>

This command exports the analysis in both XLSX and DOCX format and writes the annotated PGN file.

Exporting Images of the Board
-----------------------------

To export a PNG format image of the board position for a game that's been loaded, but not necessarily analysed, use the following options:

.. code-block:: bash

run.sh --export --reference "<unique-game-reference>" --image "<image-file-path>" --halfmoves <halfmoves>


"<halfmoves>" indicates the number of halfmoves to fast-forward by before exporting the image. It may also have the value "*" to fast-forward
to the end of the game and export the final position.


.. [#1] `Lichess win% calculation <https://lichess.org/page/accuracy>`_
9 changes: 8 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,34 @@ chess==1.11.2
contourpy==1.3.1
cssselect2==0.8.0
cycler==0.12.1
decorator==5.2.1
defusedxml==0.7.1
fonttools==4.56.0
greenlet==3.1.1
imageio==2.37.0
imageio-ffmpeg==0.6.0
kiwisolver==1.4.8
lxml==5.3.1
Mako==1.3.9
MarkupSafe==3.0.2
matplotlib==3.10.1
moviepy==2.1.2
numpy==2.2.4
packaging==24.2
pandas==2.2.3
pillow==11.1.0
pillow==10.4.0
proglog==0.1.11
pycparser==2.22
pyparsing==3.2.3
python-chess==1.999
python-dateutil==2.9.0.post0
python-docx==1.1.2
python-dotenv==1.1.0
pytz==2025.2
six==1.17.0
SQLAlchemy==2.0.40
tinycss2==1.4.0
tqdm==4.67.1
typing_extensions==4.13.0
tzdata==2025.2
webencodings==0.5.1
Expand Down
32 changes: 27 additions & 5 deletions src/chess_analyser/cli/dispatcher.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import argparse
from ..reporting import tabulate_analysis, tabulate_summary, tabulate_win_chance, \
write_analysis_spreadsheet, write_analysis_document, tabulate_players, \
tabulate_game_info, search_metadata, write_board_position_image
tabulate_game_info, search_metadata, export_board_image_after_halfmoves, export_movie
from ..analysis.analysis import analyse_game
from ..constants import PROGRAM_NAME, PROGRAM_DESCRIPTION, PROGRAM_VERSION, OPT_LOAD, OPT_ANALYSE, \
OPT_RESULTS, OPT_WHITE, OPT_BLACK, OPT_SUMMARY, OPT_WIN_CHANCE, OPT_EXPORT, OPT_PLAYERS, OPT_INFO, \
OPT_SEARCH, OPT_DELETE, OPT_VERSION, OPT_ENGINE, OPT_PGN, OPT_REFERENCE, OPT_IMAGE, \
OPT_VERBOSE, OPT_XLSX, OPT_DOCX, OPT_HALFMOVES
OPT_VERBOSE, OPT_XLSX, OPT_DOCX, OPT_HALFMOVES, OPT_MOVIE, OPT_DURATION
from ..pgn import import_pgn, export_pgn
from ..management import GAME, ANALYSIS, delete_data

Expand Down Expand Up @@ -43,15 +43,31 @@ def configure_parser():
parser.add_argument("-ref", "--reference", nargs=1, help="Reference for an imported game")
parser.add_argument("-x", "--xlsx", nargs=1, help="Path to an XLSX file to export to")
parser.add_argument("-d", "--docx", nargs=1, help="Path to a Word document to export to")
parser.add_argument("-im", "--image", nargs=1, help="Path to ain image file (PGN format) to export to")
parser.add_argument("-hm", "--halfmoves", nargs=1, help="Halfmove number to export at")
parser.add_argument("-im", "--image", nargs=1, help="Path to an image file (PNG format) to export to")
parser.add_argument("-hm", "--halfmoves", nargs=1, help="Halfmove number to export an image at")
parser.add_argument("-mov", "--movie", nargs=1, help="Path to a movie file (MP4 format) to export to")
parser.add_argument("-du", "--duration", nargs=1, help="Movie frame duration in seconds")

# Flags
parser.add_argument("-vb", "--verbose", action="store_true", help="Write analysis details to the console during analysis")

return parser


def get_float_argument_value(value):
"""
Convert an argument value to a float

:param value: Argument value
:return: Floating point number derived from the value or None
"""
try:
return float(value[0])

except ValueError:
return None


def parse_command_line():
"""
Configure the command line parser and parse the command line
Expand Down Expand Up @@ -86,6 +102,9 @@ def parse_command_line():
OPT_DOCX: args.docx[0] if args.docx else None,
OPT_IMAGE: args.image[0] if args.image else None,
OPT_HALFMOVES: int(args.halfmoves[0]) if args.halfmoves else None,
OPT_IMAGE: args.image[0] if args.image else None,
OPT_MOVIE: args.movie[0] if args.movie else None,
OPT_DURATION: get_float_argument_value(args.duration),

# Flags
OPT_VERBOSE: args.verbose
Expand Down Expand Up @@ -134,7 +153,10 @@ def dispatch_export(options):
export_pgn(options)

if options[OPT_IMAGE]:
write_board_position_image(options[OPT_REFERENCE], options[OPT_HALFMOVES], options[OPT_IMAGE])
export_board_image_after_halfmoves(options[OPT_REFERENCE], options[OPT_HALFMOVES], options[OPT_IMAGE])

if options[OPT_MOVIE]:
export_movie(options[OPT_REFERENCE], options[OPT_MOVIE], options[OPT_DURATION])


def confirm(targets):
Expand Down
2 changes: 2 additions & 0 deletions src/chess_analyser/constants/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
OPT_DOCX = "docx"
OPT_IMAGE = "image"
OPT_HALFMOVES = "halfmoves"
OPT_MOVIE = "movie"
OPT_DURATION = "duration"

OPT_VERBOSE = "verbose"

Expand Down
11 changes: 7 additions & 4 deletions src/chess_analyser/reporting/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from .console_reports import print_analysis_table_headers, print_analysis_table_row, tabulate_analysis, tabulate_summary, \
tabulate_win_chance, tabulate_players, tabulate_game_info, tabulate_games
from .document import write_analysis_document
from .images import write_board_position_image, write_win_percent_chart_image
from .images import export_current_position_image, export_board_image_after_halfmoves, export_win_percent_chart_image
from .movies import export_movie
from .spreadsheet import write_analysis_spreadsheet
from .game_info import load_game_information
from .search import search_metadata
Expand All @@ -17,9 +18,11 @@
"tabulate_game_info",
"tabulate_games",
"write_analysis_document",
"write_board_position_image",
"write_win_percent_chart_image",
"export_current_position_image",
"export_board_image_after_halfmoves",
"export_win_percent_chart_image",
"write_analysis_spreadsheet",
"load_game_information",
"search_metadata"
"search_metadata",
"export_movie"
]
6 changes: 3 additions & 3 deletions src/chess_analyser/reporting/document.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
EVALUATION_INDEX, CP_LOSS_INDEX, WIN_PERCENT_INDEX, ACCURACY_INDEX
from .constants import ANALYSIS_HEADERS, SUMMARY_HEADERS
from ..analysis.calculations import calculate_summary_statistics, extract_player_analysis
from .images import write_board_position_image, write_win_percent_chart_image
from .images import export_board_image_after_halfmoves, export_win_percent_chart_image
from .game_info import load_game_information
from docx import Document
from docx.shared import Pt
Expand Down Expand Up @@ -98,10 +98,10 @@ def write_analysis_document(options):
summary_statistics = calculate_summary_statistics(analysis)

# Generate an image of the final position
board_position_image = write_board_position_image(options[OPT_REFERENCE], "*", None)
board_position_image = export_board_image_after_halfmoves(options[OPT_REFERENCE], "*", None)

# Generate an image of the Win Chance Chart
win_percent_image = write_win_percent_chart_image(analysis)
win_percent_image = export_win_percent_chart_image(analysis)

# Create the document
document = Document()
Expand Down
30 changes: 22 additions & 8 deletions src/chess_analyser/reporting/images.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from cairosvg import svg2png
import chess
import chess.svg
from ..analysis.calculations import calculate_win_chance_chart_data
from ..database.logic import load_game
Expand All @@ -7,7 +8,7 @@
import pandas as pd


def _fast_forward_game(board, moves, halfmoves):
def fast_forward_game(board, moves, halfmoves):
"""
Fast forward a game to the point at which a number of halfmoves have been completed

Expand All @@ -32,13 +33,28 @@ def _fast_forward_game(board, moves, halfmoves):
return


def write_board_position_image(identifier, halfmoves, filename):
def export_current_position_image(board, filename):
"""
Export a PNG image of the current position of the specified board

:param board: python-chess board object
:param filename: Optional filename to export to, or None
:return: Actual filename written
"""
image_file_path = filename if filename else f"board-position-{os.getpid()}.png"
svg_image = chess.svg.board(board=board)
svg2png(bytestring=svg_image, write_to=image_file_path)
return image_file_path


def export_board_image_after_halfmoves(identifier, halfmoves, filename):
"""
Generate a PNG image of the final state of the board for a game

:param identifier: Game identifier
:param halfmoves: Fast-forward by this number of halfmoves before exporting
:param fiename: Optional output filename
:param fiename: Optional filename to export to, or None
:return: Actual filename written
"""
# Load the game
game = load_game(identifier)
Expand All @@ -49,18 +65,16 @@ def write_board_position_image(identifier, halfmoves, filename):
if not game.moves:
raise ValueError(f"No moves found for the game with ID {game.id}")

# Fast forward to the specified point in the game. A value of -1
# Fast forward to the specified point in the game
board = chess.Board()
_fast_forward_game(board, game.moves, halfmoves)

# Now create an SVG image from the board in that position and convert to PNG
image_file_path = filename if filename else f"board-position-{os.getpid()}.png"
svg_image = chess.svg.board(board=board)
svg2png(bytestring=svg_image, write_to=image_file_path)
image_file_path = export_current_position_image(board, filename)
return image_file_path


def write_win_percent_chart_image(analysis):
def export_win_percent_chart_image(analysis):
"""
Generate a PNG image containing the win percent chart

Expand Down
Loading