AdnanElAssadi commited on
Commit
4872c01
·
verified ·
1 Parent(s): bd8112a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +244 -164
app.py CHANGED
@@ -9,35 +9,33 @@ def create_reranking_interface(task_data):
9
  results = {"task_name": task_data["task_name"], "task_type": "reranking", "annotations": []}
10
  completed_samples = {s["id"]: False for s in samples}
11
 
12
- def save_ranking(rankings, sample_id):
13
- """Save the current set of rankings."""
 
 
 
14
  try:
15
- # Check if all documents have rankings
16
- all_ranked = all(r is not None and r != "" for r in rankings)
17
- if not all_ranked:
18
- return "⚠️ Please assign a rank to all documents before submitting", f"Progress: {sum(completed_samples.values())}/{len(samples)}"
19
 
20
- # Convert rankings to integers with better error handling
21
- try:
22
- processed_rankings = [int(r) for r in rankings]
23
- except ValueError:
24
- return "⚠️ Invalid ranking value. Please use only numbers.", f"Progress: {sum(completed_samples.values())}/{len(samples)}"
 
25
 
26
- # Check for duplicate rankings
27
- if len(set(processed_rankings)) != len(processed_rankings):
28
- return "⚠️ Each document must have a unique rank. Please review your rankings.", f"Progress: {sum(completed_samples.values())}/{len(samples)}"
29
-
30
  # Store this annotation in memory
31
  existing_idx = next((i for i, a in enumerate(results["annotations"]) if a["sample_id"] == sample_id), None)
32
  if existing_idx is not None:
33
  results["annotations"][existing_idx] = {
34
  "sample_id": sample_id,
35
- "rankings": processed_rankings
36
  }
37
  else:
38
  results["annotations"].append({
39
  "sample_id": sample_id,
40
- "rankings": processed_rankings
41
  })
42
 
43
  completed_samples[sample_id] = True
@@ -55,6 +53,160 @@ def create_reranking_interface(task_data):
55
  # Return specific error message
56
  return f"Error: {str(e)}", f"Progress: {sum(completed_samples.values())}/{len(samples)}"
57
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
59
  gr.Markdown(f"# {task_data['task_name']} - Human Reranking Evaluation")
60
 
@@ -66,12 +218,12 @@ def create_reranking_interface(task_data):
66
 
67
  ### How to use this interface:
68
  1. Read the query at the top
