|
3 | 3 | from thefuzz import fuzz |
4 | 4 | import discord |
5 | 5 | from config import logger, EMBED_COLOR_RED, EMBED_THUMBNAIL, EMBED_FOOTER_TEXT, EMBED_IMAGE |
| 6 | +import pronos as pr |
6 | 7 |
|
7 | | - |
8 | | -def saveResults(): |
9 | | - |
| 8 | +def load_json_file(path): |
| 9 | + """Charge un fichier JSON, retourne un dict vide si fichier absent ou invalide.""" |
| 10 | + if not os.path.exists(path): |
| 11 | + logger.warning(f"Fichier {path} non trouvé, création d'un dict vide.") |
| 12 | + return {} |
10 | 13 | try: |
11 | | - with open('../data/Results.json', 'r', encoding='utf-8') as f: |
12 | | - results = json.load(f) |
13 | | - with open('../data/Pronos.json', 'r', encoding='utf-8') as f: |
14 | | - pronos = json.load(f) |
| 14 | + with open(path, 'r', encoding='utf-8') as f: |
| 15 | + return json.load(f) |
| 16 | + except (ValueError, json.JSONDecodeError) as e: |
| 17 | + logger.error(f"Erreur lors du chargement du fichier {path}: {e}") |
| 18 | + return {} |
15 | 19 |
|
16 | | - except ValueError: |
17 | | - logger.info("Files cannot be accessed, are you sure pronostics exist ?") |
| 20 | +def match_position(entry: str, target: str, threshold: int) -> bool: |
| 21 | + """Compare les noms avec tolérance, utilise fuzzy matching sur prénom ou nom.""" |
| 22 | + parts = entry.strip().split(' ', 1) |
| 23 | + if len(parts) == 2: |
| 24 | + prenom, nom = parts |
| 25 | + else: |
| 26 | + prenom = nom = parts[0] |
| 27 | + |
| 28 | + target_parts = target.strip().split(' ', 1) |
| 29 | + if len(target_parts) != 2: |
| 30 | + logger.warning(f"Format inattendu pour target '{target}'") |
| 31 | + return False |
| 32 | + target_prenom, target_nom = target_parts |
| 33 | + |
| 34 | + return (fuzz.ratio(target_prenom.lower(), prenom.lower()) >= 90 or |
| 35 | + fuzz.ratio(target_nom.lower(), nom.lower()) >= threshold) |
| 36 | + |
| 37 | +def save_results(race_type): |
| 38 | + """ |
| 39 | + race_type doit être une des chaînes : 'Qualif', 'CourseSprint', 'Course' |
| 40 | + """ |
| 41 | + |
| 42 | + logger.info(f"save_results appelée avec race_type = {race_type}") |
| 43 | + |
| 44 | + country = pr.country_fonction() |
| 45 | + |
| 46 | + results = load_json_file('data/Results.json') |
| 47 | + pronos = load_json_file(f'data/pronos_{country}.json') |
| 48 | + barem = load_json_file('data/Barem.json') |
| 49 | + |
| 50 | + if not results or not pronos or not barem: |
| 51 | + logger.info("Fichiers nécessaires absents ou invalides, arrêt de la fonction.") |
18 | 52 | return |
19 | | - |
20 | | - try: |
21 | | - with open('data/Barem.json', 'r', encoding='utf-8') as f: |
22 | | - barem = json.load(f) |
23 | | - except ValueError: |
24 | | - logger.info("Problem with Barem") |
| 53 | + |
| 54 | + if race_type not in barem: |
| 55 | + logger.error(f"Type de course '{race_type}' non supporté dans le barème.") |
25 | 56 | return |
26 | | - |
27 | | - Premier = results['1'] |
28 | | - Second = results['2'] |
29 | | - Troisième = results['3'] |
30 | | - |
31 | | - for key in pronos.keys(): |
| 57 | + |
| 58 | + bar = barem[race_type] |
| 59 | + premier = results.get('1', '') |
| 60 | + second = results.get('2', '') |
| 61 | + troisieme = results.get('3', '') |
| 62 | + |
| 63 | + leaderboard_path = 'data/Leaderbord.json' |
| 64 | + leaderboard = load_json_file(leaderboard_path) |
| 65 | + |
| 66 | + for key, user_pronos in pronos.items(): |
32 | 67 | points = 0 |
33 | | - complex = False |
34 | | - logger.info(key) |
35 | | - Premier_Pronos = False |
36 | | - Second_Pronos = False |
37 | | - Troisième_Pronos = False |
38 | | - |
39 | | - def match_Position(entry: str, target: str, threshold: int) -> bool: |
40 | | - parts = entry.strip().split(' ', 1) |
41 | | - |
42 | | - if (len(parts) == 2): |
43 | | - prenom, nom = parts |
44 | | - else: |
45 | | - prenom = nom = parts[0] |
46 | | - |
47 | | - target_prenom, target_nom = target.strip().split(' ', 1) |
48 | | - return fuzz.ratio(target_prenom.lower(), prenom.lower()) >= 90 or fuzz.ratio(target_nom.lower(), nom.lower()) >= threshold |
49 | | - |
50 | | - Premier_Pronos = match_Position(pronos[key]['1'], Premier, 90) |
51 | | - Second_Pronos = match_Position(pronos[key]['2'], Second, 90) |
52 | | - Troisième_Pronos = match_Position(pronos[key]['3'], Troisième, 90) |
53 | | - |
54 | | - try: |
55 | | - if Premier_Pronos and Second_Pronos and Troisième_Pronos: |
56 | | - logger.info("Tout juste pour "+pronos[key]["Pseudo"]) |
57 | | - points += barem["allCorrect"] |
58 | | - complex = True |
59 | | - |
60 | | - elif Premier_Pronos and Second_Pronos: |
61 | | - logger.info("Le premier et le deuxième sont bon pour " + |
62 | | - pronos[key]['Pseudo']+" + "+str(barem["first2Correct"])+" Points") |
63 | | - points += barem["first2Correct"] |
64 | | - complex = True |
65 | | - |
66 | | - elif Second_Pronos and Troisième_Pronos: |
67 | | - |
68 | | - logger.info( |
69 | | - "Le premier est bon"+pronos[key]['Pseudo']+" + "+str(barem["last2Correct"])+" Points") |
70 | | - points += barem["last2Correct"] |
71 | | - complex = True |
72 | | - |
73 | | - elif (Troisième_Pronos and Premier_Pronos): |
74 | | - |
75 | | - logger.info("Le premier et le dernier sont bons pour " + |
76 | | - pronos[key]['Pseudo']+" + "+str(barem["1and3Correct"])+" Points") |
77 | | - points += barem["1and3Correct"] |
78 | | - complex = True |
79 | | - |
80 | | - except KeyError as e: |
81 | | - logger.info(str(e) + "is missing") |
82 | | - pass |
83 | | - except TypeError as e: |
84 | | - logger.info(e) |
85 | | - pass |
86 | | - |
87 | | - try: |
88 | | - if (Premier_Pronos and not complex): |
89 | | - logger.info("Le premier est bon pour " + |
90 | | - pronos[key]['Pseudo']+" + "+str(barem["firstCorrect"])+" Points") |
91 | | - points += barem["firstCorrect"] |
92 | | - |
93 | | - if (Second_Pronos and not complex): |
94 | | - logger.info("Le deuxième est bon pour " + |
95 | | - pronos[key]['Pseudo']+" + "+str(barem["secondCorrect"])+" Points") |
96 | | - points += barem["secondCorrect"] |
97 | | - |
98 | | - if (Troisième_Pronos and not complex): |
99 | | - logger.info("Le troisième est bon pour " + |
100 | | - pronos[key]['Pseudo']+" + "+str(barem["thirdCorrect"])+" Points") |
101 | | - points += barem["thirdCorrect"] |
102 | | - |
103 | | - except KeyError as e: |
104 | | - logger.info(str(e) + "is missing") |
105 | | - return |
106 | | - |
107 | | - except TypeError as e: |
108 | | - logger.info(e) |
109 | | - return |
110 | | - |
111 | | - file_path = 'data/Leaderbord.json' |
112 | | - |
113 | | - if not os.path.exists(file_path): |
114 | | - pointsPronos = { |
115 | | - key: { |
116 | | - "Pseudo": pronos[key]["Pseudo"], |
117 | | - "Points": points |
118 | | - } |
119 | | - } |
| 68 | + pseudo = user_pronos.get('Pseudo', 'Inconnu') |
| 69 | + |
| 70 | + logger.info(f"Calcul des points pour l'utilisateur {key} ({pseudo})") |
| 71 | + |
| 72 | + premier_in_top3 = premier in [user_pronos.get('1', ''), user_pronos.get('2', ''), user_pronos.get('3', '')] |
| 73 | + second_in_top3 = second in [user_pronos.get('1', ''), user_pronos.get('2', ''), user_pronos.get('3', '')] |
| 74 | + troisieme_in_top3 = troisieme in [user_pronos.get('1', ''), user_pronos.get('2', ''), user_pronos.get('3', '')] |
| 75 | + |
| 76 | + premier_correct = match_position(user_pronos.get('1', ''), premier, 90) |
| 77 | + second_correct = match_position(user_pronos.get('2', ''), second, 90) |
| 78 | + troisieme_correct = match_position(user_pronos.get('3', ''), troisieme, 90) |
| 79 | + |
| 80 | + all_correct = premier_correct and second_correct and troisieme_correct |
| 81 | + |
| 82 | + if all_correct: |
| 83 | + points += bar.get('allCorrect', 0) |
| 84 | + logger.info(f"Tout juste pour {pseudo}, +{bar.get('allCorrect', 0)} pts") |
120 | 85 | else: |
121 | | - with open(file_path, 'r', encoding='utf-8') as f: |
122 | | - pointsPronos = json.load(f) |
123 | | - if key not in pointsPronos: |
124 | | - pointsPronos[key] = { |
125 | | - "Pseudo": pronos[key]["Pseudo"], |
126 | | - "Points": points |
127 | | - } |
128 | | - else: |
129 | | - pointsPronos[key]["Pseudo"] = pronos[key]["Pseudo"] |
130 | | - pointsPronos[key]["Points"] += points |
| 86 | + if premier_correct: |
| 87 | + points += bar.get('correctPosition', 0) |
| 88 | + logger.info(f"Premier à la bonne place pour {pseudo}, +{bar.get('correctPosition', 0)} pts") |
| 89 | + elif premier_in_top3: |
| 90 | + points += bar.get('inTop3', 0) |
| 91 | + logger.info(f"Premier dans le top 3 pour {pseudo}, +{bar.get('inTop3', 0)} pts") |
131 | 92 |
|
132 | | - with open(file_path, 'w', encoding='utf-8') as f: |
133 | | - json.dump(pointsPronos, f, ensure_ascii=False, indent=4) |
| 93 | + if second_correct: |
| 94 | + points += bar.get('correctPosition', 0) |
| 95 | + logger.info(f"Deuxième à la bonne place pour {pseudo}, +{bar.get('correctPosition', 0)} pts") |
| 96 | + elif second_in_top3: |
| 97 | + points += bar.get('inTop3', 0) |
| 98 | + logger.info(f"Deuxième dans le top 3 pour {pseudo}, +{bar.get('inTop3', 0)} pts") |
134 | 99 |
|
| 100 | + if troisieme_correct: |
| 101 | + points += bar.get('correctPosition', 0) |
| 102 | + logger.info(f"Troisième à la bonne place pour {pseudo}, +{bar.get('correctPosition', 0)} pts") |
| 103 | + elif troisieme_in_top3: |
| 104 | + points += bar.get('inTop3', 0) |
| 105 | + logger.info(f"Troisième dans le top 3 pour {pseudo}, +{bar.get('inTop3', 0)} pts") |
135 | 106 |
|
| 107 | + if key not in leaderboard: |
| 108 | + leaderboard[key] = {"Pseudo": pseudo, "Points": points} |
| 109 | + else: |
| 110 | + leaderboard[key]["Points"] += points |
| 111 | + |
| 112 | + |
| 113 | + try: |
| 114 | + with open(leaderboard_path, 'w', encoding='utf-8') as f: |
| 115 | + json.dump(leaderboard, f, ensure_ascii=False, indent=4) |
| 116 | + logger.info(f"Leaderboard mis à jour avec {len(leaderboard)} utilisateurs.") |
| 117 | + except Exception as e: |
| 118 | + logger.error(f"Erreur lors de la sauvegarde du leaderboard: {e}") |
| 119 | + |
| 120 | + |
136 | 121 | def Leaderboard(): |
137 | | - |
138 | 122 | try: |
139 | 123 | with open("data/Leaderbord.json", 'r', encoding='utf-8') as f: |
140 | 124 | pointsPronos = json.load(f) |
141 | 125 | pronosLeaderboard = dict( |
142 | | - sorted(pointsPronos.items(), key=lambda item: item[1]['Points'], reverse=True)) |
143 | | - |
| 126 | + sorted(pointsPronos.items(), key=lambda item: item[1]['Points'], reverse=True) |
| 127 | + ) |
144 | 128 | except FileNotFoundError: |
145 | 129 | logger.info("The file doesn't exist") |
146 | | - return |
147 | | - |
| 130 | + return None |
| 131 | + except json.JSONDecodeError: |
| 132 | + logger.info("Le fichier Leaderbord.json est vide ou mal formé") |
| 133 | + return None |
148 | 134 | except KeyError: |
149 | 135 | logger.info("Erreur dans la lecture des points") |
150 | | - return |
151 | | - |
| 136 | + return None |
| 137 | + |
| 138 | + if not pronosLeaderboard: |
| 139 | + embed = discord.Embed( |
| 140 | + title="🏆 Classement des Pronostics", |
| 141 | + description="Aucun résultat à afficher pour le moment.", |
| 142 | + color=EMBED_COLOR_RED |
| 143 | + ) |
| 144 | + embed.set_footer(text=EMBED_FOOTER_TEXT) |
| 145 | + embed.set_image(url=EMBED_IMAGE) |
| 146 | + embed.set_thumbnail(url=EMBED_THUMBNAIL) |
| 147 | + return embed |
| 148 | + |
| 149 | + # Extraction sécurisée des pseudos et points |
| 150 | + try: |
| 151 | + pseudos, points = zip(*[(val['Pseudo'], val['Points']) for val in pronosLeaderboard.values()]) |
| 152 | + except ValueError: |
| 153 | + logger.info("Erreur lors de l'extraction des pseudos et points.") |
| 154 | + return None |
| 155 | + |
152 | 156 | embed = discord.Embed( |
153 | 157 | title="🏆 Classement des Pronostics", |
154 | 158 | color=EMBED_COLOR_RED |
155 | 159 | ) |
156 | | - pseudos, points = zip(*[(val['Pseudo'], val['Points']) |
157 | | - for val in pronosLeaderboard.values()]) |
158 | | - pseudos = list(pseudos) |
159 | | - points = list(points) |
160 | 160 | position = 1 |
161 | 161 | for i in range(len(pseudos)): |
162 | 162 | embed.add_field(name=f"{position}ᵉ - {pseudos[i]}", |
163 | 163 | value=f"Score : **{points[i]}**", inline=False) |
164 | | - if (i+1 < len(pseudos) and points[i] != points[i+1]): |
| 164 | + # Incrément de la position seulement si le prochain score est différent |
| 165 | + if (i + 1 < len(pseudos)) and (points[i] != points[i + 1]): |
165 | 166 | position += 1 |
| 167 | + |
166 | 168 | embed.set_footer(text=EMBED_FOOTER_TEXT) |
167 | 169 | embed.set_image(url=EMBED_IMAGE) |
168 | 170 | embed.set_thumbnail(url=EMBED_THUMBNAIL) |
|
0 commit comments