/home/techb158/workloadmatch.com/workloadmatch.com/BackUp/Manager
Edit: /home/techb158/workloadmatch.com/workloadmatch.com/BackUp/Manager/Create_Schedule_Algorithm_1.php (41412B)
prepare("SELECT Program_ID, Program_Name FROM Programs");
$stmtGroup->execute();
$programData = $stmtGroup->get_result()->fetch_all(MYSQLI_ASSOC);
$stmtGroup->close();
// =========================================
// 3. Define Utility Functions (Important Modularization)
// =========================================
// Build calendar excluding holidays, retakes, and July
function build_schedule_calendar($Start_Date, $End_Date, $classDays, $timeSlots, $mysqli, $Program_ID, $Group_ID) {
$start = new DateTime($Start_Date);
$end = $End_Date ? new DateTime($End_Date) : new DateTime('+1 year');
$calendar = [];
$holidayDates = get_holidays($mysqli, $Start_Date);
$retakeDates = get_retake_dates($mysqli, $Program_ID, $Group_ID);
$cur = clone $start;
while ($cur <= $end) {
$day = $cur->format('l');
$dateStr = $cur->format('Y-m-d');
$month = (int) $cur->format('m');
if ($month !== 7 && in_array($day, $classDays) && !in_array($dateStr, $holidayDates) && !in_array($dateStr, $retakeDates)) {
foreach ($timeSlots as $slot) {
$calendar[$dateStr][trim($slot)] = 'free';
}
}
$cur->modify('+1 day');
}
return $calendar;
}
// =========================================
// 4. Handle POST Request to Generate Schedule
// =========================================
// Format the label of the time slot, especially for Saturday sessions
function format_time_slot_label($slot, $day) {
if ($day === 'Saturday') {
if (stripos($slot, 'evening') !== false) return 'Morning (Sat)';
if (stripos($slot, 'morning') !== false) return 'Morning';
}
return $slot;
}
// Format the time range of the session based on the slot and the day
function format_time_slot_range($slot, $start, $end, $day) {
if ($day === 'Saturday') {
if (stripos($slot, 'morning') !== false) return "08:30 AM - 12:30 PM";
if (stripos($slot, 'evening') !== false) return "09:00 AM - 01:00 PM";
}
return date('h:i A', strtotime($start)) . ' - ' . date('h:i A', strtotime($end));
}
// Get group configuration from database
function get_group_info($mysqli, $Group_ID) {
$stmt = $mysqli->prepare("SELECT Time_Slot, Time_From, Time_To, class_days, Weekend_Class, Group_Name FROM Manager_Group_Name WHERE Group_ID = ?");
$stmt->bind_param("i", $Group_ID);
$stmt->execute();
return $stmt->get_result()->fetch_assoc();
}
// Retrieve time slots for the group
function fetch_slot_times($mysqli, $slotLabels, $managerStart, $managerEnd) {
$timeFrom = [];
$timeTo = [];
foreach ($slotLabels as $label) {
$label = trim($label);
$stmt = $mysqli->prepare("SELECT Time_From, Time_To FROM Time_Slot_Programs WHERE Time_Slot = ?");
$stmt->bind_param("s", $label);
$stmt->execute();
$result = $stmt->get_result();
$validSlotFound = false;
while ($row = $result->fetch_assoc()) {
$slotStart = new DateTime($row['Time_From']);
$slotEnd = new DateTime($row['Time_To']);
if ($slotStart >= $managerStart && $slotEnd <= $managerEnd) {
$timeFrom[] = $slotStart->format("H:i:s");
$timeTo[] = $slotEnd->format("H:i:s");
$validSlotFound = true;
break;
}
}
if (!$validSlotFound && count($slotLabels) === 1) {
$timeFrom[] = $managerStart->format("H:i:s");
$timeTo[] = $managerEnd->format("H:i:s");
}
}
return [$timeFrom, $timeTo];
}
// Collect all holiday dates in the current year
function get_holidays($mysqli, $Start_Date) {
$holidays = [];
$res = $mysqli->query("SELECT Event_Title, Event_Start, Event_End FROM Events WHERE Calendar_Year = YEAR('$Start_Date')");
while ($row = $res->fetch_assoc()) {
$start = new DateTime($row['Event_Start']);
$end = new DateTime($row['Event_End']);
while ($start <= $end) {
$dateStr = $start->format('Y-m-d');
$holidays[$dateStr] = $row['Event_Title']; // ๐ include the title
$start->modify('+1 day');
}
}
return $holidays;
}
// Collect all retake dates for the group and program
function get_retake_dates($mysqli, $Program_ID, $Group_ID) {
$retakeDates = [];
$stmt = $mysqli->prepare("SELECT Type, Retake_Date FROM Retake_Records WHERE Program_ID = ? AND Group_ID = ?");
$stmt->bind_param("ii", $Program_ID, $Group_ID);
$stmt->execute();
$res = $stmt->get_result();
while ($row = $res->fetch_assoc()) {
$retakeDates[$row['Retake_Date']] = $row['Type']; // e.g., ['2025-09-10' => 'Midterm']
}
return $retakeDates;
}
// Generate all valid class dates, excluding holidays, retakes, and July
function generate_valid_dates($Start_Date, $End_Date, $classDays, $holidayDates, $retakeDates) {
$validDates = [];
$cur = new DateTime($Start_Date);
$end = $End_Date ? new DateTime($End_Date) : null;
while (!$end || $cur <= $end) {
$day = $cur->format('l');
$dateStr = $cur->format('Y-m-d');
$month = (int) $cur->format('m');
if ($month === 7 || !in_array($day, $classDays) || array_key_exists($dateStr, $holidayDates) || array_key_exists($dateStr, $retakeDates)) {
$cur->modify('+1 day');
continue;
}
$validDates[] = $dateStr;
$cur->modify('+1 day');
if ($end === null && count($validDates) > 365) break; // Prevent infinite loop in fallback
}
return $validDates;
}
// Calculate the average duration of one session
function calculate_session_length($timeFrom, $timeTo) {
$sessionLength = 0;
foreach ($timeFrom as $i => $from) {
$fromTime = new DateTime($from);
$toTime = new DateTime($timeTo[$i]);
$sessionLength += ($toTime->getTimestamp() - $fromTime->getTimestamp()) / 3600;
}
$slotCount = count($timeFrom);
return $slotCount > 0 ? $sessionLength / $slotCount : 3;
}
// Calculate the number of sessions required for each course
function calculate_course_sessions($mysqli, $Program_ID, $sessionLength) {
$courseSessions = [];
$res = $mysqli->query("SELECT Course_ID, Course_Time FROM Courses WHERE Program_ID = $Program_ID");
while ($row = $res->fetch_assoc()) {
$courseID = $row['Course_ID'];
$courseHours = $row['Course_Time'];
$courseSessions[$courseID] = ceil($courseHours / $sessionLength);
}
return $courseSessions;
}
// โ
You can now call these functions in your main POST logic to keep code readable and modular.
// Helper function for fair rotation
function getNextCourseForSlot($remainingSessions, $courseList, $usedToday) {
$available = array_filter($courseList, function($courseID) use ($remainingSessions) {
return ($remainingSessions[$courseID] ?? 0) > 0;
});
$unusedToday = array_filter($available, fn($courseID) => !in_array($courseID, $usedToday));
if (!empty($unusedToday)) {
return reset($unusedToday);
}
// Allow same course in both slots if only one remains
if (count($available) === 1) {
return reset($available);
}
return null;
}
$scheduleOutput = []; // Default
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
//if (isset($_POST['reset']) || isset($_POST['Start_Date'])) {
// unset($_SESSION['deletedSaturdays']);
//}
// Step 1: Retrieve Form Data
$Program_ID = intval($_POST['Program_ID']);
$Group_ID = intval($_POST['Group_ID']);
$Start_Date = $_POST['Start_Date'];
$End_Date = $_POST['End_Date'] ?? date('Y-m-d', strtotime('+1 year', strtotime($Start_Date)));
$selectedCourses = $_POST['Selected_Courses'] ?? [];
$courseSessions = $_POST['Course_Sessions'] ?? [];
$priorityCourses = $_POST['Priority_Course'] ?? [];
$group = get_group_info($mysqli, $Group_ID);
if (!$group) {
echo "Group info not found.";
exit;
}
$classDays = explode(',', $group['class_days']);
if (!$group['Weekend_Class'] && in_array('Saturday', $classDays)) {
$classDays = array_filter($classDays, fn($day) => $day !== 'Saturday');
}
$slotLabels = explode(',', $group['Time_Slot']);
$managerStart = new DateTime(trim($group['Time_From']));
$managerEnd = new DateTime(trim($group['Time_To']));
//list($timeFrom, $timeTo) = fetch_slot_times($mysqli, $slotLabels, $managerStart, $managerEnd);
$stmt = $mysqli->prepare("SELECT Time_Slot, Time_From, Time_To FROM Group_Slot_Mapping WHERE Group_ID = ?");
$stmt->bind_param("i", $Group_ID);
$stmt->execute();
$res = $stmt->get_result();
$slotLabels = $timeFrom = $timeTo = [];
while ($row = $res->fetch_assoc()) {
$slotLabels[] = $row['Time_Slot'];
$timeFrom[] = $row['Time_From'];
$timeTo[] = $row['Time_To'];
}
$sessionLength = calculate_session_length($timeFrom, $timeTo);
$courseSessions = calculate_course_sessions($mysqli, $Program_ID, $sessionLength);
$holidayDates = get_holidays($mysqli, $Start_Date);
$retakeDates = get_retake_dates($mysqli, $Program_ID, $Group_ID);
$validDates = generate_valid_dates($Start_Date, $End_Date, $classDays, $holidayDates, $retakeDates);
//echo "
๐
Valid Dates for Scheduling (Excluding July) "; print_r($validDates); echo " ";
// ๐ Additional scheduling logic ....
// Output valid dates
//echo "
๐
Valid Dates for Scheduling (Excluding July) "; print_r($validDates); echo " ";
$totalSessionsNeeded = array_sum($courseSessions);
$availableDays = count($validDates);
// Estimate sessions per day (based on number of slots)
$sessionsPerDay = count($slotLabels);
// Calculate how many days are needed to finish all sessions
$daysRequired = ceil($totalSessionsNeeded / $sessionsPerDay);
// Get expected end date
$expectedEndDate = $validDates[$daysRequired - 1] ?? end($validDates); // fallback to last available if out of bounds
//echo "
๐
Expected End Date Based on Andragogical Days ";
//echo "
$expectedEndDate (" . (new DateTime($expectedEndDate))->format('l') . ")
";
// Sort by course priority
usort($selectedCourses, function ($a, $b) use ($priorityCourses) {
$priorityA = $priorityCourses[$a] ?? PHP_INT_MAX;
$priorityB = $priorityCourses[$b] ?? PHP_INT_MAX;
return $priorityA <=> $priorityB;
});
// echo "
๐ Sorted Courses (Priority First) "; print_r($selectedCourses); echo " ";
// Prepare for fair scheduling (one course per slot)
// Initialize
$schedule = [];
$remainingSessions = [];
foreach ($selectedCourses as $courseID) {
$remainingSessions[$courseID] = $courseSessions[$courseID] ?? 0;
}
// Queue of all courses
$courseQueue = $selectedCourses;
$currentCourses = [
0 => null, // Slot 1 (e.g. Morning)
1 => null // Slot 2 (e.g. Afternoon)
];
// Assign initial two courses
// Assign initial courses to slots without repeating
$courseQueueCopy = $selectedCourses;
$assignedCourses = [];
foreach ([0, 1] as $slotIndex) {
foreach ($courseQueueCopy as $courseID) {
if (!in_array($courseID, $assignedCourses) && $remainingSessions[$courseID] > 0) {
$currentCourses[$slotIndex] = $courseID;
$assignedCourses[] = $courseID;
break;
}
}
}
// Fetch all course names into array
$courseNames = [];
$courseRes = $mysqli->query("SELECT Course_ID, Course_Name FROM Courses");
while ($row = $courseRes->fetch_assoc()) {
$courseNames[$row['Course_ID']] = $row['Course_Name'];
}
// Start scheduling loop
foreach ($validDates as $date) {
$usedToday = []; // Reset per day
$dayName = (new DateTime($date))->format('l');
foreach ([0, 1] as $slotIndex) {
$from = $timeFrom[$slotIndex] ?? null;
$to = $timeTo[$slotIndex] ?? null;
if (!$from || !$to) continue;
$slotLabel = $slotLabels[$slotIndex];
$courseID = getNextCourseForSlot($remainingSessions, $selectedCourses, $usedToday);
if (!$courseID) continue;
$courseName = $courseNames[$courseID] ?? "Course #$courseID";
// Save the scheduled session
$schedule[] = [
'Date' => $date,
'Slot' => "Slot " . ($slotIndex + 1),
'Time' => format_time_slot_range($slotLabel, $from, $to, $dayName),
'Course_ID' => $courseID
];
$remainingSessions[$courseID]--;
$usedToday[] = $courseID;
// Stop if all sessions are done
if (array_sum($remainingSessions) <= 0) {
break 2; // exits both foreach loops
}
}
}
// ๐ Identify last scheduled session for each course
$lastSessions = [];
foreach ($schedule as $index => $entry) {
$courseID = $entry['Course_ID'];
$lastSessions[$courseID] = $index; // keeps overwriting until the last index
}
//echo "
๐ Final Generated Schedule "; print_r($schedule); echo " ";
//echo "
๐ Generated Schedule Table ";
//echo "
Group: " . htmlspecialchars($groupName) . "
";
if (!empty($schedule)) {
// Collect all displayed dates: scheduled, holidays, and retakes
$displayDates = [];
// 1. From scheduled sessions
foreach ($schedule as $row) {
$displayDates[$row['Date']][] = $row; // grouped by date
}
// 2. Add holidays (if no course scheduled on them)
foreach ($holidayDates as $hDate => $title) {
if (!isset($displayDates[$hDate])) {
$displayDates[$hDate] = []; // Add empty row so we render it
}
}
// 3. Add retakes
foreach ($retakeDates as $rDate => $type) {
if (!isset($displayDates[$rDate])) {
$displayDates[$rDate] = [];
}
}
// Sort the dates chronologically
ksort($displayDates);
} else {
echo "
๐ซ No schedule generated โ check if valid time slots, sessions, or dates are missing.
";
}
}
?>
Generate Group Schedule
";
if (!empty($displayDates)) {
echo "";
echo "
";
echo "
";
echo "
";
echo "Date Slot Time Course Action ";
foreach ($displayDates as $date => $rows) {
$dayName = (new DateTime($date))->format('l');
$isHoliday = array_key_exists($date, $holidayDates);
$isRetake = array_key_exists($date, $retakeDates);
// ๐ก Case: No sessions on this day
if (empty($rows)) {
$label = "No Sessions";
$bg = "#F9F9F9";
if ($isHoliday) {
$label = "Holiday: " . htmlspecialchars($holidayDates[$date]);
$bg = "#FFD700";
} elseif ($isRetake) {
$label = "Retake: " . htmlspecialchars($retakeDates[$date]);
$bg = "#CCE5FF";
}
echo "
($dayName) $date
- - $label - ";
continue;
}
// ๐๏ธ Loop through scheduled classes on that day
foreach ($rows as $row) {
$courseName = $courseNames[$row['Course_ID']] ?? "Course ID {$row['Course_ID']}";
// More reliable way to find session index
$index = null;
foreach ($schedule as $i => $entry) {
if (
$entry['Course_ID'] === $row['Course_ID'] &&
$entry['Date'] === $row['Date'] &&
$entry['Slot'] === $row['Slot']
) {
$index = $i;
break;
}
}
$isExam = isset($lastSessions[$row['Course_ID']]) && $index === $lastSessions[$row['Course_ID']];
// Highlight
$style = "";
if ($isExam) {
$style = "style='background-color: #FFCCCC; font-weight: bold;'";
} elseif ($isHoliday) {
$style = "style='background-color: #FFF5D1;'";
} elseif ($isRetake) {
$style = "style='background-color: #D9F0FF;'";
}
$slotIndex = (int) filter_var($row['Slot'], FILTER_SANITIZE_NUMBER_INT) - 1;
$slotName = isset($slotLabels[$slotIndex])
? format_time_slot_label($slotLabels[$slotIndex], $dayName)
: 'Unknown';
$isSaturday = ($dayName === 'Saturday');
$actionBtn = $isSaturday
? "๐งน Replace "
: "-";
echo "";
echo "($dayName) {$row['Date']} ";
echo "{$slotName} ";
echo "{$row['Time']} ";
echo "{$courseName}" . ($isExam ? " (Exam Day)" : "") . " ";
echo "{$actionBtn} ";
echo " ";
}
}
echo " Date Slot Time Course Action ";
echo "
";
}
?>