Commit ·
eb0247f
1
Parent(s): 44eddd9
Add global evaluation status and update UI for evaluated items
Browse files- app.py +17 -14
- index.html +13 -4
app.py
CHANGED
|
@@ -88,14 +88,21 @@ def fetch_annotations(db: sqlite3.Connection, annotator: str) -> dict[str, dict]
|
|
| 88 |
return {row["eval_id"]: dict(row) for row in rows}
|
| 89 |
|
| 90 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 91 |
def merge_items_with_annotations(
|
| 92 |
items: tuple[dict, ...],
|
| 93 |
annotations: dict[str, dict],
|
|
|
|
| 94 |
) -> list[dict]:
|
| 95 |
-
"""Return items with annotation values
|
| 96 |
merged = []
|
| 97 |
for item in items:
|
| 98 |
-
entry = {**item}
|
| 99 |
ann = annotations.get(item["eval_id"])
|
| 100 |
if ann:
|
| 101 |
for field in ANNOTATION_FIELDS:
|
|
@@ -148,15 +155,14 @@ def index():
|
|
| 148 |
|
| 149 |
@app.route("/api/entries")
|
| 150 |
def get_entries():
|
| 151 |
-
"""Return all evaluation items
|
| 152 |
annotator = request.args.get("annotator", "").strip()
|
| 153 |
items = load_dataset()
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
return jsonify(list(items))
|
| 160 |
|
| 161 |
|
| 162 |
@app.route("/api/annotate", methods=["POST"])
|
|
@@ -186,14 +192,11 @@ def annotate():
|
|
| 186 |
|
| 187 |
@app.route("/api/progress")
|
| 188 |
def progress():
|
| 189 |
-
"""Return annotation progress
|
| 190 |
-
annotator = request.args.get("annotator", "").strip()
|
| 191 |
total = len(load_dataset())
|
| 192 |
-
if not annotator:
|
| 193 |
-
return jsonify({"total": total, "annotated": 0})
|
| 194 |
db = get_db()
|
| 195 |
count = db.execute(
|
| 196 |
-
"SELECT COUNT(
|
| 197 |
).fetchone()[0]
|
| 198 |
db.close()
|
| 199 |
return jsonify({"total": total, "annotated": count})
|
|
|
|
| 88 |
return {row["eval_id"]: dict(row) for row in rows}
|
| 89 |
|
| 90 |
|
| 91 |
+
def fetch_evaluated_eval_ids(db: sqlite3.Connection) -> set[str]:
|
| 92 |
+
"""Return the set of eval_ids that have been annotated by any annotator."""
|
| 93 |
+
rows = db.execute("SELECT DISTINCT eval_id FROM annotations").fetchall()
|
| 94 |
+
return {row["eval_id"] for row in rows}
|
| 95 |
+
|
| 96 |
+
|
| 97 |
def merge_items_with_annotations(
|
| 98 |
items: tuple[dict, ...],
|
| 99 |
annotations: dict[str, dict],
|
| 100 |
+
evaluated_ids: set[str],
|
| 101 |
) -> list[dict]:
|
| 102 |
+
"""Return items with annotation values and global evaluation status merged in."""
|
| 103 |
merged = []
|
| 104 |
for item in items:
|
| 105 |
+
entry = {**item, "evaluated": item["eval_id"] in evaluated_ids}
|
| 106 |
ann = annotations.get(item["eval_id"])
|
| 107 |
if ann:
|
| 108 |
for field in ANNOTATION_FIELDS:
|
|
|
|
| 155 |
|
| 156 |
@app.route("/api/entries")
|
| 157 |
def get_entries():
|
| 158 |
+
"""Return all evaluation items with global evaluation status and per-annotator annotations."""
|
| 159 |
annotator = request.args.get("annotator", "").strip()
|
| 160 |
items = load_dataset()
|
| 161 |
+
db = get_db()
|
| 162 |
+
evaluated_ids = fetch_evaluated_eval_ids(db)
|
| 163 |
+
annotations = fetch_annotations(db, annotator) if annotator else {}
|
| 164 |
+
db.close()
|
| 165 |
+
return jsonify(merge_items_with_annotations(items, annotations, evaluated_ids))
|
|
|
|
| 166 |
|
| 167 |
|
| 168 |
@app.route("/api/annotate", methods=["POST"])
|
|
|
|
| 192 |
|
| 193 |
@app.route("/api/progress")
|
| 194 |
def progress():
|
| 195 |
+
"""Return global annotation progress (unique eval_ids annotated by anyone)."""
|
|
|
|
| 196 |
total = len(load_dataset())
|
|
|
|
|
|
|
| 197 |
db = get_db()
|
| 198 |
count = db.execute(
|
| 199 |
+
"SELECT COUNT(DISTINCT eval_id) FROM annotations",
|
| 200 |
).fetchone()[0]
|
| 201 |
db.close()
|
| 202 |
return jsonify({"total": total, "annotated": count})
|
index.html
CHANGED
|
@@ -190,6 +190,11 @@
|
|
| 190 |
.login-box button:hover { background: #1d4ed8; }
|
| 191 |
.login-error { color: #dc2626; font-size: 13px; margin-bottom: 8px; }
|
| 192 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 193 |
.badge-promptd {
|
| 194 |
background: #fef3c7; color: #92400e; padding: 3px 10px;
|
| 195 |
border-radius: 12px; font-size: 12px; font-weight: 600;
|
|
@@ -239,7 +244,7 @@
|
|
| 239 |
<label>Bearbeitungsstatus:</label>
|
| 240 |
<select id="filter-status" onchange="applyFilters()">
|
| 241 |
<option value="">alle</option>
|
| 242 |
-
<option value="pending">offen</option>
|
| 243 |
<option value="done">bewertet</option>
|
| 244 |
</select>
|
| 245 |
<label>LRA:</label>
|
|
@@ -262,6 +267,7 @@
|
|
| 262 |
<h2 id="title">-</h2>
|
| 263 |
<div class="meta">
|
| 264 |
<span class="badge" id="badge-rfa"></span>
|
|
|
|
| 265 |
<span class="badge-promptd" id="badge-promptd" style="display:none">Alter Prompt</span>
|
| 266 |
<span class="id" id="eval-id">-</span>
|
| 267 |
</div>
|
|
@@ -471,9 +477,9 @@ function applyFilters() {
|
|
| 471 |
const rfa = document.getElementById("filter-rfa").value;
|
| 472 |
filteredRows = [...allRows];
|
| 473 |
if (status === "pending") {
|
| 474 |
-
filteredRows = filteredRows.filter(r => !r.
|
| 475 |
} else if (status === "done") {
|
| 476 |
-
filteredRows = filteredRows.filter(r => r.
|
| 477 |
}
|
| 478 |
if (rfa) {
|
| 479 |
filteredRows = filteredRows.filter(r => r.rfa === rfa);
|
|
@@ -522,6 +528,8 @@ function render() {
|
|
| 522 |
badge.textContent = row.rfa;
|
| 523 |
badge.className = "badge badge-" + row.rfa;
|
| 524 |
|
|
|
|
|
|
|
| 525 |
const isPromptD = row.has_prior_judgement;
|
| 526 |
document.getElementById("badge-promptd").style.display = isPromptD ? "" : "none";
|
| 527 |
document.getElementById("reference-banner").style.display = isPromptD ? "" : "none";
|
|
@@ -642,9 +650,10 @@ async function saveAnnotation() {
|
|
| 642 |
for (const key of ["bewertung", "korrekt", "relevant", "vollstaendig", "kohaerenz", "anmerkungen"]) {
|
| 643 |
row[key] = data[key];
|
| 644 |
}
|
|
|
|
| 645 |
// Also update in allRows
|
| 646 |
const src = allRows.find(r => r.eval_id === row.eval_id);
|
| 647 |
-
if (src) Object.assign(src, data);
|
| 648 |
|
| 649 |
dirty = false;
|
| 650 |
document.getElementById("btn-save").disabled = true;
|
|
|
|
| 190 |
.login-box button:hover { background: #1d4ed8; }
|
| 191 |
.login-error { color: #dc2626; font-size: 13px; margin-bottom: 8px; }
|
| 192 |
|
| 193 |
+
.badge-evaluated {
|
| 194 |
+
background: #dcfce7; color: #166534; padding: 3px 10px;
|
| 195 |
+
border-radius: 12px; font-size: 12px; font-weight: 600;
|
| 196 |
+
}
|
| 197 |
+
|
| 198 |
.badge-promptd {
|
| 199 |
background: #fef3c7; color: #92400e; padding: 3px 10px;
|
| 200 |
border-radius: 12px; font-size: 12px; font-weight: 600;
|
|
|
|
| 244 |
<label>Bearbeitungsstatus:</label>
|
| 245 |
<select id="filter-status" onchange="applyFilters()">
|
| 246 |
<option value="">alle</option>
|
| 247 |
+
<option value="pending" selected>offen</option>
|
| 248 |
<option value="done">bewertet</option>
|
| 249 |
</select>
|
| 250 |
<label>LRA:</label>
|
|
|
|
| 267 |
<h2 id="title">-</h2>
|
| 268 |
<div class="meta">
|
| 269 |
<span class="badge" id="badge-rfa"></span>
|
| 270 |
+
<span class="badge-evaluated" id="badge-evaluated" style="display:none">Bewertet</span>
|
| 271 |
<span class="badge-promptd" id="badge-promptd" style="display:none">Alter Prompt</span>
|
| 272 |
<span class="id" id="eval-id">-</span>
|
| 273 |
</div>
|
|
|
|
| 477 |
const rfa = document.getElementById("filter-rfa").value;
|
| 478 |
filteredRows = [...allRows];
|
| 479 |
if (status === "pending") {
|
| 480 |
+
filteredRows = filteredRows.filter(r => !r.evaluated);
|
| 481 |
} else if (status === "done") {
|
| 482 |
+
filteredRows = filteredRows.filter(r => r.evaluated);
|
| 483 |
}
|
| 484 |
if (rfa) {
|
| 485 |
filteredRows = filteredRows.filter(r => r.rfa === rfa);
|
|
|
|
| 528 |
badge.textContent = row.rfa;
|
| 529 |
badge.className = "badge badge-" + row.rfa;
|
| 530 |
|
| 531 |
+
document.getElementById("badge-evaluated").style.display = row.evaluated ? "" : "none";
|
| 532 |
+
|
| 533 |
const isPromptD = row.has_prior_judgement;
|
| 534 |
document.getElementById("badge-promptd").style.display = isPromptD ? "" : "none";
|
| 535 |
document.getElementById("reference-banner").style.display = isPromptD ? "" : "none";
|
|
|
|
| 650 |
for (const key of ["bewertung", "korrekt", "relevant", "vollstaendig", "kohaerenz", "anmerkungen"]) {
|
| 651 |
row[key] = data[key];
|
| 652 |
}
|
| 653 |
+
row.evaluated = true;
|
| 654 |
// Also update in allRows
|
| 655 |
const src = allRows.find(r => r.eval_id === row.eval_id);
|
| 656 |
+
if (src) { Object.assign(src, data); src.evaluated = true; }
|
| 657 |
|
| 658 |
dirty = false;
|
| 659 |
document.getElementById("btn-save").disabled = true;
|