AdnanElAssadi commited on
Commit
1c3ac27
·
verified ·
1 Parent(s): 5716188

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +206 -286
app.py CHANGED
@@ -11,35 +11,153 @@ def create_reranking_interface(task_data):
11
 
12
  # Define helper functions before UI elements are created
13
  def generate_sortable_html(candidates, existing_ranks=None):
14
- """Generate the HTML for the sortable list with up/down buttons."""
15
  if existing_ranks and len(existing_ranks) == len(candidates):
 
16
  order = sorted(range(len(candidates)), key=lambda i: existing_ranks[i])
17
  else:
 
18
  order = list(range(len(candidates)))
19
-
20
- html = '<div id="sortable-container" class="sortable-container">'
21
- for rank_minus_1, idx in enumerate(order):
 
22
  if idx < len(candidates):
23
- doc = candidates[idx]
24
- rank = rank_minus_1 + 1
25
- import html as html_escaper
26
- escaped_doc = html_escaper.escape(doc)
27
 
28
- # Add navigation buttons (up/down arrows)
29
- up_disabled = "disabled" if rank == 1 else ""
30
- down_disabled = "disabled" if rank == len(candidates) else ""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
 
32
- html += f'''\
33
- <div class="sortable-item rank-bg-{rank}" data-doc-id="{idx}" data-rank="{rank}">
34
- <div class="rank-controls">
35
- <button class="rank-btn up-btn" {up_disabled} onclick="moveItemUp({rank})">▲</button>
36
- <div class="rank-badge">{rank}</div>
37
- <button class="rank-btn down-btn" {down_disabled} onclick="moveItemDown({rank})">▼</button>
38
  </div>
39
- <div class="doc-content">{escaped_doc}</div>
40
  </div>
41
- '''
42
- html += '</div>'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
  return html
44
 
45
  def save_ranking(order_json, sample_id):
@@ -150,293 +268,95 @@ def create_reranking_interface(task_data):
150
  save_btn = gr.Button("💾 Save All Results", variant="secondary")
151
 
