Amélioration suivant les recommandations de Ced.

This commit is contained in:
Gregory Trullemans 2023-06-17 18:09:01 +02:00
parent 1f832fa228
commit 8d81b8ed38
5 changed files with 181 additions and 109 deletions

View File

@ -29,7 +29,10 @@ def read_routine_file(filename: str) -> List[Routine]:
if current_cell.value is not None:
skill = Skill(current_cell.value)
current_routine.add_skill(skill)
if skill:
current_routine.add_skill(skill)
else:
break
routine_list.append(current_routine)
@ -46,16 +49,25 @@ def run_process(xlsx_filename: str, *, mean_routine:'r'=False, heatmap:'t'=False
"""
output_filename = xlsx_filename[:-5] + "_analysed.xlsx"
print("Reading XLSX file")
routine_list = read_routine_file(xlsx_filename)
print("Making statistics")
routine_stats = RoutineStatistics(routine_list)
print("Writing simple skill statistics")
routine_stats.write_skill_simple_statistics(output_filename)
print("writing detailed skill statistics")
routine_stats.write_skill_detailled_statistics(output_filename)
print("Writing combination statistics")
routine_stats.write_combination_frequency(output_filename)
if mean_routine:
print("Computing mean routine")
routine_stats.compute_routine()
print("Writing mean routine")
routine_stats.write_mean_routine(output_filename)
if heatmap:
print("Computing heatmap")
routine_stats.draw_heatmap_combination()

View File

@ -27,11 +27,13 @@ class Routine:
"""
self.skills.append(skill)
@property
def is_complete(self) -> bool:
"""Check if routine is complete (have 10 skills)"""
return len(self.skills) == 10
def get_difficulty_value(self):
@property
def difficulty(self) -> int:
"""Compute and return the difficulty value of a routine."""
if self.__difficulty is None:
self.__difficulty = 0
@ -39,11 +41,12 @@ class Routine:
self.__difficulty += skill.difficulty
return self.__difficulty
def get_number_of_skill(self) -> int:
@property
def number_of_skill(self) -> int:
"""Give the number of skill in the routine.
Return:
<int>
<int> number of skill in the routine
"""
return len(self.skills)

View File

@ -48,6 +48,7 @@ class RoutineStatistics:
- number_of_double_full_in <int> number of skill with a double twist at the begin
- number_of_frontward <int> number of frontward summersault
- number_of_backward <int> number of backward summersault
- mean_routine List<Skill> Mean routine
"""
def __init__(self, routine_list) -> None:
@ -56,21 +57,20 @@ class RoutineStatistics:
self.combination_dict_asc = dict()
self.combination_dict_desc = dict()
self.number_of_routine = len(self.routines)
self.__set_skill_statistics()
self.__set_combination_statistics()
def __set_skill_statistics(self) -> None:
""" """
self.__get_number_of_unfinished_routine
self.__get_number_of_skill_by_position
self.__get_number_of_skill_by_quantity_of_rotation
self.__get_number_of_skill_by_quantity_of_twist_at_end
self.__get_number_of_skill_by_quantity_of_twist_at_begin
self.__get_number_of_skill_by_direction
self.__get_number_of_unfinished_routine()
self.__get_number_of_skill_by_position()
self.__get_number_of_skill_by_quantity_of_rotation()
self.__get_number_of_skill_by_quantity_of_twist_at_end()
self.__get_number_of_skill_by_quantity_of_twist_at_begin()
self.__get_number_of_skill_by_direction()
self.__init_skill_dict()
self.__fillin_skill_dict()
self.__get_number_of_skill
self.__get_number_of_skill()
def __set_combination_statistics(self) -> None:
"""Creates the combination dictionary with respect to all the figures contained in the figure dictionary.
@ -112,27 +112,23 @@ class RoutineStatistics:
self.__setup_combination_dict_asc()
self.__fill_in_combination_dict_asc()
@property
def __get_number_of_skill(self) -> None:
"""Compute the total number of skills"""
self.number_of_skill = len(self.skills_dict)
@property
def __get_number_of_unfinished_routine(self) -> None:
"""Compute the number of unfinished routine"""
self.number_of_unfinished_routine = 0
for routine in self.routines:
if not routine.is_complete():
if not routine.is_complete:
self.number_of_unfinished_routine += 1
@property
def __get_number_of_skill_by_position(self) -> None:
"""Compute the number of skill for each position"""
self.number_of_tucked = self.get_number_of_skill_for_position("o")
self.number_of_picked = self.get_number_of_skill_for_position("<")
self.number_of_straight = self.get_number_of_skill_for_position("/")
@property
def __get_number_of_skill_by_quantity_of_rotation(self) -> None:
"""Compute the number of skill for each summmersault quantity"""
self.number_of_simple = self.get_number_of_skill_with_rotation_quantity(1)
@ -140,7 +136,6 @@ class RoutineStatistics:
self.number_of_triple = self.get_number_of_skill_with_rotation_quantity(3)
self.number_of_quadriple = self.get_number_of_skill_with_rotation_quantity(4)
@property
def __get_number_of_skill_by_quantity_of_twist_at_end(self) -> None:
"""Compute the number of skill for each twisting out quantity"""
self.number_of_no_twist_out = self.get_number_of_skill_with_twisting_out(0)
@ -150,7 +145,6 @@ class RoutineStatistics:
self.number_of_double_full_out = self.get_number_of_skill_with_twisting_out(4)
self.number_of_randy_out = self.get_number_of_skill_with_twisting_out(5)
@property
def __get_number_of_skill_by_quantity_of_twist_at_begin(self) -> None:
"""Compute the number of skill for each twisting in quantity"""
self.number_of_no_twist_in = self.get_number_of_skill_with_twisting_in(0)
@ -160,7 +154,6 @@ class RoutineStatistics:
self.number_of_double_full_in = self.get_number_of_skill_with_twisting_in(4)
self.number_of_randy_in = self.get_number_of_skill_with_twisting_in(5)
@property
def __get_number_of_skill_by_direction(self) -> None:
"""Compute the number of skill for each position"""
self.number_of_frontward = self.get_number_of_skill_for_direction(FRONTWARD)
@ -280,7 +273,7 @@ class RoutineStatistics:
self.combination_dict_asc[skill.numeric_code][previous_skill] += 1
previous_skill = skill.numeric_code
if routine.is_complete():
if previous_skill is not None and routine.is_complete:
self.combination_dict_asc["end"][previous_skill] += 1
else:
self.combination_dict_asc["crash"][previous_skill] += 1
@ -373,48 +366,43 @@ class RoutineStatistics:
)
@property
def get_difficulty_value(self) -> int:
def difficulty(self) -> int:
total_difficulty_score = 0
for routine in self.routines:
total_difficulty_score += routine.get_difficulty_value()
total_difficulty_score += routine.difficulty
return total_difficulty_score
@property
def get_score(self) -> int:
def score(self) -> int:
score = 0
for routine in self.routines:
score += routine.get_score()
score += routine.score()
return score
# @property
def get_number_of_skill_for_direction(self, direction=FRONTWARD) -> int:
count = 0
for routine in self.routines:
count += routine.get_number_of_skill_for_direction(direction)
return count
# @property
def get_number_of_skill_for_position(self, position: str) -> int:
count = 0
for routine in self.routines:
count += routine.get_number_of_skill_for_position(position)
return count
# @property
def get_number_of_skill_with_twisting_in(self, twist_quantity: int) -> int:
count = 0
for routine in self.routines:
count += routine.get_number_of_skill_with_twisting_in(twist_quantity)
return count
# @property
def get_number_of_skill_with_twisting_out(self, twist_quantity: int) -> int:
count = 0
for routine in self.routines:
count += routine.get_number_of_skill_with_twisting_out(twist_quantity)
return count
# @property
def get_number_of_skill_with_rotation_quantity(self, rotation_quantity: int) -> int:
count = 0
for routine in self.routines:
@ -423,7 +411,6 @@ class RoutineStatistics:
)
return count
# @property
def get_number_of_skill_with_rotation_quantity_with_twist_in(
self, rotation_quantity: int, twist_in_quantity: int
) -> int:
@ -434,7 +421,6 @@ class RoutineStatistics:
)
return count
# @property
def get_number_of_skill_position_with_twist_in(
self, position: str, twist_in_quantity: int
) -> int:
@ -445,7 +431,6 @@ class RoutineStatistics:
)
return count
# @property
def get_number_of_skill_position_with_twist_out(
self, position: str, twist_out_quantity: int
) -> int:
@ -456,7 +441,6 @@ class RoutineStatistics:
)
return count
# @property
def get_number_of_skill_with_rotation_quantity_with_twist_out(
self, rotation_quantity: int, twist_out_quantity: int
) -> int:
@ -467,7 +451,6 @@ class RoutineStatistics:
)
return count
# @property
def get_number_of_skill_with_rotation_quantity_for_position(
self, rotation_quantity: int, position: str
) -> int:
@ -478,7 +461,6 @@ class RoutineStatistics:
)
return count
# @property
def get_number_of_skill_with_direction_and_position(
self, direction: str, position: str
) -> int:
@ -489,7 +471,6 @@ class RoutineStatistics:
)
return count
# @property
def get_number_of_skill_with_direction_and_twisting_in(
self, direction: str, twist_quantity: int
) -> int:
@ -500,7 +481,6 @@ class RoutineStatistics:
)
return count
# @property
def get_number_of_skill_with_direction_and_twisting_out(
self, direction: str, twist_quantity: int
) -> int:
@ -511,7 +491,6 @@ class RoutineStatistics:
)
return count
# @property
def get_number_of_skill_with_twist_in_and_twist_out(
self, twist_in_quantity: int, twist_out_quantity: int
) -> int:
@ -563,32 +542,46 @@ class RoutineStatistics:
def build_routine_descendant(self) -> List[str]:
"""Calculates the "descendant" routine based on information and statistics."""
print("Computing descendant routine")
routine_descendant = ["TBD"] * 10
routine_descendant[0] = Skill(
tmp = Skill(
max(
self.combination_dict_desc["begin"],
key=self.combination_dict_desc["begin"].get,
)
)
for i in range(1, 10):
routine_descendant[i] = Skill(
max(
self.combination_dict_desc[routine_descendant[i - 1].numeric_code],
key=self.combination_dict_desc[
routine_descendant[i - 1].numeric_code
].get,
)
)
if tmp:
routine_descendant[0] = tmp
# for i in range(0, 10):
# print(str(i + 1) + " - " + routine_descendant[i].numeric_code)
# print("--------------------------------")
for i in range(1, 10):
if isinstance(routine_descendant[i - 1], Skill):
# print(str(i) + " - " + str(routine_descendant[i - 1]))
# print(self.combination_dict_desc[routine_descendant[i - 1]])
tmp = Skill(
max(
self.combination_dict_desc[routine_descendant[i - 1].numeric_code],
key=self.combination_dict_desc[
routine_descendant[i - 1].numeric_code
].get,
)
)
if tmp:
routine_descendant[i] = tmp
for i in range(0, 10):
if isinstance(routine_descendant[i], Skill):
print(str(i + 1) + " - " + routine_descendant[i].numeric_code)
else:
print(str(i + 1) + " - " + routine_descendant[i])
print("--------------------------------")
return routine_descendant
def build_routine_ascendant(self) -> List[str]:
"""Calculates the "ascendant" routine based on information and statistics."""
print("Computing ascendant routine")
routine_ascendant = ["TBD"] * 10
routine_ascendant[9] = Skill(
@ -606,9 +599,13 @@ class RoutineStatistics:
].get,
)
)
# for i in range(0, 10):
# print(str(i + 1) + " - " + routine_ascendant[i].numeric_code)
# print("--------------------------------")
for i in range(0, 10):
if isinstance(routine_ascendant[i], Skill):
print(str(i + 1) + " - " + routine_ascendant[i].numeric_code)
else:
print(str(i + 1) + " - " + routine_ascendant[i])
print("--------------------------------")
return routine_ascendant
@ -706,51 +703,74 @@ class RoutineStatistics:
return mean_routine
def draft_mean_routine(self):
"""Calculates the "average" routine based on information and statistics."""
mean_routine = ["TBD"] * 10
routine_descendant = self.build_routine_descendant()
routine_ascendant = self.build_routine_ascendant()
def merge_draft_routine(self, routine_descendant, routine_ascendant):
""" """
routine = ["TBD"] * 10
# set_descendant = set(routine_descendant)
# set_ascendant = set(routine_ascendant)
# la routine descendant devrait être tolérant sur les TDB quand on a lit à l'envers (ascendant)
# la routine ascendant devrait tolérant sur les TDB quand on la lit à l'endroit (descendant))
for i in range(0, 10):
# vérifier si skill majoritaire ?
if routine_descendant[i].numeric_code == routine_ascendant[i].numeric_code:
mean_routine[i] = routine_descendant[i]
# for i in range(0, 10):
# if isinstance(mean_routine[i], Skill):
# print(str(i + 1) + " - " + mean_routine[i].numeric_code)
# else:
# print(str(i + 1) + " - " + mean_routine[i])
# print("================================================")
if (
isinstance(routine_descendant[i], Skill)
and isinstance(routine_ascendant[i], Skill)
and routine_descendant[i].numeric_code == routine_ascendant[i].numeric_code
):
routine[i] = routine_descendant[i]
return mean_routine, routine_descendant, routine_ascendant
return routine
def draft_mean_routine(self):
"""Calculates the "average" routine based on information and statistics."""
print("Drafting mean routine")
self.mean_routine = ["TBD"] * 10
routine_descendant = self.build_routine_descendant()
routine_ascendant = self.build_routine_ascendant()
self.mean_routine = self.merge_draft_routine(routine_descendant, routine_ascendant)
for i in range(0, 10):
if isinstance(self.mean_routine[i], Skill):
print(str(i + 1) + " - " + self.mean_routine[i].numeric_code)
else:
print(str(i + 1) + " - " + self.mean_routine[i])
print("================================================")
return routine_descendant, routine_ascendant
def compute_routine(self) -> List[Skill]:
"""Calculates the "average" routine based on information and statistics."""
mean_routine, routine_descendant, routine_ascendant = self.draft_mean_routine()
# for key, value in self.combination_dict_desc.items():
# print(key + " - " + str(value))
routine_descendant, routine_ascendant = self.draft_mean_routine()
for i in range(0, 10):
if mean_routine[i] == "TBD":
mean_routine[i] = self.selected_skill_by_count(routine_descendant, routine_ascendant, i)
if self.mean_routine[i] == "TBD":
self.mean_routine[i] = self.selected_skill_by_count(routine_descendant, routine_ascendant, i)
if mean_routine[i] == "TBD":
mean_routine[i] = self.selected_skill_by_rank(routine_ascendant, routine_descendant, i)
if self.mean_routine[i] == "TBD":
self.mean_routine[i] = self.selected_skill_by_rank(routine_ascendant, routine_descendant, i)
if (
mean_routine[i] == "TBD"
and mean_routine[i - 1] != "TBD"
and mean_routine[i + 1] != "TBD"
self.mean_routine[i] == "TBD"
and self.mean_routine[i - 1] != "TBD"
and self.mean_routine[i + 1] != "TBD"
):
mean_routine[i] = self.selected_skill_by_max_path(routine_ascendant, routine_descendant, i)
self.mean_routine[i] = self.selected_skill_by_max_path(routine_ascendant, routine_descendant, i)
mean_routine = self.check_skill_majority(mean_routine)
return mean_routine
self.mean_routine = self.check_skill_majority(self.mean_routine)
for i in range(0, 10):
print(str(i + 1) + " - " + str(self.mean_routine[i]))
return self.mean_routine
def write_mean_routine(self, output_filename: str) -> None:
"""Creates the average routine based on calculated combinations and movement occurrences."""
routine = self.compute_routine()
xlsx_writer = StatisticsXlsxWriter()
xlsx_writer.save_mean_routine(routine, output_filename)
xlsx_writer.save_mean_routine(self.mean_routine, output_filename)
def draw_heatmap_combination(self) -> None:
"""Draw a heatmap of the skill combination"""

