/home/techb158/workloadmatch.com/Manager/Inc
NameSizeModeActions
LLMProviders/-0755rm
AddCourse.php25560644editdlrm
AddCourseTime.php19040644editdlrm
AddGroup - Copy.php46330644editdlrm
AddGroup.php56980644editdlrm
AddHoliday.php17720644editdlrm
AddTeacherLevel.php23070644editdlrm
AddUser.php128210644editdlrm
Add_Retake_Remediation.php13340644editdlrm
AIScheduleOptimizer.php119190644editdlrm
Assignment-1.php753800644editdlrm
Assignment-2.php753800644editdlrm
Assignment - BackUp- orgenal.php732720644editdlrm
Assignment - BackUp.php627610644editdlrm
Assignment - Copy (Last File Updated 2025-05-15).php729330644editdlrm
Assignment - Copy.php618260644editdlrm
Assignment.php743080644editdlrm
Assignment_BackUp.php790470644editdlrm
Assign_Teacher_Manual.php209570644editdlrm
Assign_Teacher_Manual_bak.php.disabled205350644editdlrm
Assign_Teacher_Manual_Force_conflict.php211060644editdlrm
Available_Teacher_Report.php23420644editdlrm
BatchScheduler.php35880644editdlrm
CombinedScheduler.php81300644editdlrm
ConflictResolver.php49200644editdlrm
error_log8690644editdlrm
Lockup_to_Assign.php63670644editdlrm
Process_Assignment - Copy.php42330644editdlrm
Process_Assignment.php58130644editdlrm
ScheduleEngine.php55960644editdlrm
ScheduleGenerator.php88110644editdlrm
Schedule_Report_by_Group.php30740644editdlrm
TeacherAssignmentEngine.php124870644editdlrm
Update_Course.php27170644editdlrm
Update_Course_Time.php19470644editdlrm
Update_Group - Copy.php34890644editdlrm
Update_Group.php96570644editdlrm
Update_Holiday.php23570644editdlrm
Update_Profile.php49230644editdlrm
Update_User.php55470644editdlrm
Edit: /home/techb158/workloadmatch.com/Manager/Inc/AIScheduleOptimizer.php (11919B)
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 = << "#$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 = <<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); } }