152
  js_code = """
153
- <script src="https://cdn.jsdelivr.net/npm/sortablejs@1.15.0/Sortable.min.js"></script>
154
- <script>
155
- // Ensure these functions are globally available
156
- window.moveItemUp = function(currentRank) {
157
- console.log("Moving item up:", currentRank);
158
- if (currentRank <= 1) return; // Already at the top
159
-
160
- const container = document.getElementById('sortable-container');
161
- if (!container) {
162
- console.error("Container not found");
163
- return;
164
- }
165
-
166
- const items = container.querySelectorAll('.sortable-item');
167
- const itemsArray = Array.from(items);
168
-
169
- // Find the items to swap
170
- const currentItem = itemsArray.find(item => parseInt(item.getAttribute('data-rank')) === currentRank);
171
- const aboveItem = itemsArray.find(item => parseInt(item.getAttribute('data-rank')) === currentRank - 1);
172
-
173
- if (!currentItem || !aboveItem) {
174
- console.error("Items not found", currentItem, aboveItem);
175
- return;
176
- }
177
-
178
- // Swap the items
179
- aboveItem.parentNode.insertBefore(currentItem, aboveItem);
180
-
181
- // Update ranks
182
- window.updateRanksAfterMove();
183
- };
184
-
185
- // Function to move an item down in the ranking
186
- window.moveItemDown = function(currentRank) {
187
- console.log("Moving item down:", currentRank);
188
- currentRank = parseInt(currentRank);
189
-
190
- const container = document.getElementById('sortable-container');
191
- if (!container) {
192
- console.error("Container not found");
193
- return;
194
- }
195
-
196
- const items = container.querySelectorAll('.sortable-item');
197
- if (currentRank >= items.length) return; // Already at the bottom
198
-
199
- const itemsArray = Array.from(items);
200
-
201
- // Find the items to swap
202
- const currentItem = itemsArray.find(item => parseInt(item.getAttribute('data-rank')) === currentRank);
203
- const belowItem = itemsArray.find(item => parseInt(item.getAttribute('data-rank')) === currentRank + 1);
204
-
205
- if (!currentItem || !belowItem) {
206
- console.error("Items not found", currentItem, belowItem);
207
- return;
208
- }
209
-
210
- // Swap the items
211
- belowItem.parentNode.insertBefore(belowItem, currentItem);
212
-
213
- // Update ranks
214
- window.updateRanksAfterMove();
215
- };
216
-
217
- // Update rank numbers and classes after moving
218
- window.updateRanksAfterMove = function() {
219
- console.log("Updating ranks");
220
- const container = document.getElementById('sortable-container');
221
- if (!container) return;
222
-
223
- const items = container.querySelectorAll('.sortable-item');
224
- const orderInput = document.querySelector('#current-order textarea');
225
- if (!orderInput) {
226
- console.error("Order input not found");
227
- return;
228
- }
229
-
230
- const order = [];
231
- items.forEach((item, index) => {
232
- const rank = index + 1;
233
- const docId = parseInt(item.getAttribute('data-doc-id'));
234
-
235
- // Update rank display
236
- const rankBadge = item.querySelector('.rank-badge');
237
- if (rankBadge) rankBadge.textContent = rank;
238
-
239
- // Update item classes
240
- item.className = item.className.replace(/rank-bg-\d+/g, '').trim();
241
- item.classList.add(`rank-bg-${rank}`);
242
-
243
- // Update data attribute
244
- item.setAttribute('data-rank', rank);
245
-
246
- // Update button states
247
- const upBtn = item.querySelector('.up-btn');
248
- const downBtn = item.querySelector('.down-btn');
249
-
250
- if (upBtn) {
251
- if (rank == 1) {
252
- upBtn.setAttribute('disabled', 'disabled');
253
- } else {
254
- upBtn.removeAttribute('disabled');
255
- }
256
- }
257
-
258
- if (downBtn) {
259
- if (rank == items.length) {
260
- downBtn.setAttribute('disabled', 'disabled');
261
- } else {
262
- downBtn.removeAttribute('disabled');
263
- }
264
- }
265
-
266
- order.push(docId);
267
- });
268
-
269
- // Update hidden input
270
- const newOrderValue = JSON.stringify(order);
271
- console.log("New order:", newOrderValue);
272
- if (orderInput.value !== newOrderValue) {
273
- orderInput.value = newOrderValue;
274
- try {
275
- const event = new Event('input', { bubbles: true });
276
- orderInput.dispatchEvent(event);
277
- console.log("Event dispatched");
278
- } catch (e) {
279
- console.error("Error dispatching event:", e);
280
- }
281
- }
282
- };
283
-
284
- document.addEventListener('DOMContentLoaded', function() {
285
- console.log("DOM loaded, setting up ranking interface");
286
-
287
- function initializeSortable() {
288
- // Initialize event handlers for buttons
289
- window.updateRanksAfterMove();
290
-
291
- // Keep drag-and-drop as a fallback
292
- const container = document.getElementById('sortable-container');
293
- if (!container) {
294
- console.log('Container not found, retrying...');
295
- setTimeout(initializeSortable, 200);
296
- return;
297
- }
298
-
299
- if (typeof Sortable === 'undefined') {
300
- console.log('Sortable not loaded, retrying...');
301
- setTimeout(initializeSortable, 200);
302
- return;
303
- }
304
-
305
- if (container.sortableInstance) {
306
- return;
307
- }
308
-
309
- console.log("Initializing Sortable.js");
310
- container.sortableInstance = new Sortable(container, {
311
- animation: 150,
312
- ghostClass: "sortable-ghost",
313
- onEnd: function() {
314
- window.updateRanksAfterMove();
315
- }
316
- });
317
- }
318
-
319
- // Initialize immediately and also set up a mutation observer
320
- initializeSortable();
321
-
322
- const targetNode = document.getElementById('sortable-list-container');
323
- if (targetNode) {
324
- const config = { childList: true, subtree: true };
325
- const observer = new MutationObserver(function(mutationsList) {
326
- for(const mutation of mutationsList) {
327
- if (mutation.type === 'childList') {
328
- if (document.getElementById('sortable-container')) {
329
- initializeSortable();
330
- }
331
- }
332
- }
333
- });
334
- observer.observe(targetNode, config);
335
- }
336
- });
337
-
338
- // Backup initialization - sometimes DOMContentLoaded doesn't fire reliably in iframes
339
- setTimeout(function() {
340
- if (document.getElementById('sortable-container')) {
341
- console.log("Backup initialization");
342
- if (window.updateRanksAfterMove) {
343
- window.updateRanksAfterMove();
344
- }
345
- }
346
- }, 1000);
347
- </script>
348
  <style>
349
- .sortable-container {
350
- display: flex;
351
- flex-direction: column;
352
- gap: 8px;
353
- min-height: 200px;
354
- padding: 10px;
355
- background-color: #f8f9fa;
356
- border-radius: 8px;
357
  }
358
- .sortable-item {
359
- padding: 12px 15px;
360
- background-color: #fff;
361
- border: 1px solid #e0e0e0;
362
- border-radius: 6px;
363
- cursor: grab;
364
- display: flex;
365
- align-items: center;
366
- transition: all 0.2s ease;
367
- user-select: none;
368
- }
369
- .sortable-item:hover {
370
- background-color: #f8f9fa;
371
- box-shadow: 0 2px 4px rgba(0,0,0,0.1);
372
- }
373
- .sortable-ghost {
374
- background-color: #e3f2fd !important;
375
- border-style: dashed !important;
376
- opacity: 0.8;
377
  }
378
- .sortable-chosen {
379
- cursor: grabbing;
380
- box-shadow: 0 4px 8px rgba(0,0,0,0.1);
 
 
 
 
 
381
  }
382
- .rank-controls {
 
383
  display: flex;
384
  flex-direction: column;
385
  align-items: center;
386
  margin-right: 15px;
 
387
  }
388
- .rank-badge {
389
- display: flex;
390
- align-items: center;
 
 
 
 
 
391
  justify-content: center;
392
- width: 28px;
393
- height: 28px;
394
  border-radius: 50%;
395
- background-color: #6c757d;
396
- color: white;
397
- font-weight: bold;
398
- margin: 4px 0;
399
- flex-shrink: 0;
 
 
 
 
400
  }
 
401
  .rank-btn {
402
- border: none;
403
- background: #f0f0f0;
404
- border-radius: 4px;
405
- width: 24px;
406
- height: 24px;
407
  font-size: 12px;
408
- line-height: 1;
 
 
 
409
  display: flex;
410
  align-items: center;
411
  justify-content: center;
412
- cursor: pointer;
413
- color: #333;
414
- }
415
- .rank-btn:hover:not([disabled]) {
416
- background: #e0e0e0;
417
  }
418
- .rank-btn:disabled {
419
- opacity: 0.5;
420
- cursor: not-allowed;
421
  }
422
- .doc-content {
423
- flex: 1;
424
- line-height: 1.4;
425
- word-break: break-word;
 
426
  }
427
- .rank-bg-1 .rank-badge { background-color: #198754; }
428
- .rank-bg-2 .rank-badge { background-color: #20c997; }
429
- .rank-bg-3 .rank-badge { background-color: #ffc107; color: #333; }
430
- .rank-bg-4 .rank-badge { background-color: #fd7e14; }
431
- .rank-bg-5 .rank-badge { background-color: #dc3545; }
432
- .rank-bg-6 .rank-badge, .rank-bg-7 .rank-badge { background-color: #6f42c1; }
433
- .rank-bg-8 .rank-badge, .rank-bg-9 .rank-badge { background-color: #d63384; }
434
- .rank-bg-10 .rank-badge, .rank-bg-11 .rank-badge, .rank-bg-12 .rank-badge,
435
- .rank-bg-13 .rank-badge, .rank-bg-14 .rank-badge, .rank-bg-15 .rank-badge,
436
- .rank-bg-16 .rank-badge, .rank-bg-17 .rank-badge, .rank-bg-18 .rank-badge,
437
- .rank-bg-19 .rank-badge, .rank-bg-20 .rank-badge {
438
- background-color: #6c757d;
439
  }
 
 
 
 
 
 
 
440
  </style>
441
  """
