Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Typing Improvements #67

Merged
merged 29 commits into from
Feb 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
4298501
Use builtin generics instead of importing from Typing
TomHall2020 Feb 14, 2024
b854e48
Implement generic float/array
TomHall2020 Feb 14, 2024
883d31c
Type hint return values for read_json functions in classification_utils
TomHall2020 Feb 15, 2024
8a8c93d
Type hint agb_outdoor_classifications dictionary
TomHall2020 Feb 15, 2024
c8c9206
Type hint agb_indoor_classifcations dictionary
TomHall2020 Feb 15, 2024
3cefb24
Type hint agb_old_indoor_classifications dictionary
TomHall2020 Feb 15, 2024
7f34192
Type hint agb_field_classifications dict
TomHall2020 Feb 15, 2024
7f9d964
Improved agb_field_classification dict construction, maintaining typing
TomHall2020 Feb 15, 2024
20eb5fb
Clean up for linting
TomHall2020 Feb 15, 2024
7a32cbb
Remove explict dictionary type hinting inside calculate_classificatio…
TomHall2020 Feb 15, 2024
4eb7f3c
Remove explict checks for numpy arrays in results of score_for_round
TomHall2020 Feb 15, 2024
c7d657a
Explicit type hinting for Target scoring systems
TomHall2020 Feb 15, 2024
cbfe484
Extra: derive available scoring systems in Target init from literal l…
TomHall2020 Feb 15, 2024
6613947
fix return type for Pass.scoring_system
TomHall2020 Feb 15, 2024
a16f6e9
Comments to explain type: ignore in test files
TomHall2020 Feb 16, 2024
7716084
Remove redundant conversion to int in agb_field_classification_scores
TomHall2020 Feb 16, 2024
9f30d20
Remove explicit TypeAlias annotation
TomHall2020 Feb 16, 2024
d7d5385
Accept Iterable of Passes to Round constructor
TomHall2020 Feb 16, 2024
0febdfc
Formatting
TomHall2020 Feb 16, 2024
cef05aa
Reformat with latest black
TomHall2020 Feb 16, 2024
196226d
Document use of iterables of passes in Round docs
TomHall2020 Feb 17, 2024
30aa0ab
Document allowed scoring systems directly in Target docstring
TomHall2020 Feb 17, 2024
6d5d371
Format docs conf.py file...
TomHall2020 Feb 17, 2024
629744f
Make docstring of target ScoringSystem pretty for sphinx.
jatkinson1000 Feb 17, 2024
d046027
Update Pass docstring with Literal options for scoring system.
jatkinson1000 Feb 17, 2024
26b730a
Small tweaks to Round and Target docstring examples.
jatkinson1000 Feb 17, 2024
8375fb0
Typo in examples
jatkinson1000 Feb 17, 2024
30146c3
Update pyproject.toml with pickleshare which is now required by, but …
jatkinson1000 Feb 17, 2024
ca8f83a
Remove autodoc type aliases as I think it is covered by sphinx_toolbo…
jatkinson1000 Feb 17, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
173 changes: 43 additions & 130 deletions archeryutils/classifications/agb_field_classifications.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@
# pylint: disable=duplicate-code

import re
from typing import List, Dict, Any
import numpy as np
from typing import TypedDict

from archeryutils import load_rounds
import archeryutils.classifications.classification_utils as cls_funcs
Expand All @@ -26,7 +25,14 @@
)


def _make_agb_field_classification_dict() -> Dict[str, Dict[str, Any]]:
class GroupData(TypedDict):
"""Structure for AGB Field classification data."""

classes: list[str]
class_scores: list[int]


