Upload 2 files
Browse files- app.py +44 -25
- study_utils.py +18 -0
app.py
CHANGED
|
@@ -36,6 +36,8 @@ from study_utils import (
|
|
| 36 |
log_survey_responses,
|
| 37 |
log_final_selections,
|
| 38 |
log_final_survey,
|
|
|
|
|
|
|
| 39 |
)
|
| 40 |
|
| 41 |
EMPTY_ATTR = {"attribute": "", "weight": 0.7}
|
|
@@ -1255,8 +1257,9 @@ with gr.Blocks(
|
|
| 1255 |
if errors:
|
| 1256 |
return _err("\n\n".join(errors[:10]))
|
| 1257 |
|
| 1258 |
-
# ββ
|
| 1259 |
try:
|
|
|
|
| 1260 |
for q in range(NUM_QUERIES):
|
| 1261 |
qmeta = (all_meta or {}).get(q, {})
|
| 1262 |
qr = radio_vals[q * 10:(q + 1) * 10]
|
|
@@ -1267,38 +1270,54 @@ with gr.Blocks(
|
|
| 1267 |
linear_ids = qmeta.get("linear_ids", [])
|
| 1268 |
all_ids = base_ids[:5] + linear_ids[:5]
|
| 1269 |
all_ids += [None] * (10 - len(all_ids))
|
|
|
|
| 1270 |
|
| 1271 |
for i, (img_id, rv) in enumerate(zip(all_ids, qr)):
|
| 1272 |
if img_id:
|
| 1273 |
method = "baseline" if i < 5 else "linear"
|
| 1274 |
-
|
| 1275 |
-
pid, q,
|
| 1276 |
-
|
| 1277 |
-
|
| 1278 |
-
|
| 1279 |
-
|
| 1280 |
-
|
| 1281 |
-
|
| 1282 |
-
|
| 1283 |
-
)
|
| 1284 |
-
|
| 1285 |
-
pid, q,
|
| 1286 |
-
"
|
| 1287 |
-
"
|
| 1288 |
-
qmeta.get("round_sat", 0),
|
| 1289 |
-
qmeta.get("time_elapsed", 0),
|
| 1290 |
-
|
| 1291 |
-
|
| 1292 |
-
|
| 1293 |
-
|
| 1294 |
-
|
| 1295 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1296 |
open_fb = (feedback or "").strip()
|
| 1297 |
if conc_val == "Yes" and (concept_text or "").strip():
|
| 1298 |
open_fb = (concept_text or "").strip() + "\n\n" + open_fb
|
| 1299 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1300 |
except Exception as e:
|
| 1301 |
-
return _err(f"
|
| 1302 |
|
| 1303 |
return gr.update(visible=False), gr.update(value=""), gr.update(visible=True)
|
| 1304 |
|
|
|
|
| 36 |
log_survey_responses,
|
| 37 |
log_final_selections,
|
| 38 |
log_final_survey,
|
| 39 |
+
firestore_batch_add,
|
| 40 |
+
_iso_ts,
|
| 41 |
)
|
| 42 |
|
| 43 |
EMPTY_ATTR = {"attribute": "", "weight": 0.7}
|
|
|
|
| 1257 |
if errors:
|
| 1258 |
return _err("\n\n".join(errors[:10]))
|
| 1259 |
|
| 1260 |
+
# ββ Collect all writes, then batch-commit to Firebase ββ
|
| 1261 |
try:
|
| 1262 |
+
writes = [] # list of (collection, data_dict)
|
| 1263 |
for q in range(NUM_QUERIES):
|
| 1264 |
qmeta = (all_meta or {}).get(q, {})
|
| 1265 |
qr = radio_vals[q * 10:(q + 1) * 10]
|
|
|
|
| 1270 |
linear_ids = qmeta.get("linear_ids", [])
|
| 1271 |
all_ids = base_ids[:5] + linear_ids[:5]
|
| 1272 |
all_ids += [None] * (10 - len(all_ids))
|
| 1273 |
+
ts = _iso_ts()
|
| 1274 |
|
| 1275 |
for i, (img_id, rv) in enumerate(zip(all_ids, qr)):
|
| 1276 |
if img_id:
|
| 1277 |
method = "baseline" if i < 5 else "linear"
|
| 1278 |
+
writes.append(("image_annotations", {
|
| 1279 |
+
"participant_id": pid, "query_id": q,
|
| 1280 |
+
"image_id": img_id, "method": method,
|
| 1281 |
+
"meets_intent": 1 if str(rv).strip() == "Yes" else 0,
|
| 1282 |
+
"timestamp": ts,
|
| 1283 |
+
}))
|
| 1284 |
+
writes.append(("method_comparison", {
|
| 1285 |
+
"participant_id": pid, "query_id": q,
|
| 1286 |
+
"linear_better": cv, "timestamp": ts,
|
| 1287 |
+
}))
|
| 1288 |
+
writes.append(("survey_responses", {
|
| 1289 |
+
"participant_id": pid, "query_id": q,
|
| 1290 |
+
"alignment_score": ql[0], "agency_score": ql[1],
|
| 1291 |
+
"satisfaction_score": ql[2], "frustration_score": ql[3],
|
| 1292 |
+
"round_satisfied": qmeta.get("round_sat", 0),
|
| 1293 |
+
"time_elapsed": qmeta.get("time_elapsed", 0),
|
| 1294 |
+
"timestamp": ts,
|
| 1295 |
+
}))
|
| 1296 |
+
writes.append(("final_selections", {
|
| 1297 |
+
"participant_id": pid, "query_id": q,
|
| 1298 |
+
"baseline_final_image_ids": ",".join(base_ids[:5]),
|
| 1299 |
+
"linear_final_image_ids": ",".join(linear_ids[:5]),
|
| 1300 |
+
"round_satisfied": qmeta.get("round_sat", 0),
|
| 1301 |
+
"time_elapsed": qmeta.get("time_elapsed", 0),
|
| 1302 |
+
"timestamp": ts,
|
| 1303 |
+
}))
|
| 1304 |
+
|
| 1305 |
+
# Final survey (1 document)
|
| 1306 |
open_fb = (feedback or "").strip()
|
| 1307 |
if conc_val == "Yes" and (concept_text or "").strip():
|
| 1308 |
open_fb = (concept_text or "").strip() + "\n\n" + open_fb
|
| 1309 |
+
writes.append(("final_survey", {
|
| 1310 |
+
"participant_id": pid,
|
| 1311 |
+
"preferred_system": pref_val,
|
| 1312 |
+
"concept_changed": conc_val,
|
| 1313 |
+
"open_feedback": open_fb,
|
| 1314 |
+
"timestamp": _iso_ts(),
|
| 1315 |
+
}))
|
| 1316 |
+
|
| 1317 |
+
# Single batch commit (~287 docs in 1 round-trip)
|
| 1318 |
+
firestore_batch_add(writes)
|
| 1319 |
except Exception as e:
|
| 1320 |
+
return _err(f"Save error: {str(e)}. Please try again.")
|
| 1321 |
|
| 1322 |
return gr.update(visible=False), gr.update(value=""), gr.update(visible=True)
|
| 1323 |
|
study_utils.py
CHANGED
|
@@ -91,6 +91,24 @@ def firestore_add(collection: str, data: dict) -> None:
|
|
| 91 |
db.collection(collection).add(data)
|
| 92 |
|
| 93 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 94 |
def firestore_query_exists(collection: str, field: str, value) -> bool:
|
| 95 |
"""Return True if at least one document matches field == value."""
|
| 96 |
db = _get_db()
|
|
|
|
| 91 |
db.collection(collection).add(data)
|
| 92 |
|
| 93 |
|
| 94 |
+
def firestore_batch_add(items: list[tuple[str, dict]]) -> None:
|
| 95 |
+
"""Add many documents efficiently using Firestore batch writes.
|
| 96 |
+
|
| 97 |
+
Args:
|
| 98 |
+
items: list of (collection_name, data_dict) tuples.
|
| 99 |
+
Firestore batches support up to 500 ops each;
|
| 100 |
+
this function auto-splits into multiple batches.
|
| 101 |
+
"""
|
| 102 |
+
db = _get_db()
|
| 103 |
+
BATCH_LIMIT = 450 # stay under Firestore's 500-op limit
|
| 104 |
+
for start in range(0, len(items), BATCH_LIMIT):
|
| 105 |
+
batch = db.batch()
|
| 106 |
+
for collection, data in items[start:start + BATCH_LIMIT]:
|
| 107 |
+
ref = db.collection(collection).document()
|
| 108 |
+
batch.set(ref, data)
|
| 109 |
+
batch.commit()
|
| 110 |
+
|
| 111 |
+
|
| 112 |
def firestore_query_exists(collection: str, field: str, value) -> bool:
|
| 113 |
"""Return True if at least one document matches field == value."""
|
| 114 |
db = _get_db()
|