442
  gr.HTML(js_code)
 
11
 
12
  # Define helper functions before UI elements are created
13
  def generate_sortable_html(candidates, existing_ranks=None):
14
+ """Generate the HTML for simple ranking with numbered buttons."""
15
  if existing_ranks and len(existing_ranks) == len(candidates):
16
+ # Use existing ranks to determine the current order
17
  order = sorted(range(len(candidates)), key=lambda i: existing_ranks[i])
18
  else:
19
+ # Default to original order
20
  order = list(range(len(candidates)))
21
+
22
+ # Create an array to hold the documents in their current order
23
+ ordered_docs = []
24
+ for idx in order:
25
  if idx < len(candidates):
26
+ ordered_docs.append({"doc_id": idx, "text": candidates[idx]})
 
 
 
27
 
28
+ # Generate the HTML for the ranking interface
29
+ html = """
30
+ <div class="ranking-container">
31
+ <div class="docs-container">
32
+ """
33
+
34
+ # Add each document with rank selection buttons
35
+ for i, doc in enumerate(ordered_docs):
36
+ import html as html_escaper
37
+ escaped_doc = html_escaper.escape(doc["text"])
38
+ current_rank = i + 1
39
+
40
+ html += f"""
41
+ <div class="doc-item" data-doc-id="{doc['doc_id']}">
42
+ <div class="rank-selector">
43
+ <div class="rank-number">{current_rank}</div>
44
+ <div class="rank-buttons">
45
+ """
46
+
47
+ # Add numbered rank buttons
48
+ for rank_btn in range(1, len(candidates) + 1):
49
+ selected = "selected" if rank_btn == current_rank else ""
50
+ html += f'<button class="rank-btn {selected}" data-rank="{rank_btn}" onclick="setRank({doc["doc_id"]}, {rank_btn})">{rank_btn}</button>'
51
 