def _make_agb_field_classification_dict() -> dict[str, GroupData]:
"""
Generate AGB outdoor classification data.

Expand Down Expand Up @@ -58,120 +64,40 @@ def _make_agb_field_classification_dict() -> Dict[str, Dict[str, Any]]:
"3rd Class",
]

# Generate dict of classifications
# for both bowstyles, for both genders
classification_dict = {}
classification_dict[cls_funcs.get_groupname("Compound", "Male", "Adult")] = {
"classes": agb_field_classes,
"class_scores": [393, 377, 344, 312, 279, 247],
}
classification_dict[cls_funcs.get_groupname("Compound", "Female", "Adult")] = {
"classes": agb_field_classes,
"class_scores": [376, 361, 330, 299, 268, 237],
}
classification_dict[cls_funcs.get_groupname("Recurve", "Male", "Adult")] = {
"classes": agb_field_classes,
"class_scores": [338, 317, 288, 260, 231, 203],
}
classification_dict[cls_funcs.get_groupname("Recurve", "Female", "Adult")] = {
"classes": agb_field_classes,
"class_scores": [322, 302, 275, 247, 220, 193],
}
classification_dict[cls_funcs.get_groupname("Barebow", "Male", "Adult")] = {
"classes": agb_field_classes,
"class_scores": [328, 307, 279, 252, 224, 197],
}
classification_dict[cls_funcs.get_groupname("Barebow", "Female", "Adult")] = {
"classes": agb_field_classes,
"class_scores": [303, 284, 258, 233, 207, 182],
}
classification_dict[cls_funcs.get_groupname("Longbow", "Male", "Adult")] = {
"classes": agb_field_classes,
"class_scores": [201, 188, 171, 155, 137, 121],
}
classification_dict[cls_funcs.get_groupname("Longbow", "Female", "Adult")] = {
"classes": agb_field_classes,
"class_scores": [303, 284, 258, 233, 207, 182],
}
classification_dict[cls_funcs.get_groupname("Traditional", "Male", "Adult")] = {
"classes": agb_field_classes,
"class_scores": [262, 245, 223, 202, 178, 157],
}
classification_dict[cls_funcs.get_groupname("Traditional", "Female", "Adult")] = {
"classes": agb_field_classes,
"class_scores": [197, 184, 167, 152, 134, 118],
}
classification_dict[cls_funcs.get_groupname("Flatbow", "Male", "Adult")] = {
"classes": agb_field_classes,
"class_scores": [262, 245, 223, 202, 178, 157],
}
classification_dict[cls_funcs.get_groupname("Flatbow", "Female", "Adult")] = {
"classes": agb_field_classes,
"class_scores": [197, 184, 167, 152, 134, 118],
}

# Juniors
classification_dict[cls_funcs.get_groupname("Compound", "Male", "Under 18")] = {
"classes": agb_field_classes,
"class_scores": [385, 369, 337, 306, 273, 242],
}

classification_dict[cls_funcs.get_groupname("Compound", "Female", "Under 18")] = {
"classes": agb_field_classes,
"class_scores": [357, 343, 314, 284, 255, 225],
}

classification_dict[cls_funcs.get_groupname("Recurve", "Male", "Under 18")] = {
"classes": agb_field_classes,
"class_scores": [311, 292, 265, 239, 213, 187],
agb_field_scores = {
("Compound", "Male", "Adult"): [393, 377, 344, 312, 279, 247],
("Compound", "Female", "Adult"): [376, 361, 330, 299, 268, 237],
("Recurve", "Male", "Adult"): [338, 317, 288, 260, 231, 203],
("Recurve", "Female", "Adult"): [322, 302, 275, 247, 220, 193],
("Barebow", "Male", "Adult"): [328, 307, 279, 252, 224, 197],
("Barebow", "Female", "Adult"): [303, 284, 258, 233, 207, 182],
("Longbow", "Male", "Adult"): [201, 188, 171, 155, 137, 121],
("Longbow", "Female", "Adult"): [303, 284, 258, 233, 207, 182],
("Traditional", "Male", "Adult"): [262, 245, 223, 202, 178, 157],
("Traditional", "Female", "Adult"): [197, 184, 167, 152, 134, 118],
("Flatbow", "Male", "Adult"): [262, 245, 223, 202, 178, 157],
("Flatbow", "Female", "Adult"): [197, 184, 167, 152, 134, 118],
("Compound", "Male", "Under 18"): [385, 369, 337, 306, 273, 242],
("Compound", "Female", "Under 18"): [357, 343, 314, 284, 255, 225],
("Recurve", "Male", "Under 18"): [311, 292, 265, 239, 213, 187],
("Recurve", "Female", "Under 18"): [280, 263, 239, 215, 191, 168],
("Barebow", "Male", "Under 18"): [298, 279, 254, 229, 204, 179],
("Barebow", "Female", "Under 18"): [251, 236, 214, 193, 172, 151],
("Longbow", "Male", "Under 18"): [161, 150, 137, 124, 109, 96],
("Longbow", "Female", "Under 18"): [122, 114, 103, 94, 83, 73],
("Traditional", "Male", "Under 18"): [210, 196, 178, 161, 143, 126],
("Traditional", "Female", "Under 18"): [158, 147, 134, 121, 107, 95],
("Flatbow", "Male", "Under 18"): [210, 196, 178, 161, 143, 126],
("Flatbow", "Female", "Under 18"): [158, 147, 134, 121, 107, 95],
}

classification_dict[cls_funcs.get_groupname("Recurve", "Female", "Under 18")] = {
"classes": agb_field_classes,
"class_scores": [280, 263, 239, 215, 191, 168],
}

classification_dict[cls_funcs.get_groupname("Barebow", "Male", "Under 18")] = {
"classes": agb_field_classes,
"class_scores": [298, 279, 254, 229, 204, 179],
}

classification_dict[cls_funcs.get_groupname("Barebow", "Female", "Under 18")] = {
"classes": agb_field_classes,
"class_scores": [251, 236, 214, 193, 172, 151],
}

classification_dict[cls_funcs.get_groupname("Longbow", "Male", "Under 18")] = {
"classes": agb_field_classes,
"class_scores": [161, 150, 137, 124, 109, 96],
}

classification_dict[cls_funcs.get_groupname("Longbow", "Female", "Under 18")] = {
"classes": agb_field_classes,
"class_scores": [122, 114, 103, 94, 83, 73],
}

classification_dict[cls_funcs.get_groupname("Traditional", "Male", "Under 18")] = {
"classes": agb_field_classes,
"class_scores": [210, 196, 178, 161, 143, 126],
}

classification_dict[
cls_funcs.get_groupname("Traditional", "Female", "Under 18")
] = {
"classes": agb_field_classes,
"class_scores": [158, 147, 134, 121, 107, 95],
}

classification_dict[cls_funcs.get_groupname("Flatbow", "Male", "Under 18")] = {
"classes": agb_field_classes,
"class_scores": [210, 196, 178, 161, 143, 126],
}
# Generate dict of classifications
classification_dict = {}

classification_dict[cls_funcs.get_groupname("Flatbow", "Female", "Under 18")] = {
"classes": agb_field_classes,
"class_scores": [158, 147, 134, 121, 107, 95],
}
for group, scores in agb_field_scores.items():
groupdata: GroupData = {"classes": agb_field_classes, "class_scores": scores}
groupname = cls_funcs.get_groupname(*group)
classification_dict[groupname] = groupdata

return classification_dict

Expand Down Expand Up @@ -262,9 +188,7 @@ def calculate_agb_field_classification(
return "unclassified"

# What is the highest classification this score gets?
class_scores: Dict[str, Any] = dict(
zip(group_data["classes"], group_data["class_scores"])
)
class_scores = dict(zip(group_data["classes"], group_data["class_scores"]))
for item in class_scores:
if class_scores[item] > score:
pass
Expand All @@ -277,7 +201,7 @@ def calculate_agb_field_classification(

def agb_field_classification_scores(
roundname: str, bowstyle: str, gender: str, age_group: str
) -> List[int]:
) -> list[int]:
"""
Calculate AGB field classification scores for category.

