469 lines
15 KiB
JavaScript
469 lines
15 KiB
JavaScript
// Memory Module Detection QA Interface JavaScript
|
|
|
|
const API_BASE_URL = 'http://localhost:5002';
|
|
let uploadedFile = null;
|
|
|
|
// Initialize the application
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
initializeApp();
|
|
setupEventListeners();
|
|
});
|
|
|
|
function initializeApp() {
|
|
checkApiStatus();
|
|
loadApiInfo();
|
|
}
|
|
|
|
function setupEventListeners() {
|
|
// File input change
|
|
document.getElementById('fileInput').addEventListener('change', handleFileSelect);
|
|
|
|
// Drag and drop
|
|
const uploadArea = document.getElementById('uploadArea');
|
|
uploadArea.addEventListener('dragover', handleDragOver);
|
|
uploadArea.addEventListener('dragleave', handleDragLeave);
|
|
uploadArea.addEventListener('drop', handleDrop);
|
|
|
|
// Click to upload (only on the upload area, not buttons inside it)
|
|
uploadArea.addEventListener('click', function(event) {
|
|
// Only trigger file input if clicking on the upload area itself, not buttons
|
|
if (event.target === uploadArea || (event.target.closest('.upload-content') && !event.target.closest('button'))) {
|
|
console.log('Upload area clicked, triggering file input');
|
|
const fileInput = document.getElementById('fileInput');
|
|
if (fileInput) {
|
|
fileInput.click();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
async function checkApiStatus() {
|
|
const statusElement = document.getElementById('apiStatus');
|
|
try {
|
|
const response = await fetch(`${API_BASE_URL}/health`);
|
|
if (response.ok) {
|
|
const data = await response.json();
|
|
statusElement.className = 'status-indicator online';
|
|
statusElement.innerHTML = '<i class="fas fa-circle"></i> <span>API Online</span>';
|
|
} else {
|
|
throw new Error('API not responding');
|
|
}
|
|
} catch (error) {
|
|
statusElement.className = 'status-indicator offline';
|
|
statusElement.innerHTML = '<i class="fas fa-circle"></i> <span>API Offline</span>';
|
|
}
|
|
}
|
|
|
|
async function loadApiInfo() {
|
|
const apiInfoElement = document.getElementById('apiInfo');
|
|
try {
|
|
const response = await fetch(`${API_BASE_URL}/api`);
|
|
if (response.ok) {
|
|
const data = await response.json();
|
|
displayApiInfo(data);
|
|
} else {
|
|
throw new Error('Failed to load API info');
|
|
}
|
|
} catch (error) {
|
|
apiInfoElement.innerHTML = '<div class="error">Failed to load API information</div>';
|
|
}
|
|
}
|
|
|
|
function displayApiInfo(data) {
|
|
const apiInfoElement = document.getElementById('apiInfo');
|
|
apiInfoElement.innerHTML = `
|
|
<div class="info-item">
|
|
<h3>API Status</h3>
|
|
<p>${data.message}</p>
|
|
</div>
|
|
<div class="info-item">
|
|
<h3>Version</h3>
|
|
<p>${data.version}</p>
|
|
</div>
|
|
<div class="info-item">
|
|
<h3>Model Status</h3>
|
|
<p>${data.model_loaded ? 'Loaded ✅' : 'Not Loaded ❌'}</p>
|
|
</div>
|
|
<div class="info-item">
|
|
<h3>Supported Formats</h3>
|
|
<p>${data.supported_formats.join(', ')}</p>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
function showUploadSection() {
|
|
document.getElementById('uploadSection').style.display = 'block';
|
|
document.getElementById('uploadSection').scrollIntoView({ behavior: 'smooth' });
|
|
|
|
// Ensure the upload area is properly initialized
|
|
initializeUploadArea();
|
|
}
|
|
|
|
function initializeUploadArea() {
|
|
const uploadArea = document.getElementById('uploadArea');
|
|
let fileInput = document.getElementById('fileInput');
|
|
|
|
// Completely recreate the file input element
|
|
if (fileInput) {
|
|
fileInput.remove();
|
|
}
|
|
|
|
// Create a brand new file input
|
|
const newFileInput = document.createElement('input');
|
|
newFileInput.type = 'file';
|
|
newFileInput.id = 'fileInput';
|
|
newFileInput.accept = 'image/*';
|
|
newFileInput.style.display = 'none';
|
|
newFileInput.multiple = false;
|
|
|
|
// Insert the new file input into the DOM
|
|
uploadArea.parentNode.insertBefore(newFileInput, uploadArea);
|
|
|
|
// Clear any existing event listeners on upload area by cloning
|
|
const newUploadArea = uploadArea.cloneNode(true);
|
|
uploadArea.parentNode.replaceChild(newUploadArea, uploadArea);
|
|
|
|
// Re-attach all event listeners to the new elements
|
|
newFileInput.addEventListener('change', handleFileSelect);
|
|
|
|
newUploadArea.addEventListener('dragover', handleDragOver);
|
|
newUploadArea.addEventListener('dragleave', handleDragLeave);
|
|
newUploadArea.addEventListener('drop', handleDrop);
|
|
|
|
newUploadArea.addEventListener('click', function(event) {
|
|
if (event.target === newUploadArea || (event.target.closest('.upload-content') && !event.target.closest('button'))) {
|
|
console.log('Upload area clicked, triggering file input');
|
|
const currentFileInput = document.getElementById('fileInput');
|
|
if (currentFileInput) {
|
|
currentFileInput.click();
|
|
}
|
|
}
|
|
});
|
|
|
|
console.log('Upload area completely reinitialized with fresh file input');
|
|
}
|
|
|
|
function handleFileSelect(event) {
|
|
console.log('File select event triggered');
|
|
const file = event.target.files[0];
|
|
if (file) {
|
|
console.log('File selected:', file.name);
|
|
handleFile(file);
|
|
} else {
|
|
console.log('No file selected');
|
|
}
|
|
}
|
|
|
|
function handleDragOver(event) {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
event.currentTarget.classList.add('dragover');
|
|
}
|
|
|
|
function handleDragLeave(event) {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
event.currentTarget.classList.remove('dragover');
|
|
}
|
|
|
|
function handleDrop(event) {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
event.currentTarget.classList.remove('dragover');
|
|
|
|
const files = event.dataTransfer.files;
|
|
if (files.length > 0) {
|
|
handleFile(files[0]);
|
|
}
|
|
}
|
|
|
|
function handleFile(file) {
|
|
if (!file.type.startsWith('image/')) {
|
|
alert('Please select an image file');
|
|
return;
|
|
}
|
|
|
|
uploadedFile = file;
|
|
|
|
// Show file info with change file option
|
|
const uploadArea = document.getElementById('uploadArea');
|
|
uploadArea.innerHTML = `
|
|
<div class="upload-content">
|
|
<i class="fas fa-check-circle upload-icon" style="color: #28a745;"></i>
|
|
<p><strong>File selected:</strong> ${file.name}</p>
|
|
<p class="upload-hint">Size: ${(file.size / 1024 / 1024).toFixed(2)} MB</p>
|
|
<button class="btn btn-outline" onclick="resetFileUpload()" style="margin-top: 10px;">
|
|
<i class="fas fa-sync-alt"></i> Change File
|
|
</button>
|
|
</div>
|
|
`;
|
|
|
|
// Show controls
|
|
document.getElementById('uploadControls').style.display = 'block';
|
|
}
|
|
|
|
function resetFileUpload() {
|
|
uploadedFile = null;
|
|
|
|
// Reset upload area HTML
|
|
const uploadArea = document.getElementById('uploadArea');
|
|
uploadArea.innerHTML = `
|
|
<div class="upload-content">
|
|
<i class="fas fa-cloud-upload-alt upload-icon"></i>
|
|
<p>Drag and drop an image here or click to select</p>
|
|
<p class="upload-hint">Supported formats: PNG, JPG, JPEG, GIF, BMP</p>
|
|
<button class="btn btn-outline" onclick="document.getElementById('fileInput').click()">
|
|
Select Image
|
|
</button>
|
|
</div>
|
|
`;
|
|
|
|
// Hide controls
|
|
const uploadControls = document.getElementById('uploadControls');
|
|
uploadControls.style.display = 'none';
|
|
|
|
// Remove the "Upload Another" button if it exists
|
|
const uploadAnotherBtn = uploadControls.querySelector('.upload-another');
|
|
if (uploadAnotherBtn) {
|
|
uploadAnotherBtn.remove();
|
|
}
|
|
|
|
// Hide results if showing
|
|
document.getElementById('resultsSection').style.display = 'none';
|
|
|
|
// Reinitialize the upload area with fresh event listeners
|
|
initializeUploadArea();
|
|
|
|
console.log('File upload reset completed');
|
|
}
|
|
|
|
async function processUploadedImage() {
|
|
if (!uploadedFile) {
|
|
alert('Please select an image first');
|
|
return;
|
|
}
|
|
|
|
const confidence = 0.8; // Fixed 80% threshold
|
|
showLoading('Processing uploaded image...');
|
|
|
|
try {
|
|
const formData = new FormData();
|
|
formData.append('image', uploadedFile);
|
|
formData.append('confidence', confidence);
|
|
|
|
const response = await fetch(`${API_BASE_URL}/detect`, {
|
|
method: 'POST',
|
|
body: formData
|
|
});
|
|
|
|
const result = await response.json();
|
|
hideLoading();
|
|
|
|
if (result.success) {
|
|
displayResults(result, 'Uploaded Image Detection');
|
|
// Add option to upload another file
|
|
addUploadAnotherOption();
|
|
} else {
|
|
alert(`Detection failed: ${result.error}`);
|
|
}
|
|
} catch (error) {
|
|
hideLoading();
|
|
alert(`Error: ${error.message}`);
|
|
}
|
|
}
|
|
|
|
function addUploadAnotherOption() {
|
|
const uploadControls = document.getElementById('uploadControls');
|
|
if (!uploadControls.querySelector('.upload-another')) {
|
|
const uploadAnotherBtn = document.createElement('button');
|
|
uploadAnotherBtn.className = 'btn btn-secondary upload-another';
|
|
uploadAnotherBtn.style.marginLeft = '10px';
|
|
uploadAnotherBtn.innerHTML = '<i class="fas fa-plus"></i> Upload Another Image';
|
|
uploadAnotherBtn.onclick = resetFileUpload;
|
|
uploadControls.appendChild(uploadAnotherBtn);
|
|
}
|
|
}
|
|
|
|
async function testHardcodedImage() {
|
|
showLoading('Testing hardcoded image...');
|
|
|
|
try {
|
|
console.log(`Making request to: ${API_BASE_URL}/detect/hardcoded?confidence=0.8`);
|
|
const response = await fetch(`${API_BASE_URL}/detect/hardcoded?confidence=0.8`);
|
|
console.log('Response status:', response.status);
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
}
|
|
|
|
const result = await response.json();
|
|
console.log('Response data:', result);
|
|
hideLoading();
|
|
|
|
if (result.success) {
|
|
displayResults(result, 'Hardcoded Image Test');
|
|
} else {
|
|
alert(`Test failed: ${result.error}`);
|
|
}
|
|
} catch (error) {
|
|
hideLoading();
|
|
console.error('Hardcoded test error:', error);
|
|
alert(`Error: ${error.message}`);
|
|
}
|
|
}
|
|
|
|
function displayResults(result, title) {
|
|
const resultsSection = document.getElementById('resultsSection');
|
|
const resultsContent = document.getElementById('resultsContent');
|
|
|
|
let detectionsHtml = '';
|
|
if (result.detections && result.detections.length > 0) {
|
|
detectionsHtml = result.detections.map((detection, index) => `
|
|
<div class="detection-item">
|
|
<span>Detection ${index + 1}: ${detection.class_name}</span>
|
|
<span class="detection-confidence">${(detection.confidence * 100).toFixed(1)}%</span>
|
|
</div>
|
|
`).join('');
|
|
} else {
|
|
detectionsHtml = '<div class="detection-item">No memory modules detected</div>';
|
|
}
|
|
|
|
resultsContent.innerHTML = `
|
|
<div class="result-item">
|
|
<div class="result-header">
|
|
<h3>${title}</h3>
|
|
<span class="badge">${new Date().toLocaleTimeString()}</span>
|
|
</div>
|
|
|
|
<div class="result-stats">
|
|
<div class="stat-item">
|
|
<div class="stat-value">${result.num_detections}</div>
|
|
<div class="stat-label">Detections</div>
|
|
</div>
|
|
<div class="stat-item">
|
|
<div class="stat-value">${(result.confidence_threshold * 100).toFixed(0)}%</div>
|
|
<div class="stat-label">Confidence</div>
|
|
</div>
|
|
<div class="stat-item">
|
|
<div class="stat-value">${result.success ? 'Success' : 'Failed'}</div>
|
|
<div class="stat-label">Status</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="detection-list">
|
|
<h4>Detected Memory Modules:</h4>
|
|
${detectionsHtml}
|
|
</div>
|
|
|
|
${result.annotated_image ? `
|
|
<div class="image-container">
|
|
<h4>Annotated Image:</h4>
|
|
<img src="data:image/png;base64,${result.annotated_image}"
|
|
alt="Annotated Result" class="result-image">
|
|
</div>
|
|
` : ''}
|
|
</div>
|
|
`;
|
|
|
|
resultsSection.style.display = 'block';
|
|
resultsSection.scrollIntoView({ behavior: 'smooth' });
|
|
}
|
|
|
|
async function runAllTests() {
|
|
showLoading('Running comprehensive tests...');
|
|
const testResults = [];
|
|
|
|
// Test 1: API Health
|
|
try {
|
|
const response = await fetch(`${API_BASE_URL}/health`);
|
|
const result = await response.json();
|
|
testResults.push({
|
|
name: 'API Health Check',
|
|
success: response.ok && result.status === 'healthy',
|
|
message: response.ok ? 'API is healthy' : 'API health check failed'
|
|
});
|
|
} catch (error) {
|
|
testResults.push({
|
|
name: 'API Health Check',
|
|
success: false,
|
|
message: `Error: ${error.message}`
|
|
});
|
|
}
|
|
|
|
// Test 2: Hardcoded Image
|
|
try {
|
|
const response = await fetch(`${API_BASE_URL}/detect/hardcoded`);
|
|
const result = await response.json();
|
|
testResults.push({
|
|
name: 'Hardcoded Image Detection',
|
|
success: result.success,
|
|
message: result.success ?
|
|
`Found ${result.num_detections} memory modules` :
|
|
`Error: ${result.error}`
|
|
});
|
|
} catch (error) {
|
|
testResults.push({
|
|
name: 'Hardcoded Image Detection',
|
|
success: false,
|
|
message: `Error: ${error.message}`
|
|
});
|
|
}
|
|
|
|
// Test 3: API Information
|
|
try {
|
|
const response = await fetch(`${API_BASE_URL}/api`);
|
|
const result = await response.json();
|
|
testResults.push({
|
|
name: 'API Information',
|
|
success: response.ok && result.message,
|
|
message: response.ok ? 'API info loaded successfully' : 'Failed to load API info'
|
|
});
|
|
} catch (error) {
|
|
testResults.push({
|
|
name: 'API Information',
|
|
success: false,
|
|
message: `Error: ${error.message}`
|
|
});
|
|
}
|
|
|
|
hideLoading();
|
|
displayTestResults(testResults);
|
|
}
|
|
|
|
function displayTestResults(testResults) {
|
|
const testResultsSection = document.getElementById('testResultsSection');
|
|
const testResultsContent = document.getElementById('testResults');
|
|
|
|
const successCount = testResults.filter(test => test.success).length;
|
|
const totalTests = testResults.length;
|
|
|
|
const testsHtml = testResults.map(test => `
|
|
<div class="test-item ${test.success ? 'success' : 'error'}">
|
|
<h3>
|
|
<i class="fas ${test.success ? 'fa-check-circle' : 'fa-times-circle'}"></i>
|
|
${test.name}
|
|
</h3>
|
|
<p>${test.message}</p>
|
|
</div>
|
|
`).join('');
|
|
|
|
testResultsContent.innerHTML = `
|
|
<div class="test-summary">
|
|
<h3>Test Summary: ${successCount}/${totalTests} tests passed</h3>
|
|
</div>
|
|
${testsHtml}
|
|
`;
|
|
|
|
testResultsSection.style.display = 'block';
|
|
testResultsSection.scrollIntoView({ behavior: 'smooth' });
|
|
}
|
|
|
|
function showLoading(message) {
|
|
document.getElementById('loadingText').textContent = message;
|
|
document.getElementById('loadingOverlay').style.display = 'flex';
|
|
}
|
|
|
|
function hideLoading() {
|
|
document.getElementById('loadingOverlay').style.display = 'none';
|
|
}
|