52
+ html += """
 
 
 
 
 
53
  </div>
 
54
  </div>
55
+ <div class="doc-content">
56
+ """ + escaped_doc + """
57
+ </div>
58
+ </div>
59
+ """
60
+
61
+ html += """
62
+ </div>
63
+ </div>
64
+
65
+ <script>
66
+ function setRank(docId, newRank) {
67
+ // Convert to numbers to ensure proper comparison
68
+ docId = parseInt(docId);
69
+ newRank = parseInt(newRank);
70
+
71
+ // Get all doc items
72
+ const docItems = document.querySelectorAll('.doc-item');
73
+
74
+ // First, find the document that currently has the requested rank
75
+ let targetDocItem = null;
76
+ let currentDocRank = 0;
77
+
78
+ docItems.forEach((item, index) => {
79
+ const itemDocId = parseInt(item.getAttribute('data-doc-id'));
80
+
81
+ // Find the current rank of the document we want to change
82
+ if (itemDocId === docId) {
83
+ targetDocItem = item;
84
+ currentDocRank = index + 1;
85
+ }
86
+
87
+ // Update the selected button for this doc
88
+ const buttons = item.querySelectorAll('.rank-btn');
89
+ buttons.forEach(btn => {
90
+ const btnRank = parseInt(btn.getAttribute('data-rank'));
91
+ if (btnRank === newRank && itemDocId === docId) {
92
+ btn.classList.add('selected');
93
+ } else if (btnRank !== newRank && itemDocId === docId) {
94
+ btn.classList.remove('selected');
95
+ }
96
+ });
97
+ });
98
+
99
+ // If we're not changing the rank, do nothing more
100
+ if (currentDocRank === newRank) {
101
+ updateOrderState();
102
+ return;
103
+ }
104
+
105
+ // Get the container and convert nodelist to array so we can manipulate it
106
+ const container = document.querySelector('.docs-container');
107
+ const itemsArray = Array.from(docItems);
108
+
109
+ // Remove the target doc from its current position
110
+ itemsArray.splice(currentDocRank - 1, 1);
111
+
112
+ // Insert it at the new rank position
113
+ itemsArray.splice(newRank - 1, 0, targetDocItem);
114
+
115
+ // Clear the container
116
+ while (container.firstChild) {
117
+ container.removeChild(container.firstChild);
118
+ }
119
+
120
+ // Add items back in their new order
121
+ itemsArray.forEach((item, index) => {
122
+ // Update the displayed rank number
123
+ const rankNumber = item.querySelector('.rank-number');
124
+ rankNumber.textContent = index + 1;
125
+
126
+ // Add back to container
127
+ container.appendChild(item);
128
+ });
129
+
130
+ // Update the hidden state for submission
131
+ updateOrderState();
132
+ }
133
+
134
+ function updateOrderState() {
135
+ // Get the current order of documents
136
+ const docItems = document.querySelectorAll('.doc-item');
137
+ const order = Array.from(docItems).map(item =>
138
+ parseInt(item.getAttribute('data-doc-id')));
139
+
140
+ // Find the input field and update its value
141
+ const orderInput = document.querySelector('#current-order textarea');
142
+ if (orderInput) {
143
+ orderInput.value = JSON.stringify(order);
144
+
145
+ // Trigger input event to notify Gradio
146
+ const event = new Event('input', { bubbles: true });
147
+ orderInput.dispatchEvent(event);
148
+ }
149
+ }
150
+
151
+ // Initialize the order state when the page loads
152
+ window.addEventListener('DOMContentLoaded', function() {
153
+ updateOrderState();
154
+ });
155
+
156
+ // Backup initialization for iframe environments
157
+ setTimeout(updateOrderState, 1000);
158
+ </script>
159
+ """
160
+
161
  return html
