From 156ad83548c665fa95203f8fa7fb1f3a09c4c11a Mon Sep 17 00:00:00 2001 From: Jack Atkinson Date: Sat, 3 Feb 2024 16:28:50 +0000 Subject: [PATCH 01/24] Add minimal sphinx docs with autogenerated API references. --- docs/Makefile | 20 +++++++++ docs/README.md | 14 ++++++ docs/_static/.gitignore | 0 docs/api.rst | 48 +++++++++++++++++++++ docs/archeryutils.classifications.rst | 61 +++++++++++++++++++++++++++ docs/archeryutils.handicaps.rst | 29 +++++++++++++ docs/conf.py | 42 ++++++++++++++++++ docs/index.rst | 20 +++++++++ docs/make.bat | 35 +++++++++++++++ 9 files changed, 269 insertions(+) create mode 100644 docs/Makefile create mode 100644 docs/README.md create mode 100644 docs/_static/.gitignore create mode 100644 docs/api.rst create mode 100644 docs/archeryutils.classifications.rst create mode 100644 docs/archeryutils.handicaps.rst create mode 100644 docs/conf.py create mode 100644 docs/index.rst create mode 100644 docs/make.bat diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..d4bb2cb --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = . +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..afe293b --- /dev/null +++ b/docs/README.md @@ -0,0 +1,14 @@ +# archeryutils Documentation + +The documentation for archeryutils is written in restructures text and can be build +using sphinx. + +To build from this directory run: +```bash +pip install sphinx +make clean +make html +``` + +The documentation pages can then be viewed in the `_build/html/` directory, with +index.html being the main landing page. diff --git a/docs/_static/.gitignore b/docs/_static/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/docs/api.rst b/docs/api.rst new file mode 100644 index 0000000..e3c9b96 --- /dev/null +++ b/docs/api.rst @@ -0,0 +1,48 @@ +API Documentation +================= + +Base Classes +------------ + +.. autoclass:: archeryutils.Target + :members: + :undoc-members: + :show-inheritance: + +.. autoclass:: archeryutils.Pass + :members: + :undoc-members: + :show-inheritance: + +.. autoclass:: archeryutils.Round + :members: + :undoc-members: + :show-inheritance: + +Subpackages +----------- + +.. toctree:: + :maxdepth: 2 + + archeryutils.classifications + archeryutils.handicaps + +Submodules +---------- + +archeryutils.constants module +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: archeryutils.constants + :members: + :undoc-members: + :show-inheritance: + +archeryutils.load\_rounds module +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: archeryutils.load_rounds + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/archeryutils.classifications.rst b/docs/archeryutils.classifications.rst new file mode 100644 index 0000000..56ed89e --- /dev/null +++ b/docs/archeryutils.classifications.rst @@ -0,0 +1,61 @@ +archeryutils.classifications package +==================================== + +Module contents +--------------- + +.. automodule:: archeryutils.classifications + :members: + :undoc-members: + :show-inheritance: + +Submodules +---------- + +archeryutils.classifications.classifications module +--------------------------------------------------- + +.. automodule:: archeryutils.classifications.classifications + :members: + :undoc-members: + :show-inheritance: + +archeryutils.classifications.agb\_field\_classifications module +--------------------------------------------------------------- + +.. automodule:: archeryutils.classifications.agb_field_classifications + :members: + :undoc-members: + :show-inheritance: + +archeryutils.classifications.agb\_indoor\_classifications module +---------------------------------------------------------------- + +.. automodule:: archeryutils.classifications.agb_indoor_classifications + :members: + :undoc-members: + :show-inheritance: + +archeryutils.classifications.agb\_old\_indoor\_classifications module +--------------------------------------------------------------------- + +.. automodule:: archeryutils.classifications.agb_old_indoor_classifications + :members: + :undoc-members: + :show-inheritance: + +archeryutils.classifications.agb\_outdoor\_classifications module +----------------------------------------------------------------- + +.. automodule:: archeryutils.classifications.agb_outdoor_classifications + :members: + :undoc-members: + :show-inheritance: + +archeryutils.classifications.classification\_utils module +--------------------------------------------------------- + +.. automodule:: archeryutils.classifications.classification_utils + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/archeryutils.handicaps.rst b/docs/archeryutils.handicaps.rst new file mode 100644 index 0000000..cf6d037 --- /dev/null +++ b/docs/archeryutils.handicaps.rst @@ -0,0 +1,29 @@ +archeryutils.handicaps package +============================== + +Module contents +--------------- + +.. automodule:: archeryutils.handicaps + :members: + :undoc-members: + :show-inheritance: + +Submodules +---------- + +archeryutils.handicaps.handicap\_equations module +------------------------------------------------- + +.. automodule:: archeryutils.handicaps.handicap_equations + :members: + :undoc-members: + :show-inheritance: + +archeryutils.handicaps.handicap\_functions module +------------------------------------------------- + +.. automodule:: archeryutils.handicaps.handicap_functions + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..3cb4bb7 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,42 @@ +# Configuration file for the Sphinx documentation builder. +# +# For the full list of built-in configuration values, see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Project information ----------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information + +import os +import sys + +sys.path.insert(0, os.path.abspath("..")) + +project = "archeryutils" +copyright = "2024, Jack Atkinson" +author = "Jack Atkinson" +release = "0.0.0" + +# -- General configuration --------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration + +extensions = [ + "sphinx.ext.napoleon", + "sphinx.ext.todo", + "sphinx.ext.viewcode", + "sphinx.ext.autodoc", +] + +templates_path = ["_templates"] +exclude_patterns = [ + "_build", + "Thumbs.db", + ".DS_Store", + "*tests*", + ] + + +# -- Options for HTML output ------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output + +html_theme = "sphinx_rtd_theme" +html_static_path = ["_static"] diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..0577a81 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,20 @@ +.. archeryutils documentation master file, created by + sphinx-quickstart on Sun Jan 28 13:50:36 2024. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to archeryutils's documentation! +======================================== + +.. toctree:: + :maxdepth: 3 + :caption: Contents: + + api + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 0000000..32bb245 --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=. +set BUILDDIR=_build + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.https://www.sphinx-doc.org/ + exit /b 1 +) + +if "%1" == "" goto help + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd From 1c144f985d60b3263d40d80f7eaf39481a46b0c2 Mon Sep 17 00:00:00 2001 From: Jack Atkinson Date: Sat, 3 Feb 2024 16:31:08 +0000 Subject: [PATCH 02/24] Improve docstrings in targets for documentation build. --- archeryutils/targets.py | 91 ++++++++++++++++++++++++++++++----------- 1 file changed, 68 insertions(+), 23 deletions(-) diff --git a/archeryutils/targets.py b/archeryutils/targets.py index 3441a94..ffd3409 100644 --- a/archeryutils/targets.py +++ b/archeryutils/targets.py @@ -1,4 +1,4 @@ -"""Class to represent a Target for archery applications.""" +"""Module for representing a Target for archery applications.""" from typing import Union, Tuple @@ -9,28 +9,51 @@ class Target: """ Class to represent a target. + Parameters + ---------- + scoring_system : str + target face/scoring system type. + diameter : float or tuple of float, str + Target face diameter default [centimetres]. + distance : float or tuple of float, str + linear distance from archer to target default [metres]. + indoor : bool, default=False + is round indoors for arrow diameter purposes? + Attributes ---------- scoring_system : str - target face/scoring system type + target face/scoring system type. diameter : float - Target face diameter in [metres] + Target face diameter [metres]. native_diameter_unit : str - Native unit the target size is measured in. - Converts diameter and stores in [meteres] + Native unit the target size is measured in before conversion to [metres]. distance : float - linear distance from archer to target + linear distance from archer to target [metres]. native_dist_unit : str - The native unit distance is measured in - indoor : bool - is round indoors for arrow diameter purposes? default = False - - Methods - ------- - max_score() - Returns the maximum score ring value - min_score() - Returns the minimum score ring value (excluding miss) + Native unit the target distance is measured in before conversion to [metres]. + indoor : bool, default=False + is round indoors? + + Raises + ------ + ValueError + If inappropriate scoring system or units are requested. + + Examples + -------- + A target can be defined simply: + + >>> my720target = au.Target("10_zone", 122, 70.0) + + Alternatively the units for diameter and distance can be specified using tuples: + + >>> my720target = au.Target("10_zone", (122, "cm"), (70.0, "m")) + >>> myWorcestertarget = au.Target("Worcester", (16, "inches"), (20.0, "yards")) + + Indoor rounds can be flagged as such using the `indoor` parameter: + + >>> myWA18target = au.Target("10_zone", (40, "cm"), (18.0, "m"), indoor=True) """ def __init__( @@ -83,11 +106,11 @@ def __init__( ) diameter = Length.to_metres(diameter, native_diameter_unit) - self.native_dist_unit = Length.definitive_unit(native_dist_unit) - self.distance = distance - self.native_diameter_unit = Length.definitive_unit(native_diameter_unit) - self.diameter = diameter self.scoring_system = scoring_system + self.diameter = diameter + self.native_diameter_unit = Length.definitive_unit(native_diameter_unit) + self.distance = distance + self.native_dist_unit = Length.definitive_unit(native_dist_unit) self.indoor = indoor def max_score(self) -> float: @@ -96,8 +119,19 @@ def max_score(self) -> float: Returns ------- - max_score : float - maximum score possible on this target face + float + maximum score possible on this target face. + + Raises + ------ + ValueError + If a scoring system is not accounted for in the function. + + Examples + -------- + >>> mytarget = au.Target("10_zone", (122, "cm"), (70.0, "m")) + >>> mytarget.max_score() + 10.0 """ if self.scoring_system in ("5_zone"): return 9.0 @@ -132,8 +166,19 @@ def min_score(self) -> float: Returns ------- - min_score : float + float minimum score possible on this target face + + Raises + ------ + ValueError + If a scoring system is not accounted for in the function. + + Examples + -------- + >>> mytarget = au.Target("10_zone", (122, "cm"), (70.0, "m")) + >>> mytarget.min_score() + 1.0 """ if self.scoring_system in ( "5_zone", From 72949a6a2c5273dccb7eb9da2819b8ab2a076e8f Mon Sep 17 00:00:00 2001 From: Jack Atkinson Date: Sat, 3 Feb 2024 16:31:56 +0000 Subject: [PATCH 03/24] Improve docstrings in rounds for documentation build. --- archeryutils/rounds.py | 124 +++++++++++++++++++++++++++-------------- 1 file changed, 81 insertions(+), 43 deletions(-) diff --git a/archeryutils/rounds.py b/archeryutils/rounds.py index 525f44d..14941e6 100644 --- a/archeryutils/rounds.py +++ b/archeryutils/rounds.py @@ -1,4 +1,4 @@ -"""Classes to define a Pass and Round for archery applications.""" +"""Module to define a Pass and Round classes for archery applications.""" from typing import List, Union, Tuple @@ -10,30 +10,44 @@ class Pass: """ A class used to represent a Pass. - This class represents a pass of arrows, i.e. a subunit of a Round. - e.g. a single distance or half + This class represents a pass of arrows shot at a target. + It is the sub-unit of a Round. + e.g. a single distance in a round or half of a single-distance round. - Attributes + Parameters ---------- n_arrows : int - number of arrows in this pass + number of arrows in this pass. scoring_system : str - target face/scoring system type - diameter : float - face diameter in [centimetres] - distance : float - linear distance from archer to target in [metres] - dist_unit : str - The unit distance is measured in. default = 'metres' - indoor : bool - is round indoors for arrow diameter purposes? default = False - diameter_unit : str - The unit face diameter is measured in. default = 'centimetres' - - Methods - ------- - max_score() - Returns the maximum score for Pass + target face/scoring system type. + diameter : float or tuple of float, str + face diameter in [centimetres]. + distance : float or tuple of float, str + linear distance from archer to target in [metres]. + indoor : bool, default=False + is round indoors for arrow diameter purposes? + + Attributes + ---------- + n_arrows : int + number of arrows in this pass. + target : Target + A Target object defined using input parameters. + + Examples + -------- + A Pass can be defined simply as: + + >>> my720pass = au.Pass(36, "10_zone", 122, 70.0) + + Like with the Target class, the units for diameter and distance can be specified + using tuples: + + >>> myWA18pass = au.Pass(30, "10_zone", (40, "cm"), (18.0, "m"), indoor=True) + + See Also + -------- + archeryutils.Target : The `Target` class. """ # One too many arguments, but logically this structure makes sense => disable @@ -51,33 +65,33 @@ def __init__( self.target = Target(scoring_system, diameter, distance, indoor) @property - def distance(self) -> float: - """Get distance.""" - return self.target.distance - - @property - def native_dist_unit(self) -> str: - """Get native_dist_unit.""" - return self.target.native_dist_unit + def scoring_system(self) -> str: + """Get target scoring_system.""" + return self.target.scoring_system @property def diameter(self) -> float: - """Get diameter.""" + """Get target diameter [metres].""" return self.target.diameter @property def native_diameter_unit(self) -> str: - """Get native_diameter unit.""" + """Get native_diameter_unit attribute of target.""" return self.target.native_diameter_unit @property - def scoring_system(self) -> str: - """Get scoring_system.""" - return self.target.scoring_system + def distance(self) -> float: + """Get target distance in [metres].""" + return self.target.distance + + @property + def native_dist_unit(self) -> str: + """Get native_dist_unit attribute of target.""" + return self.target.native_dist_unit @property def indoor(self) -> bool: - """Get indoor.""" + """Get indoor attribute of target.""" return self.target.indoor def max_score(self) -> float: @@ -86,7 +100,7 @@ def max_score(self) -> float: Returns ------- - max_score : float + float maximum score possible on this pass """ return self.n_arrows * self.target.max_score() @@ -99,6 +113,19 @@ class Round: Describes an archer round made up of a number of Passes. e.g. for different distances. + Parameters + ---------- + name : str + Formal name of the round + passes : list of Pass + a list of Pass classes making up the round + location : str or None, default=None + string identifing where the round is shot + body : str or None, default=None + string identifing the governing body the round belongs to + family : str or None, default=None + string identifing the family the round belongs to (e.g. wa1440, western, etc.) + Attributes ---------- name : str @@ -112,12 +139,17 @@ class Round: family : str or None string identifing the family the round belongs to (e.g. wa1440, western, etc.) - Methods - ------- - get_info() - Prints information about the round including name and breakdown of passes - max_score() - Returns the maximum score for Round + Examples + -------- + Before defining a Round we need to first define the passes that make it up: + + >>> my720pass = au.Pass(36, "10_zone", 122, 70.0) + + These can now be passed to the Round constructor as a list: + + >>> my720round = au.Round("WA 720", [my720pass, my720pass]) + + Additional, optional parameters can be used to provide 'metadata' about the round. """ @@ -177,7 +209,13 @@ def max_distance(self, unit: bool = False) -> Union[float, Tuple[float, str]]: return max_dist def get_info(self) -> None: - """Print information about the Round.""" + """ + Print information about the Round. + + Prints a summary of each Pass in the round giving number of arrows, + distance, and target size. + + """ print(f"A {self.name} consists of {len(self.passes)} passes:") for pass_i in self.passes: native_dist = Length.from_metres( From 10a65f95bfac08268bbf73db9784bb28e5146850 Mon Sep 17 00:00:00 2001 From: Jack Atkinson Date: Sat, 3 Feb 2024 18:07:26 +0000 Subject: [PATCH 04/24] Improve docstrings in constants for documentation build. --- archeryutils/constants.py | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/archeryutils/constants.py b/archeryutils/constants.py index a38110b..cbbf4b1 100644 --- a/archeryutils/constants.py +++ b/archeryutils/constants.py @@ -65,21 +65,6 @@ class Length: units as sets to allow easy membership checks in combination. Methods for conversions to and from metres are provided as classmethods. - Attributes - ---------- - yard : set[str] - metre : set[str] - cm: set[str] - inch: set[str] - - Methods - ------- - to_metres() - Convert distance in any supported unit to metres - from_metres() - Convert distance in metres to any supported unit - definitive_name() - Convert any string alias representing a distance unit to a single version. """ yard = _YARD_ALIASES @@ -98,7 +83,7 @@ class Length: @classmethod def to_metres(cls, value: float, unit: str) -> float: """ - Convert value in metres to given unit. + Convert distance in any supported unit to metres. Parameters ---------- @@ -122,7 +107,7 @@ def to_metres(cls, value: float, unit: str) -> float: @classmethod def from_metres(cls, metre_value: float, unit: str) -> float: """ - Convert value in given unit to metres. + Convert distance in metres to specified unit. Parameters ---------- @@ -146,7 +131,7 @@ def from_metres(cls, metre_value: float, unit: str) -> float: @classmethod def definitive_unit(cls, alias: str) -> str: """ - Convert alias for unit into a single definied name set in constants. + Convert any string alias representing a distance unit to a single version. Parameters ---------- From d07f84b7db008e865118773c2a0cde823abfb05a Mon Sep 17 00:00:00 2001 From: Jack Atkinson Date: Sat, 3 Feb 2024 21:46:33 +0000 Subject: [PATCH 05/24] Improve docstrings in handicaps for documentation build. --- archeryutils/handicaps/handicap_equations.py | 211 +++++++------------ archeryutils/handicaps/handicap_functions.py | 109 ++++++---- 2 files changed, 145 insertions(+), 175 deletions(-) diff --git a/archeryutils/handicaps/handicap_equations.py b/archeryutils/handicaps/handicap_equations.py index 87aa515..43652f7 100644 --- a/archeryutils/handicaps/handicap_equations.py +++ b/archeryutils/handicaps/handicap_equations.py @@ -1,40 +1,50 @@ """ -Code for archery handicap scheme calculations using various schemes. +Code for archery handicap calculations using various handicap schemes. Extended Summary ---------------- Code to calculate information using a number of schemes: - - Old Archery GB (D Lane) - - New Archery GB (J Atkinson, 2023) - - Old Archery Australia (J Park) - - New Archery Australia (J Park) + +- Old Archery GB (D Lane) +- New Archery GB (J Atkinson, 2023) +- Old Archery Australia (J Park) +- New Archery Australia (J Park) + Calculates arrow and round scores for a variety of target faces of given distance and diameter: - - 5-zone - - 10-zone - - 10-zone 6-ring - - 10-zone compound - - 10-zone 5-ring - - 10-zone 5-ring compound - - WA_field - - IFAA_field - - Beiter-hit-miss - - Worcester - - Worcester 2-ring) + +- 5-zone +- 10-zone +- 10-zone 6-ring +- 10-zone compound +- 10-zone 5-ring +- 10-zone 5-ring compound +- WA_field +- IFAA_field +- Beiter-hit-miss +- Worcester +- Worcester 2-ring) Routine Listings ---------------- -HcParams -sigma_t -sigma_r -arrow_score -score_for_round +- HcParams Class +- sigma_t +- sigma_r +- arrow_score +- score_for_passes +- score_for_round References ---------- -Old AGB - D Lane -New AGB - J Atkinson -AA & AA2 - J Park +- Old AGB: The construction of the graduated handicap tables for target archery + Lane, D (2013) + https://www.jackatkinson.net/files/Handicap_Tables_2013.pdf +- New AGB: Atkinson, J +- AA: Modelling archers’ scores at different distances to quantify score loss due to + equipment selection and technique errors + Park, J (2014) + https://doi.org/10.1177%2F1754337114539308 + """ import json @@ -48,123 +58,67 @@ @dataclass class HcParams: - """ - Class to hold information for various handicap schemes. - - Attributes - ---------- - KEY PARAMETERS AND CONSTANTS FOR NEW AGB HANDICAP SCHEME - AGB_datum : float - offset required to set handicap 0 at desired score - AGB_step : float - percentage change in group size for each handicap step - AGB_ang_0 : float - baseline angle used for group size 0.5 [millirad] - AGB_kd : float - distance scaling factor [1/metres] - - KEY PARAMETERS AND CONSTANTS FOR OLD AGB HANDICAP SCHEME - AGBo_datum : float - offset required to set handicap 0 at desired score - AGBo_step : float - percentage change in group size for each handicap step - AGBo_ang_0 : float - baseline angle used for group size 0.5 [millirad] - AGBo_k1 : float - constant used in handicap equation - AGBo_k2 : float - constant used in handicap equation - AGBo_k3 : float - constant used in handicap equation - AGBo_p1 : float - exponent of distance scaling - - KEY PARAMETERS AND CONSTANTS FOR THE ARCHERY AUSTRALIA SCHEME - AA_k0 : float - offset required to set handicap 100 at desired score - AA_ks : float - change with each step of geometric progression - AA_kd : float - distance scaling factor [1/metres] - - KEY PARAMETERS AND CONSTANTS FOR THE UPDATED ARCHERY AUSTRALIA SCHEME - AA2_k0 : float - offset required to set handicap 100 at desired score - AA2_ks : float - change with each step of geometric progression - AA2_f1 : float - 'linear' scaling factor - AA2_f2 : float - 'quadratic' scaling factor - AA2_d0 : float - Normalisation distance [metres] - - DEFAULT ARROW DIAMETER - arw_d_in : float - Diameter of an indoor arrow [metres] - arw_d_out : float - Diameter of an outdoor arrow [metres] - AGBo_arw_d : float - arrow diameter used in the old AGB algorithm by D. Lane [metres] - AA_arw_d_out : float - Diameter of an outdoor arrow in the Archery Australia scheme [metres] - - """ + """Class to hold information for various handicap schemes.""" + # Key values for the new Archery GB handicap scheme [Atkinson]. agb_hc_data: dict[str, float] = field( default_factory=lambda: ( { - "AGB_datum": 6.0, - "AGB_step": 3.5, - "AGB_ang_0": 5.0e-4, - "AGB_kd": 0.00365, + "AGB_datum": 6.0, # Offset required to set handicap 0 at desired score. + "AGB_step": 3.5, # Percentage change in group size for each handicap step. + "AGB_ang_0": 5.0e-4, # Baseline angle used for group size 0.5 [millirad]. + "AGB_kd": 0.00365, # Distance scaling factor [1/metres]. } ) ) + # Key values for the old Archery GB handicap scheme [Lane]. agb_old_hc_data: dict[str, float] = field( default_factory=lambda: ( { - "AGBo_datum": 12.9, - "AGBo_step": 3.6, - "AGBo_ang_0": 5.0e-4, - "AGBo_k1": 1.429e-6, - "AGBo_k2": 1.07, - "AGBo_k3": 4.3, - "AGBo_p1": 2.0, + "AGBo_datum": 12.9, # Offset required to set handicap 0 at desired score. + "AGBo_step": 3.6, # Percentage change in group size for each handicap step. + "AGBo_ang_0": 5.0e-4, # Baseline angle used for group size 0.5 [millirad]. + "AGBo_k1": 1.429e-6, # Constant 1 used in handicap equation. + "AGBo_k2": 1.07, # Constant 2 used in handicap equation. + "AGBo_k3": 4.3, # Constant 3 used in handicap equation. + "AGBo_p1": 2.0, # Exponent of distance scaling. } ) ) + # Key values for the original Archery Australia scheme [Park]. aa_hc_data: dict[str, float] = field( default_factory=lambda: ( { - "AA_k0": 2.37, - "AA_ks": 0.027, - "AA_kd": 0.004, + "AA_k0": 2.37, # Offset required to set handicap 100 at desired score. + "AA_ks": 0.027, # Change with each step of geometric progression. + "AA_kd": 0.004, # Distance scaling factor [1/metres]. } ) ) + # Key values for the new Archery Australia scheme [Park]. aa2_hc_data: dict[str, float] = field( default_factory=lambda: ( { - "AA2_k0": 2.57, - "AA2_ks": 0.027, - "AA2_f1": 0.815, - "AA2_f2": 0.185, - "AA2_d0": 50.0, + "AA2_k0": 2.57, # Offset required to set handicap 100 at desired score. + "AA2_ks": 0.027, # Change with each step of geometric progression. + "AA2_f1": 0.815, # 'Linear' scaling factor. + "AA2_f2": 0.185, # 'Quadratic' scaling factor. + "AA2_d0": 50.0, # Normalisation distance [metres]. } ) ) + # Key values for arrow diameter in the different schemes. arw_d_data: dict[str, float] = field( default_factory=lambda: ( { - "arw_d_in": 9.3e-3, - "arw_d_out": 5.5e-3, - "AGBo_arw_d": 7.14e-3, - "AA_arw_d_out": 5.0e-3, + "arw_d_in": 9.3e-3, # Diameter of an indoor arrow [metres]. + "arw_d_out": 5.5e-3, # Diameter of an outdoor arrow for AGB [metres]. + "AGBo_arw_d": 7.14e-3, # Diameter of an arrow in the old AGB algorithm [metres]. + "AA_arw_d_out": 5.0e-3, # Diameter of an outdoor arrow for AA [metres]. } ) ) @@ -183,7 +137,6 @@ def load_json_params(cls, jsonpath: str) -> "HcParams": ------- json_hc_params : HcParams dataclass of handicap parameters read from file - """ json_hc_params: "HcParams" = cls() with open(jsonpath, "r", encoding="utf-8") as read_file: @@ -226,7 +179,7 @@ def sigma_t( Parameters ---------- - handicap : ndarray or float + handicap : float or ndarray handicap to calculate sigma_t at hc_sys : str identifier for handicap system @@ -237,18 +190,8 @@ def sigma_t( Returns ------- - sigma_t : float or ndarray + sig_t : float or ndarray angular deviation [rad] - - References - ---------- - - The construction of the graduated handicap tables for target archery - Lane, D (2013) - https://www.jackatkinson.net/files/Handicap_Tables_2013.pdf - - Modelling archers’ scores at different distances to quantify score loss due to - equipment selection and technique errors - Park, J (2014) - https://doi.org/10.1177%2F1754337114539308 """ # Declare sig_t type for mypy sig_t: Union[float, npt.NDArray[np.float_]] @@ -335,7 +278,7 @@ def sigma_r( Parameters ---------- - handicap : ndarray or float + handicap : float or ndarray handicap to calculate sigma_t at hc_sys : str identifier for handicap system @@ -371,18 +314,18 @@ def arrow_score( ---------- target : targets.Target A Target class specifying the target to be used - handicap : ndarray or float + handicap : floar or ndarray handicap value to calculate score for hc_sys : string identifier for the handicap system hc_dat : HcParams dataclass containing parameters for handicap equations - arw_d : float + arw_d : float or None, default=None arrow diameter in [metres] Returns ------- - s_bar : float + s_bar : float or ndarray average score of the arrow for this set of parameters References @@ -523,21 +466,20 @@ def score_for_passes( ---------- rnd : rounds.Round A Round class specifying the round being shot - handicap : ndarray or float + handicap : float or ndarray handicap value to calculate score for hc_sys : string identifier for the handicap system hc_dat : HcParams dataclass containing parameters for handicap equations - arw_d : float + arw_d : float or None, default=None arrow diameter in [metres] default = None -> (use defaults) Returns ------- - pass_scores : list of float + pass_scores : ndarray average score for each pass in the round (unrounded decimal) - """ pass_scores = np.array( [ @@ -565,23 +507,22 @@ def score_for_round( ---------- rnd : rounds.Round A Round class specifying the round being shot - handicap : ndarray or float + handicap : float or ndarray handicap value to calculate score for hc_sys : string identifier for the handicap system hc_dat : HcParams dataclass containing parameters for handicap equations - arw_d : float + arw_d : float or None, default=None arrow diameter in [metres] default = None -> (use defaults) - round_score_up : bool + round_score_up : bool, default=True round score up to nearest integer value? Returns ------- - round_score : float + round_score : float or ndarray average score of the round for this set of parameters - """ # Two too many arguments. Makes sense at the moment => disable # Could try and simplify hc_sys and hc_dat in future refactor diff --git a/archeryutils/handicaps/handicap_functions.py b/archeryutils/handicaps/handicap_functions.py index e5771a2..64ba53b 100644 --- a/archeryutils/handicaps/handicap_functions.py +++ b/archeryutils/handicaps/handicap_functions.py @@ -1,5 +1,9 @@ """ -Code for doing things with archery handicap equations. +Code providing various functionalities using the archery handicap equations. + +Makes use of the basic handicap equations in handicaps.handicap_equations to do +more elaborate things such as reverse calculation of handicap from score, +generation of handicap tables, etc. Extended Summary ---------------- @@ -8,10 +12,17 @@ Routine Listings ---------------- -handicap_from_score -print_handicap_table -abbreviate -format_row +- handicap_from_score +- get_max_score_handicap +- rootfind_score_handicap +- f_root +- print_handicap_table +- check_print_table_inputs +- clean_repeated +- abbreviate +- table_as_str +- format_row +- table_to_file """ @@ -44,7 +55,7 @@ def handicap_from_score( Parameters ---------- - score : float + score : int or float score achieved on the round rnd : rounds.Round a rounds.Round class object that was shot @@ -52,16 +63,22 @@ def handicap_from_score( identifier for the handicap system hc_dat : handicaps.handicap_equations.HcParams dataclass containing parameters for handicap equations - arw_d : float + arw_d : float or None, default=None arrow diameter in [metres] default = None - int_prec : bool + int_prec : bool, default=False display results as integers? default = False, with decimal to 2dp accuracy from rootfinder Returns ------- - hc: int or float + handicap: int or float Handicap. Has type int if int_prec is True, and otherwise has type false. + + Raises + ------ + ValueError + If an invalid score for the given round is provided. + """ # Check we have a valid score max_score = rnd.max_score() @@ -135,7 +152,7 @@ def get_max_score_handicap( dataclass containing parameters for handicap equations arw_d : float arrow diameter in [metres] default = None - int_prec : bool + int_prec : bool, default=False display results as integers? default = False Returns @@ -176,7 +193,7 @@ def get_max_score_handicap( else: warnings.warn( "Handicap requested for maximum score without integer precision.\n" - "Value returned will be first handiucap that achieves this score.\n" + "Value returned will be first handicap that achieves this score.\n" "This could cause issues if you are not working in integers.", UserWarning, ) @@ -213,10 +230,10 @@ def rootfind_score_handicap( References ---------- - Brent's Method for Root Finding in Scipy + Brent's Method for Root Finding in Scipy: + - https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.brentq.html - - https://github.com/scipy/scipy/blob/dde39b7cc7dc231cec6bf5d882c8a8b5f40e73ad/ - scipy/optimize/Zeros/brentq.c + - https://github.com/scipy/scipy/blob/dde39b7/scipy/optimize/Zeros/brentq.c """ # The rootfinding algorithm here raises pylint errors for @@ -331,7 +348,7 @@ def f_root( ---------- hc_est : float current estimate of handicap - score_est : float + score_est : int or float current estimate of score based on hc_est round_est : rounds.Round round being used @@ -344,8 +361,13 @@ def f_root( Returns ------- - val-score_est : float + float difference between desired value and score estimate + + Raises + ------ + TypeError + If an array of handicaps was provided instead of a single value. """ # One too many arguments. Makes sense at the moment => disable # Could try and simplify hc_sys and hc_dat in future refactor @@ -386,7 +408,7 @@ def print_handicap_table( Parameters ---------- - hcs : ndarray or float + hcs : float or ndarray handicap value(s) to calculate score(s) for hc_sys : string identifier for the handicap system @@ -394,19 +416,19 @@ def print_handicap_table( List of Round classes to calculate scores for hc_dat : handicaps.handicap_equations.HcParams dataclass containing parameters for handicap equations - arrow_d : float + arrow_d : float or None, default=None arrow diameter in [metres] default = None - round_scores_up : bool + round_scores_up : bool, default=True round scores up to nearest integer? default = True - clean_gaps : bool + clean_gaps : bool, default=True Remove all instances of a score except the first? default = False - printout : bool + printout : bool, default=True Print to screen? default = True - filename : str + filename : str or None, default=None filepath to save table to. default = None - csvfile : str + csvfile : str or None, default=None csv filepath to save to. default = None - int_prec : bool + int_prec : bool, default=False display results as integers? default = False, with decimal to 2dp Returns @@ -487,17 +509,24 @@ def check_print_table_inputs( Parameters ---------- - hcs : ndarray or float + hcs : float or ndarray handicap value(s) to calculate score(s) for round_list : list of rounds.Round List of Round classes to calculate scores for - clean_gaps : bool + clean_gaps : bool, default=True Remove all instances of a score except the first? default = False Returns ------- hcs : ndarray handicaps prepared for use in table printing routines + + Raises + ------ + TypeError + If handicaps not provided as float or ndarray + ValueError + If no rounds are provided for the handicap table """ if not isinstance(hcs, np.ndarray): if isinstance(hcs, list): @@ -530,16 +559,16 @@ def clean_repeated( Parameters ---------- - table : np.ndarray + table : ndarray handicap table of scores - int_prec : bool + int_prec : bool, default=False return integers, not floats? - hc_sys : str + hc_sys : str, default="AGB" handicap system used - assume AGB (high -> low) unless specified Returns ------- - table : np.ndarray + table : ndarray handicap table of scores with repetitions filtered """ # NB: This assumes scores are running highest to lowest. @@ -566,8 +595,6 @@ def abbreviate(name: str) -> str: """ Replace headings within Handicap Tables with abbreviations to keep concise. - NB: This function only works with names containing space-separated words. - Parameters ---------- name : str @@ -575,8 +602,13 @@ def abbreviate(name: str) -> str: Returns ------- - shortname : str + str abbreviated round name to replace with + + Warnings + -------- + This function only works with names containing space-separated words. + """ abbreviations = { "Compound": "C", @@ -612,7 +644,7 @@ def table_as_str( handicap value(s) to calculate score(s) for table : ndarray handicap table as array - int_prec : bool + int_prec : bool, default=False return integers, not floats? Returns @@ -649,14 +681,14 @@ def format_row( ---------- row : NDArray numpy array of table row - hc_dp : int + hc_dp : int, default=0 handicap decimal places - int_prec : bool + int_prec : bool, default=False return integers, not floats? Returns ------- - formatted_row : str + str pretty string based on input array data """ if hc_dp == 0: @@ -684,9 +716,6 @@ def table_to_file(filename: str, output_str: str) -> None: output_str : str handicap table as string to save to file - Returns - ------- - None """ print("Writing handicap table to file...", end="") with open(filename, "w", encoding="utf-8") as table_file: From 13b16b45c7488f4f6bc29caf5e514dc7ed922111 Mon Sep 17 00:00:00 2001 From: Jack Atkinson Date: Sat, 3 Feb 2024 21:47:26 +0000 Subject: [PATCH 06/24] Add type hints extension to docs build. --- docs/conf.py | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/conf.py b/docs/conf.py index 3cb4bb7..8fa6410 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -24,6 +24,7 @@ "sphinx.ext.todo", "sphinx.ext.viewcode", "sphinx.ext.autodoc", + "sphinx_toolbox.more_autodoc.typehints", ] templates_path = ["_templates"] From 3e302a56ac1161d17700878eafcd4d1c9b933ab4 Mon Sep 17 00:00:00 2001 From: Jack Atkinson Date: Sat, 3 Feb 2024 21:55:03 +0000 Subject: [PATCH 07/24] Apply black to conf in docs build. --- docs/conf.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 8fa6410..c5e5d52 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -29,11 +29,11 @@ templates_path = ["_templates"] exclude_patterns = [ - "_build", - "Thumbs.db", - ".DS_Store", - "*tests*", - ] + "_build", + "Thumbs.db", + ".DS_Store", + "*tests*", +] # -- Options for HTML output ------------------------------------------------- From 7cf3d2923501612c39c33d24f8744051cc032334 Mon Sep 17 00:00:00 2001 From: Jack Atkinson Date: Sun, 4 Feb 2024 19:34:59 +0000 Subject: [PATCH 08/24] Add examples to handicap equations. --- archeryutils/handicaps/handicap_equations.py | 118 ++++++++++++++++++- 1 file changed, 114 insertions(+), 4 deletions(-) diff --git a/archeryutils/handicaps/handicap_equations.py b/archeryutils/handicaps/handicap_equations.py index 43652f7..b2b5713 100644 --- a/archeryutils/handicaps/handicap_equations.py +++ b/archeryutils/handicaps/handicap_equations.py @@ -23,7 +23,7 @@ - IFAA_field - Beiter-hit-miss - Worcester -- Worcester 2-ring) +- Worcester 2-ring Routine Listings ---------------- @@ -192,6 +192,27 @@ def sigma_t( ------- sig_t : float or ndarray angular deviation [rad] + + Raises + ------ + ValueError + If no valid handicap scheme is specified + + Examples + -------- + Angular deviation at a distance of 25m, using the AGB handicap system at a + handicap of 10 can be calculated with: + + >>> import archeryutils.handicap_equations as hc_eq + >>> hc_params = hc_eq.HcParams() + >>> hc_eq.sigma_t(10.0, "AGB", 25.0, hc_params) + 0.0009498280098103058 + + It can also be passed an array of handicaps: + + >>> hc_eq.sigma_t(np.asarray([10.0, 50.0, 100.0]), "AGB", 25.0, hc_params) + array([0.00094983, 0.00376062, 0.02100276]) + """ # Declare sig_t type for mypy sig_t: Union[float, npt.NDArray[np.float_]] @@ -291,6 +312,22 @@ def sigma_r( ------- sig_r : float or ndarray standard deviation of group size [metres] + + Examples + -------- + Deviation (in metres) at a distance of 25m, using the AGB handicap system at a + handicap of 10 can be calculated with: + + >>> import archeryutils.handicap_equations as hc_eq + >>> hc_params = hc_eq.HcParams() + >>> hc_eq.sigma_r(10.0, "AGB", 25.0, hc_params) + 0.023745700245257646 + + It can also be passed an array of handicaps: + + >>> hc_eq.sigma_t(np.asarray([10.0, 50.0, 100.0]), "AGB", 25.0, hc_params) + array([0.0237457 , 0.09401539, 0.5250691 ]) + """ sig_t = sigma_t(handicap, hc_sys, dist, hc_dat) sig_r = dist * sig_t @@ -314,7 +351,7 @@ def arrow_score( ---------- target : targets.Target A Target class specifying the target to be used - handicap : floar or ndarray + handicap : float or ndarray handicap value to calculate score for hc_sys : string identifier for the handicap system @@ -328,10 +365,35 @@ def arrow_score( s_bar : float or ndarray average score of the arrow for this set of parameters + Raises + ------ + ValueError + If the target uses a scoring system for which no handicap calculations exist. + References ---------- - The construction of the graduated handicap tables for target archery Lane, D (2013) + + Examples + -------- + Expected arrow score on a WA720 70m target, using the AGB handicap system at a + handicap of 10 can be calculated with: + + >>> import archeryutils as au + >>> my720target = au.Target("10_zone", 122, 70.0) + >>> hc_params = au.handicap_equations.HcParams() + >>> au.handicap_equations.arrow_score(my720target, 10.0, "AGB", hc_params) + 9.401182682963338 + + It can also be passed an array of handicaps: + + >>> au.handicap_equations.sigma_t(my720target, + ... np.asarray([10.0, 50.0, 100.0]), + ... "AGB", + ... hc_params) + array([9.40118268, 6.05227962, 0.46412515]) + """ # Set arrow diameter. Use provided, if AGBold or AA/AA2 scheme set value, # otherwise select default from params based on in-/out-doors @@ -475,11 +537,34 @@ def score_for_passes( arw_d : float or None, default=None arrow diameter in [metres] default = None -> (use defaults) - Returns ------- pass_scores : ndarray average score for each pass in the round (unrounded decimal) + + Examples + -------- + Expected score for each pass on a WA1440 90m, using the AGB handicap system at a + handicap of 10 can be calculated with the code below which returns an array with + one score for each pass that makes up the round: + + >>> import archeryutils as au + >>> wa_outdoor = au.load_rounds.WA_outdoor + >>> hc_params = au.handicap_equations.HcParams() + >>> au.handicap_equations.score_for_passes(wa_outdoor.wa1440_90, 10.0, "AGB", hc_params) + array([322.84091528, 338.44257659, 338.66395001, 355.87959411]) + + It can also be passed an array of handicaps: + + >>> au.handicap_equations.score_for_passes(wa_outdoor.wa1440_90, + ... np.asarray([10.0, 50.0, 100.0]), + ... "AGB", + ... hc_params) + array([[322.84091528, 162.76200686, 8.90456718], + [338.44257659, 217.88206641, 16.70850537], + [338.66395001, 216.74407488, 16.41855209], + [355.87959411, 288.77185611, 48.47897177]]) + """ pass_scores = np.array( [ @@ -518,11 +603,36 @@ def score_for_round( round_score_up : bool, default=True round score up to nearest integer value? - Returns ------- round_score : float or ndarray average score of the round for this set of parameters + + Examples + -------- + Expected score for a WA1440 90m, using the AGB handicap system at a + handicap of 10 can be calculated with: + + >>> import archeryutils as au + >>> wa_outdoor = au.load_rounds.WA_outdoor + >>> hc_params = au.handicap_equations.HcParams() + >>> au.handicap_equations.score_for_round(wa_outdoor.wa1440_90, 10.0, "AGB", hc_params) + 1356.0 + >>> au.handicap_equations.score_for_round(wa_outdoor.wa1440_90, + ... 10.0, + ... "AGB", + ... hc_params, + ... round_score_up=False) + 1355.8270359849505 + + It can also be passed an array of handicaps: + + >>> au.handicap_equations.score_for_passes(wa_outdoor.wa1440_90, + ... np.asarray([10.0, 50.0, 100.0]), + ... "AGB", + ... hc_params) + array([1356., 887., 91.]) + """ # Two too many arguments. Makes sense at the moment => disable # Could try and simplify hc_sys and hc_dat in future refactor From 7c6fbfd21a12ea172a61dc51a657918cd97f70ce Mon Sep 17 00:00:00 2001 From: Jack Atkinson Date: Sun, 4 Feb 2024 19:35:33 +0000 Subject: [PATCH 09/24] Make FILL in handicap functions private. --- archeryutils/handicaps/handicap_functions.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/archeryutils/handicaps/handicap_functions.py b/archeryutils/handicaps/handicap_functions.py index 64ba53b..2ba2731 100644 --- a/archeryutils/handicaps/handicap_functions.py +++ b/archeryutils/handicaps/handicap_functions.py @@ -36,7 +36,7 @@ import archeryutils.handicaps.handicap_equations as hc_eq from archeryutils import rounds -FILL = -9999 +_FILL = -9999 def handicap_from_score( @@ -581,7 +581,7 @@ def clean_repeated( for jscore in range(len(row)): if table[irow, jscore] == table[irow + 1, jscore]: if int_prec: - table[irow, jscore] = FILL + table[irow, jscore] = _FILL else: table[irow, jscore] = np.nan @@ -698,7 +698,7 @@ def format_row( if int_prec: return handicap_str + "".join( - "".rjust(14) if x == FILL else f"{int(x):14d}" for x in row[1:] + "".rjust(14) if x == _FILL else f"{int(x):14d}" for x in row[1:] ) return handicap_str + "".join( "".rjust(14) if np.isnan(x) else f"{x:14.8f}" for x in row[1:] From 9a73f927178548db4ebb51661d85faa8be08cb29 Mon Sep 17 00:00:00 2001 From: Jack Atkinson Date: Sun, 4 Feb 2024 21:46:08 +0000 Subject: [PATCH 10/24] Improve docstrings in handicap_functions for documentation build. --- archeryutils/handicaps/handicap_functions.py | 48 ++++++++++++++++++-- 1 file changed, 45 insertions(+), 3 deletions(-) diff --git a/archeryutils/handicaps/handicap_functions.py b/archeryutils/handicaps/handicap_functions.py index 2ba2731..00a1cd5 100644 --- a/archeryutils/handicaps/handicap_functions.py +++ b/archeryutils/handicaps/handicap_functions.py @@ -79,6 +79,24 @@ def handicap_from_score( ValueError If an invalid score for the given round is provided. + Examples + -------- + Angular deviation at a distance of 25m, using the AGB handicap system at a + handicap of 10 can be calculated with: + + >>> import archeryutils as au + >>> hc_params = au.handicap_equations.HcParams() + >>> wa_outdoor = au.load_rounds.WA_outdoor + >>> au.handicap_functions.handicap_from_score(999, wa_outdoor.wa1440_90, "AGB", hc_params) + 43.999964586102706 + >>> au.handicap_functions.handicap_from_score( + ... 999, + ... wa_outdoor.wa1440_90, + ... "AGB", hc_params, + ... int_prec=True + ... ) + 44.0 + """ # Check we have a valid score max_score = rnd.max_score() @@ -159,6 +177,7 @@ def get_max_score_handicap( ------- handicap : float appropriate handicap for this maximum score + """ max_score = rnd.max_score() @@ -368,6 +387,7 @@ def f_root( ------ TypeError If an array of handicaps was provided instead of a single value. + """ # One too many arguments. Makes sense at the moment => disable # Could try and simplify hc_sys and hc_dat in future refactor @@ -401,7 +421,7 @@ def print_handicap_table( printout: bool = True, filename: Optional[str] = None, csvfile: Optional[str] = None, - int_prec: bool = False, + int_prec: bool = True, ) -> None: """ Generate a handicap table to screen and/or file. @@ -428,12 +448,31 @@ def print_handicap_table( filepath to save table to. default = None csvfile : str or None, default=None csv filepath to save to. default = None - int_prec : bool, default=False - display results as integers? default = False, with decimal to 2dp + int_prec : bool, default=True + display results as integers? default=True, otherwise prints decimal to 2dp Returns ------- None + + Examples + -------- + >>> import archeryutils as au + >>> hc_params = au.handicap_equations.HcParams() + >>> wa_outdoor = au.load_rounds.WA_outdoor + >>> au.handicap_functions.print_handicap_table( + ... [1.0, 2.0, 3.0, 4.0, 5.0], + ... "AGB", + ... [wa_outdoor.wa1440_90, wa_outdoor.wa1440_70, wa_outdoor.wa1440_60], + ... hc_params, + ... ) + Handicap WA 1440 (90m) WA 1440 (70m) WA 1440 (60m) + 1 1396 1412 1427 + 2 1393 1409 1425 + 3 1389 1406 1423 + 4 1385 1403 1420 + 5 1380 1399 1418 + """ # Cannot see any other way to handle the options required here => ignore # pylint: disable=too-many-arguments @@ -527,6 +566,7 @@ def check_print_table_inputs( If handicaps not provided as float or ndarray ValueError If no rounds are provided for the handicap table + """ if not isinstance(hcs, np.ndarray): if isinstance(hcs, list): @@ -570,6 +610,7 @@ def clean_repeated( ------- table : ndarray handicap table of scores with repetitions filtered + """ # NB: This assumes scores are running highest to lowest. # :. Flip AA and AA2 tables before operating. @@ -651,6 +692,7 @@ def table_as_str( ------- output_str : str Handicap table formatted as a string + """ # To ensure both terminal and file output are the same, create a single string round_names = [abbreviate(r.name) for r in round_list] From 00f0f082f52b35d9d320c8dd0680861adf6ceee7 Mon Sep 17 00:00:00 2001 From: Jack Atkinson Date: Mon, 5 Feb 2024 21:36:11 +0000 Subject: [PATCH 11/24] Update classifications files for docs providing examples, fixing typos, and making some methods private. --- .../agb_field_classifications.py | 46 ++++++++++---- .../agb_indoor_classifications.py | 45 +++++++++++++- .../agb_old_indoor_classifications.py | 45 ++++++++++++-- .../agb_outdoor_classifications.py | 61 ++++++++++++++++--- .../classifications/classification_utils.py | 40 ++++++++---- .../classifications/classifications.py | 6 +- 6 files changed, 201 insertions(+), 42 deletions(-) diff --git a/archeryutils/classifications/agb_field_classifications.py b/archeryutils/classifications/agb_field_classifications.py index b944df1..0eccabd 100644 --- a/archeryutils/classifications/agb_field_classifications.py +++ b/archeryutils/classifications/agb_field_classifications.py @@ -1,17 +1,10 @@ """ -Code for calculating Archery GB classifications. - -Extended Summary ----------------- -Code to add functionality to the basic handicap equations code -in handicap_equations.py including inverse function and display. +Code for calculating Archery GB field classifications. Routine Listings ---------------- -_make_agb_field_classification_dict calculate_agb_field_classification agb_field_classification_scores - """ # Due to structure of similar classification schemes they may trigger duplicate code. @@ -54,6 +47,7 @@ def _make_agb_field_classification_dict() -> Dict[str, Dict[str, Any]]: ---------- ArcheryGB 2023 Rules of Shooting ArcheryGB Shooting Administrative Procedures - SAP7 (2023) + """ agb_field_classes = [ "Grand Master Bowman", @@ -199,7 +193,7 @@ def calculate_agb_field_classification( ---------- roundname : str name of round shot as given by 'codename' in json - score : int + score : float numerical score on the round to calculate classification for bowstyle : str archer's bowstyle under AGB outdoor target rules @@ -213,10 +207,28 @@ def calculate_agb_field_classification( classification_from_score : str the classification appropriate for this score + Raises + ------ + ValueError + If an invalid score for the requested round is provided + References ---------- ArcheryGB 2023 Rules of Shooting ArcheryGB Shooting Administrative Procedures - SAP7 (2023) + + Examples + -------- + >>> from archeryutils import classifications as class_func + >>> class_func.calculate_agb_field_classification( + ... "wa_field_24_red_marked", + ... 247, + ... "recurve", + ... "male", + ... "adult", + ... ) + '2nd Class' + """ # Check score is valid if score < 0 or score > ALL_AGBFIELD_ROUNDS[roundname].max_score(): @@ -285,13 +297,25 @@ def agb_field_classification_scores( Returns ------- - classification_scores : ndarray - abbreviation of the classification appropriate for this score + classification_scores : list of int + scores required for each classification in descending order References ---------- ArcheryGB Rules of Shooting ArcheryGB Shooting Administrative Procedures - SAP7 + + Examples + -------- + >>> from archeryutils import classifications as class_func + >>> class_func.agb_field_classification_scores( + ... "wa_field_24_red_marked", + ... "recurve", + ... "male", + ... "adult", + ... ) + [338, 317, 288, 260, 231, 203] + """ # Unused roundname argument to keep consistency with other classification functions # pylint: disable=unused-argument diff --git a/archeryutils/classifications/agb_indoor_classifications.py b/archeryutils/classifications/agb_indoor_classifications.py index 1b50323..997bbd0 100644 --- a/archeryutils/classifications/agb_indoor_classifications.py +++ b/archeryutils/classifications/agb_indoor_classifications.py @@ -3,7 +3,6 @@ Routine Listings ---------------- -_make_agb_old_indoor_classification_dict calculate_agb_indoor_classification agb_indoor_classification_scores """ @@ -143,10 +142,28 @@ def calculate_agb_indoor_classification( classification_from_score : str the classification appropriate for this score + Raises + ------ + ValueError + If an invalid score for the requested round is provided + References ---------- ArcheryGB 2023 Rules of Shooting ArcheryGB Shooting Administrative Procedures - SAP7 (2023) + + Examples + -------- + >>> from archeryutils import classifications as class_func + >>> class_func.calculate_agb_indoor_classification( + ... "wa18", + ... 547, + ... "compound", + ... "male", + ... "50+", + ... ) + 'I-B2' + """ # Check score is valid if score < 0 or score > ALL_INDOOR_ROUNDS[roundname].max_score(): @@ -191,7 +208,7 @@ def agb_indoor_classification_scores( age_group: str, ) -> List[int]: """ - Calculate new (2023) AGB indoor classification scores for category. + Calculate 2023 AGB indoor classification scores for category. Subroutine to calculate classification scores for a specific category and round. Appropriate ArcheryGB age groups and classifications. @@ -210,12 +227,34 @@ def agb_indoor_classification_scores( Returns ------- classification_scores : ndarray - scores required for each classification band + scores required for each classification in descending order References ---------- ArcheryGB Rules of Shooting ArcheryGB Shooting Administrative Procedures - SAP7 + + Examples + -------- + >>> from archeryutils import classifications as class_func + >>> class_func.agb_indoor_classification_scores( + ... "portsmouth", + ... "barebow", + ... "male", + ... "under 12", + ... ) + [411, 360, 301, 240, 183, 134, 95, 66] + + If a classification cannot be achieved a fill value of `-9999` is returned: + + >>> class_func.agb_indoor_classification_scores( + ... "worcester", + ... "compound", + ... "female", + ... "adult", + ... ) + [-9999, -9999, 298, 289, 276, 257, 233, 200] + """ # deal with reduced categories: if bowstyle.lower() in ("flatbow", "traditional", "asiatic"): diff --git a/archeryutils/classifications/agb_old_indoor_classifications.py b/archeryutils/classifications/agb_old_indoor_classifications.py index 757f451..751f8b0 100644 --- a/archeryutils/classifications/agb_old_indoor_classifications.py +++ b/archeryutils/classifications/agb_old_indoor_classifications.py @@ -1,9 +1,8 @@ """ -Code for calculating old Archery GB indoor classifications. +Code for calculating old (pre-2023) Archery GB indoor classifications. Routine Listings ---------------- -_make_AGB_old_indoor_classification_dict calculate_AGB_old_indoor_classification AGB_old_indoor_classification_scores """ @@ -113,8 +112,21 @@ def calculate_agb_old_indoor_classification( References ---------- - ArcheryGB 2023 Rules of Shooting - ArcheryGB Shooting Administrative Procedures - SAP7 (2023) + ArcheryGB Rules of Shooting + ArcheryGB Shooting Administrative Procedures - SAP7 (pre-2023) + + Examples + -------- + >>> from archeryutils import classifications as class_func + >>> class_func.calculate_agb_old_indoor_classification( + ... "wa18", + ... 547, + ... "compound", + ... "male", + ... "adult", + ... ) + 'C' + """ # Check score is valid if score < 0 or score > ALL_INDOOR_ROUNDS[roundname].max_score(): @@ -179,12 +191,35 @@ def agb_old_indoor_classification_scores( Returns ------- classification_scores : ndarray - abbreviation of the classification appropriate for this score + scores required for each classification in descending order References ---------- ArcheryGB Rules of Shooting ArcheryGB Shooting Administrative Procedures - SAP7 + + Examples + -------- + >>> from archeryutils import classifications as class_func + >>> class_func.agb_old_indoor_classification_scores( + ... "portsmouth", + ... "barebow", + ... "male", + ... "under 12", + ... ) + [592, 582, 554, 505, 432, 315, 195, 139] + + If a classification cannot be achieved a fill value of `-9999` is returned: + + >>> class_func.agb_old_indoor_classification_scores( + ... "worcester", + ... "compound", + ... "female", + ... "adult", + ... ) + [299, 296, 279, 247, 200, 132, 65, 49] + + """ # enforce compound scoring if bowstyle.lower() in ("compound"): diff --git a/archeryutils/classifications/agb_outdoor_classifications.py b/archeryutils/classifications/agb_outdoor_classifications.py index efc06f4..586eb65 100644 --- a/archeryutils/classifications/agb_outdoor_classifications.py +++ b/archeryutils/classifications/agb_outdoor_classifications.py @@ -3,7 +3,6 @@ Routine Listings ---------------- -_make_agb_outdoor_classification_dict calculate_agb_outdoor_classification agb_outdoor_classification_scores """ @@ -111,7 +110,7 @@ def _make_agb_outdoor_classification_dict() -> Dict[str, Dict[str, Any]]: ) # Get minimum distance that must be shot for this classification - classification_dict[groupname]["min_dists"][i] = assign_min_dist( + classification_dict[groupname]["min_dists"][i] = _assign_min_dist( n_class=i, gender=gender, age_group=age["age_group"], @@ -120,7 +119,7 @@ def _make_agb_outdoor_classification_dict() -> Dict[str, Dict[str, Any]]: # Assign prestige rounds for the category classification_dict[groupname]["prestige_rounds"] = ( - assign_outdoor_prestige( + _assign_outdoor_prestige( bowstyle=bowstyle["bowstyle"], age=age["age_group"], gender=gender, @@ -131,7 +130,7 @@ def _make_agb_outdoor_classification_dict() -> Dict[str, Dict[str, Any]]: return classification_dict -def assign_min_dist( +def _assign_min_dist( n_class: int, gender: str, age_group: str, @@ -162,6 +161,12 @@ def assign_min_dist( ---------- ArcheryGB 2023 Rules of Shooting ArcheryGB Shooting Administrative Procedures - SAP7 (2023) + + References + ---------- + ArcheryGB 2023 Rules of Shooting + ArcheryGB Shooting Administrative Procedures - SAP7 (2023) + """ # List of maximum distances for use in assigning maximum distance [metres] # Use metres because corresponding yards distances are >= metric ones @@ -194,7 +199,7 @@ def assign_min_dist( return dists[-1] -def assign_outdoor_prestige( +def _assign_outdoor_prestige( bowstyle: str, gender: str, age: str, @@ -348,6 +353,24 @@ def calculate_agb_outdoor_classification( ---------- ArcheryGB 2023 Rules of Shooting ArcheryGB Shooting Administrative Procedures - SAP7 (2023) + + Raises + ------ + ValueError + If an invalid score for the requested round is provided + + Examples + -------- + >>> from archeryutils import classifications as class_func + >>> class_func.calculate_agb_outdoor_classification( + ... "hereford", + ... 858, + ... "recurve", + ... "female", + ... "under 18", + ... ) + 'B1' + """ # Check score is valid if score < 0 or score > ALL_OUTDOOR_ROUNDS[roundname].max_score(): @@ -378,7 +401,7 @@ def calculate_agb_outdoor_classification( # Check if this is a prestige round and appropriate distances # remove ineligible classes from class_data - class_data = check_prestige_distance(roundname, groupname, class_data) + class_data = _check_prestige_distance(roundname, groupname, class_data) # Of the classes remaining, what is the highest classification this score gets? to_del = [] @@ -394,7 +417,7 @@ def calculate_agb_outdoor_classification( return "UC" -def check_prestige_distance( +def _check_prestige_distance( roundname: str, groupname: str, class_data: OrderedDict[str, Dict[str, Any]] ) -> OrderedDict[str, Dict[str, Any]]: """ @@ -462,12 +485,34 @@ def agb_outdoor_classification_scores( Returns ------- classification_scores : ndarray - abbreviation of the classification appropriate for this score + scores required for each classification in descending order References ---------- ArcheryGB 2023 Rules of Shooting ArcheryGB Shooting Administrative Procedures - SAP7 (2023) + + Examples + -------- + >>> from archeryutils import classifications as class_func + >>> class_func.agb_outdoor_classification_scores( + ... "hereford", + ... "recurve", + ... "female", + ... "adult", + ... ) + [1232, 1178, 1107, 1015, 900, 763, 614, 466, 336] + + If a classification cannot be achieved a fill value of `-9999` is returned: + + >>> class_func.agb_outdoor_classification_scores( + ... "bristol_ii", + ... "recurve", + ... "female", + ... "adult", + ... ) + [-9999, -9999, -9999, -9999, -9999, 931, 797, 646, 493] + """ if bowstyle.lower() in ("traditional", "flatbow", "asiatic"): bowstyle = "Barebow" diff --git a/archeryutils/classifications/classification_utils.py b/archeryutils/classifications/classification_utils.py index a261c4d..b501c1d 100644 --- a/archeryutils/classifications/classification_utils.py +++ b/archeryutils/classifications/classification_utils.py @@ -1,8 +1,4 @@ """ -Utils for classifications. - -Extended Summary ----------------- Utilities to assist in calculations of classifications. Routine Listings @@ -30,14 +26,19 @@ def read_ages_json( Parameters ---------- - age_file : Path - path to json file + age_file : Path, default="./AGB_ages.json" + path to json file with age information Returns ------- ages : list of dict AGB age category data from file + Raises + ------ + TypeError + If the contents of the data file can't be read properly + References ---------- Archery GB Rules of Shooting @@ -60,7 +61,7 @@ def read_bowstyles_json( Parameters ---------- - bowstyles_file : Path + bowstyles_file : Path, default="./AGB_bowstyles.json" path to json file Returns @@ -68,6 +69,11 @@ def read_bowstyles_json( bowstyles : list of dict AGB bowstyle category data from file + Raises + ------ + TypeError + If the contents of the data file can't be read properly + References ---------- Archery GB Rules of Shooting @@ -90,14 +96,19 @@ def read_genders_json( Parameters ---------- - genders_file : Path + genders_file : Path, default="./AGB_genders.json" path to json file Returns ------- - genders : list of dict + genders : list of str AGB gender data from file + Raises + ------ + TypeError + If the contents of the data file can't be read properly + References ---------- Archery GB Rules of Shooting @@ -130,6 +141,13 @@ def read_classes_json( classes : dict AGB classes data from file + Raises + ------ + ValueError + If an unknown classification system is specified + TypeError + If the contents of the data file can't be read properly + References ---------- Archery GB Rules of Shooting @@ -212,7 +230,7 @@ def get_age_gender_step( Returns ------- - delta_hc_age_gender : float + float age and gender handicap step for this category's MB relative to datum """ # There is a danger that gender step overtakes age step at U15/U16 @@ -220,7 +238,7 @@ def get_age_gender_step( if gender.lower() == "female" and age_cat == 3 and age_step < gender_step: return age_cat * age_step + age_step - # For females <=3 (Under 16 or older) apply gender step and age steps + # For females age_category<=3 (Under 16 or older) apply gender step and age steps if gender.lower() == "female" and age_cat <= 3: return gender_step + age_cat * age_step diff --git a/archeryutils/classifications/classifications.py b/archeryutils/classifications/classifications.py index 87d424f..a23c244 100644 --- a/archeryutils/classifications/classifications.py +++ b/archeryutils/classifications/classifications.py @@ -6,8 +6,6 @@ Code to add functionality to the basic handicap equations code in handicap_equations.py including inverse function and display. -Routine Listings ----------------- -The contents of this module have now been abstracted into several files. -This is to keep them of a manageable size. +The contents of this module have now been abstracted into several files +in order to keep them of a manageable size. """ From e02a4e24bc247c0bd864708fee68f125ae37aa71 Mon Sep 17 00:00:00 2001 From: Jack Atkinson Date: Mon, 5 Feb 2024 21:44:13 +0000 Subject: [PATCH 12/24] Add docs option to pyproject.toml with sphinx deps. --- pyproject.toml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index eef3e54..48c0377 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,6 +38,11 @@ lint = [ "pytest-mock", "pydocstyle", ] +docs = [ + "sphinx", + "sphinx_rtd_theme", + "sphinx-toolbox", +] [project.urls] "Homepage" = "https://github.com/jatkinson1000/archeryutils" From 1ef121621e1633c22d5e24109b45dbf2e6d495b8 Mon Sep 17 00:00:00 2001 From: Jack Atkinson Date: Mon, 5 Feb 2024 21:44:37 +0000 Subject: [PATCH 13/24] Update classifiers in pyproject.toml. --- pyproject.toml | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 48c0377..25af739 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,11 +13,19 @@ readme = "README.md" license = {file = "LICENSE"} requires-python = ">=3.9" classifiers = [ - "Programming Language :: Python :: 3", "License :: OSI Approved :: MIT License", - "Operating System :: OS Independent", - "Natural Language :: English", "Development Status :: 3 - Alpha", + "Natural Language :: English", + "Programming Language :: Python :: 3", + 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', + 'Programming Language :: Python :: 3.11', + 'Programming Language :: Python :: 3.12', + 'Typing :: Typed', + "Operating System :: OS Independent", + 'Operating System :: Unix', + 'Operating System :: MacOS', + 'Operating System :: Microsoft :: Windows', # "Topic :: Software Development :: Libraries :: Python Modules", ] dependencies = [ From d4db8b38d6c758d30607a6cd50462ced9b8fdf9b Mon Sep 17 00:00:00 2001 From: Jack Atkinson Date: Mon, 5 Feb 2024 22:01:00 +0000 Subject: [PATCH 14/24] Remove individual classification files from classifications docs as duplicated in top-level module contents. --- docs/archeryutils.classifications.rst | 40 --------------------------- 1 file changed, 40 deletions(-) diff --git a/docs/archeryutils.classifications.rst b/docs/archeryutils.classifications.rst index 56ed89e..2fdfc9f 100644 --- a/docs/archeryutils.classifications.rst +++ b/docs/archeryutils.classifications.rst @@ -12,46 +12,6 @@ Module contents Submodules ---------- -archeryutils.classifications.classifications module ---------------------------------------------------- - -.. automodule:: archeryutils.classifications.classifications - :members: - :undoc-members: - :show-inheritance: - -archeryutils.classifications.agb\_field\_classifications module ---------------------------------------------------------------- - -.. automodule:: archeryutils.classifications.agb_field_classifications - :members: - :undoc-members: - :show-inheritance: - -archeryutils.classifications.agb\_indoor\_classifications module ----------------------------------------------------------------- - -.. automodule:: archeryutils.classifications.agb_indoor_classifications - :members: - :undoc-members: - :show-inheritance: - -archeryutils.classifications.agb\_old\_indoor\_classifications module ---------------------------------------------------------------------- - -.. automodule:: archeryutils.classifications.agb_old_indoor_classifications - :members: - :undoc-members: - :show-inheritance: - -archeryutils.classifications.agb\_outdoor\_classifications module ------------------------------------------------------------------ - -.. automodule:: archeryutils.classifications.agb_outdoor_classifications - :members: - :undoc-members: - :show-inheritance: - archeryutils.classifications.classification\_utils module --------------------------------------------------------- From 12c8e6c83530c4e9649c80802e340198a7d82f57 Mon Sep 17 00:00:00 2001 From: Jack Atkinson Date: Tue, 6 Feb 2024 21:51:53 +0000 Subject: [PATCH 15/24] Add getting started docs pages based on examples notebook using IPython. Move API docs to subfolder. --- .../archeryutils.classifications.rst | 0 docs/{ => api}/archeryutils.handicaps.rst | 0 docs/{api.rst => api/index.rst} | 2 + docs/conf.py | 3 + docs/getting-started/index.rst | 13 + docs/getting-started/installation.rst | 74 +++++ docs/getting-started/quickstart.rst | 294 ++++++++++++++++++ docs/index.rst | 3 +- pyproject.toml | 1 + 9 files changed, 389 insertions(+), 1 deletion(-) rename docs/{ => api}/archeryutils.classifications.rst (100%) rename docs/{ => api}/archeryutils.handicaps.rst (100%) rename docs/{api.rst => api/index.rst} (98%) create mode 100644 docs/getting-started/index.rst create mode 100644 docs/getting-started/installation.rst create mode 100644 docs/getting-started/quickstart.rst diff --git a/docs/archeryutils.classifications.rst b/docs/api/archeryutils.classifications.rst similarity index 100% rename from docs/archeryutils.classifications.rst rename to docs/api/archeryutils.classifications.rst diff --git a/docs/archeryutils.handicaps.rst b/docs/api/archeryutils.handicaps.rst similarity index 100% rename from docs/archeryutils.handicaps.rst rename to docs/api/archeryutils.handicaps.rst diff --git a/docs/api.rst b/docs/api/index.rst similarity index 98% rename from docs/api.rst rename to docs/api/index.rst index e3c9b96..60f2e3d 100644 --- a/docs/api.rst +++ b/docs/api/index.rst @@ -1,3 +1,5 @@ +.. _api_doc: + API Documentation ================= diff --git a/docs/conf.py b/docs/conf.py index c5e5d52..2642f2a 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -25,6 +25,9 @@ "sphinx.ext.viewcode", "sphinx.ext.autodoc", "sphinx_toolbox.more_autodoc.typehints", + "nbsphinx", + "IPython.sphinxext.ipython_directive", + "IPython.sphinxext.ipython_console_highlighting", ] templates_path = ["_templates"] diff --git a/docs/getting-started/index.rst b/docs/getting-started/index.rst new file mode 100644 index 0000000..26a1753 --- /dev/null +++ b/docs/getting-started/index.rst @@ -0,0 +1,13 @@ +Getting Started +=============== + +This getting started guide is intended for new users of archeryutils to get them up +and running as quickly as possible whilst introducing the main concepts. +More advanced users should consult the full :ref:`api_doc`. + +.. toctree:: + :maxdepth: 3 + :hidden: + + installation + quickstart diff --git a/docs/getting-started/installation.rst b/docs/getting-started/installation.rst new file mode 100644 index 0000000..8262233 --- /dev/null +++ b/docs/getting-started/installation.rst @@ -0,0 +1,74 @@ +.. _installing: + +Installation +============ + +Required dependencies +--------------------- + +- Python (3.9 or later) +- `numpy `__ (1.20 or later) + +.. _optional-dependencies: + +Installation Instructions +------------------------- + +archeryutils is a pure python package. +It can be installed using pip by executing:: + + python -m pip install git+https://github.com/jatkinson1000/archeryutils.git + +Development Installation +------------------------ + +To install as a developer you should clone the repository from the online repo and +install as an editable package:: + + git clone git@github.com:jatkinson1000/archeryutils.git + cd archeryutils + pip install -e . + +Testing +~~~~~~~ + +To run tests on a development installation either install +`pytest `__ using pip, or install as an optional dependency:: + + pip install -e `.[test]` + +and then run:: + + pytest ./ + +from within the base directory. + +Optional dependencies +~~~~~~~~~~~~~~~~~~~~~ + +In a similar way to pytest above, there are other optional dependencies that can be +installed with archeryutils: + +lint +^^^^ + +For applying quality control to the code. + +* black (24.1.0 or later) +* pylint +* mypy (1.0.0 or later) +* coverage +* pytest (7.2.0 or later) +* pytest-mock +* pydocstyle + +docs +^^^^ + +For building documentation. + +* sphinx +* sphinx_rtd_theme +* sphinx-toolbox +* nbsphinx + diff --git a/docs/getting-started/quickstart.rst b/docs/getting-started/quickstart.rst new file mode 100644 index 0000000..2176f9a --- /dev/null +++ b/docs/getting-started/quickstart.rst @@ -0,0 +1,294 @@ +.. _quickstart: + +Quick-Start Guide +================= + +This page contains a few quick examples of how you can use archeryutils. +For full details and functionalities you should consult the rest of the documentation. + +Examples Notebook +----------------- + +The `source repository `__ contains +`a notebook of examples `__ `exercises.ipynb` +which is very similar to the examples on this page. +This is the perfect place to learn the basic functionalities of archeryutils and +experiment yourself with what the code can do. +We advise starting by running this. + +To do so online you can access the +`Binder instance `__ +we have set up. +This will spin up an interactive online session in which you can run the code. + +Alternatively you can run it locally in your browser by downloading the notebook and +running:: + + pip install jupyter + jupyter notebook examples.ipynb + + +Importing +--------- + +.. ipython:: python + + import archeryutils as au + +Target +------ + +The most basic building block of archeryutils is the :py:class:`archeryutils.Target` +class which represents a target with a particular target face at a certain distance. + +It can be invoked by specifying a scoring system, face size (cm) and distance (m): + +.. ipython:: python + + my720target = au.Target("10_zone", 122, 70.0) + mycompound720target = au.Target("10_zone_6_ring", 80, 50.0) + +In more complicated cases specific units can be passed in with the diameter and distance +as a tuple: + +.. ipython:: python + + myWorcesterTarget = au.Target( + "Worcester", diameter=(16, "inches"), distance=(20.0, "yards"), indoor=True + ) + myIFAATarget = au.Target("IFAA_field", diameter=80, distance=(80.0, "yards")) + +The target features `max_score()` and `min_score()` methods: + +.. ipython:: python + + for target in [my720target, mycompound720target, myIFAATarget, myWorcesterTarget]: + print( + f"{target.scoring_system} has max score {target.max_score()} ", + f"and min score {target.min_score()}." + ) + +Pass +---- + +The next unit up is the :py:class:`archeryutils.Pass` - a number of arrows shot at +a target: + +.. ipython:: python + + my70mPass = au.Pass(36, "10_zone", 122, 70.0) + print(my70mPass.max_score()) + +Round +----- + +Finally we have the :py:class:`archeryutils.Round` class made up of a number of Passes. + +It may also take the following optional string arguments: + +* ``location`` - where the round is shot, e.g. 'Indoor', 'Outdoor', 'Field' etc. +* ``body`` - The governing body the round is defined by, e.g. 'WA', 'IFAA', 'AGB', 'AA' etc. +* ``family`` - The larger family of rounds to which this round belongs, e.g. 'WA1440', 'WA720', 'Nationals' etc. + + +.. ipython:: python + + my720Round = au.Round( + "WA 720 (70m)", + [my70mPass, my70mPass], + location="Outdoor Target", + body="WA", + family="WA720", + ) + +Default Rounds +-------------- + +A number of useful rounds are pre-defined and come preloaded as dictionaries that can be imported: + +.. ipython:: python + + from archeryutils import load_rounds + + agb_outdoor = load_rounds.AGB_outdoor_imperial + + for round_i in agb_outdoor.values(): + print(round_i.name) + +Individial rounds are accessible via 'dot' notation (using the alias listed in agb_outdoor.keys()) as follows: + +.. ipython:: python + + agb_outdoor.york.get_info() + + agb_outdoor.york.max_score() + +Possible options for round collections are: + +* ``AGB_outdoor_imperial`` - Archery GB outdoor imperial rounds +* ``AGB_outdoor_metric`` - Archery GB outdoor metric rounds +* ``AGB_indoor`` - Archery GB indoor rounds +* ``WA_outdoor`` - World Archery outdoor rounds +* ``WA_indoor`` - World Archery indoor rounds +* ``WA_field`` - World Archery field rounds +* ``IFAA_field`` - IFAA indoor and outdoor rounds +* ``AGB_VI`` - Archery GB Visually Impaired rounds +* ``WA_VI`` - World Archery Visually Impaired rounds +* ``custom`` - custom rounds such as individual distances, 252 awards, frostbites etc. + +Handicap Schemes +---------------- + +archeryutils features support for calculations using a number of different handicap +schemes for accuracy measurement, including those of +Archery GB (Atkinson (2023), Lane (1978)) and Archery Australia (Park (2014)). + +.. ipython:: python + + from archeryutils import handicap_equations as hc_eq + from archeryutils import handicap_functions as hc_func + + hcparams = hc_eq.HcParams() + +Given a handicap and a round we can calculate the score that would be achieved: + +.. ipython:: python + + score_from_hc = hc_eq.score_for_round( + agb_outdoor.york, + 38, + "AGB", + hcparams, + ) + + print(f"A handicap of 38 on a York is a score of {score_from_hc}.") + + pass_scores = hc_eq.score_for_passes( + agb_outdoor.york, + 38, + "AGB", + hcparams, + ) + + print(f"A handicap of 38 on a York gives pass scores of {pass_scores}.") + +Perhaps more interestingly we can take a score on a particular round and convert it +to a handicap: + +.. ipython:: python + + hc_from_score = hc_func.handicap_from_score( + 950, + agb_outdoor.york, + "AGB", + hcparams, + ) + print(f"A score of 950 on a York is a continuous handicap of {hc_from_score}.") + + hc_from_score = hc_func.handicap_from_score( + 950, + agb_outdoor.york, + "AGB", + hcparams, + int_prec=True, + ) + print(f"A score of 950 on a York is a discrete handicap of {hc_from_score}.") + +There are also inbuilt methods for generating handicap tables: + +.. ipython:: python + + handicaps = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + rounds = [ + agb_outdoor.york, + agb_outdoor.hereford, + agb_outdoor.st_george, + agb_outdoor.albion, + ] + # The following allows printing of handicap tables for an entire group of rounds: + # rounds = list(load_rounds.AGB_outdoor_imperial.values()) + + hc_func.print_handicap_table( + handicaps, + "AGB", + rounds, + hcparams, + ) + +Classifications +--------------- + +Finally there is support for the various Archery GB classification schemes. + +Given a score we can calculate the classification it achieves: + +.. ipython:: python + + from archeryutils import classifications as class_func + + # AGB Outdoor + class_from_score = class_func.calculate_agb_outdoor_classification( + "hereford", + 965, + "recurve", + "male", + "50+", + ) + print( + f"A score of 965 on a Hereford is class {class_from_score} for a 50+ male recurve." + ) + + # AGB Indoor + class_from_score = class_func.calculate_agb_indoor_classification( + "wa18", + 562, + "compound", + "female", + "adult", + ) + print( + f"A score of 562 on a WA 18 is class {class_from_score} for adult female compound." + ) + + # AGB Field + class_from_score = class_func.calculate_agb_field_classification( + "wa_field_24_blue_unmarked", + 168, + "traditional", + "male", + "under 18", + ) + print( + f"A score of 168 on a WA Unmarked 24 is class {class_from_score} for an under 18 male traditional." + ) + +Or, given a round we can output the scores required for each classification band: + +.. ipython:: python + + class_scores = class_func.agb_outdoor_classification_scores( + "hereford", + "recurve", + "male", + "adult", + ) + print(class_scores) + + class_scores = class_func.agb_indoor_classification_scores( + "portsmouth", + "compound", + "female", + "adult", + ) + print(class_scores) + + class_scores = class_func.agb_field_classification_scores( + "wa_field_24_blue_marked", + "flatbow", + "female", + "under 18", + ) + print(class_scores) + + + diff --git a/docs/index.rst b/docs/index.rst index 0577a81..503882d 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -10,7 +10,8 @@ Welcome to archeryutils's documentation! :maxdepth: 3 :caption: Contents: - api + Getting Started + API Documentation Indices and tables ================== diff --git a/pyproject.toml b/pyproject.toml index 25af739..d73ffd6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -50,6 +50,7 @@ docs = [ "sphinx", "sphinx_rtd_theme", "sphinx-toolbox", + "nbsphinx", ] [project.urls] From 67c7587ba2a40dcb0677ae5eb4707b1773ddf369 Mon Sep 17 00:00:00 2001 From: Jack Atkinson Date: Sat, 10 Feb 2024 12:46:34 +0000 Subject: [PATCH 16/24] Update getting-started docs and add missing optional dependencies to pyproject.toml. --- docs/getting-started/installation.rst | 11 +++++--- docs/getting-started/quickstart.rst | 38 ++++++++++++++++----------- pyproject.toml | 3 +++ 3 files changed, 34 insertions(+), 18 deletions(-) diff --git a/docs/getting-started/installation.rst b/docs/getting-started/installation.rst index 8262233..8c81f3c 100644 --- a/docs/getting-started/installation.rst +++ b/docs/getting-started/installation.rst @@ -52,9 +52,12 @@ installed with archeryutils: lint ^^^^ -For applying quality control to the code. +For applying quality control to the code:: + + pip install -e `.[lint]` * black (24.1.0 or later) +* jupyter-black * pylint * mypy (1.0.0 or later) * coverage @@ -65,10 +68,12 @@ For applying quality control to the code. docs ^^^^ -For building documentation. +For building documentation:: + + pip install -e `.[docs]` * sphinx * sphinx_rtd_theme * sphinx-toolbox * nbsphinx - +* blackdoc diff --git a/docs/getting-started/quickstart.rst b/docs/getting-started/quickstart.rst index 2176f9a..8524a80 100644 --- a/docs/getting-started/quickstart.rst +++ b/docs/getting-started/quickstart.rst @@ -31,6 +31,8 @@ running:: Importing --------- +To import the archeryutils package use: + .. ipython:: python import archeryutils as au @@ -65,7 +67,7 @@ The target features `max_score()` and `min_score()` methods: for target in [my720target, mycompound720target, myIFAATarget, myWorcesterTarget]: print( f"{target.scoring_system} has max score {target.max_score()} ", - f"and min score {target.min_score()}." + f"and min score {target.min_score()}.", ) Pass @@ -109,9 +111,9 @@ A number of useful rounds are pre-defined and come preloaded as dictionaries tha .. ipython:: python from archeryutils import load_rounds - + agb_outdoor = load_rounds.AGB_outdoor_imperial - + for round_i in agb_outdoor.values(): print(round_i.name) @@ -120,7 +122,7 @@ Individial rounds are accessible via 'dot' notation (using the alias listed in a .. ipython:: python agb_outdoor.york.get_info() - + agb_outdoor.york.max_score() Possible options for round collections are: @@ -147,7 +149,7 @@ Archery GB (Atkinson (2023), Lane (1978)) and Archery Australia (Park (2014)). from archeryutils import handicap_equations as hc_eq from archeryutils import handicap_functions as hc_func - + hcparams = hc_eq.HcParams() Given a handicap and a round we can calculate the score that would be achieved: @@ -160,7 +162,7 @@ Given a handicap and a round we can calculate the score that would be achieved: "AGB", hcparams, ) - + print(f"A handicap of 38 on a York is a score of {score_from_hc}.") pass_scores = hc_eq.score_for_passes( @@ -169,7 +171,7 @@ Given a handicap and a round we can calculate the score that would be achieved: "AGB", hcparams, ) - + print(f"A handicap of 38 on a York gives pass scores of {pass_scores}.") Perhaps more interestingly we can take a score on a particular round and convert it @@ -184,7 +186,7 @@ to a handicap: hcparams, ) print(f"A score of 950 on a York is a continuous handicap of {hc_from_score}.") - + hc_from_score = hc_func.handicap_from_score( 950, agb_outdoor.york, @@ -207,7 +209,7 @@ There are also inbuilt methods for generating handicap tables: ] # The following allows printing of handicap tables for an entire group of rounds: # rounds = list(load_rounds.AGB_outdoor_imperial.values()) - + hc_func.print_handicap_table( handicaps, "AGB", @@ -218,14 +220,20 @@ There are also inbuilt methods for generating handicap tables: Classifications --------------- -Finally there is support for the various Archery GB classification schemes. +Finally there is support for the various Archery GB classification schemes + +For full details see the summary on +`archerycalculator.com `_, the Archery GB website +`here `_ +and `here `_, +and the Shooting Administrative Procedures. Given a score we can calculate the classification it achieves: .. ipython:: python from archeryutils import classifications as class_func - + # AGB Outdoor class_from_score = class_func.calculate_agb_outdoor_classification( "hereford", @@ -237,7 +245,7 @@ Given a score we can calculate the classification it achieves: print( f"A score of 965 on a Hereford is class {class_from_score} for a 50+ male recurve." ) - + # AGB Indoor class_from_score = class_func.calculate_agb_indoor_classification( "wa18", @@ -249,7 +257,7 @@ Given a score we can calculate the classification it achieves: print( f"A score of 562 on a WA 18 is class {class_from_score} for adult female compound." ) - + # AGB Field class_from_score = class_func.calculate_agb_field_classification( "wa_field_24_blue_unmarked", @@ -273,7 +281,7 @@ Or, given a round we can output the scores required for each classification band "adult", ) print(class_scores) - + class_scores = class_func.agb_indoor_classification_scores( "portsmouth", "compound", @@ -281,7 +289,7 @@ Or, given a round we can output the scores required for each classification band "adult", ) print(class_scores) - + class_scores = class_func.agb_field_classification_scores( "wa_field_24_blue_marked", "flatbow", diff --git a/pyproject.toml b/pyproject.toml index d73ffd6..90e833b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -39,6 +39,7 @@ test = [ ] lint = [ "black>=24.1.0", + "jupyter-black", "pylint", "mypy>=1.0.0", "coverage", @@ -51,6 +52,8 @@ docs = [ "sphinx_rtd_theme", "sphinx-toolbox", "nbsphinx", + "black>=24.1.0", + "blackdoc", ] [project.urls] From da42b97db50b558aa2fbc3e6e4281006128f3a97 Mon Sep 17 00:00:00 2001 From: Jack Atkinson Date: Sat, 10 Feb 2024 16:12:47 +0000 Subject: [PATCH 17/24] Add developer documentation to docs/. --- docs/develop/contributing.rst | 350 ++++++++++++++++++++++++++++++++++ docs/develop/contributors.rst | 21 ++ docs/develop/index.rst | 19 ++ docs/develop/whats-new.rst | 2 + docs/index.rst | 1 + 5 files changed, 393 insertions(+) create mode 100644 docs/develop/contributing.rst create mode 100644 docs/develop/contributors.rst create mode 100644 docs/develop/index.rst create mode 100644 docs/develop/whats-new.rst diff --git a/docs/develop/contributing.rst b/docs/develop/contributing.rst new file mode 100644 index 0000000..132362e --- /dev/null +++ b/docs/develop/contributing.rst @@ -0,0 +1,350 @@ +.. _contributing: + +Contributing to archeryutils +============================ + +*archeryutils* is a free open-source project to which anyone can contribute code. +If you encounter and/or fix a bug or implement a few feature please contribute it +back to the core codebase. Alternatively, if you are want to get involved but are +unsure where to start take a look at the +`open issues `_ to see if there +is anything you could tackle. + +.. contents:: Contents: + :local: + :depth: 1 + +.. note:: + + Parts of this document are based on the + `xarray contributing guide `_ + which itself is heavily based on the + `Pandas Contributing Guide `_. + + +.. _issues: + +Bug Reports and Feature Requests +-------------------------------- + +Bug reports are an important part of making *archeryutils* more stable. +Having a complete bug report allows others to reproduce the bug and provides insight +into fixing. + +Trying out the bug-producing code on the main branch is often a worthwhile exercise +to confirm that the bug still exists. +It is also worth searching existing bug reports and pull requests to see if the issue +has already been reported and/or fixed. + + +Submitting a bug report +~~~~~~~~~~~~~~~~~~~~~~~ + +If you find a bug in the code or documentation, do not hesitate to submit a ticket to the +`Issue Tracker `_. + +When reporting a bug, please include the following: + +#. A short, self-contained Python snippet reproducing the problem. + You can format the code nicely by using `GitHub Flavored Markdown + `_:: + + ```python + import archeryutils as au + myTarget = au.Target(...) + + ... + ``` + +#. Include the full version string of *archeryutils* and its dependencies. + You can use the archeryutils ``versions()`` function:: + + import archeryutils as au + au.versions() + +#. Explain why the current behavior is wrong/not desired and what you expect instead. + +The issue will then show up to the *archeryutils* community and be open to +comments/ideas from others. + +See this `stackoverflow article `_ +for more detailed tips on writing a good bug report. + +If you have fixed the issue yourself in your own version of *archeryutils* please note +this on the issue and follow up by opening a :ref:`pull request `. + + +Submitting a feature request +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If there is a feature you think would be useful to add to *archeryutils* please make +a request using the +`Issue Tracker `_. +When doing so please try to include the following details: + +#. A clear description of the functionality you would like to see added and why you feel + it would be useful. + +#. Any external references that are helpful/required to understand and implement + your request in detail. + +#. A short code snippet indicating what the desired API might look like. + +If you have already implemented the feature yourself in your own version of +*archeryutils* please note this on the issue and follow up by opening a +:ref:`pull request `. + + +Code Contributions +------------------ + +.. contents:: Contributing to the Codebase: + :local: + + +.. _linting: + +Coding standards and formatting +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Writing good code is not just about what you write. +It is also about *how* you write it. +During continuous integration testing, several tools will be run to check your code +for stylistic errors. +Generating any warnings will cause these tests to fail. +Thus, good style is a requirement for submitting code to *archeryutils*. + +*archeryutils* uses several tools to ensure consistent and quality code formatting +throughout: + +- `Black `_ for standardized + code formatting, +- `blackdoc `_ for + standardized code formatting in documentation, +- `pylint `_ for code quality checks +- `pydocstyle `_ for checking docstrings + using the numpy convention +- `mypy `_ for static type checking on + `type hints `_. + +These will be checked on all pull requests and commits to main, so it is suggested you +run them on your code before committing. + +This can be done with a development install by running the following bash commands from +the root directory: + +.. code-block:: shell + + black archeryutils + pylint archeryutils + mypy archeryutils + pydocstyle --convention=numpy archeryutils + +Sometimes it makes sense to +`disable a pylint warning `_. +We prefer that this is done on a case-by-case basis in the code. +If you have justification for turning off any warnings in your contribution please +document them in your pull request. + + +.. _testing: + +Testing +~~~~~~~ + +Testing is a vital yet often under-utilised aspect of writing good code. +At its most basic testing is important to verify that code is working correctly - we +can write a test to which we know what the output *should* be, and then compare the +results produced by the code to ensure it is doing what we intend it to. +Tests are also important for future development to ensure that any changes do +not break behaviour or have unintended consequences. +Since all previously written tests are archived with the code and are run for all new +contributions, it will quickly become apparent if behaviour changes anywhere. + +*archeryutils* uses the `pytest `_ framework for writing and +running tests. + +To run the tests from a development install run, from the top directory:: + + pytest archeryutils + +which, if successful, should produce output that looks something like: + +.. code-block:: shell + + $ pytest archeryutils + ================================= test session starts ================================= + platform darwin -- Python 3.10.13, pytest-7.4.0, pluggy-1.2.0 + rootdir: /Users/home/archeryutils + plugins: mock-3.11.1, anyio-3.7.1, xdist-3.5.0 + collected 343 items + + archeryutils/classifications/tests/test_agb_field.py .......................... [ 7%] + ..... [ 9%] + archeryutils/classifications/tests/test_agb_indoor.py ......................... [ 16%] + .............. [ 20%] + archeryutils/classifications/tests/test_agb_old_indoor.py ..................... [ 26%] + [ 26%] + archeryutils/classifications/tests/test_agb_outdoor.py ........................ [ 33%] + .......................... [ 41%] + archeryutils/classifications/tests/test_classification_utils.py .......... [ 44%] + archeryutils/handicaps/tests/test_handicap_tables.py ....................... [ 50%] + archeryutils/handicaps/tests/test_handicaps.py ................................ [ 60%] + ................................................................... [ 79%] + archeryutils/tests/test_constants.py .............. [ 83%] + archeryutils/tests/test_rounds.py .................... [ 89%] + archeryutils/tests/test_targets.py .................................... [100%] + + ================================ 343 passed in 2.12s ================================= + + +Writing tests +^^^^^^^^^^^^^ + +Full details and documentation for pytest can be found on the `pytest website `_, +but a short overview is given here: + +* Tests should be placed in their own files separate from the source code. + They should be placed in a ``tests/`` subdirectory within each package and have + filenames of the format ``test_.py``. + +* Tests are often class-based for organisation, with a test class containing all the + tests pertaining to a particular class, method, etc. + +* To run a single test on a variety of inputs use ``@pytest.mark.parameterize``. + +* Use the ``assert`` statement to compare expected and actual outputs. + For floating point comparisons apply the ``pytest.approx()`` function to the actual + output. + +We suggest reviewing the existing tests in the *archeryutils* codebase to get a feeling +for how things are structured and written. + +When considering what tests to write for your contribution consider the following: + +* Comparisons of basic usage to known outputs to ensure your code behaves as expected. + +* Response to different optional input parameters to ensure they function as expected. + +* Response to inappropriate inputs/usage to ensure that the correct failure behaviour + occurs and the correct warnings/errors are raised. + +* Checks for any `edge or corner cases `_ that + may occur in use. For *archeryutils* a classic example that shows up is the case + of handling the maximum score for a particular round. + +We aim for as much as possible of the *archeryutils* codebase to be covered by testing. +During continuous integration a +`coverage checker `_ will run +the tests and highlight any parts of the code that are not covered by tests. + + +.. _pull_request: + +Opening a pull request +~~~~~~~~~~~~~~~~~~~~~~ + +If you have something to contribute to the *archeryutils* codebase this is done by +opening a pull request to the +`main repository on GitHub `_. + +Here is a summary of the expected workflow when contributing: + +#. Make sure there is an open issue on the + `Issue Tracker `_ as + :ref:`described above ` detailing the bug/feature that you are addressing. + +#. `Fork the main repository `_ + into your own personal GitHub space, and then clone and work on this fork. + You should work on a branch within this fork with a sensible name that reflects + what you are working on. + +#. As you work on the code, commit your changes in sensible-sized chunks with clear + commit messages. + A commit should detail any changes made to perform a particular action en route + to the overall goal. When writing commit messages remember that it needs to be + clearly understandable to other developers as to what they contribute. + See previous commits in the project for examples. + + As you work keep the following aspects in mind: + + a. Do not place large changes to multiple files in a single commit. + + b. Try and remember to apply the :ref:`stylistic checks and balances ` + to your code before committing. You may consider using a + `pre-commit hook `_ to help with this. + + c. Make sure that you include :ref:`appropriate tests ` alongside your + code contributions. Code without tests will not be merged. + + d. Make sure that you include/update any docstrings in the code, and that they + conform to the `numpy style `_. + See the rest of the code for examples. + + e. Make sure that you :ref:`update the documentation ` where + necessary to reflect the additions you have made. If adding a significant + top-level feature to the code you may want to update the + :ref:`getting started ` pages and the *examples* notebook to showcase + your additions. + +#. Once you push code back to your GitHub fork you can open a pull request. + For small bug-fixes and features you may wait until you feel things are complete + before opening the pull request. + However, if you wish for feedback/intermediate review then please open the pull + request in draft mode during development. + +#. When opening a pull request ensure that it contains: + + * A sensible title summarising its contribution. + * A `reference `_ + to the issue number(s) that it is addressing. + * The following checklist + + .. code-block:: markdown + + - [ ] Source code updated to address issue + - [ ] Style and formatting applied + - [ ] Tests written to cover changes + - [ ] Docstrings included/updated in code + - [ ] Project documentation updated as necessary + +Once a pull request is opened it will be reviewed by the project maintainers and any +requests for changes/improvement fed back to the author. +Once the maintainers are happy, your code will be approved and the pull request merged! + + +.. _docs_contributions: + +Documentation Contributions +--------------------------- + +If you’re not the developer type, contributions to the documentation are still of value. +If something in the docs doesn’t make sense to you, updating the relevant section +after you figure it out is a great way to ensure it will help the next person. +If you are not comfortable with the process detailed below, then please provide +feedback by :ref:`opening an issue ` with the details. + +The documentation is written in +`reStructuredText `_ +and built using Sphinx. +The `Sphinx Documentation `_ +has an excellent +`introduction to reST `_ +in addition to other aspects of Sphinx. + +The beauty of using Sphinx is that much of the API documentation can be automatically +generated from the docstrings in the source code. +This is why it is important to put time into these. + +The rest of the documentation, such as the installation and getting started pages, and +the contribution guidelines that you are reading right now, are written out and stored +in the ``docs/`` directory of the code. + +To build the documentation on a development install run:: + + cd docs/ + make clean + make html + +This will generate HTML output files in the folder ``docs/_build/html/`` that can be +viewed in a browser. diff --git a/docs/develop/contributors.rst b/docs/develop/contributors.rst new file mode 100644 index 0000000..d065f10 --- /dev/null +++ b/docs/develop/contributors.rst @@ -0,0 +1,21 @@ +Contributors +============ + +The following people have made significant contributions to the development of +archeryutils: + +* Jack Atkinson + + * `Web `_ + * `@jatkinson1000@fosstodon.org `_ + * GitHub: `@jatkinson1000 `_ + * GitLab: `@jatkinson1000 `_ + +* Tom Hall + + * `Web `_ + * GitHub: `@TomHall2020 `_ + +* Liam Pattinson + + * GitHub: `@liampattinson `_ diff --git a/docs/develop/index.rst b/docs/develop/index.rst new file mode 100644 index 0000000..a45c71c --- /dev/null +++ b/docs/develop/index.rst @@ -0,0 +1,19 @@ +Developer/Contributor Information +================================= + +archeryutils is an open-source community project currently operating as a +`benevolent dictatorship `__. + +Contributions from anyone are welcome, be they bug reports, bug fixes, +features/enhancements, documentation improvements etc. +For details of how to get involved with any of these see :ref:`contributing`.` + + + +.. toctree:: + :maxdepth: 3 + :hidden: + + contributing + contributors + whats-new diff --git a/docs/develop/whats-new.rst b/docs/develop/whats-new.rst new file mode 100644 index 0000000..d038cc2 --- /dev/null +++ b/docs/develop/whats-new.rst @@ -0,0 +1,2 @@ +What's New +========== diff --git a/docs/index.rst b/docs/index.rst index 503882d..995b07d 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -12,6 +12,7 @@ Welcome to archeryutils's documentation! Getting Started API Documentation + Developer Information Indices and tables ================== From 0844903334e63aa1b098648c5148bffc50d816bf Mon Sep 17 00:00:00 2001 From: Jack Atkinson Date: Sat, 10 Feb 2024 16:13:28 +0000 Subject: [PATCH 18/24] Add community documentation to docs/. --- docs/community/index.rst | 52 ++++++++++++++++++++++++++++++++++++++++ docs/index.rst | 1 + 2 files changed, 53 insertions(+) create mode 100644 docs/community/index.rst diff --git a/docs/community/index.rst b/docs/community/index.rst new file mode 100644 index 0000000..d1317ae --- /dev/null +++ b/docs/community/index.rst @@ -0,0 +1,52 @@ +The archeryutils Community +================================= + +Acknowledging +------------- + +See `Github Contributors `_ +for a full list of contributors towards this project. + +If you use this software in your project or work, please provide visible +credit/citation. +The SSI provides `good guidance `_ on how to do this. + +The following entries can be used or adapted when citing this work: + +* Atkinson J., archeryutils python package [v1.0.0] (2024); software available at https://github.com/jatkinson1000/archeryutils. +* BibTeX:: + + @Manual{archeryutils, + author = {Atkinson, J}, + title = {archeryutils python package, Version~1.0.0}, + year = {2024}, + url = {https://github.com/jatkinson1000/archeryutils}, + } + +Used By +------- + +The following projects make use of this code or its derivatives in some way: + +* `archerycalculator `_ +* `MyTargets Archery App `_ +* `Golden Records `_ +* `Expert Archer `_ + +Are we missing anyone? Let us know. + + +Support +------- + +This project is developed by volunteers for the benefit of the archery community. +It is dedicated to remain a `FOSS `_ project. + +The best way to support this project, if you are able, is by directly +:ref:`contributing `. + +If you are unable to do this, however, financial support towards this and the +`archerycalculator `_ project can be given through +`Buy Me a Coffee `_ or +`donating via paypal `_. + diff --git a/docs/index.rst b/docs/index.rst index 995b07d..66f84a0 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -13,6 +13,7 @@ Welcome to archeryutils's documentation! Getting Started API Documentation Developer Information + Community Indices and tables ================== From ab1b059ce6cd87acdd80f1ffdd08f83b9a262358 Mon Sep 17 00:00:00 2001 From: Jack Atkinson Date: Sat, 10 Feb 2024 16:14:02 +0000 Subject: [PATCH 19/24] Update docs to use Sphinx pygments style for code highlighting. --- docs/conf.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/conf.py b/docs/conf.py index 2642f2a..8dfe41f 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -38,6 +38,8 @@ "*tests*", ] +pygments_style = 'sphinx' + # -- Options for HTML output ------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output From 8acd832bc825012899472518239872ad2b003854 Mon Sep 17 00:00:00 2001 From: Jack Atkinson Date: Sat, 10 Feb 2024 16:16:04 +0000 Subject: [PATCH 20/24] Ad file tree to docs/ README --- docs/README.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/docs/README.md b/docs/README.md index afe293b..651a67c 100644 --- a/docs/README.md +++ b/docs/README.md @@ -12,3 +12,34 @@ make html The documentation pages can then be viewed in the `_build/html/` directory, with index.html being the main landing page. + +``` +docs + | + |-- index.rst + | + |-- getting-started + | | + | |-- index.rst + | |-- installation.rst + | |-- quickstart.rst + | + |-- api + | | + | |-- index.rst + | |-- archeryutils.handicaps.rst + | |-- archeryutils.classifications.rst + | + |-- develop + | | + | |-- index.rst + | |-- contributing.rst + | |-- contributors.rst + | |-- whats-new.rst + | + |-- community + | | + | |-- index.rst + + +``` From 583a268129f439c39ea4756a96338df7170a8c92 Mon Sep 17 00:00:00 2001 From: Jack Atkinson Date: Sat, 10 Feb 2024 16:17:31 +0000 Subject: [PATCH 21/24] Update GitHub README to detail pip install from source. --- README.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 19d7d58..b54a77c 100644 --- a/README.md +++ b/README.md @@ -31,18 +31,23 @@ We encourage usage and welcome feature requests. It is appreciated if visible credit is given by any projects using `archeryutils`. ### Installation -To install clone the repository, navigate to `/archeryutils`, and run: +To install the library via pip for use in a project you can run: - python3 -m pip install . + python -m pip install git+https://github.com/jatkinson1000/archeryutils.git It is recommended to use a virtual environment. There are plans to host the library on PyPI in due course. +If you want a local install that you can edit instead, clone the repository, +navigate to `/archeryutils`, and run: + + python3 -m pip install . + ### Getting Started There are examples of some of the different functionalities in the jupyter notebook `examples.ipynb`. -This can be run using: +This can be run from a local install using: pip install notebook From 63eb4e97f90ff0fb34eef4497ac30c7f4351ae0c Mon Sep 17 00:00:00 2001 From: Jack Atkinson Date: Sat, 10 Feb 2024 16:24:43 +0000 Subject: [PATCH 22/24] Apply black to docs. --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 8dfe41f..0f54206 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -38,7 +38,7 @@ "*tests*", ] -pygments_style = 'sphinx' +pygments_style = "sphinx" # -- Options for HTML output ------------------------------------------------- From 6e137d850d1f396e9f4ed926f2a82eafd32a8680 Mon Sep 17 00:00:00 2001 From: Jack Atkinson Date: Sat, 10 Feb 2024 16:36:09 +0000 Subject: [PATCH 23/24] Add readthedocs yaml file in repository root. --- .readthedocs.yaml | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 .readthedocs.yaml diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 0000000..f89fc90 --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,32 @@ +# .readthedocs.yaml +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +# Required +version: 2 + +# Set the OS, Python version and other tools you might need +build: + os: ubuntu-22.04 + tools: + python: "3.12" + # You can also specify other tool versions: + # nodejs: "19" + # rust: "1.64" + # golang: "1.19" + +# Build documentation in the "docs/" directory with Sphinx +sphinx: + configuration: docs/conf.py + +# Optionally build your docs in additional formats such as PDF and ePub +# formats: +# - pdf +# - epub + +# Optional but recommended, declare the Python requirements required +# to build your documentation +# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html +# python: +# install: +# - requirements: docs/requirements.txt From 14b73b1ef62eaff6338c5dbfb0be946bf7ea3e07 Mon Sep 17 00:00:00 2001 From: Jack Atkinson Date: Sat, 10 Feb 2024 16:39:05 +0000 Subject: [PATCH 24/24] Make docs homepage contents 1 level deep to improve appearance. --- docs/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.rst b/docs/index.rst index 66f84a0..a988b8b 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -7,7 +7,7 @@ Welcome to archeryutils's documentation! ======================================== .. toctree:: - :maxdepth: 3 + :maxdepth: 1 :caption: Contents: Getting Started