/home/techb158/workloadmatch.com/api/routes
Edit: /home/techb158/workloadmatch.com/api/routes/ai_generate.php (11642B)
$p,
'display' => ProviderFactory::getProviderDisplayName($p),
'configured' => $configured,
];
}
Response::success($providers);
}
function getProgramGroups(int $programId, mysqli $mysqli): void {
require_once __DIR__ . '/../../Manager/Inc/BatchScheduler.php';
$batch = new BatchScheduler($mysqli);
$groups = $batch->getGroupsForProgram($programId);
Response::success($groups);
}
function getCalendarEvents(int $groupId, mysqli $mysqli): void {
$events = [];
$stmt = $mysqli->prepare("
SELECT scfg.Schedule_ID, scfg.Course_ID, c.Course_Name, scfg.Start_Date, scfg.End_Date,
scfg.Time_Slot, scfg.Start_Time, scfg.End_Time,
g.Group_Name
FROM schedule_course_for_group scfg
JOIN Courses c ON scfg.Course_ID = c.Course_ID
JOIN manager_group_name g ON scfg.Group_ID = g.Group_ID
WHERE scfg.Group_ID = ?
ORDER BY scfg.Start_Date
");
$stmt->bind_param('i', $groupId);
$stmt->execute();
$rows = $stmt->get_result()->fetch_all(MYSQLI_ASSOC);
$stmt->close();
$lastSessionPerCourse = [];
foreach ($rows as $r) {
$lastSessionPerCourse[$r['Course_ID']] = $r['Schedule_ID'];
}
foreach ($rows as $r) {
$isExam = $lastSessionPerCourse[$r['Course_ID']] === $r['Schedule_ID'];
$events[] = [
'id' => $r['Schedule_ID'],
'schedule_id' => $r['Schedule_ID'],
'title' => $r['Course_Name'] . ($isExam ? ' (Exam)' : '') . ' - ' . $r['Group_Name'],
'start' => $r['Start_Date'],
'end' => $r['End_Date'],
'allDay' => true,
'type' => $isExam ? 'exam' : 'class',
'color' => $isExam ? '#f56954' : '#00a65a',
'course_id' => $r['Course_ID'],
'course_name' => $r['Course_Name'],
'time_slot' => $r['Time_Slot'],
'group_name' => $r['Group_Name'],
];
}
$res = $mysqli->query("SELECT Event_Start, Event_End, Event_Title FROM Events WHERE Calendar_Year = YEAR(CURDATE())");
while ($r = $res->fetch_assoc()) {
$events[] = [
'id' => 'holiday-' . $r['Event_Start'],
'title' => 'Holiday: ' . ($r['Event_Title'] ?? $r['Event_Name'] ?? ''),
'start' => $r['Event_Start'],
'end' => $r['Event_End'],
'allDay' => true,
'type' => 'holiday',
'color' => '#f39c12',
'editable' => false,
];
}
Response::success($events);
}
function saveCalendarChanges(?array $body, mysqli $mysqli): void {
Auth::requireAnyRole(['master_profile', 'manager_profile']);
$changes = $body['changes'] ?? [];
if (empty($changes)) {
Response::error('No changes provided');
return;
}
$updated = 0;
foreach ($changes as $c) {
$scheduleId = (int)($c['schedule_id'] ?? 0);
$newDate = $c['new_date'] ?? '';
if (!$scheduleId || !$newDate) continue;
$stmt = $mysqli->prepare("UPDATE schedule_course_for_group SET Start_Date = ?, End_Date = ? WHERE Schedule_ID = ?");
$stmt->bind_param('ssi', $newDate, $newDate, $scheduleId);
if ($stmt->execute() && $stmt->affected_rows > 0) {
$updated++;
}
$stmt->close();
$stmt2 = $mysqli->prepare("UPDATE course_group_schedule_main SET Start_Date = ?, End_Date = ? WHERE Schedule_ID = ?");
$stmt2->bind_param('ssi', $newDate, $newDate, $scheduleId);
$stmt2->execute();
$stmt2->close();
}
Response::success(['updated' => $updated], "Updated $updated schedule entries");
}
function getAnalytics(int $programId, mysqli $mysqli): void {
require_once __DIR__ . '/../../Manager/Inc/ScheduleAnalytics.php';
$analytics = new ScheduleAnalytics($mysqli);
$summary = $programId ? $analytics->getProgramSummary($programId) : [];
$workload = $analytics->getTeacherWorkload($programId);
$monthly = $programId ? $analytics->getMonthlyDistribution($programId, date('Y')) : [];
$gaps = $analytics->getScheduleGaps($programId);
Response::success([
'summary' => $summary,
'workload' => $workload,
'monthly' => $monthly,
'gaps' => $gaps,
]);
}
function generateAiSchedule(?array $body, mysqli $mysqli): void {
Auth::requireAnyRole(['master_profile', 'manager_profile', 'admin_profile']);
$programId = (int)($body['program_id'] ?? 0);
$groupId = (int)($body['group_id'] ?? 0);
$startDate = $body['start_date'] ?? '';
$endDate = $body['end_date'] ?? null;
$selectedCourses = $body['selected_courses'] ?? [];
$priorityCourses = $body['priority_courses'] ?? [];
$provider = $body['provider'] ?? null;
$useAi = $body['use_ai'] ?? true;
$saveToDb = $body['save'] ?? false;
if (!$programId || !$groupId || !$startDate || empty($selectedCourses)) {
Response::validationError(['program_id', 'group_id', 'start_date', 'selected_courses' => 'Required']);
return;
}
require_once __DIR__ . '/../../Manager/Inc/CombinedScheduler.php';
require_once __DIR__ . '/../../Manager/Inc/LLMProviders/ProviderFactory.php';
$scheduler = new CombinedScheduler($mysqli);
if ($useAi && $provider) {
switch ($provider) {
case 'openai': $apiKey = getenv('OPENAI_API_KEY'); break;
case 'gemini': $apiKey = getenv('GEMINI_API_KEY'); break;
case 'claude': $apiKey = getenv('CLAUDE_API_KEY'); break;
default: $apiKey = null;
}
if ($apiKey) {
$llmProvider = ProviderFactory::create($provider, $apiKey);
$scheduler->setProvider($llmProvider);
}
} elseif ($useAi) {
try {
$llmProvider = ProviderFactory::createFromEnv();
$scheduler->setProvider($llmProvider);
} catch (Throwable $e) {
error_log("AI provider not configured, using deterministic: " . $e->getMessage());
}
}
try {
$result = $scheduler->generateAndAssign(
$programId, $groupId, $startDate, $endDate,
$selectedCourses, $priorityCourses, $saveToDb, $useAi
);
$scheduleResult = $result['schedule_result'];
$responseData = [
'ai_generated' => $scheduleResult['ai_generated'] ?? false,
'provider_used' => $provider ?? 'auto',
'schedule_entries' => count($scheduleResult['schedule'] ?? []),
'schedule' => $scheduleResult['schedule'] ?? [],
'saved_entries' => $result['saved_entries'] ?? [],
'assignment_results' => $result['assignment_results'] ?? [],
];
if (isset($scheduleResult['ai_reasoning'])) {
$responseData['ai_reasoning'] = $scheduleResult['ai_reasoning'];
}
Response::success($responseData, $saveToDb ? 'Schedule generated, saved, and teachers assigned' : 'Schedule generated (not saved)');
} catch (Throwable $e) {
Response::error('Generation failed: ' . $e->getMessage(), 500);
}
}
function generateBatchSchedule(?array $body, mysqli $mysqli): void {
Auth::requireAnyRole(['master_profile', 'manager_profile', 'admin_profile']);
$programId = (int)($body['program_id'] ?? 0);
$groupIds = $body['group_ids'] ?? [];
$startDate = $body['start_date'] ?? '';
$endDate = $body['end_date'] ?? null;
$saveToDb = $body['save'] ?? false;
$useAi = $body['use_ai'] ?? true;
$provider = $body['provider'] ?? null;
if (!$programId || empty($groupIds) || !$startDate) {
Response::validationError(['program_id', 'group_ids', 'start_date' => 'Required']);
return;
}
require_once __DIR__ . '/../../Manager/Inc/BatchScheduler.php';
require_once __DIR__ . '/../../Manager/Inc/LLMProviders/ProviderFactory.php';
$batch = new BatchScheduler($mysqli);
if ($useAi && $provider) {
switch ($provider) {
case 'openai': $apiKey = getenv('OPENAI_API_KEY'); break;
case 'gemini': $apiKey = getenv('GEMINI_API_KEY'); break;
case 'claude': $apiKey = getenv('CLAUDE_API_KEY'); break;
default: $apiKey = null;
}
if ($apiKey) {
$llmProvider = ProviderFactory::create($provider, $apiKey);
$batch->setProvider($llmProvider);
}
} elseif ($useAi) {
try {
$batch->setProvider(ProviderFactory::createFromEnv());
} catch (Throwable $e) {
error_log("Batch: AI provider not configured: " . $e->getMessage());
}
}
try {
$result = $batch->generateAll($programId, $groupIds, $startDate, $endDate, $saveToDb, $useAi);
Response::success($result, "Batch generation completed");
} catch (Throwable $e) {
Response::error('Batch generation failed: ' . $e->getMessage(), 500);
}
}