pranit144's picture
Upload 84 files
07629a7 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Facility Management System</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css">
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<style>
:root {
--primary-color: #2563eb; /* Royal Blue */
--secondary-color: #4b5563; /* Dark Gray */
--success-color: #059669; /* Emerald */
--warning-color: #d97706; /* Dark Amber */
--danger-color: #dc2626; /* Red */
--background-color: #1e293b; /* Dark Blue Gray */
--card-background: #334155; /* Lighter Blue Gray */
--sidebar-background: #0f172a; /* Darkest Blue */
--text-primary: #f8fafc; /* Almost White */
--text-secondary: #cbd5e1; /* Light Gray */
--input-background: #475569; /* Medium Gray Blue */
--border-color: #64748b; /* Border Gray */
--hover-color: #1d4ed8; /* Darker Blue */
--border-radius: 0.75rem;
}
body {
font-family: 'Inter', sans-serif;
background-color: var(--background-color);
color: var(--text-primary);
line-height: 1.6;
}
.navbar {
background-color: var(--card-background);
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
padding: 1rem 0;
}
.navbar-brand {
font-weight: 700;
color: var(--primary-color);
}
.main-container {
max-width: 1400px;
margin: 2rem auto;
padding: 0 1rem;
}
.sidebar {
background-color: var(--sidebar-background);
border-radius: var(--border-radius);
padding: 1.5rem;
box-shadow: 0 4px 6px -1px rgba(0,0,0,0.1);
}
.form-control {
background-color: #d2dae8;
color: var(--text-primary);
border-radius: 0.5rem;
border: 1px solid var(--border-color);
padding: 0.75rem;
transition: all 0.3s ease;
}
.form-control:focus {
background-color: var(--input-background);
color: var(--text-primary);
border-color: var(--primary-color);
box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.2);
}
.btn {
padding: 0.75rem 1.5rem;
border-radius: 0.5rem;
font-weight: 600;
transition: all 0.3s ease;
}
.btn-primary {
background-color: var(--primary-color);
border: none;
}
.btn-primary:hover {
background-color: var(--hover-color);
transform: translateY(-1px);
}
.facility-card {
background-color: var(--card-background);
border-radius: var(--border-radius);
border: none;
box-shadow: 0 4px 6px -1px rgba(0,0,0,0.1);
margin-bottom: 1.5rem;
overflow: hidden;
}
.facility-card .card-header {
background-color: var(--sidebar-background);
color: var(--text-primary);
padding: 1rem;
font-weight: 600;
border-bottom: none;
}
.facility-card .card-body {
padding: 1.5rem;
}
.upload-section {
background-color: var(--card-background);
border: 2px dashed var(--border-color);
border-radius: 0.5rem;
padding: 1.5rem;
text-align: center;
transition: all 0.3s ease;
}
.upload-section:hover {
border-color: var(--primary-color);
}
.grade-container {
background-color: var(--card-background);
border-radius: var(--border-radius);
padding: 2rem;
text-align: center;
margin-top: 2rem;
}
.grade-display {
font-size: 3rem;
font-weight: 700;
margin: 1rem 0;
color: var(--primary-color);
}
.stats-card {
background-color: var(--card-background);
border-radius: var(--border-radius);
padding: 1.5rem;
margin-bottom: 1rem;
}
.stats-value {
font-size: 1.5rem;
font-weight: 700;
color: var(--primary-color);
}
/* Add more modern styling for tables */
.table {
background-color: var(--card-background);
border-radius: var(--border-radius);
overflow: hidden;
color: var(--text-primary);
}
.table thead th {
background-color: var(--sidebar-background);
border-bottom: 2px solid #e2e8f0;
color: var(--text-secondary);
font-weight: 600;
text-transform: uppercase;
font-size: 0.875rem;
border-bottom-color: #475569;
}
/* Toast notifications */
.toast {
position: fixed;
top: 1rem;
right: 1rem;
padding: 1rem 1.5rem;
border-radius: 0.5rem;
background-color: var(--card-background);
box-shadow: 0 4px 6px -1px rgba(0,0,0,0.1);
z-index: 1000;
color: var(--text-primary);
}
/* Loading animation */
.loading-spinner {
width: 2.5rem;
height: 2.5rem;
border: 3px solid #f3f3f3;
border-top: 3px solid var(--primary-color);
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.container {
background-color: var(--card-background);
border-radius: var(--border-radius);
padding: 20px;
margin-top: 20px;
}
input[type="text"], textarea {
background-color: #cbd5e1;
color: var(--text-primary);
border: 3px solid #141111;
}
button {
background-color: #4a4a4a;
color: #ffffff;
border: none;
padding: 8px 16px;
border-radius: 4px;
}
button:hover {
background-color: #5a5a5a;
}
.card {
background-color: var(--card-background);
border: 1px solid var(--border-color);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
color: var(--text-primary);
}
.card-header {
background-color: var(--sidebar-background);
color: white;
border-bottom: none;
font-weight: 600;
}
.card-body {
background-color: var(--card-background);
color: var(--text-primary);
}
/* Update input styles */
input[type="number"] {
background-color: var(--input-background);
color: var(--text-primary);
border-color: #475569;
}
input[type="number"]:focus {
background-color: var(--input-background);
color: var(--text-primary);
border-color: var(--primary-color);
box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.2);
}
/* Update button styles to match theme */
button {
background-color: var(--primary-color);
color: var(--text-primary);
border: none;
padding: 8px 16px;
border-radius: var(--border-radius);
transition: all 0.3s ease;
}
button:hover {
background-color: var(--hover-color);
transform: translateY(-1px);
}
/* Update input group styling */
.input-group-text {
background-color: var(--input-background);
color: var(--text-secondary);
border: 1px solid var(--border-color);
}
/* Update verification result styling */
.uploaded-image {
background-color: var(--card-background);
border: 1px solid var(--border-color);
border-radius: var(--border-radius);
padding: 1rem;
margin-bottom: 1rem;
}
.img-thumbnail {
background-color: var(--input-background);
border-color: #475569;
}
/* Update form label color */
.form-label {
color: var(--text-primary);
font-weight: 500;
}
/* Add styles for grade colors with better visibility */
.grade-a { color: #34d399; } /* Light Green */
.grade-b { color: #60a5fa; } /* Light Blue */
.grade-c { color: #fbbf24; } /* Light Yellow */
.grade-d { color: #fb923c; } /* Light Orange */
.grade-f { color: #f87171; } /* Light Red */
/* Update table borders */
.table-bordered > :not(caption) > * {
border-color: #475569;
}
.table-hover tbody tr:hover {
background-color: var(--input-background);
}
/* Add these styles for the facility cards */
.facility-type-cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 1.5rem;
margin-bottom: 2rem;
}
.facility-type-card {
background-color: var(--card-background);
border: 1px solid var(--border-color);
border-radius: var(--border-radius);
padding: 1.5rem;
transition: all 0.3s ease;
}
.facility-type-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
.facility-type-card h3 {
color: var(--text-primary);
margin-bottom: 1rem;
font-size: 1.25rem;
}
.facility-type-card .icon {
font-size: 2rem;
margin-bottom: 1rem;
color: var(--primary-color);
}
.model-info {
color: var(--text-secondary);
font-size: 0.875rem;
margin-bottom: 1rem;
padding: 0.5rem;
background-color: var(--input-background);
border-radius: var(--border-radius);
text-align: center;
}
/* Add/update these styles */
.upload-grid {
display: flex;
flex-direction: column;
gap: 1.5rem;
width: 100%;
max-width: 800px;
margin: 0 auto;
}
.upload-card {
background-color: var(--card-background);
border: 1px solid var(--border-color);
border-radius: var(--border-radius);
padding: 1.5rem;
width: 100%;
}
.results-grid {
display: flex;
flex-direction: column;
gap: 1.5rem;
width: 100%;
}
.result-card {
background-color: var(--card-background);
border: 1px solid var(--border-color);
border-radius: var(--border-radius);
padding: 1.5rem;
width: 100%;
}
.detections-list {
margin-top: 1rem;
border: 1px solid var(--border-color);
border-radius: var(--border-radius);
overflow: hidden;
}
.detection-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.75rem 1rem;
border-bottom: 1px solid var(--border-color);
}
.detection-item:last-child {
border-bottom: none;
}
/* Add these styles for improved object detection UI */
.detection-section {
background-color: var(--card-background);
border-radius: var(--border-radius);
padding: 2rem;
margin-bottom: 2rem;
}
.detection-header {
text-align: center;
margin-bottom: 2rem;
color: var(--text-primary);
}
.detection-header h2 {
font-size: 2rem;
margin-bottom: 1rem;
}
.upload-grid {
display: flex;
flex-direction: column;
gap: 2rem;
}
.upload-card {
background-color: var(--sidebar-background);
border: 1px solid var(--border-color);
border-radius: var(--border-radius);
padding: 2rem;
transition: all 0.3s ease;
}
.upload-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
.upload-card h3 {
color: var(--text-primary);
font-size: 1.5rem;
margin-bottom: 1rem;
display: flex;
align-items: center;
gap: 0.5rem;
}
.upload-card h3 i {
color: var(--primary-color);
}
.custom-file-upload {
background-color: var(--input-background);
border: 2px dashed var(--border-color);
border-radius: var(--border-radius);
padding: 2rem;
text-align: center;
cursor: pointer;
transition: all 0.3s ease;
display: block;
margin-bottom: 1rem;
}
.custom-file-upload:hover {
border-color: var(--primary-color);
background-color: rgba(99, 102, 241, 0.1);
}
.custom-file-upload i {
font-size: 2rem;
color: var(--primary-color);
margin-bottom: 1rem;
display: block;
}
.detection-results {
background-color: var(--card-background);
border-radius: var(--border-radius);
padding: 1.5rem;
margin-top: 2rem;
}
.detection-results h4 {
color: var(--text-primary);
margin-bottom: 1rem;
padding-bottom: 0.5rem;
border-bottom: 1px solid var(--border-color);
}
.table {
margin-bottom: 0;
}
.table thead th {
background-color: var(--input-background);
color: var(--text-primary);
font-weight: 600;
padding: 1rem;
}
.table tbody td {
padding: 1rem;
color: var(--text-secondary);
}
.confidence-badge {
background-color: var(--primary-color);
color: white;
padding: 0.25rem 0.75rem;
border-radius: 9999px;
font-size: 0.875rem;
}
#detect-all {
background-color: var(--primary-color);
color: white;
padding: 1rem 2rem;
font-size: 1.1rem;
font-weight: 600;
margin-top: 2rem;
transition: all 0.3s ease;
}
#detect-all:hover {
background-color: var(--hover-color);
transform: translateY(-2px);
}
.loading {
text-align: center;
padding: 2rem;
}
.loading-spinner {
margin: 0 auto 1rem;
}
.stats-panel {
background-color: var(--sidebar-background);
border-radius: var(--border-radius);
padding: 2rem;
margin-top: 2rem;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1.5rem;
}
.stats-item {
text-align: center;
padding: 1.5rem;
background-color: var(--card-background);
border-radius: var(--border-radius);
}
.stats-value {
font-size: 2rem;
font-weight: 700;
color: var(--primary-color);
display: block;
margin-top: 0.5rem;
}
</style>
</head>
<body>
<div class="container mt-5">
<h1 class="text-center mb-4">Facility Management System</h1>
<div class="row">
<!-- Sidebar -->
<div class="col-md-4 sidebar">
<h4>Input Form</h4>
<form id="facility-form">
<div class="mb-3">
<label for="num_students" class="form-label">Number of Students:</label>
<input type="number" id="num_students" class="form-control" required>
</div>
<div class="mb-3">
<label for="num_divisions" class="form-label">Number of Divisions:</label>
<input type="number" id="num_divisions" class="form-control" required>
</div>
<div class="mb-3">
<label for="num_courses" class="form-label">Number of Courses:</label>
<input type="number" id="num_courses" class="form-control" required>
</div>
<div class="mb-3">
<label for="course_duration" class="form-label">Course Duration (Years):</label>
<input type="number" id="course_duration" class="form-control" required>
</div>
<button type="submit" class="btn btn-primary w-100">Calculate</button>
</form>
</div>
<!-- Facilities Section -->
<div class="col-md-8">
<div id="facilities-section">
{% for facility in facilities %}
<div class="card">
<div class="card-header">{{ facility }}</div>
<div class="card-body">
<!-- Facility Requirements -->
<div class="card-body">
<!-- Facility Requirements -->
<h5>Requirements:</h5>
<div class="input-group mb-3">
<input
type="text"
id="requirement-{{ facility | replace(' ', '_') | lower }}"
class="form-control text-center"
value="0"
readonly>
<span class="input-group-text">/</span>
<input
type="number"
id="actual-{{ facility | replace(' ', '_') | lower }}"
class="form-control text-center"
placeholder="Enter Actual Count">
</div>
</div>
<!-- Upload Images -->
<h5 class="mt-4">Upload Images:</h5>
<form enctype="multipart/form-data">
<div class="input-group">
<input type="file" class="form-control" id="files-{{ facility | replace(' ', '_') | lower }}" name="images" multiple>
<button type="button" class="btn btn-success upload-btn" data-facility="{{ facility | replace(' ', '_') | lower }}">Verify Images</button>
</div>
<div id="verification-result-{{ facility | replace(' ', '_') | lower }}" class="mt-3">
<!-- Uploaded images and verification results will be displayed here -->
</div>
</form>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
</div>
<div class="container">
<div class="header">
<h1>Object Detection</h1>
</div>
<div class="toast" id="toast"></div>
<div class="debug-panel" id="debug-panel">
<div class="debug-content"></div>
</div>
<div class="main-content">
<div class="detection-section">
<div class="detection-header">
<h2>Object Detection</h2>
<p>Upload images for each facility to detect and count objects</p>
</div>
<form id="upload-form">
<div class="upload-grid">
<!-- Classroom Section -->
<div class="upload-card">
<h3><i class="fas fa-chalkboard"></i> Classroom</h3>
<label class="custom-file-upload">
<input type="file" id="image-classroom" name="image_classroom" accept="image/*" style="display: none;">
<i class="fas fa-cloud-upload-alt"></i>
<span>Drop classroom image here or click to upload</span>
</label>
<p id="file-name-classroom" class="file-name"></p>
<div class="detection-results">
<h4>Detected Objects</h4>
<table class="table">
<thead>
<tr>
<th>Object</th>
<th>Confidence</th>
<th>Count</th>
</tr>
</thead>
<tbody id="classroom-detections"></tbody>
</table>
</div>
</div>
<!-- Chemical Lab Section -->
<div class="upload-card">
<h3><i class="fas fa-flask"></i> Chemical Lab</h3>
<label class="custom-file-upload">
<input type="file" id="image-chemical-lab" name="image_chemical_lab" accept="image/*" style="display: none;">
<i class="fas fa-cloud-upload-alt"></i>
<span>Drop lab image here or click to upload</span>
</label>
<p id="file-name-chemical-lab" class="file-name"></p>
<div class="detection-results">
<h4>Detected Objects</h4>
<table class="table">
<thead>
<tr>
<th>Object</th>
<th>Confidence</th>
<th>Count</th>
</tr>
</thead>
<tbody id="chemical-lab-detections"></tbody>
</table>
</div>
</div>
<!-- Mechanical Workshop Section -->
<div class="upload-card">
<h3><i class="fas fa-tools"></i> Mechanical Workshop</h3>
<label class="custom-file-upload">
<input type="file" id="image-mechanical-workshop" name="image_mechanical_workshop" accept="image/*" style="display: none;">
<i class="fas fa-cloud-upload-alt"></i>
<span>Drop workshop image here or click to upload</span>
</label>
<p id="file-name-mechanical-workshop" class="file-name"></p>
<div class="detection-results">
<h4>Detected Objects</h4>
<table class="table">
<thead>
<tr>
<th>Object</th>
<th>Confidence</th>
<th>Count</th>
</tr>
</thead>
<tbody id="mechanical-workshop-detections"></tbody>
</table>
</div>
</div>
<!-- Computer Lab Section -->
<div class="upload-card">
<h3><i class="fas fa-desktop"></i> Computer Lab</h3>
<label class="custom-file-upload">
<input type="file" id="image-computer-lab" name="image_computer_lab" accept="image/*" style="display: none;">
<i class="fas fa-cloud-upload-alt"></i>
<span>Drop computer lab image here or click to upload</span>
</label>
<p id="file-name-computer-lab" class="file-name"></p>
<div class="detection-results">
<h4>Detected Objects</h4>
<table class="table">
<thead>
<tr>
<th>Object</th>
<th>Confidence</th>
<th>Count</th>
</tr>
</thead>
<tbody id="computer-lab-detections"></tbody>
</table>
</div>
</div>
<!-- CCTV Section -->
<div class="upload-card">
<h3><i class="fas fa-video"></i> CCTV Detection</h3>
<label class="custom-file-upload">
<input type="file" id="image-cctv" name="image_cctv" accept="image/*" style="display: none;">
<i class="fas fa-cloud-upload-alt"></i>
<span>Drop CCTV image here or click to upload</span>
</label>
<p id="file-name-cctv" class="file-name"></p>
<div class="detection-results">
<h4>Detected Objects</h4>
<table class="table">
<thead>
<tr>
<th>Object</th>
<th>Confidence</th>
<th>Count</th>
</tr>
</thead>
<tbody id="cctv-detections"></tbody>
</table>
</div>
</div>
<!-- Notice Board Section -->
<div class="upload-card">
<h3><i class="fas fa-clipboard"></i> Notice Board Detection</h3>
<label class="custom-file-upload">
<input type="file" id="image-notice-board" name="image_notice_board" accept="image/*" style="display: none;">
<i class="fas fa-cloud-upload-alt"></i>
<span>Drop notice board image here or click to upload</span>
</label>
<p id="file-name-notice-board" class="file-name"></p>
<div class="detection-results">
<h4>Detected Objects</h4>
<table class="table">
<thead>
<tr>
<th>Object</th>
<th>Confidence</th>
<th>Count</th>
</tr>
</thead>
<tbody id="notice-board-detections"></tbody>
</table>
</div>
</div>
<!-- Bench Section -->
<div class="upload-card">
<h3><i class="fas fa-chair"></i> Bench Detection</h3>
<label class="custom-file-upload">
<input type="file" id="image-bench" name="image_bench" accept="image/*" style="display: none;">
<i class="fas fa-cloud-upload-alt"></i>
<span>Drop bench image here or click to upload</span>
</label>
<p id="file-name-bench" class="file-name"></p>
<div class="detection-results">
<h4>Detected Objects</h4>
<table class="table">
<thead>
<tr>
<th>Object</th>
<th>Confidence</th>
<th>Count</th>
</tr>
</thead>
<tbody id="bench-detections"></tbody>
</table>
</div>
</div>
</div>
<button type="submit" class="btn btn-primary w-100" id="detect-all">
<i class="fas fa-search"></i>
Detect Objects in All Images
</button>
</form>
</div>
<div class="error-message" id="error-message"></div>
<div class="loading" id="loading">
<div class="loading-spinner"></div>
<p>Analyzing image...</p>
</div>
<div class="stats-panel" id="stats-panel" style="display: none;">
<h3>Analysis Summary</h3>
<div class="stats-grid">
<div class="stats-item">
<span>Total Objects Detected</span>
<span id="total-detections" class="stats-value">0</span>
</div>
<div class="stats-item">
<span>Processing Duration</span>
<span id="processing-time" class="stats-value">0ms</span>
</div>
</div>
</div>
<div class="image-container">
<div id="result">
<img id="result-image" alt="Detection result">
<div class="detection-count" id="detection-count" style="display: none;"></div>
</div>
</div>
</div>
<div class="sidebar">
<h2 class="detections-title">Detected Objects</h2>
<div id="detections"></div>
</div>
</div>
<div class="card mt-5">
<div class="card-header">
Deficiency Table
</div>
<div class="card-body">
<table class="table table-bordered table-hover text-center" id="deficiency-table">
<thead class="table-primary">
<tr>
<th>Facility Name</th>
<th>Required Count</th>
<th>Actual Count</th>
<th>Number of Images Verified</th>
<th>Status</th>
</tr>
</thead>
<tbody id="deficiency-table-body">
<!-- Dynamic rows will be added here -->
</tbody>
</table>
</div>
</div>
<div class="grade-display-section text-center mt-4">
<h3>Your Grade:</h3>
<div id="grade-display" class="grade-display"></div>
</div>
<div class="text-center mt-4">
<h3>Download Reports</h3>
<button id="download-pdf" class="btn btn-primary">Download PDF Report</button>
<button id="download-excel" class="btn btn-success">Download Excel Report</button>
</div>
<script>
$('#facility-form').on('submit', function (e) {
e.preventDefault();
const data = {
num_students: $('#num_students').val(),
num_divisions: $('#num_divisions').val(),
num_courses: $('#num_courses').val(),
course_duration: $('#course_duration').val(),
};
$.ajax({
url: '/calculate',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify(data),
success: function (response) {
for (const [facility, value] of Object.entries(response)) {
const facilityId = facility.replace(/ /g, '_').toLowerCase();
$(`#requirement-${facilityId}`).val(value);
// Optionally, clear or prefill the "Actual Count" input box
$(`#actual-${facilityId}`).val('');
}
},
error: function () {
alert('Failed to calculate facilities. Please try again.');
}
});
});
// Capture "Actual Count" values for further processing
function getActualCounts() {
const actualCounts = {};
$('.card').each(function () {
const facility = $(this).find('.card-header').text().trim();
const facilityId = facility.replace(/ /g, '_').toLowerCase();
const actualCount = $(`#actual-${facilityId}`).val();
actualCounts[facility] = actualCount ? parseInt(actualCount, 10) : 0;
});
console.log(actualCounts);
// Perform additional processing with `actualCounts` as needed
}
// Handle Multiple File Upload
$('.upload-btn').on('click', function () {
const facility = $(this).data('facility');
const fileInput = $(`#files-${facility}`)[0];
const files = fileInput.files;
if (files.length === 0) {
alert('Please select at least one image.');
return;
}
const formData = new FormData();
Array.from(files).forEach((file) => {
formData.append('images', file);
});
$.ajax({
url: `/upload/${facility.replace(/_/g, ' ')}`,
type: 'POST',
processData: false,
contentType: false,
data: formData,
success: function (response) {
let resultsHtml = '';
response.forEach((result, index) => {
const fileReader = new FileReader();
// Render each image
fileReader.onload = function (e) {
resultsHtml += `
<div class="uploaded-image">
<img src="${e.target.result}" alt="Uploaded Image" class="img-thumbnail" style="max-width: 200px;">
<p class="text-center mt-2">
<strong>Label:</strong> ${result.label}<br>
<strong>Confidence:</strong> ${result.confidence.toFixed(2)}
</p>
<p class="text-center ${result.confidence >= 0.8 ? 'text-success' : 'text-danger'}">
${result.confidence >= 0.8 ? 'Verified' : 'Not Verified'}
</p>
</div>
`;
// Update the results section for the facility after all images are processed
if (index === response.length - 1) {
$(`#verification-result-${facility}`).html(resultsHtml);
}
};
// Read the file to get its data URL
fileReader.readAsDataURL(files[index]);
});
},
error: function () {
$(`#verification-result-${facility}`).html(`
<div class="alert alert-danger">Failed to verify images.</div>
`);
}
});
});
// Handle Answer Submission
$('.submit-answers').on('click', function () {
const facility = $(this).data('facility');
const answers = [];
$(this).closest('.question-form').find('.answer-box').each(function () {
answers.push({
question: $(this).data('question'),
answer: $(this).val()
});
});
$.ajax({
url: '/submit_answers',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({ facility: facility, answers: answers }),
success: function (response) {
alert(response.message);
},
error: function () {
alert('Failed to submit answers.');
}
});
});
const loadingDiv = document.getElementById('loading');
const errorMessage = document.getElementById('error-message');
const form = document.getElementById('upload-form');
const detectButton = form.querySelector('button[type="submit"]');
const imageInputs = {
classroom: document.getElementById('image-classroom'),
chemical_lab: document.getElementById('image-chemical-lab'),
mechanical_workshop: document.getElementById('image-mechanical-workshop'),
computer_lab: document.getElementById('image-computer-lab'),
cctv: document.getElementById('image-cctv'),
notice_board: document.getElementById('image-notice-board'),
bench: document.getElementById('image-bench')
};
// Handle file selection for each input
Object.entries(imageInputs).forEach(([key, input]) => {
input.addEventListener('change', (e) => {
const file = e.target.files[0];
const fileNameElement = document.getElementById(`file-name-${key.replace('_', '-')}`);
if (file) {
fileNameElement.textContent = `Selected: ${file.name}`;
updateDetectButton();
} else {
fileNameElement.textContent = '';
}
// Clear previous results when new file is selected
clearResults();
});
});
function clearResults() {
const resultContainer = document.getElementById('result');
resultContainer.innerHTML = '<img id="result-image" alt="Detection result" style="display: none;">';
document.getElementById('stats-panel').style.display = 'none';
errorMessage.style.display = 'none';
}
function updateDetectButton() {
const allFilesSelected = Object.values(imageInputs).every(input => input.files.length > 0);
detectButton.disabled = !allFilesSelected;
}
form.addEventListener('submit', async (e) => {
e.preventDefault();
const formData = new FormData();
let allFilesValid = true;
// Clear previous results
clearResults();
Object.entries(imageInputs).forEach(([key, input]) => {
const file = input.files[0];
if (!file) {
showToast(`Please select an image for ${key.replace('_', ' ')}`, 'error');
allFilesValid = false;
return;
}
formData.append(`image_${key}`, file);
});
if (!allFilesValid) return;
const startTime = performance.now();
loadingDiv.style.display = 'block';
detectButton.disabled = true;
errorMessage.style.display = 'none';
try {
const response = await fetch('/detect', {
method: 'POST',
body: formData
});
const data = await response.json();
if (data.success) {
const processingTime = Math.round(performance.now() - startTime);
displayResults(data.results, processingTime);
} else {
errorMessage.textContent = data.error || 'Error processing images';
errorMessage.style.display = 'block';
showToast(data.error || 'Error processing images', 'error');
}
} catch (error) {
errorMessage.textContent = 'Network error or server not responding';
errorMessage.style.display = 'block';
showToast('Network error or server not responding', 'error');
console.error('Error:', error);
} finally {
loadingDiv.style.display = 'none';
detectButton.disabled = false;
}
});
function displayResults(results, processingTime) {
const resultContainer = document.getElementById('result');
resultContainer.innerHTML = '<div class="results-grid"></div>';
const grid = resultContainer.querySelector('.results-grid');
let totalDetections = 0;
Object.entries(results).forEach(([environment, data]) => {
const card = document.createElement('div');
card.className = 'result-card';
// Format the environment name for display
const envName = environment.split('_')
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
.join(' ');
card.innerHTML = `
<h3>${envName}</h3>
<img src="${data.image}" alt="${environment}" style="width: 100%">
<div class="detections-count">${data.detections.length} objects detected</div>
<div class="detections-list">
${data.detections.map(det => `
<div class="detection-item">
<span>${det.class}</span>
<span class="confidence-badge">${(det.confidence * 100).toFixed(1)}%</span>
</div>
`).join('')}
</div>
`;
grid.appendChild(card);
totalDetections += data.detections.length;
});
// Update stats panel
document.getElementById('total-detections').textContent = totalDetections;
document.getElementById('processing-time').textContent = `${processingTime}ms`;
document.getElementById('stats-panel').style.display = 'block';
showToast(`Processing completed in ${processingTime}ms`);
}
function showToast(message, type = 'success') {
const toast = document.getElementById('toast');
toast.textContent = message;
toast.className = `toast ${type}`;
toast.style.display = 'block';
setTimeout(() => {
toast.style.display = 'none';
}, 3000);
}
function debugLog(message) {
const debugPanel = document.getElementById('debug-panel');
const content = debugPanel.querySelector('.debug-content');
const timestamp = new Date().toLocaleTimeString();
content.innerHTML += `[${timestamp}] ${message}<br>`;
content.scrollTop = content.scrollHeight;
}
// Handle window resize
let resizeTimeout;
window.addEventListener('resize', () => {
clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(() => {
const highlightBoxes = document.querySelectorAll('.highlight-box');
const labels = document.querySelectorAll('.detection-label');
highlightBoxes.forEach(box => box.style.display = 'none');
labels.forEach(label => label.style.display = 'none');
}, 250);
});
// Add debug panel toggle
document.addEventListener('keypress', (e) => {
if (e.key === 'd') {
const debugPanel = document.getElementById('debug-panel');
debugPanel.classList.toggle('show');
}
});
function updateDeficiencyTableAndGrade() {
const tableBody = $('#deficiency-table-body');
tableBody.empty(); // Clear existing rows
let totalDeficiencies = 0;
let totalVerifiedImages = 0;
const totalFacilities = $('.card').length;
$('.card').each(function () {
const facilityName = $(this).find('.card-header').text().trim();
const facilityId = facilityName.replace(/ /g, '_').toLowerCase();
const requiredCount = parseInt($(`#requirement-${facilityId}`).val(), 10) || 0;
const actualCount = parseInt($(`#actual-${facilityId}`).val(), 10) || 0;
// Count verified images
const verifiedImages = $(`#verification-result-${facilityId} .uploaded-image .text-success`).length;
totalVerifiedImages += verifiedImages;
// Determine deficiency
const deficiency = requiredCount > actualCount;
if (deficiency) totalDeficiencies++;
const status = deficiency
? `<span class="text-danger">&#10008; Deficiency</span>`
: `<span class="text-success">&#10004; No Deficiency</span>`;
// Add a new row to the table
tableBody.append(`
<tr>
<td>${facilityName}</td>
<td>${requiredCount}</td>
<td>${actualCount}</td>
<td>${verifiedImages}</td>
<td>${status}</td>
</tr>
`);
});
// Calculate grade
const grade = calculateGrade(totalDeficiencies, totalVerifiedImages, totalFacilities);
// Update grade display
const gradeDisplay = $('#grade-display');
gradeDisplay
.removeClass('grade-a grade-b grade-c grade-d grade-f')
.addClass(`grade-${grade.toLowerCase()}`)
.text(grade);
}
function calculateGrade(totalDeficiencies, totalVerifiedImages, totalFacilities) {
const deficiencyRate = totalDeficiencies / totalFacilities;
const verificationBonus = totalVerifiedImages / totalFacilities;
if (deficiencyRate <= 0.1 && verificationBonus >= 0.8) return 'A';
if (deficiencyRate <= 0.2 && verificationBonus >= 0.6) return 'B';
if (deficiencyRate <= 0.3 && verificationBonus >= 0.4) return 'C';
if (deficiencyRate <= 0.4 && verificationBonus >= 0.2) return 'D';
return 'F';
}
// Attach event listeners
$('#facility-form').on('submit', function (e) {
e.preventDefault();
updateDeficiencyTableAndGrade();
});
$('.form-control').on('input', function () {
updateDeficiencyTableAndGrade();
});
$('.upload-btn').on('click', function () {
setTimeout(() => {
updateDeficiencyTableAndGrade();
}, 1000); // Allow time for verification results to render
});
// Handle PDF download
$('#download-pdf').on('click', function () {
window.location.href = '/download_report';
});
// Handle Excel download
$('#download-excel').on('click', function () {
window.location.href = '/download_excel';
});
// Add FontAwesome for icons
document.head.innerHTML += '<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">';
</script>
</body>
</html>