constantinSch commited on
Commit
eb0247f
·
1 Parent(s): 44eddd9

Add global evaluation status and update UI for evaluated items

Browse files
Files changed (2) hide show
  1. app.py +17 -14
  2. 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 merged in (does not mutate originals)."""
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, optionally with annotations merged for an annotator."""
152
  annotator = request.args.get("annotator", "").strip()
153
  items = load_dataset()
154
- if annotator:
155
- db = get_db()
156
- annotations = fetch_annotations(db, annotator)
157
- db.close()
158
- return jsonify(merge_items_with_annotations(items, annotations))
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 for an annotator."""
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(*) FROM annotations WHERE annotator = ?", (annotator,),
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.bewertung);
475
  } else if (status === "done") {
476
- filteredRows = filteredRows.filter(r => r.bewertung);
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;