選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

count_analysis.py 4.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. from typing import List
  2. import million.analyze.message_evaluation as msg_val
  3. from million.model.message import Message
  4. def check_extra_or_missing_letter(word: str, reference: str) -> bool:
  5. """
  6. Cette méthode vérifie si la str word contient une et une seule lettre
  7. de trop ou de moins par rapport à la str reference
  8. """
  9. len_word = len(word)
  10. len_ref = len(reference)
  11. if abs(len_word - len_ref) != 1:
  12. return False
  13. shortest = word if len_word < len_ref else reference
  14. longest = word if len_word > len_ref else reference
  15. for i in range(len(shortest)):
  16. if shortest[i] != longest[i]:
  17. return shortest[i:] == longest[i + 1 :]
  18. return True
  19. def check_single_letter_differ(word: str, reference: str) -> bool:
  20. """
  21. Cette méthode vérifie si la str word contient une et une seule
  22. lettre différente par rapport à la str reference
  23. """
  24. return sum(1 for x, y in zip(reference, word) if x != y) == 1
  25. def check_letter_swap(word: str, reference: str) -> bool:
  26. """
  27. Cette méthode vérifie si la str word contient un et un seul
  28. échange de lettres consécutives par rapport à la str reference
  29. """
  30. if len(word) != len(reference):
  31. return False
  32. for i in range(len(word) - 1):
  33. if word[i] != reference[i]:
  34. return word[i + 1] + word[i] + word[i + 2 :] == reference[i:]
  35. return False
  36. def check_typo(word: str, reference: str) -> bool:
  37. """
  38. Cette méthode vérifie si la str word contient une typo en se référant à la str reference
  39. """
  40. if len(reference) == len(word):
  41. return check_single_letter_differ(word, reference) or check_letter_swap(
  42. word, reference
  43. )
  44. else:
  45. return check_extra_or_missing_letter(word, reference)
  46. def _check_message_concatenation(messages: List[Message], index: int, expected: int) -> bool:
  47. """
  48. Cette méthode détermine si la liste messages contient le compte expected à partir de l'index donné
  49. en concaténant les valeurs des messages suivants.
  50. Cette méthode permet de trouver un compte qui a été étalé sur plusieurs messages
  51. """
  52. reference = str(expected)
  53. testing = ""
  54. offset = 0
  55. while len(testing) < len(reference):
  56. next_message = messages[index + offset]
  57. offset += 1
  58. if next_message.sender_name == messages[index].sender_name:
  59. testing += str(msg_val.get(next_message))
  60. return testing == reference
  61. def _heavy_check(messages: List[Message], index: int, expected: int) -> bool:
  62. """
  63. Cette méthode détermine si la liste messages contient le compte expected à partir de l'index donné.
  64. Elle utilise pour cela des méthodes complexes qui ne permettent de trouver un résultat
  65. seulement si on est sortis du cas nominal
  66. """
  67. # TODO
  68. # - créer une méthode pour gérer le cas où plusieurs comptages sont contenus dans le même corps de message
  69. # - créer une méthode pour le cas où les chiffres sont représentés par un substitut au sein du corps du message
  70. # i.e. un nombre écrit en toutes lettres (français ou breton), 🍁 pour 420, @Elias Cheddar pour 69
  71. m = messages[index]
  72. word = str(msg_val.get(m))
  73. return _check_message_concatenation(messages, index, expected) or \
  74. check_typo(word, str(expected)) and msg_val.get(messages[index+1]) == expected+1
  75. def _check_value_around(messages, index, expected, amplitude_after, amplitude_before):
  76. for i in range(1, amplitude_after + 1):
  77. if index + i < len(messages) and expected == msg_val.get(messages[index + i]):
  78. return index + i
  79. for i in range(1, amplitude_before + 1):
  80. if expected == msg_val.get(messages[index - i]):
  81. return index - i
  82. return None
  83. def search_value_at(messages, index, expected, do_heavy_check=True, amplitude_after=1000, amplitude_before=10):
  84. """
  85. Cette méthode détermine si la liste messages contient le compte expected à partir de l'index donné.
  86. Le paramètre amplitude détermine la plage où effectuer les recherches autour de l'index donné.
  87. Le paramètre do_heavy_check précise si on doit pousser l'analyse avec des méthodes plus lourdes en cas d'échec
  88. """
  89. # Si le message courant contient la valeur, on renvoie
  90. curr_value = msg_val.get(messages[index])
  91. if expected == curr_value:
  92. return index
  93. # Sinon on regarde aux alentours
  94. jump_index = _check_value_around(messages, index, expected, amplitude_after, amplitude_before)
  95. if jump_index is not None:
  96. return jump_index
  97. # Enfin, si on ne trouve pas la valeur à l'index donné et dans l'amplitude donnée
  98. # On performe une vérification lourde à cet endroit
  99. if do_heavy_check and _heavy_check(messages, index, expected):
  100. return index
  101. # Si tout cela n'a rien donné, on renvoie None
  102. return None