69
- 2. Review each document carefully
70
- 3. Assign a rank to each document (1 = most relevant, higher numbers = less relevant)
71
- 4. Each document must have a unique rank
72
- 5. Click "Submit Rankings" when you're done with the current query
73
- 6. Use "Previous" and "Next" to navigate between queries
74
- 7. Click "Save All Results" periodically to ensure your work is saved
75
  """.format(instructions=task_data["instructions"]))
76
 
77
  current_sample_id = gr.State(value=samples[0]["id"])
@@ -84,75 +236,17 @@ def create_reranking_interface(task_data):
84
  gr.Markdown("## Query:")
85
  query_text = gr.Textbox(value=samples[0]["query"], label="", interactive=False)
86
 
87
- gr.Markdown("## Documents to Rank:")
88
-
89
- # Create document displays and ranking dropdowns in synchronized pairs
90
- doc_containers = []
91
- ranking_dropdowns = []
92
-
93
- with gr.Column():
94
- for i, doc in enumerate(samples[0]["candidates"]):
95
- with gr.Row():
96
- doc_box = gr.Textbox(
97
- value=doc,
98
- label=f"Document {i+1}",
99
- interactive=False
100
- )
101
- doc_containers.append(doc_box)
102
-
103
- # Use Dropdown instead of Number for ranking
104
- # Ranks from 1 to N (number of candidates)
105
- rank_dropdown = gr.Dropdown(
106
- choices=[str(j) for j in range(1, len(samples[0]["candidates"])+1)],
107
- label=f"Rank",
108
- value=None,
109
- interactive=True
110
- )
111
- ranking_dropdowns.append(rank_dropdown)
112
-
113
- # Add quick rank buttons
114
- with gr.Row():
115
- rank_high_btn = gr.Button("Rank High (1-3)", size="sm")
116
- rank_med_btn = gr.Button("Rank Medium (4-7)", size="sm")
117
- rank_low_btn = gr.Button("Rank Low (8+)", size="sm")
118
-
119
- # Set ranks directly instead of incrementing/decrementing
120
- # Helper functions to quickly set ranks in different ranges
121
- def set_high_rank(i):
122
- def set_rank():
123
- return ["1", "2", "3"][min(i, 2)] # First 3 docs get ranks 1,2,3
124
- return set_rank
125
-
126
- def set_medium_rank(i):
127
- def set_rank():
128
- base = 4
129
- return str(min(base + i % 4, len(samples[0]["candidates"])))
130
- return set_rank
131
-
132
- def set_low_rank(i):
133
- def set_rank():
134
- base = 8
135
- return str(min(base + i % 10, len(samples[0]["candidates"])))
136
- return set_rank
137
-
138
- # Connect rank buttons
139
- rank_high_btn.click(
140
- set_high_rank(i),
141
- inputs=[],
142
- outputs=[rank_dropdown]
143
- )
144
-
145
- rank_med_btn.click(
146
- set_medium_rank(i),
147
- inputs=[],
148
- outputs=[rank_dropdown]
149
- )
150
-
151
- rank_low_btn.click(
152
- set_low_rank(i),
153
- inputs=[],
154
- outputs=[rank_dropdown]
155
- )
156
 
157
  with gr.Row():
158
  prev_btn = gr.Button("← Previous Query", size="sm")
@@ -161,107 +255,93 @@ def create_reranking_interface(task_data):
161
 
162
  save_btn = gr.Button("💾 Save All Results", variant="secondary")
163
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
164
  def load_sample(sample_id):
165
- """Load a specific sample into the interface."""
166
- sample = next((s for s in samples if s["id"] == sample_id), None)
167
- if not sample:
168
- return [query_text.value] + [d.value for d in doc_containers] + [None] * len(ranking_dropdowns) + [current_sample_id.value, progress_text.value, status_box.value]
169
-
170
- # Update query
171
- new_query = sample["query"]
172
-
173
- # Update documents
174
- new_docs = []
175
- for i, doc in enumerate(sample["candidates"]):
176
- if i < len(doc_containers):
177
- new_docs.append(doc)
178
-
179
- # Initialize rankings
180
- new_rankings = [None] * len(ranking_dropdowns)
181
-
182
- # Check if this sample has already been annotated
183
- existing_annotation = next((a for a in results["annotations"] if a["sample_id"] == sample_id), None)
184
- if existing_annotation:
185
- # Restore previous rankings
186
- for i, rank in enumerate(existing_annotation["rankings"]):
187
- if i < len(new_rankings) and rank is not None:
188
- new_rankings[i] = str(rank) # Convert to string for dropdown
189
-
190
- # Update progress
191
- current_idx = samples.index(sample)
192
- new_progress = f"Progress: {sum(completed_samples.values())}/{len(samples)}"
193
-
194
- new_status = f"Viewing query {current_idx + 1} of {len(samples)}"
195
- if completed_samples[sample_id]:
196
- new_status += " (already completed)"
197
-
198
- return [new_query] + new_docs + new_rankings + [sample["id"], new_progress, new_status]
199
 
200
- def next_sample(current_id):
201
- """Load the next sample."""
 
202
  current_sample = next((s for s in samples if s["id"] == current_id), None)
203
  if not current_sample:
204
  return current_id
205
 
206
  current_idx = samples.index(current_sample)
207
- if current_idx < len(samples) - 1:
 
208
  next_sample = samples[current_idx + 1]
209
  return next_sample["id"]
210
- return current_id
211
-
212
- def prev_sample(current_id):
213
- """Load the previous sample."""
214
- current_sample = next((s for s in samples if s["id"] == current_id), None)
215
- if not current_sample:
216
- return current_id
217
-
218
- current_idx = samples.index(current_sample)
219
- if current_idx > 0:
220
  prev_sample = samples[current_idx - 1]
221
  return prev_sample["id"]
 
222
  return current_id
223
 
224
- def save_results():
225
- """Save all collected results to a file."""
226
- output_path = f"{task_data['task_name']}_human_results.json"
227
- with open(output_path, "w") as f:
228
- json.dump(results, f, indent=2)
229
- return f"✅ Results saved to {output_path} ({len(results['annotations'])} annotations)"
230
-
231
- # Define a wrapper function that collects all the dropdown values into a list
232
- def save_ranking_wrapper(*args):
233
- # The last argument is the sample_id, all others are rankings
234
- rankings = args[:-1]
235
- sample_id = args[-1]
236
- return save_ranking(rankings, sample_id)
237
-
238
- # Connect events
239
- submit_btn.click(
240
- save_ranking_wrapper,
241
- inputs=ranking_dropdowns + [current_sample_id],
242
- outputs=[status_box, progress_text]
243
- )
244
-
245
  next_btn.click(
246
- next_sample,
247
  inputs=[current_sample_id],
248
  outputs=[current_sample_id]
249
  ).then(
250
  load_sample,
251
  inputs=[current_sample_id],
252
- outputs=[query_text] + doc_containers + ranking_dropdowns + [current_sample_id, progress_text, status_box]
253
  )
254
 
255
  prev_btn.click(
256
- prev_sample,
257
  inputs=[current_sample_id],
258
  outputs=[current_sample_id]
259
  ).then(
260
  load_sample,
261
  inputs=[current_sample_id],
262
- outputs=[query_text] + doc_containers + ranking_dropdowns + [current_sample_id, progress_text, status_box]
 
 
 
 
 
 
 
263
  )
264
 
 
 
 
 
 
 
 
 
265
  save_btn.click(save_results, outputs=[status_box])
266
 
267
  return demo
 
9
  results = {"task_name": task_data["task_name"], "task_type": "reranking", "annotations": []}
10
  completed_samples = {s["id"]: False for s in samples}
11
 
12
+ # Track the current ordering of documents
13
+ current_document_order = gr.State([])
14
+
15
+ def save_ranking(sample_id, doc_order):
16
+ """Save the current document ordering as rankings."""
17
  try:
18
+ if not doc_order:
19
+ return "⚠️ No document ordering found", f"Progress: {sum(completed_samples.values())}/{len(samples)}"
 
 
20
 
21
+ # Convert document positions to rankings
22
+ # The first document (position 0) gets rank 1, etc.
23
+ rankings = []
24
+ for i in range(len(doc_order)):
25
+ doc_idx = doc_order.index(i)
26
+ rankings.append(doc_idx + 1) # Convert to 1-based ranks
27
 
 
 
 
 
28
  # Store this annotation in memory
29
  existing_idx = next((i for i, a in enumerate(results["annotations"]) if a["sample_id"] == sample_id), None)
30
  if existing_idx is not None:
31
  results["annotations"][existing_idx] = {
32
  "sample_id": sample_id,
33
+ "rankings": rankings
34
  }
35
  else:
36
  results["annotations"].append({
37
  "sample_id": sample_id,
38
+ "rankings": rankings
39
  })
40
 
41
  completed_samples[sample_id] = True
 
53
  # Return specific error message
54
  return f"Error: {str(e)}", f"Progress: {sum(completed_samples.values())}/{len(samples)}"
55
 
56
+ def initialize_documents(sample_id):
57
+ """Initialize the document order and content for a sample."""
58
+ sample = next((s for s in samples if s["id"] == sample_id), None)
59
+ if not sample:
60
+ return [], "", "Query not found", f"Progress: {sum(completed_samples.values())}/{len(samples)}"
61
+
62
+ # Get the documents for this sample
63
+ docs = sample["candidates"]
64
+
65
+ # Initialize the document order (0, 1, 2, ..., n-1)
66
+ doc_order = list(range(len(docs)))
67
+
68
+ # Check if this sample has already been annotated to restore ordering
69
+ existing_annotation = next((a for a in results["annotations"] if a["sample_id"] == sample_id), None)
70
+ if existing_annotation and "rankings" in existing_annotation:
71
+ # Convert rankings back to positions
72
+ # If document 0 has rank 3, it should be in position 2
73
+ sorted_positions = []
74
+ for i in range(len(existing_annotation["rankings"])):
75
+ rank = existing_annotation["rankings"][i]
76
+ sorted_positions.append((i, rank))
77
+
78
+ # Sort by rank (ascending)
79
+ sorted_positions.sort(key=lambda x: x[1])
80
+
81
+ # Extract the original indices in their ranked order
82
+ doc_order = [pos[0] for pos in sorted_positions]
83
+
84
+ # Current sample query
85
+ query = sample["query"]
86
+
87
+ # Status message
88
+ status = f"Viewing query {samples.index(sample) + 1} of {len(samples)}"
89
+ if completed_samples[sample_id]:
90
+ status += " (already completed)"
91
+
92
+ return doc_order, query, status, f"Progress: {sum(completed_samples.values())}/{len(samples)}"
93
+
94
+ def move_document(doc_order, doc_idx, direction):
95
+ """Move a document up or down in the order."""
96
+ if not doc_order:
97
+ return doc_order
98
+
99
+ # Create a copy of the order to avoid reference issues
100
+ new_order = doc_order.copy()
101
+
102
+ # Find the current position of the document in the order
103
+ current_pos = new_order.index(doc_idx)
104
+
105
+ # Calculate the new position
106
+ if direction == "up" and current_pos > 0:
107
+ # Swap with the document above
108
+ new_order[current_pos], new_order[current_pos - 1] = new_order[current_pos - 1], new_order[current_pos]
109
+ elif direction == "down" and current_pos < len(new_order) - 1:
110
+ # Swap with the document below
111
+ new_order[current_pos], new_order[current_pos + 1] = new_order[current_pos + 1], new_order[current_pos]
112
+
113
+ return new_order
114
+
115
+ def render_documents(doc_order, sample_id):
116
+ """Render the documents in the specified order."""
117
+ sample = next((s for s in samples if s["id"] == sample_id), None)
118
+ if not sample or not doc_order:
119
+ return gr.HTML.update(value="<p>No documents to display</p>")
120
+
121
+ docs = sample["candidates"]
122
+
123
+ # Build HTML for the document list
124
+ html = "<div class='document-list'>"
125
+
126
+ for pos, doc_idx in enumerate(doc_order):
127
+ if doc_idx < len(docs):
128
+ doc_text = docs[doc_idx]
129
+
130
+ # Calculate the rank (position + 1)
131
+ rank = pos + 1
132
+
133
+ # Create a container for each document with buttons and rank
134
+ html += f"""
135
+ <div class='document-item' id='doc-{doc_idx}'>
136
+ <div class='document-controls'>
137
+ <div class='rank-display'>Rank: {rank}</div>
138
+ <button class='move-up-btn' onclick='moveDocument({doc_idx}, "up")'>↑ Move Up</button>
139
+ <button class='move-down-btn' onclick='moveDocument({doc_idx}, "down")'>↓ Move Down</button>
140
+ </div>
141
+ <div class='document-content'>
142
+ <p><strong>Document {doc_idx + 1}:</strong> {doc_text}</p>
143
+ </div>
144
+ </div>
145
+ """
146
+
147
+ html += "</div>"
148
+
149
+ # Add custom CSS
150
+ html += """
151
+ <style>
152
+ .document-list {
153
+ display: flex;
154
+ flex-direction: column;
155
+ gap: 10px;
156
+ }
157
+ .document-item {
158
+ border: 1px solid #ddd;
159
+ border-radius: 8px;
160
+ padding: 15px;
161
+ background-color: #f9f9f9;
162
+ display: flex;
163
+ flex-direction: column;
164
+ }
165
+ .document-controls {
166
+ display: flex;
167
+ align-items: center;
168
+ gap: 10px;
169
+ margin-bottom: 10px;
170
+ }
171
+ .rank-display {
172
+ font-weight: bold;
173
+ min-width: 80px;
174
+ }
175
+ .document-content {
176
+ padding: 5px;
177
+ background-color: white;
178
+ border-radius: 4px;
179
+ }
180
+ button {
181
+ padding: 5px 10px;
182
+ border-radius: 4px;
183
+ border: 1px solid #ccc;
184
+ cursor: pointer;
185
+ }
186
+ .move-up-btn {
187
+ background-color: #e0f7fa;
188
+ }
189
+ .move-down-btn {
190
+ background-color: #fff3e0;
191
+ }
192
+ </style>
193
+ """
194
+
195
+ # Add JavaScript to handle button clicks
196
+ html += """
197
+ <script>
198
+ function moveDocument(docIdx, direction) {
199
+ // Call the Python function via Gradio's API
200
+ const event = new CustomEvent('move-document', {
201
+ detail: { docIdx: docIdx, direction: direction }
202
+ });
203
+ document.dispatchEvent(event);
204
+ }
205
+ </script>
206
+ """
207
+
208
+ return gr.HTML.update(value=html)
209
+
210
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
211
  gr.Markdown(f"# {task_data['task_name']} - Human Reranking Evaluation")
212
 
 
218
 
219
  ### How to use this interface:
220
  1. Read the query at the top
221
+ 2. Review each document in the list
222
+ 3. Use the "Move Up" and "Move Down" buttons to arrange documents by relevance
223
+ (most relevant at the top, least relevant at the bottom)
224
+ 4. Click "Submit Rankings" when you're done with the current query
225
+ 5. Use "Previous" and "Next" to navigate between queries
226
+ 6. Click "Save All Results" periodically to ensure your work is saved
227
  """.format(instructions=task_data["instructions"]))