Expand Down Expand Up @@ -330,15 +254,4 @@ def agb_field_classification_scores(
group_data = agb_field_classifications[groupname]

# Get scores required on this round for each classification
class_scores = group_data["class_scores"]

# Make sure that hc.eq.score_for_round did not return array to satisfy mypy
if any(isinstance(x, np.ndarray) for x in class_scores):
raise TypeError(
"score_for_round is attempting to return an array when float expected."
)
# Score threshold should be int (score_for_round called with round=True)
# Enforce this for better code and to satisfy mypy
int_class_scores = [int(x) for x in class_scores]

return int_class_scores
return group_data["class_scores"]
46 changes: 27 additions & 19 deletions archeryutils/classifications/agb_indoor_classifications.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@
# => disable for classification files and tests
# pylint: disable=duplicate-code

from typing import List, Dict, Any
from typing import TypedDict
import numpy as np
import numpy.typing as npt

from archeryutils import load_rounds
from archeryutils.handicaps import handicap_equations as hc_eq
Expand All @@ -27,7 +28,15 @@
)


def _make_agb_indoor_classification_dict() -> Dict[str, Dict[str, Any]]:
class GroupData(TypedDict):
"""Structure for AGB Indoor classification data."""

classes: list[str]
classes_long: list[str]
class_HC: npt.NDArray[np.float_]


