/
home
/
techb158
/
workloadmatch.com
/
Manager
/
Inc
/
/home/techb158/workloadmatch.com/Manager/Inc
mkdir
upload
Name
Size
Mode
Actions
LLMProviders/
-
0755
rm
AddCourse.php
2556
0644
edit
dl
rm
AddCourseTime.php
1904
0644
edit
dl
rm
AddGroup - Copy.php
4633
0644
edit
dl
rm
AddGroup.php
5698
0644
edit
dl
rm
AddHoliday.php
1772
0644
edit
dl
rm
AddTeacherLevel.php
2307
0644
edit
dl
rm
AddUser.php
12821
0644
edit
dl
rm
Add_Retake_Remediation.php
1334
0644
edit
dl
rm
AIScheduleOptimizer.php
11919
0644
edit
dl
rm
Assignment-1.php
75380
0644
edit
dl
rm
Assignment-2.php
75380
0644
edit
dl
rm
Assignment - BackUp- orgenal.php
73272
0644
edit
dl
rm
Assignment - BackUp.php
62761
0644
edit
dl
rm
Assignment - Copy (Last File Updated 2025-05-15).php
72933
0644
edit
dl
rm
Assignment - Copy.php
61826
0644
edit
dl
rm
Assignment.php
74308
0644
edit
dl
rm
Assignment_BackUp.php
79047
0644
edit
dl
rm
Assign_Teacher_Manual.php
20957
0644
edit
dl
rm
Assign_Teacher_Manual_bak.php.disabled
20535
0644
edit
dl
rm
Assign_Teacher_Manual_Force_conflict.php
21106
0644
edit
dl
rm
Available_Teacher_Report.php
2342
0644
edit
dl
rm
BatchScheduler.php
3588
0644
edit
dl
rm
CombinedScheduler.php
8130
0644
edit
dl
rm
ConflictResolver.php
4920
0644
edit
dl
rm
error_log
869
0644
edit
dl
rm
Lockup_to_Assign.php
6367
0644
edit
dl
rm
Process_Assignment - Copy.php
4233
0644
edit
dl
rm
Process_Assignment.php
5813
0644
edit
dl
rm
ScheduleEngine.php
5596
0644
edit
dl
rm
ScheduleGenerator.php
8811
0644
edit
dl
rm
Schedule_Report_by_Group.php
3074
0644
edit
dl
rm
TeacherAssignmentEngine.php
12487
0644
edit
dl
rm
Update_Course.php
2717
0644
edit
dl
rm
Update_Course_Time.php
1947
0644
edit
dl
rm
Update_Group - Copy.php
3489
0644
edit
dl
rm
Update_Group.php
9657
0644
edit
dl
rm
Update_Holiday.php
2357
0644
edit
dl
rm
Update_Profile.php
4923
0644
edit
dl
rm
Update_User.php
5547
0644
edit
dl
rm
Edit:
/home/techb158/workloadmatch.com/Manager/Inc/AIScheduleOptimizer.php
(11919B)
<?php require_once __DIR__ . '/ScheduleEngine.php'; require_once __DIR__ . '/ScheduleGenerator.php'; require_once __DIR__ . '/LLMProviders/ProviderFactory.php'; class AIScheduleOptimizer { private ScheduleEngine $engine; private ScheduleGenerator $generator; private mysqli $db; private ?ProviderInterface $provider; private array $lastResponse = []; public function __construct(mysqli $db, ?ProviderInterface $provider = null) { $this->db = $db; $this->engine = new ScheduleEngine($db); $this->generator = new ScheduleGenerator($db); $this->provider = $provider; } public function setProvider(ProviderInterface $provider): void { $this->provider = $provider; } public function hasProvider(): bool { return $this->provider !== null; } public function getLastResponse(): array { return $this->lastResponse; } public function optimize(int $programId, int $groupId, string $startDate, ?string $endDate = null, array $selectedCourses = [], array $priorityCourses = []): array { $group = $this->engine->getGroupInfo($groupId); if (!$group) { throw new RuntimeException("Group not found: $groupId"); } $courseSessions = $this->buildCourseSessionData($programId, $groupId); $teacherData = $this->buildTeacherData($programId); $constraints = $this->buildConstraints($programId, $groupId, $startDate, $group); if ($this->provider) { try { return $this->optimizeWithAI($programId, $groupId, $startDate, $endDate, $selectedCourses, $priorityCourses, $courseSessions, $teacherData, $constraints, $group); } catch (\Throwable $e) { error_log("AI optimization failed, falling back to deterministic: " . $e->getMessage()); return $this->fallbackGenerate($programId, $groupId, $startDate, $endDate, $selectedCourses, $priorityCourses); } } return $this->fallbackGenerate($programId, $groupId, $startDate, $endDate, $selectedCourses, $priorityCourses); } private function buildCourseSessionData(int $programId, int $groupId): array { $group = $this->engine->getGroupInfo($groupId); $slotLabels = explode(',', $group['Time_Slot']); $managerStart = new DateTime(trim($group['Time_From'])); $managerEnd = new DateTime(trim($group['Time_To'])); [$timeFrom, $timeTo] = $this->engine->fetchSlotTimes($slotLabels, $managerStart, $managerEnd); $sessionLength = $this->engine->calculateSessionLength($timeFrom, $timeTo); $sessions = $this->engine->calculateCourseSessions($programId, $sessionLength); $names = $this->engine->getCourseNames(); $result = []; foreach ($sessions as $courseId => $count) { $result[] = [ 'course_id' => $courseId, 'course_name' => $names[$courseId] ?? "Course #$courseId", 'sessions' => $count, ]; } return $result; } private function buildTeacherData(int $programId): array { $stmt = $this->db->prepare(" SELECT tp.Teacher_ID, tp.First_Name, tp.Last_Name, tp.Seniority_ID, tp.Time_Slot AS Availability, tp.Load_Hours, ttl.Top_Teacher_ID FROM teacher_profile tp LEFT JOIN top_teacher_list ttl ON tp.Teacher_ID = ttl.Teacher_ID AND ttl.Program_ID = ? "); $stmt->bind_param('i', $programId); $stmt->execute(); $teachers = $stmt->get_result()->fetch_all(MYSQLI_ASSOC); $stmt->close(); foreach ($teachers as &$t) { $stmt = $this->db->prepare(" SELECT Unavailable_From, Unavailable_To FROM teacher_unavailability WHERE Teacher_ID = ? AND status = 'confirmed' "); $stmt->bind_param('i', $t['Teacher_ID']); $stmt->execute(); $t['unavailability'] = $stmt->get_result()->fetch_all(MYSQLI_ASSOC); $stmt->close(); $stmt = $this->db->prepare(" SELECT Course_ID FROM teacher_course_preferences WHERE Teacher_ID = ? AND Priority IN (1,2) "); $stmt->bind_param('i', $t['Teacher_ID']); $stmt->execute(); $t['preferred_courses'] = array_column($stmt->get_result()->fetch_all(MYSQLI_ASSOC), 'Course_ID'); $stmt->close(); } unset($t); return $teachers; } private function buildConstraints(int $programId, int $groupId, string $startDate, array $group): array { $classDays = explode(',', $group['class_days']); if (!$group['Weekend_Class'] && in_array('Saturday', $classDays)) { $classDays = array_filter($classDays, fn($d) => $d !== 'Saturday'); } return [ 'class_days' => array_values($classDays), 'time_slots' => explode(',', $group['Time_Slot']), 'time_from' => $group['Time_From'], 'time_to' => $group['Time_To'], 'holidays' => $this->engine->getHolidays($startDate), 'retake_dates' => $this->engine->getRetakeDates($programId, $groupId), 'exclude_month' => 7, ]; } private function optimizeWithAI(int $programId, int $groupId, string $startDate, ?string $endDate, array $selectedCourses, array $priorityCourses, array $courseSessions, array $teacherData, array $constraints, array $group): array { $slotCount = is_array($group['Time_Slot']) ? count($group['Time_Slot']) : count(explode(',', $group['Time_Slot'])); $systemPrompt = <<<PROMPT You are an expert academic scheduling assistant. You generate optimal course schedules for educational programs. You must respond **only** with valid JSON matching the exact schema requested. Never include markdown, explanation, or extra text outside the JSON. Rules: 1. Each course must be scheduled for exactly its required number of sessions. 2. Sessions must only be placed on valid class days (class days, excluding holidays, retake dates, and July). 3. Each day can have at most {$slotCount} session(s) (one per time slot). 4. The last session of each course is its exam day. 5. Teachers can only teach courses they are qualified for or prefer. 6. A teacher cannot be assigned to overlapping times. 7. Distribute courses evenly — avoid scheduling the same course on consecutive days if possible. 8. Prioritize courses marked as priority. PROMPT; $courseList = []; $selectedSet = !empty($selectedCourses) ? $selectedCourses : array_column($courseSessions, 'course_id'); foreach ($courseSessions as $cs) { if (in_array($cs['course_id'], $selectedSet)) { $isPriority = isset($priorityCourses[$cs['course_id']]) ? 'YES' : 'no'; $courseList[] = "- Course #{$cs['course_id']} ({$cs['course_name']}): {$cs['sessions']} sessions, priority: {$isPriority}"; } } $teacherList = []; foreach ($teacherData as $t) { $level = !empty($t['Top_Teacher_ID']) ? 'premium' : ($t['Seniority_ID'] == 1 ? 'qualified' : 'unqualified'); $unavail = []; foreach ($t['unavailability'] as $u) { $unavail[] = "{$u['Unavailable_From']} to {$u['Unavailable_To']}"; } $prefs = array_map(fn($id) => "#$id", $t['preferred_courses']); $teacherList[] = "- Teacher #{$t['Teacher_ID']} ({$t['First_Name']} {$t['Last_Name']}), level: {$level}, load: {$t['Load_Hours']}h, slots: {$t['Availability']}" . (!empty($unavail) ? ", unavailable: " . implode('; ', $unavail) : "") . (!empty($prefs) ? ", prefers courses: " . implode(', ', $prefs) : ""); } $holidayStr = !empty($constraints['holidays']) ? implode(', ', $constraints['holidays']) : 'none'; $retakeStr = !empty($constraints['retake_dates']) ? implode(', ', $constraints['retake_dates']) : 'none'; $endDateStr = $endDate ?? date('Y-m-d', strtotime('+1 year', strtotime($startDate))); $userPrompt = <<<PROMPT Schedule the following courses for group #{$groupId} in program #{$programId}. Start date: {$startDate} End date: {$endDateStr} Class days: {$this->implode($constraints['class_days'])} Time slots: {$this->implode($constraints['time_slots'])} ({$constraints['time_from']} - {$constraints['time_to']}) Holidays (no classes): {$holidayStr} Retake dates (no classes): {$retakeStr} No classes in July. Courses to schedule: {$this->implode($courseList, "\n")} Available teachers: {$this->implode($teacherList, "\n")} Respond with JSON exactly in this format: { "schedule": [ { "date": "YYYY-MM-DD", "slot": "Morning", "course_id": 101, "teacher_id": 5, "is_exam": false } ], "summary": { "total_sessions": 50, "total_days_used": 25, "estimated_end_date": "YYYY-MM-DD" } } PROMPT; $this->lastResponse = $this->provider->sendPrompt($systemPrompt, $userPrompt); return $this->parseAIResponse($this->lastResponse, $programId, $groupId); } private function parseAIResponse(array $response, int $programId, int $groupId): array { $schedule = $response['schedule'] ?? $response['sessions'] ?? $response['assignments'] ?? []; if (empty($schedule) || !is_array($schedule)) { throw new RuntimeException("AI returned no valid schedule entries"); } $courseNames = $this->engine->getCourseNames(); $parsed = []; $displayDates = []; foreach ($schedule as $entry) { $date = $entry['date'] ?? $entry['Date'] ?? ''; $slot = $entry['slot'] ?? $entry['Slot'] ?? $entry['time_slot'] ?? ''; $courseId = $entry['course_id'] ?? $entry['Course_ID'] ?? $entry['courseId'] ?? 0; $time = $entry['time'] ?? $entry['Time'] ?? ''; $isExam = !empty($entry['is_exam']) || !empty($entry['Is_Exam']); if (!$date || !$courseId) continue; $dayName = (new DateTime($date))->format('l'); $slotLabel = $this->engine->formatTimeSlotLabel($slot, $dayName); if (!$time) { $time = $this->engine->formatTimeSlotRange($slot, '08:30:00', '12:30:00', $dayName); } $entryData = [ 'Date' => $date, 'Slot' => $slotLabel, 'Time' => $time, 'Course_ID' => (int)$courseId, ]; $parsed[] = $entryData; $displayDates[$date][] = $entryData; } ksort($displayDates); return [ 'schedule' => $parsed, 'displayDates' => $displayDates, 'courseNames' => $courseNames, 'lastSessions' => $this->computeLastSessions($parsed), 'ai_reasoning' => $response['summary'] ?? $response['reasoning'] ?? null, 'ai_generated' => true, ]; } private function computeLastSessions(array $schedule): array { $last = []; foreach ($schedule as $i => $entry) { $last[$entry['Course_ID']] = $i; } return $last; } private function fallbackGenerate(int $programId, int $groupId, string $startDate, ?string $endDate, array $selectedCourses, array $priorityCourses): array { $result = $this->generator->generate($programId, $groupId, $startDate, $endDate, $selectedCourses, $priorityCourses); $result['ai_generated'] = false; return $result; } private function implode(array $items, string $glue = ', '): string { return implode($glue, $items); } }
Save
cmd:
run