162
 
163
  def save_ranking(order_json, sample_id):
 
268
  save_btn = gr.Button("💾 Save All Results", variant="secondary")
269
 
270
  js_code = """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
271
  <style>
272
+ /* Simple and reliable ranking interface styles */
273
+ .ranking-container {
274
+ width: 100%;
275
+ max-width: 100%;
276
+ margin: 0 auto;
 
 
 
277
  }
278
+
279
+ .docs-container {
280
+ display: flex;
281
+ flex-direction: column;
282
+ gap: 15px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
283
  }
284
+
285
+ .doc-item {
286
+ display: flex;
287
+ padding: 15px;
288
+ background: white;
289
+ border: 1px solid #e0e0e0;
290
+ border-radius: 8px;
291
+ box-shadow: 0 2px 4px rgba(0,0,0,0.05);
292
  }
293
+
294
+ .rank-selector {
295
  display: flex;
296
  flex-direction: column;
297
  align-items: center;
298
  margin-right: 15px;
299
+ min-width: 40px;
300
  }
301
+
302
+ .rank-number {
303
+ font-size: 18px;
304
+ font-weight: bold;
305
+ width: 30px;
306
+ height: 30px;
307
+ display: flex;
308
+ align-items: center;
309
  justify-content: center;
310
+ background: #3b82f6;
311
+ color: white;
312
  border-radius: 50%;
313
+ margin-bottom: 8px;
314
+ }
315
+
316
+ .rank-buttons {
317
+ display: flex;
318
+ flex-wrap: wrap;
319
+ gap: 3px;
320
+ max-width: 120px;
321
+ justify-content: center;
322
  }
323
+
324
  .rank-btn {
325
+ width: 26px;
326
+ height: 26px;
327
+ padding: 0;
 
 
328
  font-size: 12px;
329
+ border: 1px solid #d1d5db;
330
+ background: #f9fafb;
331
+ border-radius: 4px;
332
+ cursor: pointer;
333
  display: flex;
334
  align-items: center;
335
  justify-content: center;
 
 
 
 
 
336
  }
337
+
338
+ .rank-btn:hover {
339
+ background: #e5e7eb;
340
  }
341
+
342
+ .rank-btn.selected {
343
+ background: #3b82f6;
344
+ color: white;
345
+ border-color: #2563eb;
346
  }
347
+
348
+ .doc-content {
349
+ flex: 1;
350
+ padding: 5px;
351
+ line-height: 1.5;
 
 
 
 
 
 
 
352
  }
353
+
354
+ /* Color coding the rank numbers by position */
355
+ .doc-item:nth-child(1) .rank-number { background-color: #10b981; /* green */ }
356
+ .doc-item:nth-child(2) .rank-number { background-color: #3b82f6; /* blue */ }
357
+ .doc-item:nth-child(3) .rank-number { background-color: #f59e0b; /* yellow */ }
358
+ .doc-item:nth-child(4) .rank-number { background-color: #ef4444; /* red */ }
359
+ .doc-item:nth-child(5) .rank-number { background-color: #8b5cf6; /* purple */ }
360
  </style>
361
  """
362
  gr.HTML(js_code)