228
 
229
  current_sample_id = gr.State(value=samples[0]["id"])
 
236
  gr.Markdown("## Query:")
237
  query_text = gr.Textbox(value=samples[0]["query"], label="", interactive=False)
238
 
239
+ gr.Markdown("## Documents (Arrange in order of relevance, most relevant at top):")
240
+
241
+ # Container to display documents in their current order
242
+ document_list = gr.HTML()
243
+
244
+ # For Gradio's event handling, we need actual buttons
245
+ # These are invisible and triggered by JavaScript
246
+ with gr.Row(visible=False):
247
+ up_btn = gr.Button("Up")
248
+ down_btn = gr.Button("Down")
249
+ doc_index = gr.Number(0, label="Document Index")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
250
 
251
  with gr.Row():
252
  prev_btn = gr.Button("← Previous Query", size="sm")
 
255
 
256
  save_btn = gr.Button("💾 Save All Results", variant="secondary")
257
 
258
+ # Initialize the document order for the first sample
259
+ doc_order, query_val, status_val, progress_val = initialize_documents(samples[0]["id"])
260
+ current_document_order.value = doc_order
261
+ query_text.value = query_val
262
+ status_box.value = status_val
263
+ progress_text.value = progress_val
264
+
265
+ # Render the documents in their initial order
266
+ document_list.value = render_documents(doc_order, samples[0]["id"]).value
267
+
268
+ # Connect events for up/down buttons (these are triggered by JavaScript)
269
+ def doc_move_handler(doc_idx, direction, current_order, sample_id):
270
+ new_order = move_document(current_order, doc_idx, direction)
271
+ html_update = render_documents(new_order, sample_id)
272
+ return new_order, html_update
273
+
274
+ up_btn.click(
275
+ doc_move_handler,
276
+ inputs=[doc_index, gr.Textbox(value="up"), current_document_order, current_sample_id],
277
+ outputs=[current_document_order, document_list]
278
+ )
279
+
280
+ down_btn.click(
281
+ doc_move_handler,
282
+ inputs=[doc_index, gr.Textbox(value="down"), current_document_order, current_sample_id],
283
+ outputs=[current_document_order, document_list]
284
+ )
285
+
286
+ # Load a sample and update the interface
287
  def load_sample(sample_id):
