File "update_single_allocation.php"
Full path: /home2/santhos2/public_html/bharath-coe.firstmatrix.in.net/api/invigilation/update_single_allocation.php
File size: 11.63 KiB (11913 bytes)
MIME-type: text/x-php
Charset: utf-8
<?php
require_once '../../config/config.php';
require_once '../helper/audit_log.php';
header('Content-Type: application/json');
// Include the invigilator assignment function with consecutive day prevention
function assignInvigilatorsToHall($pdo, $timetableId, $hallId, $numStudents) {
try {
// Clear existing invigilation duties for this hall and timetable
$clearStmt = $pdo->prepare("DELETE FROM invigilation_duty WHERE timetable_id = ? AND hall_id = ?");
$clearStmt->execute([$timetableId, $hallId]);
// Get available invigilators (staff with role 'Invigilator' and status 'Active')
$invigilatorsStmt = $pdo->prepare("
SELECT staff_id, first_name, last_name, staff_code
FROM staff
WHERE role = 'Invigilator' AND status = 'Active'
ORDER BY RAND()
");
$invigilatorsStmt->execute();
$invigilators = $invigilatorsStmt->fetchAll(PDO::FETCH_ASSOC);
if (empty($invigilators)) {
error_log("No active invigilators found");
return 0;
}
// Calculate number of invigilators needed based on hall capacity
// Rule: 1 invigilator per 30 students, minimum 1, maximum 3
$invigilatorsNeeded = max(1, min(3, ceil($numStudents / 30)));
// Select invigilators (avoid duplicates and consecutive day assignments)
$selectedInvigilators = [];
$usedInvigilators = [];
// Get invigilators already assigned to other halls in same timetable
$usedStmt = $pdo->prepare("
SELECT DISTINCT staff_id
FROM invigilation_duty
WHERE timetable_id = ? AND hall_id != ?
");
$usedStmt->execute([$timetableId, $hallId]);
$usedInvigilators = $usedStmt->fetchAll(PDO::FETCH_COLUMN);
// Get invigilators assigned to same hall on consecutive days (previous day)
$consecutiveDayStmt = $pdo->prepare("
SELECT DISTINCT id.staff_id
FROM invigilation_duty id
JOIN exam_timetable et ON id.timetable_id = et.timetable_id
JOIN exam_timetable current_et ON current_et.timetable_id = ?
WHERE id.hall_id = ?
AND et.exam_date = DATE_SUB(current_et.exam_date, INTERVAL 1 DAY)
AND et.session = current_et.session
");
$consecutiveDayStmt->execute([$timetableId, $hallId]);
$consecutiveDayInvigilators = $consecutiveDayStmt->fetchAll(PDO::FETCH_COLUMN);
// Combine both exclusion lists
$excludedInvigilators = array_unique(array_merge($usedInvigilators, $consecutiveDayInvigilators));
foreach ($invigilators as $invigilator) {
if (count($selectedInvigilators) >= $invigilatorsNeeded) break;
// Skip if already assigned to another hall in same timetable OR same hall on consecutive day
if (in_array($invigilator['staff_id'], $excludedInvigilators)) continue;
$selectedInvigilators[] = $invigilator;
}
// If we don't have enough unique invigilators, assign any available ones
// (This fallback may include consecutive day assignments if no other options)
if (count($selectedInvigilators) < $invigilatorsNeeded) {
foreach ($invigilators as $invigilator) {
if (count($selectedInvigilators) >= $invigilatorsNeeded) break;
if (!in_array($invigilator['staff_id'], array_column($selectedInvigilators, 'staff_id'))) {
$selectedInvigilators[] = $invigilator;
}
}
}
// Assign invigilators to the hall
$dutyStmt = $pdo->prepare("
INSERT INTO invigilation_duty (staff_id, timetable_id, hall_id, role, status)
VALUES (?, ?, ?, ?, 'Assigned')
");
foreach ($selectedInvigilators as $index => $invigilator) {
$role = ($index === 0) ? 'Chief Invigilator' : 'Invigilator';
$dutyStmt->execute([
$invigilator['staff_id'],
$timetableId,
$hallId,
$role
]);
}
error_log("Assigned " . count($selectedInvigilators) . " invigilators to hall $hallId for timetable $timetableId");
// Log invigilator assignment
write_audit_log($pdo, [
'module' => 'Hall Allocations & Invigilation',
'action' => 'create',
'entity_table' => 'invigilation_duty',
'entity_id' => 'hall_' . $hallId . '_timetable_' . $timetableId,
'after_data' => [
'hall_id' => $hallId,
'timetable_id' => $timetableId,
'num_students' => $numStudents,
'invigilators_assigned' => count($selectedInvigilators),
'invigilators' => $selectedInvigilators,
'consecutive_day_prevention' => count($consecutiveDayInvigilators) > 0,
'excluded_consecutive_invigilators' => count($consecutiveDayInvigilators)
],
'message' => 'Assigned invigilators to hall allocation (with consecutive day prevention)'
]);
return count($selectedInvigilators);
} catch (Exception $e) {
error_log("Error assigning invigilators: " . $e->getMessage());
return 0;
}
}
try {
$allocationId = (int)($_POST['allocation_id'] ?? 0);
if ($allocationId <= 0) {
throw new Exception('Valid allocation ID is required');
}
// Get allocation details
$allocationStmt = $pdo->prepare("
SELECT
ha.allocation_id,
ha.timetable_id,
ha.hall_id,
ha.num_students,
h.hall_name,
et.exam_date,
et.session,
sub.subject_code,
sub.subject_name
FROM hall_allocations ha
JOIN halls h ON ha.hall_id = h.hall_id
JOIN exam_timetable et ON ha.timetable_id = et.timetable_id
JOIN exam_subjects es ON et.exam_subject_id = es.exam_subject_id
JOIN subjects sub ON es.subject_id = sub.subject_id
WHERE ha.allocation_id = ?
");
$allocationStmt->execute([$allocationId]);
$allocation = $allocationStmt->fetch(PDO::FETCH_ASSOC);
if (!$allocation) {
throw new Exception('Hall allocation not found');
}
// Update invigilators for this allocation
$invigilatorsAssigned = updateInvigilatorsForAllocation(
$pdo,
(int)$allocation['timetable_id'],
(int)$allocation['hall_id'],
(int)$allocation['num_students']
);
// Log the single allocation update
write_audit_log($pdo, [
'module' => 'Hall Allocations & Invigilation',
'action' => 'update',
'entity_table' => 'invigilation_duty',
'entity_id' => 'allocation_' . $allocationId,
'after_data' => [
'allocation_id' => $allocationId,
'hall_name' => $allocation['hall_name'],
'invigilators_assigned' => $invigilatorsAssigned,
'num_students' => $allocation['num_students'],
'exam_details' => [
'subject' => $allocation['subject_code'] . ' - ' . $allocation['subject_name'],
'date' => $allocation['exam_date'],
'session' => $allocation['session']
]
],
'message' => 'Updated invigilators for single hall allocation'
]);
echo json_encode([
'status' => 'success',
'message' => 'Invigilators updated successfully for this allocation',
'allocation_id' => $allocationId,
'hall_name' => $allocation['hall_name'],
'invigilators_assigned' => $invigilatorsAssigned,
'num_students' => $allocation['num_students'],
'exam_details' => [
'subject' => $allocation['subject_code'] . ' - ' . $allocation['subject_name'],
'date' => $allocation['exam_date'],
'session' => $allocation['session']
]
]);
} catch (Exception $e) {
http_response_code(500);
echo json_encode([
'status' => 'error',
'message' => $e->getMessage()
]);
}
/**
* Update invigilators for a specific hall allocation
* @param PDO $pdo Database connection
* @param int $timetableId Timetable ID
* @param int $hallId Hall ID
* @param int $numStudents Number of students in the hall
* @return int Number of invigilators assigned
*/
function updateInvigilatorsForAllocation($pdo, $timetableId, $hallId, $numStudents) {
try {
// Clear existing invigilation duties for this hall and timetable
$clearStmt = $pdo->prepare("DELETE FROM invigilation_duty WHERE timetable_id = ? AND hall_id = ?");
$clearStmt->execute([$timetableId, $hallId]);
// Get available invigilators (staff with role 'Invigilator')
$invigilatorsStmt = $pdo->prepare("
SELECT staff_id, first_name, last_name, staff_code
FROM staff
WHERE role = 'Invigilator' AND status = 'Active'
ORDER BY RAND()
");
$invigilatorsStmt->execute();
$invigilators = $invigilatorsStmt->fetchAll(PDO::FETCH_ASSOC);
if (empty($invigilators)) {
error_log("No invigilators available for hall $hallId");
return 0;
}
// Calculate number of invigilators needed based on hall capacity
// Rule: 1 invigilator per 30 students, minimum 1, maximum 3
$invigilatorsNeeded = max(1, min(3, ceil($numStudents / 30)));
// Select invigilators (avoid duplicates by checking existing duties for same timetable)
$selectedInvigilators = [];
$usedInvigilators = [];
// Get invigilators already assigned to other halls in same timetable
$usedStmt = $pdo->prepare("
SELECT DISTINCT staff_id
FROM invigilation_duty
WHERE timetable_id = ? AND hall_id != ?
");
$usedStmt->execute([$timetableId, $hallId]);
$usedInvigilators = $usedStmt->fetchAll(PDO::FETCH_COLUMN);
foreach ($invigilators as $invigilator) {
if (count($selectedInvigilators) >= $invigilatorsNeeded) break;
// Skip if already assigned to another hall in same timetable
if (in_array($invigilator['staff_id'], $usedInvigilators)) continue;
$selectedInvigilators[] = $invigilator;
}
// If we don't have enough unique invigilators, assign any available ones
if (count($selectedInvigilators) < $invigilatorsNeeded) {
foreach ($invigilators as $invigilator) {
if (count($selectedInvigilators) >= $invigilatorsNeeded) break;
if (!in_array($invigilator['staff_id'], array_column($selectedInvigilators, 'staff_id'))) {
$selectedInvigilators[] = $invigilator;
}
}
}
// Assign invigilators to the hall
$dutyStmt = $pdo->prepare("
INSERT INTO invigilation_duty (staff_id, timetable_id, hall_id, role, status)
VALUES (?, ?, ?, ?, 'Assigned')
");
foreach ($selectedInvigilators as $index => $invigilator) {
$role = ($index === 0) ? 'Chief Invigilator' : 'Invigilator';
$dutyStmt->execute([
$invigilator['staff_id'],
$timetableId,
$hallId,
$role
]);
}
error_log("Updated invigilators for hall $hallId: " . count($selectedInvigilators) . " invigilators assigned");
return count($selectedInvigilators);
} catch (Exception $e) {
error_log("Error updating invigilators for hall $hallId: " . $e->getMessage());
return 0;
}
}
?>