v0.2
Browse files- .DS_Store +0 -0
- README copy.md +0 -1
- README.md +1 -12
- app.py +441 -221
- arena.py +117 -52
- database/estimates.csv +0 -5
- database/inputs.csv +2 -0
- database/prompts.csv +1 -6
- database/switches.csv +5 -0
- database/teams.csv +16 -0
- database/users.csv +47 -0
- database/votes.csv +1 -11
- db.py +89 -5
- plot.py +68 -0
- poetry.lock +77 -1
- prompts.csv +0 -5
- pyproject.toml +1 -1
- routes.py +0 -7
.DS_Store
ADDED
Binary file (6.15 kB). View file
|
|
README copy.md
DELETED
@@ -1 +0,0 @@
|
|
1 |
-
# prompt-arena
|
|
|
|
README.md
CHANGED
@@ -1,12 +1 @@
|
|
1 |
-
|
2 |
-
title: Arena
|
3 |
-
emoji: 🐨
|
4 |
-
colorFrom: indigo
|
5 |
-
colorTo: red
|
6 |
-
sdk: gradio
|
7 |
-
sdk_version: 5.29.0
|
8 |
-
app_file: app.py
|
9 |
-
pinned: false
|
10 |
-
---
|
11 |
-
|
12 |
-
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
|
|
1 |
+
# prompt-arena
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app.py
CHANGED
@@ -1,176 +1,39 @@
|
|
1 |
import gradio as gr
|
2 |
-
import pandas as pd
|
3 |
from arena import Arena
|
4 |
-
import
|
5 |
-
|
6 |
|
7 |
LABEL_A = "Proposition A"
|
8 |
LABEL_B = "Proposition B"
|
9 |
|
10 |
ARENA = Arena()
|
11 |
-
ARENA.init_estimates()
|
12 |
-
|
13 |
-
|
14 |
-
def select_and_display_match():
|
15 |
-
try:
|
16 |
-
prompt_a, prompt_b = ARENA.select_match()
|
17 |
-
state = {"prompt_a_id": prompt_a["id"], "prompt_b_id": prompt_b["id"]}
|
18 |
-
vote_a_btn_update = gr.update(interactive=True)
|
19 |
-
vote_b_btn_update = gr.update(interactive=True)
|
20 |
-
new_match_btn_update = gr.update(interactive=False)
|
21 |
-
return (
|
22 |
-
prompt_a["text"],
|
23 |
-
prompt_b["text"],
|
24 |
-
state,
|
25 |
-
vote_a_btn_update,
|
26 |
-
vote_b_btn_update,
|
27 |
-
new_match_btn_update,
|
28 |
-
)
|
29 |
-
except Exception as e:
|
30 |
-
return f"Erreur lors de la sélection d'un match: {str(e)}", "", "", {}
|
31 |
-
|
32 |
-
|
33 |
-
def record_winner_a(state):
|
34 |
-
try:
|
35 |
-
prompt_a_id = state["prompt_a_id"]
|
36 |
-
prompt_b_id = state["prompt_b_id"]
|
37 |
-
ARENA.record_result(prompt_a_id, prompt_b_id)
|
38 |
-
progress_info = ARENA.get_progress()
|
39 |
-
rankings_table = ARENA.get_rankings()
|
40 |
-
vote_a_btn_update = gr.update(interactive=False)
|
41 |
-
vote_b_btn_update = gr.update(interactive=False)
|
42 |
-
new_match_btn_update = gr.update(interactive=True)
|
43 |
-
return (
|
44 |
-
f"Vous avez choisi : {LABEL_A}",
|
45 |
-
progress_info,
|
46 |
-
rankings_table,
|
47 |
-
vote_a_btn_update,
|
48 |
-
vote_b_btn_update,
|
49 |
-
new_match_btn_update,
|
50 |
-
)
|
51 |
-
except Exception as e:
|
52 |
-
return (
|
53 |
-
f"Erreur lors de l'enregistrement du résultat: {str(e)}",
|
54 |
-
"",
|
55 |
-
pd.DataFrame(),
|
56 |
-
)
|
57 |
-
|
58 |
-
|
59 |
-
def record_winner_b(state):
|
60 |
-
try:
|
61 |
-
prompt_a_id = state["prompt_a_id"]
|
62 |
-
prompt_b_id = state["prompt_b_id"]
|
63 |
-
ARENA.record_result(prompt_b_id, prompt_a_id)
|
64 |
-
progress_info = ARENA.get_progress()
|
65 |
-
rankings_table = ARENA.get_rankings()
|
66 |
-
vote_a_btn_update = gr.update(interactive=False)
|
67 |
-
vote_b_btn_update = gr.update(interactive=False)
|
68 |
-
new_match_btn_update = gr.update(interactive=True)
|
69 |
-
return (
|
70 |
-
f"Vous avez choisi : {LABEL_B}",
|
71 |
-
progress_info,
|
72 |
-
rankings_table,
|
73 |
-
vote_a_btn_update,
|
74 |
-
vote_b_btn_update,
|
75 |
-
new_match_btn_update,
|
76 |
-
)
|
77 |
-
except Exception as e:
|
78 |
-
return (
|
79 |
-
f"Erreur lors de l'enregistrement du résultat: {str(e)}",
|
80 |
-
"",
|
81 |
-
pd.DataFrame(),
|
82 |
-
)
|
83 |
-
|
84 |
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
|
|
89 |
|
|
|
|
|
|
|
|
|
|
|
90 |
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
def welcome_user(request: gr.Request):
|
97 |
-
return request.username
|
98 |
-
|
99 |
-
|
100 |
-
def plot_estimates_distribution():
|
101 |
-
"""Affiche une gaussienne par prompt (Plotly) + lignes verticales pointillées sur les moyennes."""
|
102 |
-
estimates = ARENA.load("estimates")
|
103 |
-
prompts = ARENA.load("prompts")
|
104 |
-
if estimates.empty or prompts.empty:
|
105 |
-
fig = go.Figure()
|
106 |
-
fig.add_annotation(
|
107 |
-
text="Aucune estimation disponible", x=0.5, y=0.5, showarrow=False
|
108 |
-
)
|
109 |
-
return fig
|
110 |
-
x = np.linspace(
|
111 |
-
estimates["mu"].min() - 3 * estimates["sigma"].max(),
|
112 |
-
estimates["mu"].max() + 3 * estimates["sigma"].max(),
|
113 |
-
500,
|
114 |
-
)
|
115 |
-
fig = go.Figure()
|
116 |
-
shapes = []
|
117 |
-
# Une gaussienne par prompt
|
118 |
-
for _, row in estimates.iterrows():
|
119 |
-
mu = row["mu"]
|
120 |
-
sigma = row["sigma"]
|
121 |
-
prompt_id = row["prompt_id"] if "prompt_id" in row else row["id"]
|
122 |
-
# Chercher le nom du prompt
|
123 |
-
name = str(prompt_id)
|
124 |
-
if "name" in prompts.columns:
|
125 |
-
match = prompts[prompts["id"] == prompt_id]
|
126 |
-
if not match.empty:
|
127 |
-
name = match.iloc[0]["name"]
|
128 |
-
y = 1 / (sigma * np.sqrt(2 * np.pi)) * np.exp(-0.5 * ((x - mu) / sigma) ** 2)
|
129 |
-
fig.add_trace(
|
130 |
-
go.Scatter(
|
131 |
-
x=x,
|
132 |
-
y=y,
|
133 |
-
mode="lines",
|
134 |
-
name=f"{name}",
|
135 |
-
hovertemplate=f"<b>{name}</b><br>Score (mu): {mu:.2f}<br>Sigma: {sigma:.2f}<extra></extra>",
|
136 |
-
)
|
137 |
-
)
|
138 |
-
# Ajout de la ligne verticale pointillée à mu (en gris)
|
139 |
-
shapes.append(
|
140 |
-
dict(
|
141 |
-
type="line",
|
142 |
-
x0=mu,
|
143 |
-
x1=mu,
|
144 |
-
y0=0,
|
145 |
-
y1=max(y),
|
146 |
-
line=dict(
|
147 |
-
color="gray",
|
148 |
-
width=2,
|
149 |
-
dash="dot",
|
150 |
-
),
|
151 |
-
xref="x",
|
152 |
-
yref="y",
|
153 |
-
)
|
154 |
-
)
|
155 |
-
fig.update_layout(
|
156 |
-
title="Distribution gaussienne de chaque prompt",
|
157 |
-
xaxis_title="Score (mu)",
|
158 |
-
yaxis_title="Densité",
|
159 |
-
template="plotly_white",
|
160 |
-
shapes=shapes,
|
161 |
-
)
|
162 |
-
return fig
|
163 |
-
|
164 |
|
165 |
with gr.Blocks(
|
166 |
title="Prompt Arena",
|
|
|
167 |
# theme=gr.themes.Default.load("theme_schema_miku.json"),
|
168 |
) as demo:
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
username = gr.Markdown("")
|
173 |
-
gr.Button("Logout", link="/logout", scale=0, min_width=50)
|
174 |
|
175 |
gr.Markdown(
|
176 |
'<h1 style="text-align:center;"> Concours du meilleur Prompt Engineer </h1>'
|
@@ -181,120 +44,477 @@ with gr.Blocks(
|
|
181 |
value=ARENA.get_progress(),
|
182 |
interactive=False,
|
183 |
lines=2,
|
|
|
184 |
)
|
185 |
|
186 |
with gr.Tabs() as tabs:
|
187 |
-
|
188 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
189 |
with gr.Row():
|
190 |
new_match_btn = gr.Button("Lancer un nouveau match", variant="primary")
|
191 |
|
192 |
with gr.Row():
|
193 |
with gr.Column():
|
194 |
-
|
195 |
-
|
|
|
196 |
with gr.Column():
|
197 |
-
|
198 |
-
|
199 |
-
|
|
|
200 |
|
201 |
# with gr.TabItem("Classement"):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
202 |
rankings_table = gr.DataFrame(
|
203 |
-
label="Classement des
|
204 |
value=ARENA.get_rankings(),
|
205 |
interactive=True,
|
206 |
)
|
207 |
|
208 |
-
|
209 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
210 |
with gr.Accordion("Prompts", open=False):
|
211 |
prompts_table = gr.DataFrame(
|
212 |
-
value=
|
213 |
interactive=True,
|
214 |
)
|
215 |
with gr.Accordion("Estimates", open=False):
|
216 |
estimates_table = gr.DataFrame(
|
217 |
label="Estimations",
|
218 |
-
value=
|
219 |
interactive=True,
|
220 |
)
|
221 |
with gr.Accordion("Votes", open=False):
|
222 |
votes_table = gr.DataFrame(
|
223 |
label="Votes",
|
224 |
-
value=
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
225 |
interactive=True,
|
226 |
)
|
227 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
228 |
prompts_table.change(
|
229 |
-
|
230 |
inputs=[gr.Markdown("prompts", visible=False), prompts_table],
|
231 |
outputs=None,
|
232 |
)
|
233 |
estimates_table.change(
|
234 |
-
|
235 |
inputs=[gr.Markdown("estimates", visible=False), estimates_table],
|
236 |
outputs=None,
|
237 |
)
|
238 |
votes_table.change(
|
239 |
-
|
240 |
inputs=[gr.Markdown("votes", visible=False), votes_table],
|
241 |
outputs=None,
|
242 |
)
|
|
|
|
|
|
|
|
|
|
|
243 |
|
244 |
-
|
245 |
-
|
246 |
-
|
247 |
-
|
248 |
-
|
249 |
-
proposition_b,
|
250 |
-
state,
|
251 |
-
vote_a_btn,
|
252 |
-
vote_b_btn,
|
253 |
-
new_match_btn,
|
254 |
-
],
|
255 |
-
)
|
256 |
|
257 |
-
|
258 |
-
|
259 |
-
|
260 |
-
|
|
|
|
|
|
|
|
|
261 |
outputs=[
|
262 |
-
|
263 |
-
|
264 |
-
|
265 |
-
|
266 |
-
|
267 |
-
new_match_btn,
|
268 |
],
|
269 |
)
|
270 |
-
|
271 |
-
|
272 |
-
|
273 |
-
|
274 |
-
|
275 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
276 |
rankings_table,
|
277 |
-
|
278 |
-
|
279 |
-
new_match_btn,
|
280 |
],
|
281 |
)
|
282 |
|
283 |
-
demo.load(admin_visible, None, admin_tab)
|
284 |
-
demo.load(welcome_user, None, username)
|
285 |
-
|
286 |
-
|
287 |
-
def arena_auth(username, password):
|
288 |
-
if username == "admin":
|
289 |
-
return (
|
290 |
-
password == "fred"
|
291 |
-
) # todo : mettre le mot de passe en variable d'environnement
|
292 |
-
else:
|
293 |
-
return username == password
|
294 |
-
|
295 |
|
296 |
# Exemple d'utilisation
|
297 |
if __name__ == "__main__":
|
298 |
-
demo.launch(
|
299 |
-
auth_message="Connexion à l'arène des prompts"
|
300 |
-
) # ajouter share=True pour partager l'interface
|
|
|
1 |
import gradio as gr
|
|
|
2 |
from arena import Arena
|
3 |
+
import db
|
4 |
+
from plot import plot_estimates_distribution
|
5 |
|
6 |
LABEL_A = "Proposition A"
|
7 |
LABEL_B = "Proposition B"
|
8 |
|
9 |
ARENA = Arena()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
10 |
|
11 |
+
css_code = """
|
12 |
+
/* Cible le premier bloc Markdown par son ID et lui donne un fond vert */
|
13 |
+
#green_background input {
|
14 |
+
background-color: #41f707; /* Vert très clair */
|
15 |
+
}
|
16 |
|
17 |
+
/* Cible le second bloc Markdown par son ID et lui donne un fond rouge */
|
18 |
+
#red_background input {
|
19 |
+
background-color: #FFCCCC
|
20 |
+
}
|
21 |
+
"""
|
22 |
|
23 |
+
"""
|
24 |
+
###############
|
25 |
+
Gradio UI setup
|
26 |
+
###############
|
27 |
+
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
28 |
|
29 |
with gr.Blocks(
|
30 |
title="Prompt Arena",
|
31 |
+
css=css_code,
|
32 |
# theme=gr.themes.Default.load("theme_schema_miku.json"),
|
33 |
) as demo:
|
34 |
+
match_state = gr.State()
|
35 |
+
user_state = gr.State()
|
36 |
+
timer = gr.Timer(1)
|
|
|
|
|
37 |
|
38 |
gr.Markdown(
|
39 |
'<h1 style="text-align:center;"> Concours du meilleur Prompt Engineer </h1>'
|
|
|
44 |
value=ARENA.get_progress(),
|
45 |
interactive=False,
|
46 |
lines=2,
|
47 |
+
visible=False,
|
48 |
)
|
49 |
|
50 |
with gr.Tabs() as tabs:
|
51 |
+
"""
|
52 |
+
##################
|
53 |
+
Identification Tab
|
54 |
+
##################
|
55 |
+
"""
|
56 |
+
# Onglet Identification
|
57 |
+
with gr.TabItem("Identification", id=0) as identification_tab:
|
58 |
+
with gr.Row():
|
59 |
+
user_code_box = gr.Textbox(
|
60 |
+
label="Code d'identification",
|
61 |
+
placeholder="Saisissez votre code d'identification à 4 lettres",
|
62 |
+
interactive=True,
|
63 |
+
scale=0,
|
64 |
+
min_width=250,
|
65 |
+
max_lines=1,
|
66 |
+
)
|
67 |
+
validate_btn = gr.Button(
|
68 |
+
"Valider", variant="primary", scale=0, min_width=250
|
69 |
+
)
|
70 |
+
message = gr.Markdown("")
|
71 |
+
|
72 |
+
"""
|
73 |
+
######################
|
74 |
+
Identification Actions
|
75 |
+
######################
|
76 |
+
"""
|
77 |
+
|
78 |
+
@validate_btn.click(inputs=[user_code_box], outputs=[message, user_state])
|
79 |
+
def on_validate(code):
|
80 |
+
if not code or not code.strip():
|
81 |
+
return {
|
82 |
+
message: gr.update(
|
83 |
+
value="Veuillez entrer votre code d'identification.",
|
84 |
+
visible=True,
|
85 |
+
)
|
86 |
+
}
|
87 |
+
user = db.get_user(code)
|
88 |
+
if not user:
|
89 |
+
return {
|
90 |
+
message: gr.update(
|
91 |
+
value="Code invalide. Veuillez vérifier votre code d'identification.",
|
92 |
+
visible=True,
|
93 |
+
)
|
94 |
+
}
|
95 |
+
return {
|
96 |
+
message: gr.update(
|
97 |
+
value=f"Code reconnu ! Bienvenue joueur {user['username']}",
|
98 |
+
visible=True,
|
99 |
+
),
|
100 |
+
user_state: user,
|
101 |
+
}
|
102 |
+
|
103 |
+
"""
|
104 |
+
##################
|
105 |
+
Candidate Tab
|
106 |
+
##################
|
107 |
+
"""
|
108 |
+
|
109 |
+
with gr.TabItem("Candidat", id=1, visible=False) as candidat_tab:
|
110 |
+
with gr.Row():
|
111 |
+
with gr.Column():
|
112 |
+
label_input = gr.Markdown(value="Sujet du concours")
|
113 |
+
competition_input = gr.Markdown(
|
114 |
+
value=db.load("inputs")
|
115 |
+
.query("name == 'Competition'")["text"]
|
116 |
+
.values[0],
|
117 |
+
container=True,
|
118 |
+
max_height=100,
|
119 |
+
)
|
120 |
+
with gr.Row():
|
121 |
+
with gr.Column():
|
122 |
+
label_candidat = gr.Markdown(
|
123 |
+
value="Votre candidat au concours",
|
124 |
+
)
|
125 |
+
candidat_prompt = gr.Textbox(
|
126 |
+
show_label=False,
|
127 |
+
placeholder="Saisissez votre texte candidat",
|
128 |
+
interactive=True,
|
129 |
+
scale=0,
|
130 |
+
min_width=600,
|
131 |
+
lines=10,
|
132 |
+
max_lines=10,
|
133 |
+
)
|
134 |
+
submit_prompt_btn = gr.Button(
|
135 |
+
"Valider", variant="primary", scale=0, min_width=250
|
136 |
+
)
|
137 |
+
|
138 |
+
message_prompt = gr.Markdown("", visible=False)
|
139 |
+
|
140 |
+
with gr.Column():
|
141 |
+
label_preview = gr.Markdown(
|
142 |
+
value="Aperçu du rendu (pour les contenus markdown ou html)",
|
143 |
+
visible=False,
|
144 |
+
)
|
145 |
+
candidat_preview = gr.Markdown(
|
146 |
+
value="", max_height=300, visible=False, container=True
|
147 |
+
)
|
148 |
+
|
149 |
+
candidat_prompt.change(
|
150 |
+
lambda text: (
|
151 |
+
gr.update(value=text if text else "", visible=True if text else False),
|
152 |
+
gr.update(visible=True if text else False),
|
153 |
+
),
|
154 |
+
inputs=candidat_prompt,
|
155 |
+
outputs=[candidat_preview, label_preview],
|
156 |
+
)
|
157 |
+
|
158 |
+
"""
|
159 |
+
##################
|
160 |
+
Compétition Tab
|
161 |
+
##################
|
162 |
+
"""
|
163 |
+
|
164 |
+
with gr.TabItem("Compétition", id=2, visible=False) as competition_tab:
|
165 |
with gr.Row():
|
166 |
new_match_btn = gr.Button("Lancer un nouveau match", variant="primary")
|
167 |
|
168 |
with gr.Row():
|
169 |
with gr.Column():
|
170 |
+
label_prop_a = gr.Markdown(LABEL_A, visible=False)
|
171 |
+
proposition_a = gr.Markdown(container=True, visible=False)
|
172 |
+
vote_a_btn = gr.Button("Choisir " + LABEL_A, visible=False)
|
173 |
with gr.Column():
|
174 |
+
label_prop_b = gr.Markdown(LABEL_B, visible=False)
|
175 |
+
proposition_b = gr.Markdown(container=True, visible=False)
|
176 |
+
vote_b_btn = gr.Button("Choisir " + LABEL_B, visible=False)
|
177 |
+
result = gr.Markdown("")
|
178 |
|
179 |
# with gr.TabItem("Classement"):
|
180 |
+
|
181 |
+
@new_match_btn.click(
|
182 |
+
inputs=[user_state],
|
183 |
+
outputs=[
|
184 |
+
label_prop_a,
|
185 |
+
label_prop_b,
|
186 |
+
proposition_a,
|
187 |
+
proposition_b,
|
188 |
+
match_state,
|
189 |
+
vote_a_btn,
|
190 |
+
vote_b_btn,
|
191 |
+
new_match_btn,
|
192 |
+
result,
|
193 |
+
],
|
194 |
+
)
|
195 |
+
def select_and_display_match(user_state):
|
196 |
+
selected_match = ARENA.select_match(user_state)
|
197 |
+
if not selected_match:
|
198 |
+
return {
|
199 |
+
label_prop_a: gr.update(visible=False),
|
200 |
+
label_prop_b: gr.update(visible=False),
|
201 |
+
proposition_a: gr.update(value="", visible=False),
|
202 |
+
proposition_b: gr.update(value="", visible=False),
|
203 |
+
match_state: {},
|
204 |
+
vote_a_btn: gr.update(visible=False),
|
205 |
+
vote_b_btn: gr.update(visible=False),
|
206 |
+
new_match_btn: gr.update(visible=False),
|
207 |
+
result: gr.update(
|
208 |
+
value="Vous avez déjà voté pour tous les matchs disponibles. Veuillez attendre la fin de la compétition"
|
209 |
+
),
|
210 |
+
}
|
211 |
+
else:
|
212 |
+
prompt_a, prompt_b = selected_match
|
213 |
+
return {
|
214 |
+
label_prop_a: gr.update(visible=True),
|
215 |
+
label_prop_b: gr.update(visible=True),
|
216 |
+
proposition_a: gr.update(value=prompt_a["text"], visible=True),
|
217 |
+
proposition_b: gr.update(value=prompt_b["text"], visible=True),
|
218 |
+
match_state: {
|
219 |
+
"prompt_a_id": prompt_a["id"],
|
220 |
+
"prompt_b_id": prompt_b["id"],
|
221 |
+
},
|
222 |
+
vote_a_btn: gr.update(visible=True),
|
223 |
+
vote_b_btn: gr.update(visible=True),
|
224 |
+
new_match_btn: gr.update(visible=False),
|
225 |
+
result: gr.update(value=""), # Reset result
|
226 |
+
}
|
227 |
+
|
228 |
+
@vote_a_btn.click(
|
229 |
+
inputs=[match_state, user_state],
|
230 |
+
outputs=[
|
231 |
+
result,
|
232 |
+
vote_a_btn,
|
233 |
+
vote_b_btn,
|
234 |
+
new_match_btn,
|
235 |
+
],
|
236 |
+
)
|
237 |
+
def record_winner_a(match_state, user_state):
|
238 |
+
prompt_a_id = match_state["prompt_a_id"]
|
239 |
+
prompt_b_id = match_state["prompt_b_id"]
|
240 |
+
ARENA.record_result(prompt_a_id, prompt_b_id, user_state["id"])
|
241 |
+
return {
|
242 |
+
result: f"Vous avez choisi : {LABEL_A}",
|
243 |
+
vote_a_btn: gr.update(visible=False),
|
244 |
+
vote_b_btn: gr.update(visible=False),
|
245 |
+
new_match_btn: gr.update(visible=True),
|
246 |
+
}
|
247 |
+
|
248 |
+
@vote_b_btn.click(
|
249 |
+
inputs=[match_state, user_state],
|
250 |
+
outputs=[
|
251 |
+
result,
|
252 |
+
vote_a_btn,
|
253 |
+
vote_b_btn,
|
254 |
+
new_match_btn,
|
255 |
+
],
|
256 |
+
)
|
257 |
+
def record_winner_b(match_state, user_state):
|
258 |
+
prompt_a_id = match_state["prompt_a_id"]
|
259 |
+
prompt_b_id = match_state["prompt_b_id"]
|
260 |
+
ARENA.record_result(prompt_b_id, prompt_a_id, user_state["id"])
|
261 |
+
return {
|
262 |
+
result: f"Vous avez choisi : {LABEL_B}",
|
263 |
+
vote_a_btn: gr.update(visible=False),
|
264 |
+
vote_b_btn: gr.update(visible=False),
|
265 |
+
new_match_btn: gr.update(visible=True),
|
266 |
+
}
|
267 |
+
|
268 |
+
"""
|
269 |
+
##################
|
270 |
+
Result Tab
|
271 |
+
##################
|
272 |
+
"""
|
273 |
+
with gr.TabItem("Classement") as result_tab:
|
274 |
rankings_table = gr.DataFrame(
|
275 |
+
label="Classement des équipes",
|
276 |
value=ARENA.get_rankings(),
|
277 |
interactive=True,
|
278 |
)
|
279 |
|
280 |
+
"""
|
281 |
+
##################
|
282 |
+
Follow up Tab
|
283 |
+
##################
|
284 |
+
"""
|
285 |
+
|
286 |
+
with gr.TabItem("Followup") as admin_followup_tab:
|
287 |
+
with gr.Accordion("Matrice de compétition", open=False):
|
288 |
+
competition_matrix = gr.DataFrame(
|
289 |
+
value=ARENA.get_competition_matrix(),
|
290 |
+
interactive=True,
|
291 |
+
)
|
292 |
+
with gr.Accordion("Distribution des estimations", open=False):
|
293 |
+
plot_estimates = gr.Plot(
|
294 |
+
plot_estimates_distribution,
|
295 |
+
label="Distribution des estimations",
|
296 |
+
)
|
297 |
+
|
298 |
+
"""
|
299 |
+
##################
|
300 |
+
Database Tab
|
301 |
+
##################
|
302 |
+
"""
|
303 |
+
with gr.TabItem("Database") as admin_db_tab:
|
304 |
+
with gr.Accordion("Switches", open=False):
|
305 |
+
switches_table = gr.DataFrame(
|
306 |
+
value=db.load("switches"),
|
307 |
+
interactive=True,
|
308 |
+
)
|
309 |
+
with gr.Accordion("Inputs", open=False):
|
310 |
+
inputs_table = gr.DataFrame(
|
311 |
+
value=db.load("inputs"),
|
312 |
+
interactive=True,
|
313 |
+
)
|
314 |
with gr.Accordion("Prompts", open=False):
|
315 |
prompts_table = gr.DataFrame(
|
316 |
+
value=db.load("prompts"),
|
317 |
interactive=True,
|
318 |
)
|
319 |
with gr.Accordion("Estimates", open=False):
|
320 |
estimates_table = gr.DataFrame(
|
321 |
label="Estimations",
|
322 |
+
value=db.load("estimates"),
|
323 |
interactive=True,
|
324 |
)
|
325 |
with gr.Accordion("Votes", open=False):
|
326 |
votes_table = gr.DataFrame(
|
327 |
label="Votes",
|
328 |
+
value=db.load("votes"),
|
329 |
+
interactive=True,
|
330 |
+
)
|
331 |
+
with gr.Accordion("Teams", open=False):
|
332 |
+
teams_table = gr.DataFrame(
|
333 |
+
label="Teams",
|
334 |
+
value=db.load("teams"),
|
335 |
+
interactive=True,
|
336 |
+
)
|
337 |
+
with gr.Accordion("Users", open=False):
|
338 |
+
users_table = gr.DataFrame(
|
339 |
+
label="Users",
|
340 |
+
value=db.load("users"),
|
341 |
interactive=True,
|
342 |
)
|
343 |
+
|
344 |
+
switches_table.change(
|
345 |
+
db.replace,
|
346 |
+
inputs=[gr.Markdown("switches", visible=False), switches_table],
|
347 |
+
outputs=None,
|
348 |
+
)
|
349 |
+
inputs_table.change(
|
350 |
+
db.replace,
|
351 |
+
inputs=[gr.Markdown("inputs", visible=False), inputs_table],
|
352 |
+
outputs=None,
|
353 |
+
)
|
354 |
prompts_table.change(
|
355 |
+
db.replace,
|
356 |
inputs=[gr.Markdown("prompts", visible=False), prompts_table],
|
357 |
outputs=None,
|
358 |
)
|
359 |
estimates_table.change(
|
360 |
+
db.replace,
|
361 |
inputs=[gr.Markdown("estimates", visible=False), estimates_table],
|
362 |
outputs=None,
|
363 |
)
|
364 |
votes_table.change(
|
365 |
+
db.replace,
|
366 |
inputs=[gr.Markdown("votes", visible=False), votes_table],
|
367 |
outputs=None,
|
368 |
)
|
369 |
+
teams_table.change(
|
370 |
+
db.replace,
|
371 |
+
inputs=[gr.Markdown("teams", visible=False), teams_table],
|
372 |
+
outputs=None,
|
373 |
+
)
|
374 |
|
375 |
+
users_table.change(
|
376 |
+
db.replace,
|
377 |
+
inputs=[gr.Markdown("users", visible=False), users_table],
|
378 |
+
outputs=None,
|
379 |
+
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
380 |
|
381 |
+
"""
|
382 |
+
######################
|
383 |
+
Candidat Actions
|
384 |
+
######################
|
385 |
+
"""
|
386 |
+
|
387 |
+
@submit_prompt_btn.click(
|
388 |
+
inputs=[candidat_prompt, user_state],
|
389 |
outputs=[
|
390 |
+
submit_prompt_btn,
|
391 |
+
candidat_prompt,
|
392 |
+
message_prompt,
|
393 |
+
competition_tab,
|
394 |
+
tabs,
|
|
|
395 |
],
|
396 |
)
|
397 |
+
def submit_prompt(prompt_text, user_state):
|
398 |
+
if not prompt_text or not prompt_text.strip():
|
399 |
+
return {
|
400 |
+
message_prompt: gr.update(
|
401 |
+
value="Veuillez saisir un prompt",
|
402 |
+
visible=True,
|
403 |
+
),
|
404 |
+
}
|
405 |
+
db_team_prompt = db.get_prompt(user_state["team"])
|
406 |
+
if db_team_prompt:
|
407 |
+
return {
|
408 |
+
submit_prompt_btn: gr.update(interactive=False),
|
409 |
+
candidat_prompt: gr.update(value=db_team_prompt, interactive=False),
|
410 |
+
message_prompt: gr.update(
|
411 |
+
value="Votre équipe a déjà enregistré un candidat.",
|
412 |
+
visible=True,
|
413 |
+
),
|
414 |
+
}
|
415 |
+
db.insert("prompts", {"name": f"{user_state['team']}", "text": prompt_text})
|
416 |
+
# Initialize estimates for the newly submitted prompt
|
417 |
+
all_prompts = db.load("prompts")
|
418 |
+
prompt_id = all_prompts[all_prompts["name"] == user_state["team"]].iloc[0]["id"]
|
419 |
+
ARENA.init_estimates(prompt_id)
|
420 |
+
return {
|
421 |
+
submit_prompt_btn: gr.update(interactive=False),
|
422 |
+
candidat_prompt: gr.update(interactive=False),
|
423 |
+
message_prompt: gr.update(
|
424 |
+
value="Prompt soumis avec succès !", visible=True
|
425 |
+
),
|
426 |
+
competition_tab: gr.update(visible=True),
|
427 |
+
tabs: gr.Tabs(selected=2),
|
428 |
+
}
|
429 |
+
|
430 |
+
"""
|
431 |
+
######################
|
432 |
+
Periodic refresh
|
433 |
+
######################
|
434 |
+
"""
|
435 |
+
|
436 |
+
def refresh(user_state):
|
437 |
+
if user_state is None:
|
438 |
+
return {
|
439 |
+
candidat_tab: gr.update(visible=False),
|
440 |
+
competition_tab: gr.update(visible=False),
|
441 |
+
result_tab: gr.update(visible=False),
|
442 |
+
admin_db_tab: gr.update(visible=False),
|
443 |
+
admin_followup_tab: gr.update(visible=False),
|
444 |
+
}
|
445 |
+
|
446 |
+
team = user_state.get("team", "")
|
447 |
+
|
448 |
+
update_prompt = {}
|
449 |
+
prompt_team = db.get_prompt(team)
|
450 |
+
if prompt_team:
|
451 |
+
update_prompt = {
|
452 |
+
candidat_prompt: gr.update(value=prompt_team, interactive=False),
|
453 |
+
submit_prompt_btn: gr.update(interactive=False, visible=False),
|
454 |
+
message_prompt: gr.update(
|
455 |
+
value="Votre équipe a déjà enregistré un candidat.", visible=True
|
456 |
+
),
|
457 |
+
}
|
458 |
+
|
459 |
+
update_tabs = {}
|
460 |
+
if user_state["team"] == "Admin":
|
461 |
+
update_tabs = {
|
462 |
+
candidat_tab: gr.update(visible=True),
|
463 |
+
competition_tab: gr.update(visible=True),
|
464 |
+
result_tab: gr.update(visible=True),
|
465 |
+
admin_db_tab: gr.update(visible=True),
|
466 |
+
admin_followup_tab: gr.update(visible=True),
|
467 |
+
}
|
468 |
+
else:
|
469 |
+
update_tabs = {
|
470 |
+
candidat_tab: gr.update(visible=db.get_status("Candidate")),
|
471 |
+
competition_tab: gr.update(visible=db.get_status("Competition")),
|
472 |
+
result_tab: gr.update(visible=db.get_status("Result")),
|
473 |
+
admin_db_tab: gr.update(visible=False),
|
474 |
+
admin_followup_tab: gr.update(visible=False),
|
475 |
+
}
|
476 |
+
|
477 |
+
update_tables = {
|
478 |
+
prompts_table: gr.update(value=db.load("prompts")),
|
479 |
+
estimates_table: gr.update(value=db.load("estimates")),
|
480 |
+
votes_table: gr.update(value=db.load("votes")),
|
481 |
+
teams_table: gr.update(value=db.load("teams")),
|
482 |
+
users_table: gr.update(value=db.load("users")),
|
483 |
+
competition_input: gr.update(
|
484 |
+
value=db.load("inputs").query("name == 'Competition'")["text"].values[0]
|
485 |
+
),
|
486 |
+
plot_estimates: gr.update(value=plot_estimates_distribution()),
|
487 |
+
rankings_table: gr.update(value=ARENA.get_rankings()),
|
488 |
+
competition_matrix: gr.update(value=ARENA.get_competition_matrix()),
|
489 |
+
}
|
490 |
+
|
491 |
+
return {**update_prompt, **update_tabs, **update_tables}
|
492 |
+
|
493 |
+
timer.tick(
|
494 |
+
refresh,
|
495 |
+
[user_state],
|
496 |
+
[
|
497 |
+
prompts_table,
|
498 |
+
estimates_table,
|
499 |
+
votes_table,
|
500 |
+
teams_table,
|
501 |
+
users_table,
|
502 |
+
candidat_prompt,
|
503 |
+
submit_prompt_btn,
|
504 |
+
message_prompt,
|
505 |
+
candidat_tab,
|
506 |
+
competition_tab,
|
507 |
+
result_tab,
|
508 |
+
admin_db_tab,
|
509 |
+
admin_followup_tab,
|
510 |
+
plot_estimates,
|
511 |
rankings_table,
|
512 |
+
competition_matrix,
|
513 |
+
competition_input,
|
|
|
514 |
],
|
515 |
)
|
516 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
517 |
|
518 |
# Exemple d'utilisation
|
519 |
if __name__ == "__main__":
|
520 |
+
demo.launch() # ajouter share=True pour partager l'interface
|
|
|
|
arena.py
CHANGED
@@ -1,7 +1,5 @@
|
|
1 |
import trueskill as ts
|
2 |
import pandas as pd
|
3 |
-
import random
|
4 |
-
import datetime
|
5 |
from typing import Dict, List, Tuple, Union
|
6 |
import db
|
7 |
import pandas as pd
|
@@ -22,41 +20,28 @@ class Arena:
|
|
22 |
Une arène pour comparer et classer des prompts en utilisant l'algorithme TrueSkill.
|
23 |
"""
|
24 |
|
25 |
-
def init_estimates(self,
|
26 |
"""
|
27 |
-
Initialise les estimations
|
28 |
-
reboot : si le fichier estimates.csv existe déjà, on le laisse tel quel.
|
29 |
"""
|
30 |
estimates = db.load("estimates")
|
31 |
-
if
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
{
|
38 |
-
"prompt_id": i,
|
39 |
-
"mu": MU_init,
|
40 |
-
"sigma": SIGMA_init,
|
41 |
-
},
|
42 |
-
)
|
43 |
-
|
44 |
-
def load(self, table_name: str) -> pd.DataFrame:
|
45 |
-
"""
|
46 |
-
fonction back pour l'UI.
|
47 |
-
Charge les données d'une table depuis le fichier CSV.
|
48 |
-
"""
|
49 |
-
return db.load(table_name)
|
50 |
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
|
|
58 |
|
59 |
-
def select_match(self) -> Tuple[Prompt, Prompt]:
|
60 |
"""
|
61 |
Sélectionne deux prompts pour un match en privilégiant ceux avec une grande incertitude.
|
62 |
Returns:
|
@@ -64,31 +49,62 @@ class Arena:
|
|
64 |
"""
|
65 |
# le prompt le plus incertain (sigma le plus élevé)
|
66 |
estimates = db.load("estimates")
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
.
|
74 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
75 |
)
|
76 |
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
82 |
|
83 |
-
#
|
84 |
-
|
85 |
-
# for i in range(len(prompt_ids)):
|
86 |
-
# for j in range(i + 1, min(i + 6, len(prompt_ids))):
|
87 |
-
# close_pairs.append((prompt_ids[i], prompt_ids[j]))
|
88 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
89 |
return prompt_a, prompt_b
|
90 |
|
91 |
-
def record_result(self, winner_id: str, loser_id: str) -> None:
|
92 |
# Obtenir les ratings actuels
|
93 |
estimates = db.load("estimates")
|
94 |
winner_estimate = (
|
@@ -117,6 +133,7 @@ class Arena:
|
|
117 |
{
|
118 |
"winner_id": winner_id,
|
119 |
"loser_id": loser_id,
|
|
|
120 |
# "timestamp": datetime.datetime.now().isoformat(),
|
121 |
},
|
122 |
)
|
@@ -137,8 +154,56 @@ class Arena:
|
|
137 |
rankings = prompts.merge(estimates, left_on="id", right_on="prompt_id").drop(
|
138 |
columns=["id", "prompt_id"]
|
139 |
)
|
140 |
-
|
|
|
|
|
|
|
141 |
# eventuellement afficher plutôt mu - 3 sigma pour être conservateur
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
142 |
|
143 |
def get_progress(self) -> str:
|
144 |
"""
|
|
|
1 |
import trueskill as ts
|
2 |
import pandas as pd
|
|
|
|
|
3 |
from typing import Dict, List, Tuple, Union
|
4 |
import db
|
5 |
import pandas as pd
|
|
|
20 |
Une arène pour comparer et classer des prompts en utilisant l'algorithme TrueSkill.
|
21 |
"""
|
22 |
|
23 |
+
def init_estimates(self, prompt_id) -> None:
|
24 |
"""
|
25 |
+
Initialise les estimations d'un prompt avec des ratings TrueSkill par défaut.
|
|
|
26 |
"""
|
27 |
estimates = db.load("estimates")
|
28 |
+
if prompt_id in estimates["prompt_id"].values:
|
29 |
+
# supprimer la ligne existante
|
30 |
+
db.delete(
|
31 |
+
"estimates",
|
32 |
+
int(estimates[estimates["prompt_id"] == prompt_id].iloc[0].id),
|
33 |
+
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
34 |
|
35 |
+
db.insert(
|
36 |
+
"estimates",
|
37 |
+
{
|
38 |
+
"prompt_id": prompt_id,
|
39 |
+
"mu": MU_init,
|
40 |
+
"sigma": SIGMA_init,
|
41 |
+
},
|
42 |
+
)
|
43 |
|
44 |
+
def select_match(self, user_state) -> Tuple[Prompt, Prompt] | None:
|
45 |
"""
|
46 |
Sélectionne deux prompts pour un match en privilégiant ceux avec une grande incertitude.
|
47 |
Returns:
|
|
|
49 |
"""
|
50 |
# le prompt le plus incertain (sigma le plus élevé)
|
51 |
estimates = db.load("estimates")
|
52 |
+
# retirer le prompt de l'utilisateur pour qu'il ne puisse pas voter pour son propre prompt
|
53 |
+
estimates = estimates[
|
54 |
+
estimates["prompt_id"] != db.get_prompt_id(user_state["team"])
|
55 |
+
]
|
56 |
+
|
57 |
+
def order_match(id_a, id_b):
|
58 |
+
"""Return a tuple of ids ordered by the id."""
|
59 |
+
return (id_a, id_b) if id_a < id_b else (id_b, id_a)
|
60 |
+
|
61 |
+
# les matchs possibles entre les prompts par ordre d'incertitude décroissant
|
62 |
+
matches = (
|
63 |
+
estimates.merge(estimates, how="cross", suffixes=["_a", "_b"])
|
64 |
+
.query("id_a != id_b")
|
65 |
+
.assign(delta_mu=lambda df_: abs(df_["mu_a"] - df_["mu_b"]))
|
66 |
+
.sort_values(by=["sigma_a", "delta_mu"], ascending=[False, True])
|
67 |
+
.assign(
|
68 |
+
match=lambda df_: df_.apply(
|
69 |
+
lambda row: order_match(int(row["id_a"]), int(row["id_b"])), axis=1
|
70 |
+
)
|
71 |
+
)
|
72 |
)
|
73 |
|
74 |
+
user_votes = db.load("votes").loc[
|
75 |
+
lambda df_: df_["user_id"] == user_state["id"]
|
76 |
+
]
|
77 |
+
|
78 |
+
if user_votes.empty:
|
79 |
+
user_votes = user_votes.assign(match=[])
|
80 |
+
else: # les votes de l'utilisateur
|
81 |
+
user_votes = user_votes.assign(
|
82 |
+
match=lambda df_: df_.apply(
|
83 |
+
lambda row: order_match(
|
84 |
+
int(row["winner_id"]), int(row["loser_id"])
|
85 |
+
),
|
86 |
+
axis=1,
|
87 |
+
)
|
88 |
+
)
|
89 |
|
90 |
+
# on ne garde que les matchs qui n'ont pas encore été votés par l'utilisateur
|
91 |
+
user_matches = matches.loc[~matches["match"].isin(user_votes["match"])]
|
|
|
|
|
|
|
92 |
|
93 |
+
if user_matches.empty:
|
94 |
+
# Si l'utilisateur a déjà voté sur tous les matchs, on ne peut pas en sélectionner de nouveaux
|
95 |
+
return None
|
96 |
+
|
97 |
+
selected_match = user_matches.iloc[0]
|
98 |
+
prompts = db.load("prompts")
|
99 |
+
prompt_a = (
|
100 |
+
prompts.query(f"id == {selected_match['prompt_id_a']}").iloc[0].to_dict()
|
101 |
+
)
|
102 |
+
prompt_b = (
|
103 |
+
prompts.query(f"id == {selected_match['prompt_id_b']}").iloc[0].to_dict()
|
104 |
+
)
|
105 |
return prompt_a, prompt_b
|
106 |
|
107 |
+
def record_result(self, winner_id: str, loser_id: str, user_id: str) -> None:
|
108 |
# Obtenir les ratings actuels
|
109 |
estimates = db.load("estimates")
|
110 |
winner_estimate = (
|
|
|
133 |
{
|
134 |
"winner_id": winner_id,
|
135 |
"loser_id": loser_id,
|
136 |
+
"user_id": user_id,
|
137 |
# "timestamp": datetime.datetime.now().isoformat(),
|
138 |
},
|
139 |
)
|
|
|
154 |
rankings = prompts.merge(estimates, left_on="id", right_on="prompt_id").drop(
|
155 |
columns=["id", "prompt_id"]
|
156 |
)
|
157 |
+
rankings = rankings.sort_values(by="mu", ascending=False)
|
158 |
+
# ajouter un colonne position
|
159 |
+
rankings["position"] = range(1, len(rankings) + 1)
|
160 |
+
|
161 |
# eventuellement afficher plutôt mu - 3 sigma pour être conservateur
|
162 |
+
# rankings["score"] = rankings["mu"] - 3 * rankings["sigma"]
|
163 |
+
return rankings[["position", "team"]]
|
164 |
+
|
165 |
+
def get_competition_matrix(self) -> pd.DataFrame:
|
166 |
+
"""
|
167 |
+
Obtient la matrice de combats des prompts.
|
168 |
+
|
169 |
+
Returns:
|
170 |
+
DataFrame contenant en ligne et en colonne les noms d'équipes,
|
171 |
+
et dans la cellule le pourcentage de victoires de l'équipe de la ligne contre l'équipe de la colonne.
|
172 |
+
"""
|
173 |
+
|
174 |
+
prompts = db.load("prompts")
|
175 |
+
votes = db.load("votes")
|
176 |
+
|
177 |
+
competition_matrix = pd.DataFrame(
|
178 |
+
index=prompts["team"], columns=prompts["team"], data=0
|
179 |
+
)
|
180 |
+
competition_matrix.index.name = None
|
181 |
+
competition_matrix.columns.name = None
|
182 |
+
|
183 |
+
wins = competition_matrix.copy()
|
184 |
+
matches = competition_matrix.copy()
|
185 |
+
|
186 |
+
for _, row in votes.iterrows():
|
187 |
+
winner_name = prompts.loc[prompts["id"] == row["winner_id"], "team"].values[
|
188 |
+
0
|
189 |
+
]
|
190 |
+
loser_name = prompts.loc[prompts["id"] == row["loser_id"], "team"].values[0]
|
191 |
+
wins.at[winner_name, loser_name] += 1
|
192 |
+
matches.at[winner_name, loser_name] += 1
|
193 |
+
matches.at[loser_name, winner_name] += 1
|
194 |
+
|
195 |
+
competition_matrix = wins.div(matches)
|
196 |
+
|
197 |
+
competition_matrix = competition_matrix.map(
|
198 |
+
lambda x: "" if pd.isna(x) or x == 0 else f"{x:.0%}"
|
199 |
+
)
|
200 |
+
|
201 |
+
for i in range(len(competition_matrix)):
|
202 |
+
competition_matrix.iloc[i, i] = "X"
|
203 |
+
|
204 |
+
competition_matrix = competition_matrix.replace("", "?").reset_index(names="")
|
205 |
+
|
206 |
+
return competition_matrix
|
207 |
|
208 |
def get_progress(self) -> str:
|
209 |
"""
|
database/estimates.csv
CHANGED
@@ -1,6 +1 @@
|
|
1 |
id,prompt_id,mu,sigma
|
2 |
-
1,1,40.23772508094016,2
|
3 |
-
2,2,26.46775376631428,4
|
4 |
-
3,3,22.084687989982868,3.547977413152066
|
5 |
-
4,4,24.390885439458472,3.2275080660341553
|
6 |
-
5,5,9.29883828658184,3.5631194000412036
|
|
|
1 |
id,prompt_id,mu,sigma
|
|
|
|
|
|
|
|
|
|
database/inputs.csv
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
1 |
+
id,name,text
|
2 |
+
1,Competition,"Insérer le sujet de la compétition"
|
database/prompts.csv
CHANGED
@@ -1,6 +1 @@
|
|
1 |
-
id,
|
2 |
-
1,A,Prompt A 100
|
3 |
-
2,B,Prompt B 90
|
4 |
-
3,C,Prompt C 60
|
5 |
-
4,D,Prompt D 55
|
6 |
-
5,E,Prompt E 30
|
|
|
1 |
+
id,team,text
|
|
|
|
|
|
|
|
|
|
database/switches.csv
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
id,name,status
|
2 |
+
1,Candidate,0
|
3 |
+
2,Competition,0
|
4 |
+
3,Result,0
|
5 |
+
4,Admin,0
|
database/teams.csv
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
id,name
|
2 |
+
1,Rock
|
3 |
+
2,Pop
|
4 |
+
3,Jazz
|
5 |
+
4,Blues
|
6 |
+
5,Salsa
|
7 |
+
6,Reggae
|
8 |
+
7,Disco
|
9 |
+
8,Tango
|
10 |
+
9,Soul
|
11 |
+
10,Rap
|
12 |
+
11,Techno
|
13 |
+
12,Folk
|
14 |
+
13,Bossa
|
15 |
+
14,Country
|
16 |
+
15,Gospel
|
database/users.csv
ADDED
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
id,team,username,code
|
2 |
+
0,Admin,Admin,MSTR
|
3 |
+
1,Rock,Rock 1,BGSN
|
4 |
+
2,Rock,Rock 2,SGEF
|
5 |
+
3,Rock,Rock 3,RSET
|
6 |
+
4,Pop,Pop 1,BHWQ
|
7 |
+
5,Pop,Pop 2,GEGX
|
8 |
+
6,Pop,Pop 3,WTRR
|
9 |
+
7,Jazz,Jazz 1,FZBH
|
10 |
+
8,Jazz,Jazz 2,SZUO
|
11 |
+
9,Jazz,Jazz 3,JCCO
|
12 |
+
10,Blues,Blues 1,WKTP
|
13 |
+
11,Blues,Blues 2,DAOA
|
14 |
+
12,Blues,Blues 3,EEPL
|
15 |
+
13,Salsa,Salsa 1,OZPN
|
16 |
+
14,Salsa,Salsa 2,REBM
|
17 |
+
15,Salsa,Salsa 3,DKAH
|
18 |
+
16,Reggae,Reggae 1,VUCY
|
19 |
+
17,Reggae,Reggae 2,VISH
|
20 |
+
18,Reggae,Reggae 3,RWAG
|
21 |
+
19,Disco,Disco 1,PXRJ
|
22 |
+
20,Disco,Disco 2,JJQL
|
23 |
+
21,Disco,Disco 3,HNUT
|
24 |
+
22,Tango,Tango 1,MHBJ
|
25 |
+
23,Tango,Tango 2,BSLF
|
26 |
+
24,Tango,Tango 3,XNKK
|
27 |
+
25,Soul,Soul 1,IZGQ
|
28 |
+
26,Soul,Soul 2,OUGL
|
29 |
+
27,Soul,Soul 3,XEAO
|
30 |
+
28,Rap,Rap 1,CYMX
|
31 |
+
29,Rap,Rap 2,WAFZ
|
32 |
+
30,Rap,Rap 3,BPIR
|
33 |
+
31,Techno,Techno 1,TMCY
|
34 |
+
32,Techno,Techno 2,TXMK
|
35 |
+
33,Techno,Techno 3,NVUO
|
36 |
+
34,Folk,Folk 1,ZATS
|
37 |
+
35,Folk,Folk 2,LNWA
|
38 |
+
36,Folk,Folk 3,HOKK
|
39 |
+
37,Bossa,Bossa 1,FWQM
|
40 |
+
38,Bossa,Bossa 2,UKWF
|
41 |
+
39,Bossa,Bossa 3,EHDR
|
42 |
+
40,Country,Country 1,MKHF
|
43 |
+
41,Country,Country 2,UGCW
|
44 |
+
42,Country,Country 3,KBSO
|
45 |
+
43,Gospel,Gospel 1,IZJB
|
46 |
+
44,Gospel,Gospel 2,HIJK
|
47 |
+
45,Gospel,Gospel 3,JSNN
|
database/votes.csv
CHANGED
@@ -1,11 +1 @@
|
|
1 |
-
id,winner_id,loser_id
|
2 |
-
1,4,5
|
3 |
-
2,1,2
|
4 |
-
3,2,3
|
5 |
-
4,4,5
|
6 |
-
5,1,2
|
7 |
-
6,4,5
|
8 |
-
7,1,2
|
9 |
-
8,4,3
|
10 |
-
9,3,5
|
11 |
-
10,3,5
|
|
|
1 |
+
id,winner_id,loser_id,user_id
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
db.py
CHANGED
@@ -12,7 +12,7 @@ Pour être à jour des mises à jours des autres utilisateurs, il faut recharger
|
|
12 |
DATABASE = {
|
13 |
"prompts": {
|
14 |
"filename": "database/prompts.csv",
|
15 |
-
"columns": ["id", "
|
16 |
},
|
17 |
"estimates": {
|
18 |
"filename": "database/estimates.csv",
|
@@ -22,6 +22,22 @@ DATABASE = {
|
|
22 |
"filename": "database/votes.csv",
|
23 |
"columns": ["id", "winner_id", "loser_id"],
|
24 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
25 |
}
|
26 |
|
27 |
|
@@ -53,10 +69,22 @@ def update(table_name: str, row_id: int, data: dict) -> None:
|
|
53 |
"""
|
54 |
df = load(table_name)
|
55 |
idx = df.index[df["id"] == row_id]
|
56 |
-
if
|
57 |
-
|
58 |
-
|
59 |
-
df.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
60 |
|
61 |
|
62 |
def replace(table_name: str, df: pd.DataFrame) -> None:
|
@@ -66,3 +94,59 @@ def replace(table_name: str, df: pd.DataFrame) -> None:
|
|
66 |
db.replace("prompts", df)
|
67 |
"""
|
68 |
df.to_csv(DATABASE[table_name]["filename"], index=False)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
12 |
DATABASE = {
|
13 |
"prompts": {
|
14 |
"filename": "database/prompts.csv",
|
15 |
+
"columns": ["id", "team", "text"],
|
16 |
},
|
17 |
"estimates": {
|
18 |
"filename": "database/estimates.csv",
|
|
|
22 |
"filename": "database/votes.csv",
|
23 |
"columns": ["id", "winner_id", "loser_id"],
|
24 |
},
|
25 |
+
"teams": {
|
26 |
+
"filename": "database/teams.csv",
|
27 |
+
"columns": ["id", "name"],
|
28 |
+
},
|
29 |
+
"users": {
|
30 |
+
"filename": "database/users.csv",
|
31 |
+
"columns": ["id", "team", "username", "code"],
|
32 |
+
},
|
33 |
+
"switches": {
|
34 |
+
"filename": "database/switches.csv",
|
35 |
+
"columns": ["id", "name", "status"],
|
36 |
+
},
|
37 |
+
"inputs": {
|
38 |
+
"filename": "database/inputs.csv",
|
39 |
+
"columns": ["id", "name", "text"],
|
40 |
+
},
|
41 |
}
|
42 |
|
43 |
|
|
|
69 |
"""
|
70 |
df = load(table_name)
|
71 |
idx = df.index[df["id"] == row_id]
|
72 |
+
if idx.empty:
|
73 |
+
idx = len(df)
|
74 |
+
for key, value in data.items():
|
75 |
+
df.loc[idx, key] = value
|
76 |
+
df.to_csv(DATABASE[table_name]["filename"], index=False)
|
77 |
+
|
78 |
+
|
79 |
+
def delete(table_name: str, id: int) -> None:
|
80 |
+
"""
|
81 |
+
Supprime une ligne de la table spécifiée par son id.
|
82 |
+
Exemple :
|
83 |
+
db.delete("prompts", 5)
|
84 |
+
"""
|
85 |
+
df = load(table_name)
|
86 |
+
df = df[df["id"] != id]
|
87 |
+
df.to_csv(DATABASE[table_name]["filename"], index=False)
|
88 |
|
89 |
|
90 |
def replace(table_name: str, df: pd.DataFrame) -> None:
|
|
|
94 |
db.replace("prompts", df)
|
95 |
"""
|
96 |
df.to_csv(DATABASE[table_name]["filename"], index=False)
|
97 |
+
|
98 |
+
|
99 |
+
def get_prompt(team: str) -> str | None:
|
100 |
+
"""
|
101 |
+
Retourne le prompt de l'équipe spécifiée.
|
102 |
+
Si l'équipe n'a pas de prompt, retourne une chaîne vide.
|
103 |
+
"""
|
104 |
+
prompts_df = load("prompts")
|
105 |
+
row = prompts_df[prompts_df["team"] == team]
|
106 |
+
if not row.empty:
|
107 |
+
return row.iloc[0]["text"]
|
108 |
+
return None
|
109 |
+
|
110 |
+
|
111 |
+
def get_prompt_id(team: str) -> str | None:
|
112 |
+
"""
|
113 |
+
Retourne le prompt de l'équipe spécifiée.
|
114 |
+
Si l'équipe n'a pas de prompt, retourne une chaîne vide.
|
115 |
+
"""
|
116 |
+
prompts_df = load("prompts")
|
117 |
+
row = prompts_df[prompts_df["team"] == team]
|
118 |
+
if not row.empty:
|
119 |
+
return row.iloc[0]["id"]
|
120 |
+
return None
|
121 |
+
|
122 |
+
|
123 |
+
def get_status(name: str) -> bool:
|
124 |
+
"""
|
125 |
+
Retourne le statut du switch spécifié.
|
126 |
+
Si le nom du switch n'existe pas en base, retourne 0.
|
127 |
+
"""
|
128 |
+
switches_df = load("switches")
|
129 |
+
row = switches_df[switches_df["name"] == name]
|
130 |
+
if not row.empty:
|
131 |
+
return bool(int(row.fillna(0).iloc[0]["status"]))
|
132 |
+
return False
|
133 |
+
|
134 |
+
|
135 |
+
def set_status(name: str, status: int) -> None:
|
136 |
+
"""
|
137 |
+
Met à jour le statut du switch spécifié.
|
138 |
+
Si le nom du switch n'existe pas, l'ajoute avec le statut donné.
|
139 |
+
"""
|
140 |
+
switches_df = load("switches")
|
141 |
+
switches_df.loc[switches_df["name"] == name, "status"] = status
|
142 |
+
replace("switches", switches_df)
|
143 |
+
|
144 |
+
|
145 |
+
def get_user(code: str) -> dict | None:
|
146 |
+
"""
|
147 |
+
Retourne les informations de l'utilisateur si le code est valide.
|
148 |
+
Si le code est invalide, retourne None.
|
149 |
+
"""
|
150 |
+
users_df = load("users")
|
151 |
+
row = users_df.loc[users_df["code"] == code, ["id", "team", "username"]]
|
152 |
+
return row.to_dict(orient="records")[0] if not row.empty else None
|
plot.py
ADDED
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import db
|
2 |
+
import numpy as np
|
3 |
+
import plotly.graph_objects as go
|
4 |
+
|
5 |
+
|
6 |
+
def plot_estimates_distribution():
|
7 |
+
"""Affiche une gaussienne par prompt (Plotly) + lignes verticales pointillées sur les moyennes."""
|
8 |
+
estimates = db.load("estimates")
|
9 |
+
prompts = db.load("prompts")
|
10 |
+
if estimates.empty or prompts.empty:
|
11 |
+
fig = go.Figure()
|
12 |
+
fig.add_annotation(
|
13 |
+
text="Aucune estimation disponible", x=0.5, y=0.5, showarrow=False
|
14 |
+
)
|
15 |
+
return fig
|
16 |
+
x = np.linspace(
|
17 |
+
estimates["mu"].min() - 3 * estimates["sigma"].max(),
|
18 |
+
estimates["mu"].max() + 3 * estimates["sigma"].max(),
|
19 |
+
500,
|
20 |
+
)
|
21 |
+
fig = go.Figure()
|
22 |
+
shapes = []
|
23 |
+
# Une gaussienne par prompt
|
24 |
+
for _, row in estimates.iterrows():
|
25 |
+
mu = row["mu"]
|
26 |
+
sigma = row["sigma"]
|
27 |
+
prompt_id = row["prompt_id"] if "prompt_id" in row else row["id"]
|
28 |
+
# Chercher le nom du prompt
|
29 |
+
name = str(prompt_id)
|
30 |
+
if "name" in prompts.columns:
|
31 |
+
match = prompts[prompts["id"] == prompt_id]
|
32 |
+
if not match.empty:
|
33 |
+
name = match.iloc[0]["name"]
|
34 |
+
y = 1 / (sigma * np.sqrt(2 * np.pi)) * np.exp(-0.5 * ((x - mu) / sigma) ** 2)
|
35 |
+
fig.add_trace(
|
36 |
+
go.Scatter(
|
37 |
+
x=x,
|
38 |
+
y=y,
|
39 |
+
mode="lines",
|
40 |
+
name=f"{name}",
|
41 |
+
hovertemplate=f"<b>{name}</b><br>Score (mu): {mu:.2f}<br>Sigma: {sigma:.2f}<extra></extra>",
|
42 |
+
)
|
43 |
+
)
|
44 |
+
# Ajout de la ligne verticale pointillée à mu (en gris)
|
45 |
+
shapes.append(
|
46 |
+
dict(
|
47 |
+
type="line",
|
48 |
+
x0=mu,
|
49 |
+
x1=mu,
|
50 |
+
y0=0,
|
51 |
+
y1=max(y),
|
52 |
+
line=dict(
|
53 |
+
color="gray",
|
54 |
+
width=2,
|
55 |
+
dash="dot",
|
56 |
+
),
|
57 |
+
xref="x",
|
58 |
+
yref="y",
|
59 |
+
)
|
60 |
+
)
|
61 |
+
fig.update_layout(
|
62 |
+
title="Distribution gaussienne de chaque prompt",
|
63 |
+
xaxis_title="Score (mu)",
|
64 |
+
yaxis_title="Densité",
|
65 |
+
template="plotly_white",
|
66 |
+
shapes=shapes,
|
67 |
+
)
|
68 |
+
return fig
|
poetry.lock
CHANGED
@@ -2019,6 +2019,70 @@ httpx = "*"
|
|
2019 |
[package.extras]
|
2020 |
dev = ["pytest"]
|
2021 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2022 |
[[package]]
|
2023 |
name = "semantic-version"
|
2024 |
version = "2.10.0"
|
@@ -2110,6 +2174,18 @@ anyio = ">=3.6.2,<5"
|
|
2110 |
[package.extras]
|
2111 |
full = ["httpx (>=0.27.0,<0.29.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.18)", "pyyaml"]
|
2112 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2113 |
[[package]]
|
2114 |
name = "tomlkit"
|
2115 |
version = "0.13.3"
|
@@ -2386,4 +2462,4 @@ files = [
|
|
2386 |
[metadata]
|
2387 |
lock-version = "2.1"
|
2388 |
python-versions = ">=3.12,<4.0"
|
2389 |
-
content-hash = "
|
|
|
2019 |
[package.extras]
|
2020 |
dev = ["pytest"]
|
2021 |
|
2022 |
+
[[package]]
|
2023 |
+
name = "scipy"
|
2024 |
+
version = "1.15.3"
|
2025 |
+
description = "Fundamental algorithms for scientific computing in Python"
|
2026 |
+
optional = false
|
2027 |
+
python-versions = ">=3.10"
|
2028 |
+
groups = ["main"]
|
2029 |
+
files = [
|
2030 |
+
{file = "scipy-1.15.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:a345928c86d535060c9c2b25e71e87c39ab2f22fc96e9636bd74d1dbf9de448c"},
|
2031 |
+
{file = "scipy-1.15.3-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:ad3432cb0f9ed87477a8d97f03b763fd1d57709f1bbde3c9369b1dff5503b253"},
|
2032 |
+
{file = "scipy-1.15.3-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:aef683a9ae6eb00728a542b796f52a5477b78252edede72b8327a886ab63293f"},
|
2033 |
+
{file = "scipy-1.15.3-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:1c832e1bd78dea67d5c16f786681b28dd695a8cb1fb90af2e27580d3d0967e92"},
|
2034 |
+
{file = "scipy-1.15.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:263961f658ce2165bbd7b99fa5135195c3a12d9bef045345016b8b50c315cb82"},
|
2035 |
+
{file = "scipy-1.15.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e2abc762b0811e09a0d3258abee2d98e0c703eee49464ce0069590846f31d40"},
|
2036 |
+
{file = "scipy-1.15.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ed7284b21a7a0c8f1b6e5977ac05396c0d008b89e05498c8b7e8f4a1423bba0e"},
|
2037 |
+
{file = "scipy-1.15.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5380741e53df2c566f4d234b100a484b420af85deb39ea35a1cc1be84ff53a5c"},
|
2038 |
+
{file = "scipy-1.15.3-cp310-cp310-win_amd64.whl", hash = "sha256:9d61e97b186a57350f6d6fd72640f9e99d5a4a2b8fbf4b9ee9a841eab327dc13"},
|
2039 |
+
{file = "scipy-1.15.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:993439ce220d25e3696d1b23b233dd010169b62f6456488567e830654ee37a6b"},
|
2040 |
+
{file = "scipy-1.15.3-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:34716e281f181a02341ddeaad584205bd2fd3c242063bd3423d61ac259ca7eba"},
|
2041 |
+
{file = "scipy-1.15.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3b0334816afb8b91dab859281b1b9786934392aa3d527cd847e41bb6f45bee65"},
|
2042 |
+
{file = "scipy-1.15.3-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:6db907c7368e3092e24919b5e31c76998b0ce1684d51a90943cb0ed1b4ffd6c1"},
|
2043 |
+
{file = "scipy-1.15.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:721d6b4ef5dc82ca8968c25b111e307083d7ca9091bc38163fb89243e85e3889"},
|
2044 |
+
{file = "scipy-1.15.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39cb9c62e471b1bb3750066ecc3a3f3052b37751c7c3dfd0fd7e48900ed52982"},
|
2045 |
+
{file = "scipy-1.15.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:795c46999bae845966368a3c013e0e00947932d68e235702b5c3f6ea799aa8c9"},
|
2046 |
+
{file = "scipy-1.15.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:18aaacb735ab38b38db42cb01f6b92a2d0d4b6aabefeb07f02849e47f8fb3594"},
|
2047 |
+
{file = "scipy-1.15.3-cp311-cp311-win_amd64.whl", hash = "sha256:ae48a786a28412d744c62fd7816a4118ef97e5be0bee968ce8f0a2fba7acf3bb"},
|
2048 |
+
{file = "scipy-1.15.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6ac6310fdbfb7aa6612408bd2f07295bcbd3fda00d2d702178434751fe48e019"},
|
2049 |
+
{file = "scipy-1.15.3-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:185cd3d6d05ca4b44a8f1595af87f9c372bb6acf9c808e99aa3e9aa03bd98cf6"},
|
2050 |
+
{file = "scipy-1.15.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:05dc6abcd105e1a29f95eada46d4a3f251743cfd7d3ae8ddb4088047f24ea477"},
|
2051 |
+
{file = "scipy-1.15.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:06efcba926324df1696931a57a176c80848ccd67ce6ad020c810736bfd58eb1c"},
|
2052 |
+
{file = "scipy-1.15.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c05045d8b9bfd807ee1b9f38761993297b10b245f012b11b13b91ba8945f7e45"},
|
2053 |
+
{file = "scipy-1.15.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:271e3713e645149ea5ea3e97b57fdab61ce61333f97cfae392c28ba786f9bb49"},
|
2054 |
+
{file = "scipy-1.15.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6cfd56fc1a8e53f6e89ba3a7a7251f7396412d655bca2aa5611c8ec9a6784a1e"},
|
2055 |
+
{file = "scipy-1.15.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0ff17c0bb1cb32952c09217d8d1eed9b53d1463e5f1dd6052c7857f83127d539"},
|
2056 |
+
{file = "scipy-1.15.3-cp312-cp312-win_amd64.whl", hash = "sha256:52092bc0472cfd17df49ff17e70624345efece4e1a12b23783a1ac59a1b728ed"},
|
2057 |
+
{file = "scipy-1.15.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2c620736bcc334782e24d173c0fdbb7590a0a436d2fdf39310a8902505008759"},
|
2058 |
+
{file = "scipy-1.15.3-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:7e11270a000969409d37ed399585ee530b9ef6aa99d50c019de4cb01e8e54e62"},
|
2059 |
+
{file = "scipy-1.15.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:8c9ed3ba2c8a2ce098163a9bdb26f891746d02136995df25227a20e71c396ebb"},
|
2060 |
+
{file = "scipy-1.15.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:0bdd905264c0c9cfa74a4772cdb2070171790381a5c4d312c973382fc6eaf730"},
|
2061 |
+
{file = "scipy-1.15.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79167bba085c31f38603e11a267d862957cbb3ce018d8b38f79ac043bc92d825"},
|
2062 |
+
{file = "scipy-1.15.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9deabd6d547aee2c9a81dee6cc96c6d7e9a9b1953f74850c179f91fdc729cb7"},
|
2063 |
+
{file = "scipy-1.15.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:dde4fc32993071ac0c7dd2d82569e544f0bdaff66269cb475e0f369adad13f11"},
|
2064 |
+
{file = "scipy-1.15.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f77f853d584e72e874d87357ad70f44b437331507d1c311457bed8ed2b956126"},
|
2065 |
+
{file = "scipy-1.15.3-cp313-cp313-win_amd64.whl", hash = "sha256:b90ab29d0c37ec9bf55424c064312930ca5f4bde15ee8619ee44e69319aab163"},
|
2066 |
+
{file = "scipy-1.15.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3ac07623267feb3ae308487c260ac684b32ea35fd81e12845039952f558047b8"},
|
2067 |
+
{file = "scipy-1.15.3-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:6487aa99c2a3d509a5227d9a5e889ff05830a06b2ce08ec30df6d79db5fcd5c5"},
|
2068 |
+
{file = "scipy-1.15.3-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:50f9e62461c95d933d5c5ef4a1f2ebf9a2b4e83b0db374cb3f1de104d935922e"},
|
2069 |
+
{file = "scipy-1.15.3-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:14ed70039d182f411ffc74789a16df3835e05dc469b898233a245cdfd7f162cb"},
|
2070 |
+
{file = "scipy-1.15.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a769105537aa07a69468a0eefcd121be52006db61cdd8cac8a0e68980bbb723"},
|
2071 |
+
{file = "scipy-1.15.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9db984639887e3dffb3928d118145ffe40eff2fa40cb241a306ec57c219ebbbb"},
|
2072 |
+
{file = "scipy-1.15.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:40e54d5c7e7ebf1aa596c374c49fa3135f04648a0caabcb66c52884b943f02b4"},
|
2073 |
+
{file = "scipy-1.15.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:5e721fed53187e71d0ccf382b6bf977644c533e506c4d33c3fb24de89f5c3ed5"},
|
2074 |
+
{file = "scipy-1.15.3-cp313-cp313t-win_amd64.whl", hash = "sha256:76ad1fb5f8752eabf0fa02e4cc0336b4e8f021e2d5f061ed37d6d264db35e3ca"},
|
2075 |
+
{file = "scipy-1.15.3.tar.gz", hash = "sha256:eae3cf522bc7df64b42cad3925c876e1b0b6c35c1337c93e12c0f366f55b0eaf"},
|
2076 |
+
]
|
2077 |
+
|
2078 |
+
[package.dependencies]
|
2079 |
+
numpy = ">=1.23.5,<2.5"
|
2080 |
+
|
2081 |
+
[package.extras]
|
2082 |
+
dev = ["cython-lint (>=0.12.2)", "doit (>=0.36.0)", "mypy (==1.10.0)", "pycodestyle", "pydevtool", "rich-click", "ruff (>=0.0.292)", "types-psutil", "typing_extensions"]
|
2083 |
+
doc = ["intersphinx_registry", "jupyterlite-pyodide-kernel", "jupyterlite-sphinx (>=0.19.1)", "jupytext", "matplotlib (>=3.5)", "myst-nb", "numpydoc", "pooch", "pydata-sphinx-theme (>=0.15.2)", "sphinx (>=5.0.0,<8.0.0)", "sphinx-copybutton", "sphinx-design (>=0.4.0)"]
|
2084 |
+
test = ["Cython", "array-api-strict (>=2.0,<2.1.1)", "asv", "gmpy2", "hypothesis (>=6.30)", "meson", "mpmath", "ninja ; sys_platform != \"emscripten\"", "pooch", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "scikit-umfpack", "threadpoolctl"]
|
2085 |
+
|
2086 |
[[package]]
|
2087 |
name = "semantic-version"
|
2088 |
version = "2.10.0"
|
|
|
2174 |
[package.extras]
|
2175 |
full = ["httpx (>=0.27.0,<0.29.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.18)", "pyyaml"]
|
2176 |
|
2177 |
+
[[package]]
|
2178 |
+
name = "tinydb"
|
2179 |
+
version = "4.8.2"
|
2180 |
+
description = "TinyDB is a tiny, document oriented database optimized for your happiness :)"
|
2181 |
+
optional = false
|
2182 |
+
python-versions = "<4.0,>=3.8"
|
2183 |
+
groups = ["main"]
|
2184 |
+
files = [
|
2185 |
+
{file = "tinydb-4.8.2-py3-none-any.whl", hash = "sha256:f97030ee5cbc91eeadd1d7af07ab0e48ceb04aa63d4a983adbaca4cba16e86c3"},
|
2186 |
+
{file = "tinydb-4.8.2.tar.gz", hash = "sha256:f7dfc39b8d7fda7a1ca62a8dbb449ffd340a117c1206b68c50b1a481fb95181d"},
|
2187 |
+
]
|
2188 |
+
|
2189 |
[[package]]
|
2190 |
name = "tomlkit"
|
2191 |
version = "0.13.3"
|
|
|
2462 |
[metadata]
|
2463 |
lock-version = "2.1"
|
2464 |
python-versions = ">=3.12,<4.0"
|
2465 |
+
content-hash = "e5d2bd5217695205ea0868e860217189bd487dd86a49d59a14ee98051186cf32"
|
prompts.csv
DELETED
@@ -1,5 +0,0 @@
|
|
1 |
-
Prompt A 100
|
2 |
-
Prompt B 90
|
3 |
-
Prompt C 60
|
4 |
-
Prompt D 55
|
5 |
-
Prompt E 30
|
|
|
|
|
|
|
|
|
|
|
|
pyproject.toml
CHANGED
@@ -11,7 +11,7 @@ dependencies = [
|
|
11 |
"trueskill (>=0.4.5,<0.5.0)",
|
12 |
"gradio (>=4.0.0)",
|
13 |
"pandas (>=2.0.0)",
|
14 |
-
"plotly (>=6.1.2,<7.0.0)"
|
15 |
]
|
16 |
|
17 |
|
|
|
11 |
"trueskill (>=0.4.5,<0.5.0)",
|
12 |
"gradio (>=4.0.0)",
|
13 |
"pandas (>=2.0.0)",
|
14 |
+
"plotly (>=6.1.2,<7.0.0)"
|
15 |
]
|
16 |
|
17 |
|
routes.py
DELETED
@@ -1,7 +0,0 @@
|
|
1 |
-
@app.get("/logout")
|
2 |
-
def logout():
|
3 |
-
response = RedirectResponse(url="/", status_code=status.HTTP_302_FOUND)
|
4 |
-
response.delete_cookie(key=f"access-token")
|
5 |
-
response.delete_cookie(key=f"access-token-unsecure")
|
6 |
-
print("Logout user!")
|
7 |
-
return response
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|