Spaces:
Sleeping
Sleeping
Commit
·
da0b374
1
Parent(s):
44d1643
Fix Safari compatibility issues: CSV download and image navigation
Browse files- app.py +6 -0
- static/script.js +52 -32
app.py
CHANGED
|
@@ -179,6 +179,12 @@ def download_file(job_id, filename):
|
|
| 179 |
|
| 180 |
if safe_filename.lower() == "results.csv":
|
| 181 |
mime_type = 'text/csv'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 182 |
|
| 183 |
return send_file(str(file_path), mimetype=mime_type)
|
| 184 |
except Exception as e:
|
|
|
|
| 179 |
|
| 180 |
if safe_filename.lower() == "results.csv":
|
| 181 |
mime_type = 'text/csv'
|
| 182 |
+
return send_file(
|
| 183 |
+
str(file_path),
|
| 184 |
+
mimetype=mime_type,
|
| 185 |
+
as_attachment=True,
|
| 186 |
+
download_name=safe_filename
|
| 187 |
+
)
|
| 188 |
|
| 189 |
return send_file(str(file_path), mimetype=mime_type)
|
| 190 |
except Exception as e:
|
static/script.js
CHANGED
|
@@ -611,38 +611,50 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
| 611 |
|
| 612 |
if (result.annotated_filename) {
|
| 613 |
const imageUrl = `/results/${currentJobId}/${result.annotated_filename}`;
|
| 614 |
-
previewImage.src = imageUrl;
|
| 615 |
-
previewImage.alt = result.filename;
|
| 616 |
|
| 617 |
-
//
|
| 618 |
-
|
| 619 |
-
|
| 620 |
-
|
| 621 |
-
|
| 622 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 623 |
|
| 624 |
-
|
| 625 |
-
|
| 626 |
-
|
| 627 |
-
|
| 628 |
-
|
| 629 |
-
|
| 630 |
-
|
| 631 |
-
|
| 632 |
|
| 633 |
-
|
| 634 |
-
|
| 635 |
-
|
| 636 |
-
|
| 637 |
-
|
| 638 |
-
|
| 639 |
-
if (
|
| 640 |
-
|
| 641 |
}
|
| 642 |
-
|
|
|
|
|
|
|
|
|
|
| 643 |
|
| 644 |
-
|
| 645 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 646 |
} else {
|
| 647 |
clearPreview();
|
| 648 |
}
|
|
@@ -686,16 +698,24 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
| 686 |
updatePanCursorState();
|
| 687 |
}
|
| 688 |
|
| 689 |
-
// Image Navigation
|
| 690 |
-
|
| 691 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 692 |
displayImage(currentImageIndex - 1);
|
|
|
|
| 693 |
}
|
| 694 |
});
|
| 695 |
|
| 696 |
-
nextBtn.addEventListener('click', () => {
|
| 697 |
-
|
|
|
|
|
|
|
| 698 |
displayImage(currentImageIndex + 1);
|
|
|
|
| 699 |
}
|
| 700 |
});
|
| 701 |
|
|
|
|
| 611 |
|
| 612 |
if (result.annotated_filename) {
|
| 613 |
const imageUrl = `/results/${currentJobId}/${result.annotated_filename}`;
|
|
|
|
|
|
|
| 614 |
|
| 615 |
+
// Create a new image object to handle loading
|
| 616 |
+
const tempImage = new Image();
|
| 617 |
+
tempImage.onload = function() {
|
| 618 |
+
previewImage.src = imageUrl;
|
| 619 |
+
previewImage.alt = result.filename;
|
| 620 |
+
|
| 621 |
+
// Update image info with the new function
|
| 622 |
+
updateImageInfo();
|
| 623 |
+
|
| 624 |
+
// Enable zoom controls
|
| 625 |
+
zoomInBtn.disabled = false;
|
| 626 |
+
zoomOutBtn.disabled = false;
|
| 627 |
|
| 628 |
+
// Calculate which page this image should be on
|
| 629 |
+
const targetPage = Math.floor(index / RESULTS_PER_PAGE) + 1;
|
| 630 |
+
|
| 631 |
+
// If we're not on the correct page, switch to it
|
| 632 |
+
if (currentPage !== targetPage) {
|
| 633 |
+
currentPage = targetPage;
|
| 634 |
+
displayResultsPage(currentPage);
|
| 635 |
+
}
|
| 636 |
|
| 637 |
+
// Remove selection from all rows
|
| 638 |
+
document.querySelectorAll('.results-table tr').forEach(r => r.classList.remove('selected'));
|
| 639 |
+
|
| 640 |
+
// Find and highlight the corresponding row
|
| 641 |
+
const rows = Array.from(resultsTableBody.querySelectorAll('tr'));
|
| 642 |
+
const targetRow = rows.find(row => parseInt(row.dataset.originalIndex, 10) === index);
|
| 643 |
+
if (targetRow) {
|
| 644 |
+
targetRow.classList.add('selected');
|
| 645 |
}
|
| 646 |
+
|
| 647 |
+
// Reset panning when a new image is displayed
|
| 648 |
+
resetPanZoom();
|
| 649 |
+
};
|
| 650 |
|
| 651 |
+
tempImage.onerror = function() {
|
| 652 |
+
console.error('Failed to load image:', imageUrl);
|
| 653 |
+
clearPreview();
|
| 654 |
+
};
|
| 655 |
+
|
| 656 |
+
// Start loading the image
|
| 657 |
+
tempImage.src = imageUrl;
|
| 658 |
} else {
|
| 659 |
clearPreview();
|
| 660 |
}
|
|
|
|
| 698 |
updatePanCursorState();
|
| 699 |
}
|
| 700 |
|
| 701 |
+
// Image Navigation with debouncing
|
| 702 |
+
let navigationInProgress = false;
|
| 703 |
+
|
| 704 |
+
prevBtn.addEventListener('click', (e) => {
|
| 705 |
+
e.preventDefault();
|
| 706 |
+
if (!navigationInProgress && currentImageIndex > 0) {
|
| 707 |
+
navigationInProgress = true;
|
| 708 |
displayImage(currentImageIndex - 1);
|
| 709 |
+
setTimeout(() => { navigationInProgress = false; }, 300);
|
| 710 |
}
|
| 711 |
});
|
| 712 |
|
| 713 |
+
nextBtn.addEventListener('click', (e) => {
|
| 714 |
+
e.preventDefault();
|
| 715 |
+
if (!navigationInProgress && currentImageIndex < currentResults.length - 1) {
|
| 716 |
+
navigationInProgress = true;
|
| 717 |
displayImage(currentImageIndex + 1);
|
| 718 |
+
setTimeout(() => { navigationInProgress = false; }, 300);
|
| 719 |
}
|
| 720 |
});
|
| 721 |
|