from typing import List import million.analyze.message_evaluation as msg_val from million.model.message import Message def check_extra_or_missing_letter(word: str, reference: str) -> bool: len_word = len(word) len_ref = len(reference) if abs(len_word - len_ref) != 1: return False shortest = word if len_word < len_ref else reference longest = word if len_word > len_ref else reference for i in range(len(shortest)): if shortest[i] != longest[i]: return shortest[i:] == longest[i + 1 :] return True def check_single_letter_differ(word: str, reference: str) -> bool: return sum(1 for x, y in zip(reference, word) if x != y) == 1 def check_letter_swap(word: str, reference: str) -> bool: if len(word) != len(reference): return False for i in range(len(word) - 1): if word[i] != reference[i]: return word[i + 1] + word[i] + word[i + 2 :] == reference[i:] return False def check_typo(word: str, reference: str) -> bool: if len(reference) == len(word): return check_single_letter_differ(word, reference) or check_letter_swap( word, reference ) else: return check_extra_or_missing_letter(word, reference) def _check_message_concatenation(messages: List[Message], index: int, expected: int) -> bool: """ Cette méthode détermine si la liste messages contient le compte expected à partir de l'index donné en concaténant les valeurs des messages suivants. Cette méthode permet de trouver un compte qui a été étalé sur plusieurs messages """ reference = str(expected) testing = "" offset = 0 while len(testing) < len(reference): next_message = messages[index + offset] offset += 1 if next_message.sender_name == messages[index].sender_name: testing += str(msg_val.get(next_message)) return testing == reference def _heavy_check(messages: List[Message], index: int, expected: int) -> bool: """ Cette méthode détermine si la liste messages contient le compte expected à partir de l'index donné. Elle utilise pour cela des méthodes complexes qui ne permettent de trouver un résultat seulement si on est sortis du cas nominal """ # TODO Si on ne trouve pas le chiffre dans la plage, appliquer différentes méthodes "HEAVYCHECK" avant de le déclarer perdu # - créer une méthode qui concatène des messages consécutifs jusqu'a trouver la bonne valeur si la valeur est plus basse # - créer une méthode pour les messages ou 2 valeurs sont collées si la valeur est plus haute # - sub certains caractères par des chiffres selon une table préconcue, les chiffres en toute lettre par leur équivalent # - virer tout sauf les chiffres m = messages[index] word = str(msg_val.get(m)) return _check_message_concatenation(messages, index, expected) or \ check_typo(word, str(expected)) and msg_val.get(messages[index+1]) == expected+1 def _check_value_around(messages, index, expected, amplitude): for i in range(1, amplitude + 1): if index + i < len(messages) and expected == msg_val.get(messages[index + i]): return index + i if expected == msg_val.get(messages[index - i]): return index - i return None def search_value_at(messages, index, expected, do_heavy_check=True, amplitude=1000): """ Cette méthode détermine si la liste messages contient le compte expected à partir de l'index donné. Le paramètre amplitude détermine la plage où effectuer les recherches autour de l'index donné. Le paramètre do_heavy_check précise si on doit pousser l'analyse avec des méthodes plus lourdes en cas d'échec """ # Si le message courant contient la valeur, on renvoie curr_value = msg_val.get(messages[index]) if expected == curr_value: return index # Sinon on regarde aux alentours jump_index = _check_value_around(messages, index, expected, amplitude) if jump_index is not None: return jump_index # Enfin, si on ne trouve pas la valeur à l'index donné et dans l'amplitude donnée # On performe une vérification lourde à cet endroit if do_heavy_check and _heavy_check(messages, index, expected): return index # Si tout cela n'a rien donné, on renvoie None return None