Spaces:
Running
Running
deploy sd35m: 40 pairs
Browse files- app/main.py +25 -15
- app/static/index.html +5 -4
app/main.py
CHANGED
|
@@ -107,20 +107,26 @@ async def register(req: Request):
|
|
| 107 |
return {"token": token, "pairs": pairs}
|
| 108 |
|
| 109 |
def _summarize(votes):
|
| 110 |
-
"""Per-participant own results (thank-you page):
|
| 111 |
-
|
|
|
|
|
|
|
|
|
|
| 112 |
for v in votes:
|
| 113 |
p = PAIRS.get(int(v.get("pair_id", -1)))
|
| 114 |
ch = v.get("choice")
|
| 115 |
-
if not p or p["kind"] != "real" or ch not in ("left", "right"):
|
| 116 |
continue
|
| 117 |
-
|
| 118 |
-
overall[
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
|
|
|
|
|
|
|
|
|
| 124 |
|
| 125 |
@app.post("/api/submit")
|
| 126 |
async def submit(req: Request):
|
|
@@ -167,13 +173,17 @@ def stats(key: str = ""):
|
|
| 167 |
SUM(CASE WHEN guided_chosen IS NULL THEN 1 ELSE 0 END) ties
|
| 168 |
FROM votes WHERE kind='real' AND question=? AND token IN
|
| 169 |
(SELECT token FROM participants WHERE completed=1) GROUP BY style""", (q,)).fetchall()
|
| 170 |
-
tot = c.execute(f"""SELECT COUNT(*) n, SUM(guided_chosen) g
|
| 171 |
-
|
|
|
|
| 172 |
AND token IN (SELECT token FROM participants WHERE completed=1)""", (q,)).fetchone()
|
|
|
|
| 173 |
out["by_question"][q] = {
|
| 174 |
-
"
|
| 175 |
-
"
|
| 176 |
-
"
|
|
|
|
|
|
|
| 177 |
return out
|
| 178 |
|
| 179 |
@app.get("/api/export")
|
|
|
|
| 107 |
return {"token": token, "pairs": pairs}
|
| 108 |
|
| 109 |
def _summarize(votes):
|
| 110 |
+
"""Per-participant own results (thank-you page): three-way win/tie/lose vs our method, overall + per style.
|
| 111 |
+
win = chose ours, tie = 'cannot decide', lose = chose the other; rates are over ALL comparisons (incl. ties)."""
|
| 112 |
+
def tri():
|
| 113 |
+
return [0, 0, 0] # [ours_win, tie, other_win]
|
| 114 |
+
overall = tri(); by_style = {}
|
| 115 |
for v in votes:
|
| 116 |
p = PAIRS.get(int(v.get("pair_id", -1)))
|
| 117 |
ch = v.get("choice")
|
| 118 |
+
if not p or p["kind"] != "real" or ch not in ("left", "right", "cannot"):
|
| 119 |
continue
|
| 120 |
+
k = 1 if ch == "cannot" else (0 if ch == p["guided_side"] else 2)
|
| 121 |
+
overall[k] += 1
|
| 122 |
+
by_style.setdefault(p["style"], tri())[k] += 1
|
| 123 |
+
def pack(t):
|
| 124 |
+
n = sum(t)
|
| 125 |
+
return {"win": t[0], "tie": t[1], "lose": t[2], "n": n,
|
| 126 |
+
"win_rate": (t[0] / n) if n else None,
|
| 127 |
+
"tie_rate": (t[1] / n) if n else None,
|
| 128 |
+
"lose_rate": (t[2] / n) if n else None}
|
| 129 |
+
return {"pref": {**pack(overall), "by_style": {k: pack(t) for k, t in by_style.items()}}}
|
| 130 |
|
| 131 |
@app.post("/api/submit")
|
| 132 |
async def submit(req: Request):
|
|
|
|
| 173 |
SUM(CASE WHEN guided_chosen IS NULL THEN 1 ELSE 0 END) ties
|
| 174 |
FROM votes WHERE kind='real' AND question=? AND token IN
|
| 175 |
(SELECT token FROM participants WHERE completed=1) GROUP BY style""", (q,)).fetchall()
|
| 176 |
+
tot = c.execute(f"""SELECT COUNT(*) n, SUM(guided_chosen) g,
|
| 177 |
+
SUM(CASE WHEN guided_chosen IS NULL THEN 1 ELSE 0 END) ties
|
| 178 |
+
FROM votes WHERE kind='real' AND question=?
|
| 179 |
AND token IN (SELECT token FROM participants WHERE completed=1)""", (q,)).fetchone()
|
| 180 |
+
N = tot["n"] or 0; W = tot["g"] or 0; T = tot["ties"] or 0; L = N - W - T # win / tie / lose
|
| 181 |
out["by_question"][q] = {
|
| 182 |
+
"n": N, "win": W, "tie": T, "lose": L,
|
| 183 |
+
"win_rate": (W/N) if N else None, "tie_rate": (T/N) if N else None, "lose_rate": (L/N) if N else None,
|
| 184 |
+
"decisive_win_rate": (W/(W+L)) if (W+L) else None, # ties dropped (sign-test denominator)
|
| 185 |
+
"by_style": {r["style"]: {"n": r["n"], "win": r["g"] or 0, "tie": r["ties"] or 0,
|
| 186 |
+
"lose": (r["n"] - (r["g"] or 0) - (r["ties"] or 0))} for r in rows}}
|
| 187 |
return out
|
| 188 |
|
| 189 |
@app.get("/api/export")
|
app/static/index.html
CHANGED
|
@@ -112,12 +112,13 @@ function renderResults(res){
|
|
| 112 |
let rows="";
|
| 113 |
for(const s of ks){
|
| 114 |
const ps=p.by_style[s]||{};
|
| 115 |
-
rows+=`<tr><td>${STYLE_LBL[s]||s}</td><td>${pct(ps.
|
| 116 |
}
|
| 117 |
-
const table = ks.length>1 ? `<table><tr class="mut"><th>Style</th><th>
|
| 118 |
$("myresults").innerHTML = `
|
| 119 |
-
<p class="center">
|
| 120 |
-
|
|
|
|
| 121 |
}
|
| 122 |
fetch("/api/info").then(r=>r.json()).then(d=>{ $("npairs").textContent=d.n_pairs; $("mins").textContent=d.est_minutes; }).catch(()=>{ $("npairs").textContent="a few dozen"; });
|
| 123 |
$("start").onclick=start;
|
|
|
|
| 112 |
let rows="";
|
| 113 |
for(const s of ks){
|
| 114 |
const ps=p.by_style[s]||{};
|
| 115 |
+
rows+=`<tr><td>${STYLE_LBL[s]||s}</td><td>${pct(ps.win_rate)}</td><td>${pct(ps.tie_rate)}</td><td>${pct(ps.lose_rate)}</td></tr>`;
|
| 116 |
}
|
| 117 |
+
const table = ks.length>1 ? `<table><tr class="mut"><th>Style</th><th>Ours</th><th>No pref.</th><th>Other</th></tr>${rows}</table>` : "";
|
| 118 |
$("myresults").innerHTML = `
|
| 119 |
+
<p class="center">Over your <b>${p.n}</b> comparisons: you preferred <b>ours ${pct(p.win_rate)}</b>,
|
| 120 |
+
had <b>no preference ${pct(p.tie_rate)}</b>, and preferred <b>the other ${pct(p.lose_rate)}</b>
|
| 121 |
+
<span class="mut">(the method identity was hidden).</span></p>${table}`;
|
| 122 |
}
|
| 123 |
fetch("/api/info").then(r=>r.json()).then(d=>{ $("npairs").textContent=d.n_pairs; $("mins").textContent=d.est_minutes; }).catch(()=>{ $("npairs").textContent="a few dozen"; });
|
| 124 |
$("start").onclick=start;
|