AccessibilityCheckerBackend / docs /batch-processing.html
accessibilitychecker's picture
Upload folder using huggingface_hub
bbfde3f verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Batch Document Processing</title>
<style>
body { font-family: Arial, sans-serif; max-width: 1200px; margin: 0 auto; padding: 20px; }
.upload-area { border: 2px dashed #ccc; padding: 40px; text-align: center; margin: 20px 0; }
.upload-area.dragover { border-color: #007cba; background-color: #f0f8ff; }
.file-list { margin: 20px 0; }
.file-item { padding: 10px; border: 1px solid #ddd; margin: 5px 0; display: flex; justify-content: space-between; align-items: center; }
.file-item.processing { background-color: #fff3cd; }
.file-item.success { background-color: #d4edda; }
.file-item.error { background-color: #f8d7da; }
.progress-bar { width: 100%; height: 20px; background-color: #f0f0f0; border-radius: 10px; overflow: hidden; margin: 10px 0; }
.progress-fill { height: 100%; background-color: #007cba; transition: width 0.3s ease; }
.results { margin: 20px 0; }
.batch-history { margin: 30px 0; }
.batch-item { padding: 15px; border: 1px solid #ddd; margin: 10px 0; border-radius: 5px; }
button { padding: 10px 20px; margin: 5px; cursor: pointer; }
.btn-primary { background-color: #007cba; color: white; border: none; }
.btn-secondary { background-color: #6c757d; color: white; border: none; }
.btn-danger { background-color: #dc3545; color: white; border: none; }
</style>
</head>
<body>
<h1>Accessibility Checker - Batch Processing</h1>
<div class="upload-section">
<h2>Upload Multiple Documents</h2>
<div id="uploadArea" class="upload-area">
<p>Drop up to 10 DOCX files here, or click to select</p>
<input type="file" id="fileInput" multiple accept=".docx" style="display: none;">
<button onclick="document.getElementById('fileInput').click()" class="btn-primary">Select Files</button>
</div>
<div id="fileList" class="file-list"></div>
<div id="progressSection" style="display: none;">
<h3>Processing Files...</h3>
<div class="progress-bar">
<div id="progressFill" class="progress-fill" style="width: 0%;"></div>
</div>
<div id="progressText">Preparing upload...</div>
</div>
<button id="uploadBtn" onclick="uploadFiles()" class="btn-primary" style="display: none;">
Upload and Process Files
</button>
</div>
<div id="results" class="results" style="display: none;">
<h2>Processing Results</h2>
<div id="resultsContent"></div>
</div>
<div class="batch-history">
<h2>Previous Batches</h2>
<button onclick="loadBatchHistory()" class="btn-secondary">Load Batch History</button>
<div id="batchHistory"></div>
</div>
<script>
let selectedFiles = [];
let sessionId = null;
const API_BASE = window.location.origin; // Adjust as needed
// Session management
async function initializeSession() {
try {
const response = await fetch(`${API_BASE}/api/session`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' }
});
const data = await response.json();
sessionId = data.sessionId;
console.log('Session initialized:', sessionId);
// Start heartbeat to keep session alive
startHeartbeat();
// Load existing session data
loadSessionData();
} catch (error) {
console.error('Failed to initialize session:', error);
}
}
function startHeartbeat() {
// Send heartbeat every 5 minutes
setInterval(async () => {
if (sessionId) {
try {
await fetch(`${API_BASE}/api/session`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Session-ID': sessionId
}
});
} catch (error) {
console.warn('Heartbeat failed:', error);
}
}
}, 5 * 60 * 1000); // 5 minutes
}
async function loadSessionData() {
if (!sessionId) return;
try {
const response = await fetch(`${API_BASE}/api/session?sessionId=${sessionId}`);
const sessionData = await response.json();
// Display existing batches from this session
displaySessionHistory(sessionData);
} catch (error) {
console.warn('Failed to load session data:', error);
}
}
function displaySessionHistory(sessionData) {
const historyDiv = document.getElementById('batchHistory');
if (sessionData.batches.length === 0) {
historyDiv.innerHTML = '<p>No batches in this session yet.</p>';
return;
}
historyDiv.innerHTML = '<h3>This Session:</h3>' +
sessionData.batches.map(batch => `
<div class="batch-item">
<h4>Batch ${batch.batchId}</h4>
<p><strong>Files:</strong> ${batch.totalFiles} (${batch.successful} successful, ${batch.failed} failed)</p>
<p><strong>Processed:</strong> ${new Date(batch.timestamp).toLocaleString()}</p>
<button onclick="downloadBatch('${batch.batchId}')" class="btn-primary">Download</button>
</div>
`).join('');
}
// Cleanup on page unload
window.addEventListener('beforeunload', () => {
// Note: Session will auto-expire after 1 hour of inactivity
// No need to manually cleanup as the server handles it
});
// File selection and drag/drop
document.getElementById('fileInput').addEventListener('change', handleFileSelect);
const uploadArea = document.getElementById('uploadArea');
uploadArea.addEventListener('dragover', (e) => {
e.preventDefault();
uploadArea.classList.add('dragover');
});
uploadArea.addEventListener('dragleave', () => {
uploadArea.classList.remove('dragover');
});
uploadArea.addEventListener('drop', (e) => {
e.preventDefault();
uploadArea.classList.remove('dragover');
const files = Array.from(e.dataTransfer.files).filter(f => f.name.endsWith('.docx'));
handleFiles(files);
});
function handleFileSelect(e) {
const files = Array.from(e.target.files);
handleFiles(files);
}
function handleFiles(files) {
selectedFiles = files.slice(0, 10); // Limit to 10 files
displayFileList();
document.getElementById('uploadBtn').style.display = selectedFiles.length > 0 ? 'block' : 'none';
}
function displayFileList() {
const fileList = document.getElementById('fileList');
if (selectedFiles.length === 0) {
fileList.innerHTML = '';
return;
}
fileList.innerHTML = `<h3>Selected Files (${selectedFiles.length}):</h3>`;
selectedFiles.forEach((file, index) => {
const fileItem = document.createElement('div');
fileItem.className = 'file-item';
fileItem.innerHTML = `
<div>
<strong>${file.name}</strong>
<br><small>${(file.size / 1024).toFixed(1)} KB</small>
</div>
<button onclick="removeFile(${index})" class="btn-danger">Remove</button>
`;
fileList.appendChild(fileItem);
});
}
function removeFile(index) {
selectedFiles.splice(index, 1);
displayFileList();
document.getElementById('uploadBtn').style.display = selectedFiles.length > 0 ? 'block' : 'none';
}
async function uploadFiles() {
if (selectedFiles.length === 0 || !sessionId) return;
document.getElementById('progressSection').style.display = 'block';
document.getElementById('uploadBtn').disabled = true;
const formData = new FormData();
selectedFiles.forEach((file, index) => {
formData.append(`file${index}`, file);
});
try {
updateProgress(10, 'Uploading files...');
const response = await fetch(`${API_BASE}/api/batch-upload`, {
method: 'POST',
headers: {
'X-Session-ID': sessionId
},
body: formData
});
updateProgress(90, 'Processing files...');
if (!response.ok) {
throw new Error(`Upload failed: ${response.statusText}`);
}
const result = await response.json();
updateProgress(100, 'Complete!');
displayResults(result);
// Refresh session data to show new batch
loadSessionData();
// Clear selection
selectedFiles = [];
displayFileList();
document.getElementById('uploadBtn').style.display = 'none';
} catch (error) {
console.error('Upload error:', error);
updateProgress(0, `Error: ${error.message}`);
} finally {
document.getElementById('uploadBtn').disabled = false;
setTimeout(() => {
document.getElementById('progressSection').style.display = 'none';
}, 2000);
}
}
function updateProgress(percent, text) {
document.getElementById('progressFill').style.width = percent + '%';
document.getElementById('progressText').textContent = text;
}
function displayResults(result) {
const resultsDiv = document.getElementById('results');
const resultsContent = document.getElementById('resultsContent');
resultsContent.innerHTML = `
<div class="batch-item">
<h3>Batch ${result.batchId}</h3>
<p><strong>Total Files:</strong> ${result.summary.totalFiles}</p>
<p><strong>Successful:</strong> ${result.summary.successful}</p>
<p><strong>Failed:</strong> ${result.summary.failed}</p>
<button onclick="downloadBatch('${result.batchId}')" class="btn-primary">
Download Remediated Files
</button>
<h4>File Details:</h4>
<div class="file-list">
${result.results.map(r => `
<div class="file-item ${r.success ? 'success' : 'error'}">
<div>
<strong>${r.filename}</strong>
${r.success ?
`<br><small>✓ Processed successfully</small>` :
`<br><small>✗ Error: ${r.error}</small>`
}
</div>
</div>
`).join('')}
</div>
</div>
`;
resultsDiv.style.display = 'block';
}
async function downloadBatch(batchId) {
if (sessionId) {
window.open(`${API_BASE}/api/batch-download?batchId=${batchId}&sessionId=${sessionId}`, '_blank');
}
}
async function deleteBatch(batchId) {
if (!confirm(`Delete batch ${batchId}?`)) return;
try {
const response = await fetch(`${API_BASE}/api/reports?batchId=${batchId}`, {
method: 'DELETE'
});
if (response.ok) {
loadBatchHistory(); // Refresh the list
} else {
alert('Failed to delete batch');
}
} catch (error) {
console.error('Error deleting batch:', error);
}
}
// Initialize session on page load
initializeSession();
</script>
</body>
</html>