def _make_agb_indoor_classification_dict() -> dict[str, GroupData]:
"""
Generate new (2023) AGB indoor classification data.

Expand Down Expand Up @@ -55,6 +64,8 @@ def _make_agb_indoor_classification_dict() -> Dict[str, Dict[str, Any]]:
# For score purposes in classifications we use the full face, not the triple.
# Option of having triple is handled in get classification function
# Compound version of rounds is handled below.
# One too many locals, but better than repeated dictionary assignment => disable
# pylint: disable=too-many-locals

# Read in age group info as list of dicts
agb_ages = cls_funcs.read_ages_json()
Expand All @@ -79,11 +90,6 @@ def _make_agb_indoor_classification_dict() -> Dict[str, Dict[str, Any]]:
bowstyle["bowstyle"], gender, age["age_group"]
)

classification_dict[groupname] = {
"classes": agb_classes_in,
"classes_long": agb_classes_in_long,
}

# set step from datum based on age and gender steps required
delta_hc_age_gender = cls_funcs.get_age_gender_step(
gender,
Expand All @@ -92,17 +98,24 @@ def _make_agb_indoor_classification_dict() -> Dict[str, Dict[str, Any]]:
bowstyle["genderStep_in"],
)

classification_dict[groupname]["class_HC"] = np.empty(
len(agb_classes_in)
)
for i in range(len(agb_classes_in)):
classifications_count = len(agb_classes_in)

class_hc = np.empty(classifications_count)
for i in range(classifications_count):
# Assign handicap for this classification
classification_dict[groupname]["class_HC"][i] = (
class_hc[i] = (
bowstyle["datum_in"]
+ delta_hc_age_gender
+ (i - 1) * bowstyle["classStep_in"]
)

groupdata: GroupData = {
"classes": agb_classes_in,
"classes_long": agb_classes_in_long,
"class_HC": class_hc,
}
classification_dict[groupname] = groupdata
jatkinson1000 marked this conversation as resolved.
Show resolved Hide resolved

return classification_dict


Expand Down Expand Up @@ -183,7 +196,7 @@ def calculate_agb_indoor_classification(

groupname = cls_funcs.get_groupname(bowstyle, gender, age_group)
group_data = agb_indoor_classifications[groupname]
class_data: Dict[str, Any] = dict(zip(group_data["classes"], all_class_scores))
class_data = dict(zip(group_data["classes"], all_class_scores))

# What is the highest classification this score gets?
# < 0 handles max scores, > score handles higher classifications
Expand All @@ -206,7 +219,7 @@ def agb_indoor_classification_scores(
bowstyle: str,
gender: str,
age_group: str,
) -> List[int]:
) -> list[int]:
"""
Calculate 2023 AGB indoor classification scores for category.

Expand Down Expand Up @@ -283,11 +296,6 @@ def agb_indoor_classification_scores(
for i, class_i in enumerate(group_data["classes"])
]

# Make sure that hc.eq.score_for_round did not return array to satisfy mypy
if any(isinstance(x, np.ndarray) for x in class_scores):
raise TypeError(
"score_for_round is attempting to return an array when float expected."
)
# Score threshold should be int (score_for_round called with round=True)
# Enforce this for better code and to satisfy mypy
int_class_scores = [int(x) for x in class_scores]
Expand Down
Loading
Loading