Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -301,61 +301,7 @@ def create_reranking_interface(task_data):
|
|
301 |
margin: 0 auto;
|
302 |
font-size: 12px !important;
|
303 |
}
|
304 |
-
|
305 |
-
/* Add styling for validation button */
|
306 |
-
.validate-btn {
|
307 |
-
margin-top: 10px !important;
|
308 |
-
width: 100% !important;
|
309 |
-
}
|
310 |
-
|
311 |
-
/* Add loading animation for dropdowns */
|
312 |
-
.rank-dropdown.loading select {
|
313 |
-
background-color: #f0f0f0 !important;
|
314 |
-
cursor: wait !important;
|
315 |
-
}
|
316 |
-
|
317 |
-
/* Quick tools improvements */
|
318 |
-
.tools-container button {
|
319 |
-
transition: all 0.3s ease !important;
|
320 |
-
}
|
321 |
-
|
322 |
-
.tools-container button:active {
|
323 |
-
transform: scale(0.98) !important;
|
324 |
-
}
|
325 |
-
|
326 |
-
/* Add this to make dropdown updates more responsive */
|
327 |
-
.rank-dropdown select {
|
328 |
-
transition: background-color 0.2s ease !important;
|
329 |
-
}
|
330 |
</style>
|
331 |
-
|
332 |
-
<script>
|
333 |
-
// Add simple loading state indication for quick rank buttons
|
334 |
-
function addLoadingBehavior() {
|
335 |
-
const quickButtons = document.querySelectorAll('.tools-container button');
|
336 |
-
if (quickButtons) {
|
337 |
-
quickButtons.forEach(btn => {
|
338 |
-
btn.addEventListener('click', function() {
|
339 |
-
// Show loading state in the button
|
340 |
-
const originalText = this.textContent;
|
341 |
-
this.textContent = "Applying...";
|
342 |
-
|
343 |
-
// Reset after a short delay
|
344 |
-
setTimeout(() => {
|
345 |
-
this.textContent = originalText;
|
346 |
-
}, 1000);
|
347 |
-
});
|
348 |
-
});
|
349 |
-
}
|
350 |
-
}
|
351 |
-
|
352 |
-
// Run when DOM is loaded
|
353 |
-
if (document.readyState === 'loading') {
|
354 |
-
document.addEventListener('DOMContentLoaded', addLoadingBehavior);
|
355 |
-
} else {
|
356 |
-
addLoadingBehavior();
|
357 |
-
}
|
358 |
-
</script>
|
359 |
""")
|
360 |
|
361 |
def validate_rankings(*rankings):
|
@@ -440,6 +386,46 @@ def create_reranking_interface(task_data):
|
|
440 |
|
441 |
return status, progress
|
442 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
443 |
def auto_save_and_navigate(direction, current_id, auto_save, *rankings):
|
444 |
"""Save rankings if auto-save is enabled, then navigate."""
|
445 |
# Extract rankings (remove validation indicators)
|
@@ -499,143 +485,74 @@ def create_reranking_interface(task_data):
|
|
499 |
except Exception as e:
|
500 |
return f"Error saving results: {str(e)}"
|
501 |
|
502 |
-
#
|
503 |
-
def handle_next(current_id, auto_save, *rankings):
|
504 |
-
"""Optimized handler for next query navigation."""
|
505 |
-
# First, handle auto-save (only if needed)
|
506 |
-
new_id, status, progress = auto_save_and_navigate("next", current_id, auto_save, *rankings)
|
507 |
-
|
508 |
-
# Return the new sample ID for a more efficient update
|
509 |
-
# The load_sample function will handle all UI updates in one batch
|
510 |
-
return new_id
|
511 |
-
|
512 |
-
def handle_prev(current_id, auto_save, *rankings):
|
513 |
-
"""Optimized handler for previous query navigation."""
|
514 |
-
# First, handle auto-save (only if needed)
|
515 |
-
new_id, status, progress = auto_save_and_navigate("prev", current_id, auto_save, *rankings)
|
516 |
-
|
517 |
-
# Return the new sample ID for a more efficient update
|
518 |
-
# The load_sample function will handle all UI updates in one batch
|
519 |
-
return new_id
|
520 |
-
|
521 |
-
# Optimize the load_sample function to handle all UI updates efficiently
|
522 |
-
def load_sample(sample_id):
|
523 |
-
"""Load a specific sample into the interface."""
|
524 |
-
# Clear all validation indicators first for a cleaner transition
|
525 |
-
blank_validations = [""] * len(validation_indicators)
|
526 |
-
|
527 |
-
# Find the sample data
|
528 |
-
sample = next((s for s in samples if s["id"] == sample_id), None)
|
529 |
-
if not sample:
|
530 |
-
return [query_text.value] + [d.value for d in doc_containers] + [""] * len(ranking_inputs) + blank_validations + [sample_id, progress_text.value, status_box.value]
|
531 |
-
|
532 |
-
# Update query
|
533 |
-
new_query = sample["query"]
|
534 |
-
|
535 |
-
# Update documents
|
536 |
-
new_docs = []
|
537 |
-
for i, doc in enumerate(sample["candidates"]):
|
538 |
-
if i < len(doc_containers):
|
539 |
-
new_docs.append(doc)
|
540 |
-
|
541 |
-
# Initialize rankings
|
542 |
-
new_rankings = [""] * len(ranking_inputs)
|
543 |
-
|
544 |
-
# Check if this sample has already been annotated
|
545 |
-
existing_annotation = next((a for a in results["annotations"] if a["sample_id"] == sample_id), None)
|
546 |
-
if existing_annotation:
|
547 |
-
# Restore previous rankings
|
548 |
-
for i, rank in enumerate(existing_annotation["rankings"]):
|
549 |
-
if i < len(new_rankings) and rank is not None:
|
550 |
-
new_rankings[i] = str(rank)
|
551 |
-
|
552 |
-
# Update progress
|
553 |
-
current_idx = samples.index(sample)
|
554 |
-
new_progress = f"Progress: {sum(completed_samples.values())}/{len(samples)}"
|
555 |
-
|
556 |
-
new_status = f"Viewing query {current_idx + 1} of {len(samples)}"
|
557 |
-
if completed_samples[sample_id]:
|
558 |
-
new_status += " (already completed)"
|
559 |
-
|
560 |
-
# Initialize validation indicators (but don't run validation until needed)
|
561 |
-
# This improves load time
|
562 |
-
|
563 |
-
return [new_query] + new_docs + new_rankings + blank_validations + [sample_id, new_progress, new_status]
|
564 |
-
|
565 |
-
# Optimize the quick ranking functions for better performance
|
566 |
def assign_sequential_ranks():
|
567 |
-
|
568 |
-
sequential_ranks = [str(i+1) for i in range(len(samples[0]["candidates"]))]
|
569 |
-
# Return ranks only - validation will be done separately for better performance
|
570 |
-
return sequential_ranks
|
571 |
|
572 |
def assign_reverse_ranks():
|
573 |
-
"""Assign reverse rankings (n,n-1...) to all documents."""
|
574 |
n = len(samples[0]["candidates"])
|
575 |
-
|
576 |
-
# Return ranks only - validation will be done separately for better performance
|
577 |
-
return reverse_ranks
|
578 |
|
579 |
def clear_rankings():
|
580 |
-
""
|
581 |
-
empty_ranks = [""] * len(samples[0]["candidates"])
|
582 |
-
# Return empty ranks only - validation will be done separately for better performance
|
583 |
-
return empty_ranks
|
584 |
|
585 |
-
#
|
586 |
-
def update_status_after_ranking(operation_name):
|
587 |
-
"""Update status box after a ranking operation."""
|
588 |
-
return f"✅ {operation_name} applied successfully"
|
589 |
-
|
590 |
-
# Connect quick ranking buttons with more efficient handling
|
591 |
sequential_btn.click(
|
592 |
fn=assign_sequential_ranks,
|
593 |
inputs=None,
|
594 |
outputs=ranking_inputs
|
595 |
-
).then(
|
596 |
-
fn=lambda: update_status_after_ranking("Sequential ranking"),
|
597 |
-
inputs=None,
|
598 |
-
outputs=[status_box]
|
599 |
)
|
600 |
|
601 |
reverse_btn.click(
|
602 |
fn=assign_reverse_ranks,
|
603 |
inputs=None,
|
604 |
outputs=ranking_inputs
|
605 |
-
).then(
|
606 |
-
fn=lambda: update_status_after_ranking("Reverse ranking"),
|
607 |
-
inputs=None,
|
608 |
-
outputs=[status_box]
|
609 |
)
|
610 |
|
611 |
clear_btn.click(
|
612 |
fn=clear_rankings,
|
613 |
inputs=None,
|
614 |
outputs=ranking_inputs
|
615 |
-
).then(
|
616 |
-
fn=lambda: update_status_after_ranking("Rankings cleared"),
|
617 |
-
inputs=None,
|
618 |
-
outputs=[status_box]
|
619 |
)
|
620 |
|
621 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
622 |
next_btn.click(
|
623 |
fn=handle_next,
|
624 |
inputs=[current_sample_id, auto_save_toggle] + ranking_inputs,
|
625 |
-
outputs=[current_sample_id] # Only update the sample ID
|
626 |
-
).then(
|
627 |
-
fn=load_sample, # Then load the sample with all UI updates in one batch
|
628 |
-
inputs=[current_sample_id],
|
629 |
outputs=[query_text] + doc_containers + ranking_inputs + validation_indicators + [current_sample_id, progress_text, status_box]
|
630 |
)
|
631 |
|
632 |
prev_btn.click(
|
633 |
fn=handle_prev,
|
634 |
inputs=[current_sample_id, auto_save_toggle] + ranking_inputs,
|
635 |
-
outputs=[current_sample_id] # Only update the sample ID
|
636 |
-
).then(
|
637 |
-
fn=load_sample, # Then load the sample with all UI updates in one batch
|
638 |
-
inputs=[current_sample_id],
|
639 |
outputs=[query_text] + doc_containers + ranking_inputs + validation_indicators + [current_sample_id, progress_text, status_box]
|
640 |
)
|
641 |
|
@@ -656,6 +573,14 @@ def create_reranking_interface(task_data):
|
|
656 |
outputs=[auto_save_enabled]
|
657 |
)
|
658 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
659 |
# Add a real-time validation for the entire set to check for duplicates
|
660 |
def validate_all_inputs(*rankings):
|
661 |
"""Check all inputs for duplicate ranks and provide feedback."""
|
|
|
301 |
margin: 0 auto;
|
302 |
font-size: 12px !important;
|
303 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
304 |
</style>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
305 |
""")
|
306 |
|
307 |
def validate_rankings(*rankings):
|
|
|
386 |
|
387 |
return status, progress
|
388 |
|
389 |
+
def load_sample(sample_id):
|
390 |
+
"""Load a specific sample into the interface."""
|
391 |
+
sample = next((s for s in samples if s["id"] == sample_id), None)
|
392 |
+
if not sample:
|
393 |
+
return [query_text.value] + [d.value for d in doc_containers] + [""] * len(ranking_inputs) + [""] * len(validation_indicators) + [sample_id, progress_text.value, status_box.value]
|
394 |
+
|
395 |
+
# Update query
|
396 |
+
new_query = sample["query"]
|
397 |
+
|
398 |
+
# Update documents
|
399 |
+
new_docs = []
|
400 |
+
for i, doc in enumerate(sample["candidates"]):
|
401 |
+
if i < len(doc_containers):
|
402 |
+
new_docs.append(doc)
|
403 |
+
|
404 |
+
# Initialize rankings
|
405 |
+
new_rankings = [""] * len(ranking_inputs)
|
406 |
+
|
407 |
+
# Check if this sample has already been annotated
|
408 |
+
existing_annotation = next((a for a in results["annotations"] if a["sample_id"] == sample_id), None)
|
409 |
+
if existing_annotation:
|
410 |
+
# Restore previous rankings
|
411 |
+
for i, rank in enumerate(existing_annotation["rankings"]):
|
412 |
+
if i < len(new_rankings) and rank is not None:
|
413 |
+
new_rankings[i] = str(rank)
|
414 |
+
|
415 |
+
# Update progress
|
416 |
+
current_idx = samples.index(sample)
|
417 |
+
new_progress = f"Progress: {sum(completed_samples.values())}/{len(samples)}"
|
418 |
+
|
419 |
+
new_status = f"Viewing query {current_idx + 1} of {len(samples)}"
|
420 |
+
if completed_samples[sample_id]:
|
421 |
+
new_status += " (already completed)"
|
422 |
+
|
423 |
+
# Initialize validation indicators
|
424 |
+
validation_results = validate_rankings(*new_rankings)
|
425 |
+
validation_indicators_values = validation_results[:-1] # Remove validity flag
|
426 |
+
|
427 |
+
return [new_query] + new_docs + new_rankings + validation_indicators_values + [sample_id, new_progress, new_status]
|
428 |
+
|
429 |
def auto_save_and_navigate(direction, current_id, auto_save, *rankings):
|
430 |
"""Save rankings if auto-save is enabled, then navigate."""
|
431 |
# Extract rankings (remove validation indicators)
|
|
|
485 |
except Exception as e:
|
486 |
return f"Error saving results: {str(e)}"
|
487 |
|
488 |
+
# Define functions for the quick ranking buttons
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
489 |
def assign_sequential_ranks():
|
490 |
+
return [str(i+1) for i in range(len(samples[0]["candidates"]))]
|
|
|
|
|
|
|
491 |
|
492 |
def assign_reverse_ranks():
|
|
|
493 |
n = len(samples[0]["candidates"])
|
494 |
+
return [str(n-i) for i in range(n)]
|
|
|
|
|
495 |
|
496 |
def clear_rankings():
|
497 |
+
return [""] * len(samples[0]["candidates"])
|
|
|
|
|
|
|
498 |
|
499 |
+
# Connect quick ranking buttons
|
|
|
|
|
|
|
|
|
|
|
500 |
sequential_btn.click(
|
501 |
fn=assign_sequential_ranks,
|
502 |
inputs=None,
|
503 |
outputs=ranking_inputs
|
|
|
|
|
|
|
|
|
504 |
)
|
505 |
|
506 |
reverse_btn.click(
|
507 |
fn=assign_reverse_ranks,
|
508 |
inputs=None,
|
509 |
outputs=ranking_inputs
|
|
|
|
|
|
|
|
|
510 |
)
|
511 |
|
512 |
clear_btn.click(
|
513 |
fn=clear_rankings,
|
514 |
inputs=None,
|
515 |
outputs=ranking_inputs
|
|
|
|
|
|
|
|
|
516 |
)
|
517 |
|
518 |
+
# Wire up events (Gradio 3.x syntax)
|
519 |
+
submit_btn.click(
|
520 |
+
fn=submit_rankings,
|
521 |
+
inputs=ranking_inputs + [current_sample_id],
|
522 |
+
outputs=[status_box, progress_text]
|
523 |
+
)
|
524 |
+
|
525 |
+
# Auto-save and navigate events
|
526 |
+
def handle_next(current_id, auto_save, *rankings):
|
527 |
+
# First, handle auto-save
|
528 |
+
new_id, status, progress = auto_save_and_navigate("next", current_id, auto_save, *rankings)
|
529 |
+
# Then, load the new sample
|
530 |
+
outputs = load_sample(new_id)
|
531 |
+
# Add the status and progress
|
532 |
+
outputs[-2] = progress if status else outputs[-2]
|
533 |
+
outputs[-1] = status if status else outputs[-1]
|
534 |
+
return outputs
|
535 |
+
|
536 |
+
def handle_prev(current_id, auto_save, *rankings):
|
537 |
+
# First, handle auto-save
|
538 |
+
new_id, status, progress = auto_save_and_navigate("prev", current_id, auto_save, *rankings)
|
539 |
+
# Then, load the new sample
|
540 |
+
outputs = load_sample(new_id)
|
541 |
+
# Add the status and progress
|
542 |
+
outputs[-2] = progress if status else outputs[-2]
|
543 |
+
outputs[-1] = status if status else outputs[-1]
|
544 |
+
return outputs
|
545 |
+
|
546 |
+
# Connect navigation with Gradio 3.x syntax
|
547 |
next_btn.click(
|
548 |
fn=handle_next,
|
549 |
inputs=[current_sample_id, auto_save_toggle] + ranking_inputs,
|
|
|
|
|
|
|
|
|
550 |
outputs=[query_text] + doc_containers + ranking_inputs + validation_indicators + [current_sample_id, progress_text, status_box]
|
551 |
)
|
552 |
|
553 |
prev_btn.click(
|
554 |
fn=handle_prev,
|
555 |
inputs=[current_sample_id, auto_save_toggle] + ranking_inputs,
|
|
|
|
|
|
|
|
|
556 |
outputs=[query_text] + doc_containers + ranking_inputs + validation_indicators + [current_sample_id, progress_text, status_box]
|
557 |
)
|
558 |
|
|
|
573 |
outputs=[auto_save_enabled]
|
574 |
)
|
575 |
|
576 |
+
# Connect validation to ranking inputs for real-time feedback
|
577 |
+
for ranking in ranking_inputs:
|
578 |
+
ranking.change(
|
579 |
+
fn=on_ranking_change,
|
580 |
+
inputs=ranking_inputs,
|
581 |
+
outputs=validation_indicators
|
582 |
+
)
|
583 |
+
|
584 |
# Add a real-time validation for the entire set to check for duplicates
|
585 |
def validate_all_inputs(*rankings):
|
586 |
"""Check all inputs for duplicate ranks and provide feedback."""
|