View File

@ -2,6 +2,25 @@ FRONTWARD = 1
BACKWARD = 0
def check_numeric_code(numeric_code):
if numeric_code == "begin" or numeric_code == "crash" or numeric_code == "end" or numeric_code == "TDB":
return False
length = len(numeric_code)
if length > 8:
return False
# numbers_count = sum(c.isdigit() for c in numeric_code)
letters_count = sum(c.isalpha() for c in numeric_code)
# others_count = length - numbers_count - letters_count
if letters_count > 2:
return False
# si lettre ET point en même temps, erreur !
return True
class Skill:
"""Class skill
@ -17,9 +36,36 @@ class Skill:
- twist_in_quantity <int>
- twist_out_quantity <int>
Numeric_code must contains the FIG Numeric Code of the skill, without space,
"""
def __get_direction(self):
def __new__(cls, skill_string):
numeric_code = skill_string.strip().replace("\s", "")
if check_numeric_code(numeric_code):
obj = super().__new__(cls)
return obj
else:
return None
def __init__(self, numeric_code):
self.numeric_code = numeric_code
self.__compute_direction()
self.__compute_position()
self.__compute_rotation_quantity()
self.__compute_twisting_part()
self.__compute_quart_sommersault_rotation()
self.__compute_half_twist_rotation()
self.__compute_difficulty()
self.twist_in_quantity = 0 if self.twisting[0] == "-" else int(self.twisting[0])
self.twist_out_quantity = (
0 if self.twisting[-1:] == "-" else int(self.twisting[-1:])
)
def __compute_direction(self) -> None:
"""Analyse the numeric code to determine the direction (frontward/backward) of the skill.
If numerci code begin with a dot (e.g.: .4-/), the skill is frontward. If not but there is a dot (e.g.: 3.-o),
it's a backward skill. Else, there is no direction (e.g.: seat, stand/feet, …).
"""
if self.numeric_code.startswith("."):
self.direction = FRONTWARD
elif self.numeric_code.find("."):
@ -27,14 +73,18 @@ class Skill:
else:
self.direction = None
def __get_position(self):
def __compute_position(self) -> None:
"""Analyse the numeric code to determine the position (tuck (o), pike (<), straight (/) or none) of the skill.
We assume that the skill position is ALWAYS the last character of the numeric code.
Exemple: 4.-o .8-1< 8.22/
"""
position = self.numeric_code[-1:]
if position == "o" or position == "<" or position == "/":
self.position = position
else:
self.position = None
def __get_rotation_quantity(self):
def __compute_rotation_quantity(self) -> None:
rotation_quantity = len(self.numeric_code)
self.twisting_start_position = 2
if rotation_quantity == 4:
@ -54,10 +104,10 @@ class Skill:
self.twisting_start_position = None
print(f"Unknow skill: {self.numeric_code}")
def __get_twisting_part(self):
self.twisting = self.numeric_code[self.twisting_start_position : -1]
def __compute_twisting_part(self) -> None:
self.twisting = self.numeric_code[self.twisting_start_position:-1]
def __get_quart_sommersault_rotation(self):
def __compute_quart_sommersault_rotation(self) -> None:
if self.rotation_quantity == 1 or self.rotation_quantity == 2:
if self.direction == FRONTWARD:
self.quart_summersault_rotation = self.numeric_code[1]
@ -71,13 +121,13 @@ class Skill:
else:
self.quart_summersault_rotation = None
def __get_half_twist_rotation(self):
def __compute_half_twist_rotation(self) -> None:
self.half_twist_rotation = 0
for x in self.twisting:
if x != "-":
self.half_twist_rotation += int(x)
def __get_difficulty_value(self):
def __compute_difficulty(self) -> None:
self.difficulty = int(self.half_twist_rotation) * 0.1
self.difficulty += int(self.quart_summersault_rotation) * 0.1
self.difficulty += (int(self.quart_summersault_rotation) // 4) * 0.1
@ -86,19 +136,6 @@ class Skill:
if self.position == "<" or self.position == "/":
self.difficulty += (int(self.quart_summersault_rotation) // 4) * 0.1
def __init__(self, skill_string):
self.numeric_code = skill_string.strip().replace("\s", "")
self.__get_direction()
self.__get_position()
self.__get_rotation_quantity()
self.__get_twisting_part()
self.__get_quart_sommersault_rotation()
self.__get_half_twist_rotation()
self.__get_difficulty_value()
self.twist_in_quantity = 0 if self.twisting[0] == "-" else int(self.twisting[0])
self.twist_out_quantity = (
0 if self.twisting[-1:] == "-" else int(self.twisting[-1:])
)
def __str__(self):
return f"{self.numeric_code}: {self.quart_summersault_rotation} quart of {self.direction} with {self.half_twist_rotation} in {self.position} position (difficulty: {self.difficulty})."
def __str__(self) -> str:
return f"""{self.numeric_code}: {self.quart_summersault_rotation} quart of {self.direction} with
{self.half_twist_rotation} in {self.position} position (difficulty: {self.difficulty})."""

View File

@ -508,12 +508,12 @@ class StatisticsXlsxWriter:
index += 1
self.write_header(worksheet, "A" + str(index), "Diff.")
worksheet["B" + str(index)] = stats.get_difficulty_value
worksheet["B" + str(index)] = stats.difficulty
worksheet["C" + str(index)] = (
stats.get_difficulty_value / stats.number_of_skill
stats.difficulty / stats.number_of_skill
)
worksheet["D" + str(index)] = (
stats.get_difficulty_value / stats.number_of_routine
stats.difficulty / stats.number_of_routine
)
return index