Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -11,151 +11,89 @@ 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
|
|
|
|
|
15 |
if existing_ranks and len(existing_ranks) == len(candidates):
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
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
|
35 |
-
for i, doc in enumerate(
|
36 |
import html as html_escaper
|
37 |
-
escaped_doc = html_escaper.escape(doc
|
38 |
-
current_rank = i + 1
|
39 |
|
40 |
html += f"""
|
41 |
-
<div class="
|
42 |
<div class="rank-selector">
|
43 |
-
<
|
44 |
-
<div class="rank-buttons">
|
45 |
"""
|
46 |
|
47 |
-
# Add
|
48 |
-
for
|
49 |
-
selected = "selected" if
|
50 |
-
html += f'<
|
51 |
-
|
52 |
-
html += """
|
53 |
-
</
|
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 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
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 |
-
|
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(
|
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
|
152 |
-
|
153 |
-
|
154 |
-
});
|
155 |
|
156 |
// Backup initialization for iframe environments
|
157 |
-
setTimeout(
|
|
|
|
|
158 |
</script>
|
|
|
159 |
"""
|
160 |
|
161 |
return html
|
@@ -269,94 +207,50 @@ def create_reranking_interface(task_data):
|
|
269 |
|
270 |
js_code = """
|
271 |
<style>
|
272 |
-
/* Simple
|
273 |
-
.ranking-
|
274 |
width: 100%;
|
275 |
max-width: 100%;
|
276 |
margin: 0 auto;
|
277 |
}
|
278 |
|
279 |
-
.
|
280 |
-
|
281 |
-
|
282 |
-
|
|
|
|
|
283 |
}
|
284 |
|
285 |
-
.
|
286 |
display: flex;
|
287 |
-
|
|
|
|
|
288 |
background: white;
|
289 |
border: 1px solid #e0e0e0;
|
290 |
-
border-radius:
|
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:
|
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-
|
325 |
-
width:
|
326 |
-
|
327 |
-
padding: 0;
|
328 |
-
font-size: 12px;
|
329 |
border: 1px solid #d1d5db;
|
330 |
-
background: #f9fafb;
|
331 |
border-radius: 4px;
|
332 |
-
|
333 |
-
|
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)
|
@@ -627,7 +521,7 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
|
|
627 |
return filename
|
628 |
return None
|
629 |
|
630 |
-
# Update
|
631 |
def update_result_dropdown():
|
632 |
return gr.Dropdown.update(choices=[f for f in os.listdir(".") if f.endswith("_human_results.json")])
|
633 |
|
|
|
11 |
|
12 |
# Define helper functions before UI elements are created
|
13 |
def generate_sortable_html(candidates, existing_ranks=None):
|
14 |
+
"""Generate HTML with simple dropdowns for ranking."""
|
15 |
+
# Use existing ranks if available
|
16 |
+
ranks = [0] * len(candidates)
|
17 |
if existing_ranks and len(existing_ranks) == len(candidates):
|
18 |
+
ranks = existing_ranks.copy()
|
19 |
+
|
20 |
+
# Generate a unique ID for this set of dropdowns to avoid conflicts
|
21 |
+
import random
|
22 |
+
import time
|
23 |
+
dropdown_group_id = f"rank_group_{int(time.time())}_{random.randint(1000, 9999)}"
|
24 |
+
|
25 |
+
html = f"""
|
26 |
+
<div class="ranking-simple">
|
27 |
+
<input type="hidden" id="rank-order-state" value="">
|
28 |
+
<div class="rank-instructions">Select a rank (1-{len(candidates)}) for each document.</div>
|
|
|
|
|
|
|
|
|
|
|
29 |
"""
|
30 |
|
31 |
+
# Add each document with a dropdown selector
|
32 |
+
for i, doc in enumerate(candidates):
|
33 |
import html as html_escaper
|
34 |
+
escaped_doc = html_escaper.escape(doc)
|
35 |
+
current_rank = ranks[i] if ranks[i] > 0 else i + 1
|
36 |
|
37 |
html += f"""
|
38 |
+
<div class="rank-item" data-doc-id="{i}">
|
39 |
<div class="rank-selector">
|
40 |
+
<select class="rank-dropdown" data-doc-id="{i}" onchange="updateRankOrder('{dropdown_group_id}')">
|
|
|
41 |
"""
|
42 |
|
43 |
+
# Add options 1 through N
|
44 |
+
for rank in range(1, len(candidates) + 1):
|
45 |
+
selected = "selected" if rank == current_rank else ""
|
46 |
+
html += f'<option value="{rank}" {selected}>{rank}</option>'
|
47 |
+
|
48 |
+
html += f"""
|
49 |
+
</select>
|
|
|
|
|
|
|
50 |
</div>
|
51 |
+
<div class="doc-content">{escaped_doc}</div>
|
52 |
</div>
|
53 |
"""
|
|
|
|
|
|
|
|
|
54 |
|
55 |
+
# Add the JavaScript for handling rank updates
|
56 |
+
html += f"""
|
57 |
<script>
|
58 |
+
// Function to update the hidden state when dropdowns change
|
59 |
+
function updateRankOrder(groupId) {{
|
60 |
+
const items = document.querySelectorAll('.rank-item');
|
61 |
+
const selectedRanks = new Map();
|
62 |
+
const docOrder = [];
|
63 |
+
|
64 |
+
// First collect all selected ranks
|
65 |
+
items.forEach(item => {{
|
66 |
+
const docId = parseInt(item.getAttribute('data-doc-id'));
|
67 |
+
const dropdown = item.querySelector('.rank-dropdown');
|
68 |
+
const rank = parseInt(dropdown.value);
|
69 |
+
selectedRanks.set(docId, rank);
|
70 |
+
}});
|
71 |
+
|
72 |
+
// Sort documents by their selected rank
|
73 |
+
const sortedDocs = Array.from(selectedRanks.entries())
|
74 |
+
.sort((a, b) => a[1] - b[1])
|
75 |
+
.map(entry => entry[0]);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
76 |
|
77 |
+
// Update the order state
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
78 |
const orderInput = document.querySelector('#current-order textarea');
|
79 |
+
if (orderInput) {{
|
80 |
+
orderInput.value = JSON.stringify(sortedDocs);
|
81 |
+
const event = new Event('input', {{ bubbles: true }});
|
|
|
|
|
82 |
orderInput.dispatchEvent(event);
|
83 |
+
}}
|
84 |
+
}}
|
85 |
|
86 |
+
// Initialize on page load
|
87 |
+
document.addEventListener('DOMContentLoaded', function() {{
|
88 |
+
updateRankOrder('{dropdown_group_id}');
|
89 |
+
}});
|
90 |
|
91 |
// Backup initialization for iframe environments
|
92 |
+
setTimeout(function() {{
|
93 |
+
updateRankOrder('{dropdown_group_id}');
|
94 |
+
}}, 1000);
|
95 |
</script>
|
96 |
+
</div>
|
97 |
"""
|
98 |
|
99 |
return html
|
|
|
207 |
|
208 |
js_code = """
|
209 |
<style>
|
210 |
+
/* Simple dropdown ranking styles */
|
211 |
+
.ranking-simple {
|
212 |
width: 100%;
|
213 |
max-width: 100%;
|
214 |
margin: 0 auto;
|
215 |
}
|
216 |
|
217 |
+
.rank-instructions {
|
218 |
+
margin-bottom: 15px;
|
219 |
+
padding: 10px;
|
220 |
+
background-color: #f0f9ff;
|
221 |
+
border-left: 4px solid #3b82f6;
|
222 |
+
border-radius: 4px;
|
223 |
}
|
224 |
|
225 |
+
.rank-item {
|
226 |
display: flex;
|
227 |
+
align-items: flex-start;
|
228 |
+
padding: 12px;
|
229 |
+
margin-bottom: 10px;
|
230 |
background: white;
|
231 |
border: 1px solid #e0e0e0;
|
232 |
+
border-radius: 6px;
|
|
|
233 |
}
|
234 |
|
235 |
.rank-selector {
|
|
|
|
|
|
|
236 |
margin-right: 15px;
|
237 |
+
min-width: 70px;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
238 |
}
|
239 |
|
240 |
+
.rank-dropdown {
|
241 |
+
width: 60px;
|
242 |
+
padding: 6px;
|
|
|
|
|
243 |
border: 1px solid #d1d5db;
|
|
|
244 |
border-radius: 4px;
|
245 |
+
background-color: white;
|
246 |
+
font-size: 14px;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
247 |
}
|
248 |
|
249 |
.doc-content {
|
250 |
flex: 1;
|
|
|
251 |
line-height: 1.5;
|
252 |
+
padding: 5px 0;
|
253 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
254 |
</style>
|
255 |
"""
|
256 |
gr.HTML(js_code)
|
|
|
521 |
return filename
|
522 |
return None
|
523 |
|
524 |
+
# Update dropdown when refreshing results
|
525 |
def update_result_dropdown():
|
526 |
return gr.Dropdown.update(choices=[f for f in os.listdir(".") if f.endswith("_human_results.json")])
|
527 |
|