Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -11,7 +11,7 @@ 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."""
|
15 |
if existing_ranks and len(existing_ranks) == len(candidates):
|
16 |
order = sorted(range(len(candidates)), key=lambda i: existing_ranks[i])
|
17 |
else:
|
@@ -24,9 +24,18 @@ def create_reranking_interface(task_data):
|
|
24 |
rank = rank_minus_1 + 1
|
25 |
import html as html_escaper
|
26 |
escaped_doc = html_escaper.escape(doc)
|
|
|
|
|
|
|
|
|
|
|
27 |
html += f'''\
|
28 |
-
<div class="sortable-item rank-bg-{rank}" data-doc-id="{idx}">
|
29 |
-
<div class="rank-
|
|
|
|
|
|
|
|
|
30 |
<div class="doc-content">{escaped_doc}</div>
|
31 |
</div>
|
32 |
'''
|
@@ -143,8 +152,115 @@ def create_reranking_interface(task_data):
|
|
143 |
js_code = """
|
144 |
<script src="https://cdn.jsdelivr.net/npm/sortablejs@1.15.0/Sortable.min.js"></script>
|
145 |
<script>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
146 |
document.addEventListener('DOMContentLoaded', function() {
|
147 |
function initializeSortable() {
|
|
|
|
|
|
|
|
|
148 |
const container = document.getElementById('sortable-container');
|
149 |
if (!container) {
|
150 |
console.log('Container not found, retrying...');
|
@@ -166,37 +282,9 @@ def create_reranking_interface(task_data):
|
|
166 |
animation: 150,
|
167 |
ghostClass: "sortable-ghost",
|
168 |
onEnd: function() {
|
169 |
-
|
170 |
}
|
171 |
});
|
172 |
-
|
173 |
-
updateRanksAndSyncState();
|
174 |
-
}
|
175 |
-
|
176 |
-
function updateRanksAndSyncState() {
|
177 |
-
const container = document.getElementById('sortable-container');
|
178 |
-
if (!container) return;
|
179 |
-
|
180 |
-
const items = container.querySelectorAll('.sortable-item');
|
181 |
-
const orderInput = document.querySelector('#current-order textarea');
|
182 |
-
if (!orderInput) return;
|
183 |
-
|
184 |
-
const order = [];
|
185 |
-
items.forEach((item, index) => {
|
186 |
-
const rankBadge = item.querySelector('.rank-badge');
|
187 |
-
if (rankBadge) rankBadge.textContent = (index + 1);
|
188 |
-
item.className = item.className.replace(/rank-bg-\d+/g, '').trim();
|
189 |
-
item.classList.add(`rank-bg-${index + 1}`);
|
190 |
-
const docId = parseInt(item.getAttribute('data-doc-id'));
|
191 |
-
order.push(docId);
|
192 |
-
});
|
193 |
-
|
194 |
-
const newOrderValue = JSON.stringify(order);
|
195 |
-
if (orderInput.value !== newOrderValue) {
|
196 |
-
orderInput.value = newOrderValue;
|
197 |
-
const event = new Event('input', { bubbles: true });
|
198 |
-
orderInput.dispatchEvent(event);
|
199 |
-
}
|
200 |
}
|
201 |
|
202 |
// Initialize immediately and also set up a mutation observer
|
@@ -252,6 +340,12 @@ def create_reranking_interface(task_data):
|
|
252 |
cursor: grabbing;
|
253 |
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
|
254 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
255 |
.rank-badge {
|
256 |
display: flex;
|
257 |
align-items: center;
|
@@ -262,9 +356,30 @@ def create_reranking_interface(task_data):
|
|
262 |
background-color: #6c757d;
|
263 |
color: white;
|
264 |
font-weight: bold;
|
265 |
-
margin
|
266 |
flex-shrink: 0;
|
267 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
268 |
.doc-content {
|
269 |
flex: 1;
|
270 |
line-height: 1.4;
|
|
|
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:
|
|
|
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 |
'''
|
|
|
152 |
js_code = """
|
153 |
<script src="https://cdn.jsdelivr.net/npm/sortablejs@1.15.0/Sortable.min.js"></script>
|
154 |
<script>
|
155 |
+
// Function to move an item up in the ranking
|
156 |
+
function moveItemUp(currentRank) {
|
157 |
+
if (currentRank <= 1) return; // Already at the top
|
158 |
+
|
159 |
+
const container = document.getElementById('sortable-container');
|
160 |
+
if (!container) return;
|
161 |
+
|
162 |
+
const items = container.querySelectorAll('.sortable-item');
|
163 |
+
const itemsArray = Array.from(items);
|
164 |
+
|
165 |
+
// Find the items to swap
|
166 |
+
const currentItem = itemsArray.find(item => item.getAttribute('data-rank') == currentRank);
|
167 |
+
const aboveItem = itemsArray.find(item => item.getAttribute('data-rank') == currentRank - 1);
|
168 |
+
|
169 |
+
if (!currentItem || !aboveItem) return;
|
170 |
+
|
171 |
+
// Swap the items
|
172 |
+
aboveItem.parentNode.insertBefore(currentItem, aboveItem);
|
173 |
+
|
174 |
+
// Update ranks
|
175 |
+
updateRanksAfterMove();
|
176 |
+
}
|
177 |
+
|
178 |
+
// Function to move an item down in the ranking
|
179 |
+
function moveItemDown(currentRank) {
|
180 |
+
const container = document.getElementById('sortable-container');
|
181 |
+
if (!container) return;
|
182 |
+
|
183 |
+
const items = container.querySelectorAll('.sortable-item');
|
184 |
+
if (currentRank >= items.length) return; // Already at the bottom
|
185 |
+
|
186 |
+
const itemsArray = Array.from(items);
|
187 |
+
|
188 |
+
// Find the items to swap
|
189 |
+
const currentItem = itemsArray.find(item => item.getAttribute('data-rank') == currentRank);
|
190 |
+
const belowItem = itemsArray.find(item => item.getAttribute('data-rank') == currentRank + 1);
|
191 |
+
|
192 |
+
if (!currentItem || !belowItem) return;
|
193 |
+
|
194 |
+
// Swap the items
|
195 |
+
belowItem.parentNode.insertBefore(belowItem, currentItem);
|
196 |
+
|
197 |
+
// Update ranks
|
198 |
+
updateRanksAfterMove();
|
199 |
+
}
|
200 |
+
|
201 |
+
// Update rank numbers and classes after moving
|
202 |
+
function updateRanksAfterMove() {
|
203 |
+
const container = document.getElementById('sortable-container');
|
204 |
+
if (!container) return;
|
205 |
+
|
206 |
+
const items = container.querySelectorAll('.sortable-item');
|
207 |
+
const orderInput = document.querySelector('#current-order textarea');
|
208 |
+
if (!orderInput) return;
|
209 |
+
|
210 |
+
const order = [];
|
211 |
+
items.forEach((item, index) => {
|
212 |
+
const rank = index + 1;
|
213 |
+
const docId = parseInt(item.getAttribute('data-doc-id'));
|
214 |
+
|
215 |
+
// Update rank display
|
216 |
+
const rankBadge = item.querySelector('.rank-badge');
|
217 |
+
if (rankBadge) rankBadge.textContent = rank;
|
218 |
+
|
219 |
+
// Update item classes
|
220 |
+
item.className = item.className.replace(/rank-bg-\d+/g, '').trim();
|
221 |
+
item.classList.add(`rank-bg-${rank}`);
|
222 |
+
|
223 |
+
// Update data attribute
|
224 |
+
item.setAttribute('data-rank', rank);
|
225 |
+
|
226 |
+
// Update button states
|
227 |
+
const upBtn = item.querySelector('.up-btn');
|
228 |
+
const downBtn = item.querySelector('.down-btn');
|
229 |
+
|
230 |
+
if (upBtn) {
|
231 |
+
if (rank == 1) {
|
232 |
+
upBtn.setAttribute('disabled', 'disabled');
|
233 |
+
} else {
|
234 |
+
upBtn.removeAttribute('disabled');
|
235 |
+
}
|
236 |
+
}
|
237 |
+
|
238 |
+
if (downBtn) {
|
239 |
+
if (rank == items.length) {
|
240 |
+
downBtn.setAttribute('disabled', 'disabled');
|
241 |
+
} else {
|
242 |
+
downBtn.removeAttribute('disabled');
|
243 |
+
}
|
244 |
+
}
|
245 |
+
|
246 |
+
order.push(docId);
|
247 |
+
});
|
248 |
+
|
249 |
+
// Update hidden input
|
250 |
+
const newOrderValue = JSON.stringify(order);
|
251 |
+
if (orderInput.value !== newOrderValue) {
|
252 |
+
orderInput.value = newOrderValue;
|
253 |
+
const event = new Event('input', { bubbles: true });
|
254 |
+
orderInput.dispatchEvent(event);
|
255 |
+
}
|
256 |
+
}
|
257 |
+
|
258 |
document.addEventListener('DOMContentLoaded', function() {
|
259 |
function initializeSortable() {
|
260 |
+
// Initialize event handlers for buttons
|
261 |
+
updateRanksAfterMove();
|
262 |
+
|
263 |
+
// Keep drag-and-drop as a fallback
|
264 |
const container = document.getElementById('sortable-container');
|
265 |
if (!container) {
|
266 |
console.log('Container not found, retrying...');
|
|
|
282 |
animation: 150,
|
283 |
ghostClass: "sortable-ghost",
|
284 |
onEnd: function() {
|
285 |
+
updateRanksAfterMove();
|
286 |
}
|
287 |
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
288 |
}
|
289 |
|
290 |
// Initialize immediately and also set up a mutation observer
|
|
|
340 |
cursor: grabbing;
|
341 |
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
|
342 |
}
|
343 |
+
.rank-controls {
|
344 |
+
display: flex;
|
345 |
+
flex-direction: column;
|
346 |
+
align-items: center;
|
347 |
+
margin-right: 15px;
|
348 |
+
}
|
349 |
.rank-badge {
|
350 |
display: flex;
|
351 |
align-items: center;
|
|
|
356 |
background-color: #6c757d;
|
357 |
color: white;
|
358 |
font-weight: bold;
|
359 |
+
margin: 4px 0;
|
360 |
flex-shrink: 0;
|
361 |
}
|
362 |
+
.rank-btn {
|
363 |
+
border: none;
|
364 |
+
background: #f0f0f0;
|
365 |
+
border-radius: 4px;
|
366 |
+
width: 24px;
|
367 |
+
height: 24px;
|
368 |
+
font-size: 12px;
|
369 |
+
line-height: 1;
|
370 |
+
display: flex;
|
371 |
+
align-items: center;
|
372 |
+
justify-content: center;
|
373 |
+
cursor: pointer;
|
374 |
+
color: #333;
|
375 |
+
}
|
376 |
+
.rank-btn:hover:not([disabled]) {
|
377 |
+
background: #e0e0e0;
|
378 |
+
}
|
379 |
+
.rank-btn:disabled {
|
380 |
+
opacity: 0.5;
|
381 |
+
cursor: not-allowed;
|
382 |
+
}
|
383 |
.doc-content {
|
384 |
flex: 1;
|
385 |
line-height: 1.4;
|