Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -32,9 +32,9 @@ def create_reranking_interface(task_data):
|
|
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>
|
@@ -152,60 +152,88 @@ def create_reranking_interface(task_data):
|
|
152 |
js_code = """
|
153 |
<script src="https://cdn.jsdelivr.net/npm/sortablejs@1.15.0/Sortable.min.js"></script>
|
154 |
<script>
|
155 |
-
//
|
156 |
-
function
|
|
|
157 |
if (currentRank <= 1) return; // Already at the top
|
158 |
|
159 |
const container = document.getElementById('sortable-container');
|
160 |
-
if (!container)
|
|
|
|
|
|
|
161 |
|
162 |
-
const items = container.querySelectorAll('.sortable-item');
|
163 |
-
|
164 |
|
165 |
-
// Find the items to swap
|
166 |
-
const currentItem =
|
167 |
-
const aboveItem =
|
168 |
|
169 |
-
if (!currentItem || !aboveItem)
|
|
|
|
|
|
|
170 |
|
171 |
-
|
172 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
173 |
|
174 |
// Update ranks
|
175 |
-
updateRanksAfterMove();
|
176 |
-
}
|
177 |
|
178 |
-
|
179 |
-
|
|
|
180 |
const container = document.getElementById('sortable-container');
|
181 |
-
if (!container)
|
|
|
|
|
|
|
|
|
|
|
|
|
182 |
|
183 |
-
const items = container.querySelectorAll('.sortable-item');
|
184 |
if (currentRank >= items.length) return; // Already at the bottom
|
185 |
|
186 |
-
|
|
|
|
|
187 |
|
188 |
-
|
189 |
-
|
190 |
-
|
|
|
191 |
|
192 |
-
|
193 |
|
194 |
-
// Swap the items
|
195 |
-
|
196 |
|
197 |
// Update ranks
|
198 |
-
updateRanksAfterMove();
|
199 |
-
}
|
200 |
|
201 |
-
|
202 |
-
|
203 |
const container = document.getElementById('sortable-container');
|
204 |
-
if (!container)
|
|
|
|
|
|
|
205 |
|
206 |
-
const items = container.querySelectorAll('.sortable-item');
|
207 |
const orderInput = document.querySelector('#current-order textarea');
|
208 |
-
if (!orderInput)
|
|
|
|
|
|
|
209 |
|
210 |
const order = [];
|
211 |
items.forEach((item, index) => {
|
@@ -217,7 +245,7 @@ def create_reranking_interface(task_data):
|
|
217 |
if (rankBadge) rankBadge.textContent = rank;
|
218 |
|
219 |
// Update item classes
|
220 |
-
item.className = item.className.replace(/rank-bg
|
221 |
item.classList.add(`rank-bg-${rank}`);
|
222 |
|
223 |
// Update data attribute
|
@@ -246,50 +274,73 @@ def create_reranking_interface(task_data):
|
|
246 |
order.push(docId);
|
247 |
});
|
248 |
|
249 |
-
// Update hidden input
|
|
|
250 |
const newOrderValue = JSON.stringify(order);
|
251 |
-
|
252 |
-
|
253 |
-
|
254 |
-
|
255 |
-
|
256 |
-
}
|
257 |
|
258 |
document.addEventListener('DOMContentLoaded', function() {
|
259 |
-
|
260 |
-
|
261 |
-
|
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
|
267 |
-
setTimeout(
|
268 |
return;
|
269 |
}
|
270 |
|
271 |
-
|
272 |
-
console.log('Sortable not loaded, retrying...');
|
273 |
-
setTimeout(initializeSortable, 200);
|
274 |
-
return;
|
275 |
-
}
|
276 |
|
277 |
-
|
278 |
-
|
279 |
-
|
280 |
|
281 |
-
|
282 |
-
|
283 |
-
|
284 |
-
|
285 |
-
|
286 |
-
}
|
287 |
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
288 |
}
|
289 |
|
290 |
-
// Initialize immediately
|
291 |
-
|
292 |
|
|
|
293 |
const targetNode = document.getElementById('sortable-list-container');
|
294 |
if (targetNode) {
|
295 |
const config = { childList: true, subtree: true };
|
@@ -297,7 +348,8 @@ def create_reranking_interface(task_data):
|
|
297 |
for(const mutation of mutationsList) {
|
298 |
if (mutation.type === 'childList') {
|
299 |
if (document.getElementById('sortable-container')) {
|
300 |
-
|
|
|
301 |
}
|
302 |
}
|
303 |
}
|
@@ -310,41 +362,30 @@ def create_reranking_interface(task_data):
|
|
310 |
.sortable-container {
|
311 |
display: flex;
|
312 |
flex-direction: column;
|
313 |
-
gap:
|
314 |
min-height: 200px;
|
315 |
-
padding:
|
316 |
background-color: #f8f9fa;
|
317 |
border-radius: 8px;
|
318 |
}
|
319 |
.sortable-item {
|
320 |
-
padding:
|
321 |
background-color: #fff;
|
322 |
border: 1px solid #e0e0e0;
|
323 |
border-radius: 6px;
|
324 |
-
cursor: grab;
|
325 |
display: flex;
|
326 |
align-items: center;
|
327 |
transition: all 0.2s ease;
|
328 |
-
user-select: none;
|
329 |
}
|
330 |
.sortable-item:hover {
|
331 |
background-color: #f8f9fa;
|
332 |
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
333 |
}
|
334 |
-
.sortable-ghost {
|
335 |
-
background-color: #e3f2fd !important;
|
336 |
-
border-style: dashed !important;
|
337 |
-
opacity: 0.8;
|
338 |
-
}
|
339 |
-
.sortable-chosen {
|
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:
|
348 |
}
|
349 |
.rank-badge {
|
350 |
display: flex;
|
@@ -356,25 +397,27 @@ def create_reranking_interface(task_data):
|
|
356 |
background-color: #6c757d;
|
357 |
color: white;
|
358 |
font-weight: bold;
|
359 |
-
margin:
|
360 |
flex-shrink: 0;
|
361 |
}
|
362 |
.rank-btn {
|
|
|
|
|
363 |
border: none;
|
364 |
-
background: #f0f0f0;
|
365 |
border-radius: 4px;
|
366 |
-
|
367 |
-
|
368 |
-
font-size: 12px;
|
369 |
-
line-height: 1;
|
370 |
display: flex;
|
371 |
align-items: center;
|
372 |
justify-content: center;
|
373 |
-
|
374 |
-
color: #333;
|
375 |
}
|
376 |
.rank-btn:hover:not([disabled]) {
|
377 |
-
background: #e0e0e0;
|
|
|
|
|
|
|
378 |
}
|
379 |
.rank-btn:disabled {
|
380 |
opacity: 0.5;
|
@@ -382,7 +425,7 @@ def create_reranking_interface(task_data):
|
|
382 |
}
|
383 |
.doc-content {
|
384 |
flex: 1;
|
385 |
-
line-height: 1.
|
386 |
word-break: break-word;
|
387 |
}
|
388 |
.rank-bg-1 .rank-badge { background-color: #198754; }
|
|
|
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 type="button" class="rank-btn up-btn" {up_disabled} onclick="window.moveItemUp({rank})">▲</button>
|
36 |
<div class="rank-badge">{rank}</div>
|
37 |
+
<button type="button" class="rank-btn down-btn" {down_disabled} onclick="window.moveItemDown({rank})">▼</button>
|
38 |
</div>
|
39 |
<div class="doc-content">{escaped_doc}</div>
|
40 |
</div>
|
|
|
152 |
js_code = """
|
153 |
<script src="https://cdn.jsdelivr.net/npm/sortablejs@1.15.0/Sortable.min.js"></script>
|
154 |
<script>
|
155 |
+
// Make the functions 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 = Array.from(container.querySelectorAll('.sortable-item'));
|
167 |
+
console.log('Found items:', items.length);
|
168 |
|
169 |
+
// Find the items to swap by their data-rank attribute
|
170 |
+
const currentItem = items.find(item => parseInt(item.getAttribute('data-rank')) === currentRank);
|
171 |
+
const aboveItem = items.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 |
+
console.log('Swapping items:', currentItem, aboveItem);
|
179 |
+
|
180 |
+
// Swap the items in the DOM
|
181 |
+
if (aboveItem.previousElementSibling) {
|
182 |
+
container.insertBefore(currentItem, aboveItem);
|
183 |
+
} else {
|
184 |
+
container.insertBefore(currentItem, container.firstChild);
|
185 |
+
}
|
186 |
|
187 |
// Update ranks
|
188 |
+
window.updateRanksAfterMove();
|
189 |
+
};
|
190 |
|
191 |
+
window.moveItemDown = function(currentRank) {
|
192 |
+
console.log('Moving item down:', currentRank);
|
193 |
+
|
194 |
const container = document.getElementById('sortable-container');
|
195 |
+
if (!container) {
|
196 |
+
console.error('Container not found');
|
197 |
+
return;
|
198 |
+
}
|
199 |
+
|
200 |
+
const items = Array.from(container.querySelectorAll('.sortable-item'));
|
201 |
+
console.log('Found items:', items.length);
|
202 |
|
|
|
203 |
if (currentRank >= items.length) return; // Already at the bottom
|
204 |
|
205 |
+
// Find the items to swap by their data-rank attribute
|
206 |
+
const currentItem = items.find(item => parseInt(item.getAttribute('data-rank')) === currentRank);
|
207 |
+
const belowItem = items.find(item => parseInt(item.getAttribute('data-rank')) === currentRank + 1);
|
208 |
|
209 |
+
if (!currentItem || !belowItem) {
|
210 |
+
console.error('Items not found for moving down');
|
211 |
+
return;
|
212 |
+
}
|
213 |
|
214 |
+
console.log('Swapping items down:', currentItem, belowItem);
|
215 |
|
216 |
+
// Swap the items in the DOM - insert the current item after the below item
|
217 |
+
container.insertBefore(currentItem, belowItem.nextElementSibling);
|
218 |
|
219 |
// Update ranks
|
220 |
+
window.updateRanksAfterMove();
|
221 |
+
};
|
222 |
|
223 |
+
window.updateRanksAfterMove = function() {
|
224 |
+
console.log('Updating ranks');
|
225 |
const container = document.getElementById('sortable-container');
|
226 |
+
if (!container) {
|
227 |
+
console.error('Container not found for rank update');
|
228 |
+
return;
|
229 |
+
}
|
230 |
|
231 |
+
const items = Array.from(container.querySelectorAll('.sortable-item'));
|
232 |
const orderInput = document.querySelector('#current-order textarea');
|
233 |
+
if (!orderInput) {
|
234 |
+
console.error('Order input not found');
|
235 |
+
return;
|
236 |
+
}
|
237 |
|
238 |
const order = [];
|
239 |
items.forEach((item, index) => {
|
|
|
245 |
if (rankBadge) rankBadge.textContent = rank;
|
246 |
|
247 |
// Update item classes
|
248 |
+
item.className = item.className.replace(/rank-bg-\\d+/g, '').trim();
|
249 |
item.classList.add(`rank-bg-${rank}`);
|
250 |
|
251 |
// Update data attribute
|
|
|
274 |
order.push(docId);
|
275 |
});
|
276 |
|
277 |
+
// Update hidden input with JSON
|
278 |
+
console.log('New order:', order);
|
279 |
const newOrderValue = JSON.stringify(order);
|
280 |
+
orderInput.value = newOrderValue;
|
281 |
+
|
282 |
+
// Trigger input event
|
283 |
+
const event = new Event('input', { bubbles: true });
|
284 |
+
orderInput.dispatchEvent(event);
|
285 |
+
};
|
286 |
|
287 |
document.addEventListener('DOMContentLoaded', function() {
|
288 |
+
console.log('DOM loaded, initializing ranking interface');
|
289 |
+
|
290 |
+
// Function to initialize the interface
|
291 |
+
function initializeRankingInterface() {
|
|
|
292 |
const container = document.getElementById('sortable-container');
|
293 |
if (!container) {
|
294 |
+
console.log('Container not found, retrying in 200ms');
|
295 |
+
setTimeout(initializeRankingInterface, 200);
|
296 |
return;
|
297 |
}
|
298 |
|
299 |
+
console.log('Sortable container found, setting up');
|
|
|
|
|
|
|
|
|
300 |
|
301 |
+
// Add click events directly to buttons as a backup
|
302 |
+
const upButtons = container.querySelectorAll('.up-btn');
|
303 |
+
const downButtons = container.querySelectorAll('.down-btn');
|
304 |
|
305 |
+
upButtons.forEach(btn => {
|
306 |
+
btn.addEventListener('click', function() {
|
307 |
+
const item = this.closest('.sortable-item');
|
308 |
+
const rank = parseInt(item.getAttribute('data-rank'));
|
309 |
+
window.moveItemUp(rank);
|
310 |
+
});
|
311 |
});
|
312 |
+
|
313 |
+
downButtons.forEach(btn => {
|
314 |
+
btn.addEventListener('click', function() {
|
315 |
+
const item = this.closest('.sortable-item');
|
316 |
+
const rank = parseInt(item.getAttribute('data-rank'));
|
317 |
+
window.moveItemDown(rank);
|
318 |
+
});
|
319 |
+
});
|
320 |
+
|
321 |
+
// Initialize drag-and-drop as fallback
|
322 |
+
if (typeof Sortable !== 'undefined') {
|
323 |
+
if (!container.sortableInstance) {
|
324 |
+
container.sortableInstance = new Sortable(container, {
|
325 |
+
animation: 150,
|
326 |
+
ghostClass: "sortable-ghost",
|
327 |
+
onEnd: function() {
|
328 |
+
window.updateRanksAfterMove();
|
329 |
+
}
|
330 |
+
});
|
331 |
+
}
|
332 |
+
} else {
|
333 |
+
console.log('Sortable library not available');
|
334 |
+
}
|
335 |
+
|
336 |
+
// Initialize the ranking
|
337 |
+
window.updateRanksAfterMove();
|
338 |
}
|
339 |
|
340 |
+
// Initialize immediately
|
341 |
+
initializeRankingInterface();
|
342 |
|
343 |
+
// Also observe DOM changes to reinitialize when needed
|
344 |
const targetNode = document.getElementById('sortable-list-container');
|
345 |
if (targetNode) {
|
346 |
const config = { childList: true, subtree: true };
|
|
|
348 |
for(const mutation of mutationsList) {
|
349 |
if (mutation.type === 'childList') {
|
350 |
if (document.getElementById('sortable-container')) {
|
351 |
+
console.log('DOM changed, reinitializing');
|
352 |
+
initializeRankingInterface();
|
353 |
}
|
354 |
}
|
355 |
}
|
|
|
362 |
.sortable-container {
|
363 |
display: flex;
|
364 |
flex-direction: column;
|
365 |
+
gap: 12px;
|
366 |
min-height: 200px;
|
367 |
+
padding: 16px;
|
368 |
background-color: #f8f9fa;
|
369 |
border-radius: 8px;
|
370 |
}
|
371 |
.sortable-item {
|
372 |
+
padding: 14px;
|
373 |
background-color: #fff;
|
374 |
border: 1px solid #e0e0e0;
|
375 |
border-radius: 6px;
|
|
|
376 |
display: flex;
|
377 |
align-items: center;
|
378 |
transition: all 0.2s ease;
|
|
|
379 |
}
|
380 |
.sortable-item:hover {
|
381 |
background-color: #f8f9fa;
|
382 |
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
383 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
384 |
.rank-controls {
|
385 |
+
display: flex;
|
386 |
flex-direction: column;
|
387 |
align-items: center;
|
388 |
+
margin-right: 16px;
|
389 |
}
|
390 |
.rank-badge {
|
391 |
display: flex;
|
|
|
397 |
background-color: #6c757d;
|
398 |
color: white;
|
399 |
font-weight: bold;
|
400 |
+
margin: 6px 0;
|
401 |
flex-shrink: 0;
|
402 |
}
|
403 |
.rank-btn {
|
404 |
+
width: 28px;
|
405 |
+
height: 28px;
|
406 |
border: none;
|
407 |
+
background-color: #f0f0f0;
|
408 |
border-radius: 4px;
|
409 |
+
margin: 2px 0;
|
410 |
+
cursor: pointer;
|
|
|
|
|
411 |
display: flex;
|
412 |
align-items: center;
|
413 |
justify-content: center;
|
414 |
+
font-size: 14px;
|
|
|
415 |
}
|
416 |
.rank-btn:hover:not([disabled]) {
|
417 |
+
background-color: #e0e0e0;
|
418 |
+
}
|
419 |
+
.rank-btn:active:not([disabled]) {
|
420 |
+
background-color: #d0d0d0;
|
421 |
}
|
422 |
.rank-btn:disabled {
|
423 |
opacity: 0.5;
|
|
|
425 |
}
|
426 |
.doc-content {
|
427 |
flex: 1;
|
428 |
+
line-height: 1.5;
|
429 |
word-break: break-word;
|
430 |
}
|
431 |
.rank-bg-1 .rank-badge { background-color: #198754; }
|