288
+ doc_order, query_val, status_val, progress_val = initialize_documents(sample_id)
289
+ html_update = render_documents(doc_order, sample_id)
290
+ return doc_order, query_val, html_update, status_val, progress_val
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
291
 
292
+ # Navigation events
293
+ def nav_sample(current_id, direction):
294
+ """Navigate to the previous or next sample."""
295
  current_sample = next((s for s in samples if s["id"] == current_id), None)
296
  if not current_sample:
297
  return current_id
298
 
299
  current_idx = samples.index(current_sample)
300
+
301
+ if direction == "next" and current_idx < len(samples) - 1:
302
  next_sample = samples[current_idx + 1]
303
  return next_sample["id"]
304
+ elif direction == "prev" and current_idx > 0:
 
 
 
 
 
 
 
 
 
305
  prev_sample = samples[current_idx - 1]
306
  return prev_sample["id"]
307
+
308
  return current_id
309
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
310
  next_btn.click(
311
+ lambda id: nav_sample(id, "next"),
312
  inputs=[current_sample_id],
313
  outputs=[current_sample_id]
314
  ).then(
315
  load_sample,
316
  inputs=[current_sample_id],
317
+ outputs=[current_document_order, query_text, document_list, status_box, progress_text]
318
  )
319
 
320
  prev_btn.click(
321
+ lambda id: nav_sample(id, "prev"),
322
  inputs=[current_sample_id],
323
  outputs=[current_sample_id]
324
  ).then(
325
  load_sample,
326
  inputs=[current_sample_id],
327
+ outputs=[current_document_order, query_text, document_list, status_box, progress_text]
328
+ )
329
+
330
+ # Submit rankings
331
+ submit_btn.click(
332
+ save_ranking,
333
+ inputs=[current_sample_id, current_document_order],
334
+ outputs=[status_box, progress_text]
335
  )
336
 
337
+ # Save all results
338
+ def save_results():
339
+ """Save all collected results to a file."""
340
+ output_path = f"{task_data['task_name']}_human_results.json"
341
+ with open(output_path, "w") as f:
342
+ json.dump(results, f, indent=2)
343
+ return f"✅ Results saved to {output_path} ({len(results['annotations'])} annotations)"
344
+
345
  save_btn.click(save_results, outputs=[status_box])
346
 
347
  return demo