{"repo": "asley/chhs-testing", "files": {"modules/ChatBot/moduleFunctions.php": {"5eacf702b1d14d58114e1990619ea4913b19ff59": {"t": 1785745766.0855227, "n": 21952, "h": "6781a141c0d264fc", "c": ".\n*/\n\nuse Gibbon\\Domain\\System\\SettingGateway;\nuse Gibbon\\Forms\\Form;\nuse Gibbon\\Services\\Format as GibbonFormat;\nuse Gibbon\\Domain\\System\\ModuleGateway;\nuse Gibbon\\Domain\\DataSet;\nuse Gibbon\\Tables\\DataTable;\n\n// Include Gibbon core functions\nrequire_once __DIR__ . '/../../functions.php';\n\n/**\n * Get a setting value by scope\n * @param PDO $connection2 The database connection\n * @param string $scope The setting scope\n * @param string $name The setting name\n * @return string The setting value\n */\nfunction getSettingByScope($connection2, $scope, $name) {\n try {\n $sql = \"SELECT value FROM gibbonSetting WHERE scope=:scope AND name=:name\";\n $result = $connection2->prepare($sql);\n $result->execute(['scope' => $scope, 'name' => $name]);\n if ($result->rowCount() == 1) {\n $row = $result->fetch();\n return $row['value'];\n }\n } catch (PDOException $e) {\n return '';\n }\n return '';\n}\n\n/**\n * Check if a file is a valid PDF\n * @param string $file The file path\n * @return bool Whether the file is a valid PDF\n */\nfunction isValidPDF($file) {\n if (empty($file)) return false;\n \n // Check file extension\n $extension = strtolower(pathinfo($file, PATHINFO_EXTENSION));\n if ($extension !== 'pdf') return false;\n \n // Check file mime type\n $finfo = finfo_open(FILEINFO_MIME_TYPE);\n $mimeType = finfo_file($finfo, $file);\n finfo_close($finfo);\n \n return $mimeType === 'application/pdf';\n}\n\n/**\n * Extract text from PDF file\n * @param string $pdfFile The PDF file path\n * @return string The extracted text\n */\nfunction extractTextFromPDF($pdfFile) {\n if (!isValidPDF($pdfFile)) {\n throw new Exception('Invalid PDF file');\n }\n \n // Create temporary files\n $tempPdf = tempnam(sys_get_temp_dir(), 'pdf_');\n $tempTxt = tempnam(sys_get_temp_dir(), 'txt_');\n \n try {\n // Copy PDF to temp location\n if (!copy($pdfFile, $tempPdf)) {\n throw new Exception('Failed to copy PDF file');\n }\n \n // Convert PDF to text\n exec(\"pdftotext '{$tempPdf}' '{$tempTxt}' 2>&1\", $output, $returnVar);\n \n if ($returnVar !== 0) {\n throw new Exception('Failed to convert PDF: ' . implode(\"\\n\", $output));\n }\n \n // Read converted text\n $text = file_get_contents($tempTxt);\n \n return $text;\n \n } finally {\n // Clean up temp files\n if (file_exists($tempPdf)) unlink($tempPdf);\n if (file_exists($tempTxt)) unlink($tempTxt);\n }\n}\n\n/**\n * Store the API key securely\n * @param PDO $connection2 The database connection\n * @param string $apiKey The API key to store\n * @return bool Whether the operation was successful\n */\nfunction storeAPIKey($connection2, $apiKey) {\n try {\n $sql = \"INSERT INTO gibbonSetting (scope, name, nameDisplay, description, value) \n VALUES ('ChatBot', 'deepseek_api_key', 'DeepSeek API Key', 'API key for DeepSeek integration', :apiKey)\n ON DUPLICATE KEY UPDATE value = :apiKey\";\n \n $result = $connection2->prepare($sql);\n return $result->execute(['apiKey' => $apiKey]);\n } catch (PDOException $e) {\n error_log('Error storing API key: ' . $e->getMessage());\n return false;\n }\n}\n\n/**\n * Get the API key securely\n * @param PDO $connection2 The database connection\n * @return string The API key\n */\nfunction getSecureAPIKey($connection2) {\n try {\n $sql = \"SELECT value FROM gibbonSetting WHERE scope='ChatBot' AND name='deepseek_api_key'\";\n $result = $connection2->prepare($sql);\n $result->execute();\n if ($result->rowCount() == 1) {\n $row = $result->fetch();\n return $row['value'];\n }\n } catch (PDOException $e) {\n return '';\n }\n return '';\n}\n\n/**\n * Get student assessment data\n * @param PDO $connection2 The database connection\n * @param int $studentID The student ID\n * @return array The assessment data\n */\nfunction getStudentAssessmentData($connection2, $studentID) {\n try {\n $sql = \"SELECT \n ia.name as assessment_name,\n iac.attainmentValue,\n iac.effortValue,\n iac.comment as teacher_feedback,\n c.name as course_name\n FROM gibbonInternalAssessmentEntry iac\n JOIN gibbonInternalAssessmentColumn ia ON (ia.gibbonInternalAssessmentColumnID=iac.gibbonInternalAssessmentColumnID)\n JOIN gibbonCourse c ON (c.gibbonCourseID=ia.gibbonCourseID)\n WHERE iac.gibbonPersonID=:studentID\n ORDER BY ia.timestampCreated DESC\";\n \n $result = $connection2->prepare($sql);\n $result->execute(['studentID' => $studentID]);\n return $result->fetchAll(PDO::FETCH_ASSOC);\n } catch (Exception $e) {\n error_log('Error getting student assessment data: ' . $e->getMessage());\n return [];\n }\n}\n\n/**\n * Make a secure call to the OpenAI API\n * \n * @param string $apiKey\n * @param array $data\n * @return array|null\n */\nfunction callOpenAIAPI($apiKey, $data) {\n try {\n // Initialize cURL session\n $ch = curl_init('https://api.openai.com/v1/chat/completions');\n \n // Set cURL options\n curl_setopt_array($ch, [\n CURLOPT_RETURNTRANSFER => true,\n CURLOPT_POST => true,\n CURLOPT_HTTPHEADER => [\n 'Content-Type: application/json',\n 'Authorization: Bearer ' . $apiKey\n ],\n CURLOPT_POSTFIELDS => json_encode($data),\n CURLOPT_SSL_VERIFYPEER => true, // Verify SSL certificate\n CURLOPT_SSL_VERIFYHOST => 2, // Verify hostname\n CURLOPT_CONNECTTIMEOUT => 10, // Connection timeout\n CURLOPT_TIMEOUT => 30, // Response timeout\n CURLOPT_PROTOCOLS => CURLPROTO_HTTPS, // Only allow HTTPS\n ]);\n \n // Execute request\n $response = curl_exec($ch);\n $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);\n \n // Check for errors\n if ($httpCode !== 200) {\n error_log('OpenAI API error: HTTP ' . $httpCode);\n error_log('Response: ' . $response);\n return null;\n }\n \n // Close cURL session\n curl_close($ch);\n \n // Parse and return response\n return json_decode($response, true);\n \n } catch (Exception $e) {\n error_log('Error calling OpenAI API: ' . $e->getMessage());\n return null;\n }\n}\n\n// Only declare if not already defined\nif (!function_exists('isActionAccessible')) {\n /**\n * Check if a user has access to a specific action\n * @param mixed $guid The global unique identifier\n * @param PDO $connection2 The database connection\n * @param string $address The action address to check\n * @return bool Whether the user has access\n */\n function isActionAccessible($guid, $connection2, $address) {\n try {\n $data = array('gibbonRoleID' => $_SESSION[$guid]['gibbonRoleIDCurrent'], 'address' => $address);\n $sql = \"SELECT DISTINCT gibbonAction.gibbonActionID \n FROM gibbonAction \n JOIN gibbonPermission ON (gibbonAction.gibbonActionID=gibbonPermission.gibbonActionID) \n WHERE (gibbonPermission.gibbonRoleID=:gibbonRoleID AND gibbonAction.URLList LIKE CONCAT('%', :address, '%'))\";\n $result = $connection2->prepare($sql);\n $result->execute($data);\n return ($result->rowCount() > 0);\n } catch (PDOException $e) {\n return false;\n }\n }\n}\n\n/**\n * Helper functions for the ChatBot module\n */\n\n/**\n * Check if a user has access to the ChatBot module\n *\n * @param string $guid\n * @param PDO $connection2\n * @return bool\n */\nfunction chatbotCheckAccess($guid, $connection2) {\n return isActionAccessible($guid, $connection2, '/modules/ChatBot/chatbot.php');\n}\n\n/**\n * Get the DeepSeek API key from settings\n *\n * @param SettingGateway $settingGateway\n * @return string|null\n */\nfunction chatbotGetApiKey($settingGateway) {\n return $settingGateway->getSettingByScope('ChatBot', 'deepseek_api_key');\n}\n\n/**\n * Format chat messages for display\n *\n * @param array $messages\n * @return string\n */\nfunction chatbotFormatMessages($messages) {\n $output = '';\n foreach ($messages as $message) {\n $type = $message['type'];\n $content = $message['content'];\n $output .= \"
\";\n $output .= \"
{$content}
\";\n $output .= \"
\";\n }\n return $output;\n}\n\n/**\n * Validate chat message\n *\n * @param string $message\n * @return bool\n */\nfunction chatbotValidateMessage($message) {\n return !empty(trim($message));\n}\n\n/**\n * Sanitize chat message\n *\n * @param string $message\n * @return string\n */\nfunction chatbotSanitizeMessage($message) {\n return strip_tags(trim($message));\n}\n\n/**\n * Store AI recommendations for a student\n * @param PDO $connection2 The database connection\n * @param int $gibbonPersonID The student ID\n * @param int $gibbonCourseClassID The course class ID\n * @param int $assessmentID The assessment ID\n * @param string $recommendation The AI recommendation\n * @return bool Whether the operation was successful\n */\nfunction storeRecommendation($connection2, $gibbonPersonID, $gibbonCourseClassID, $assessmentID, $recommendation) {\n try {\n $sql = \"INSERT INTO gibbonChatBotRecommendations \n (gibbonPersonID, gibbonCourseClassID, assessmentID, recommendation) \n VALUES \n (:gibbonPersonID, :gibbonCourseClassID, :assessmentID, :recommendation)\";\n \n $result = $connection2->prepare($sql);\n return $result->execute([\n 'gibbonPersonID' => $gibbonPersonID,\n 'gibbonCourseClassID' => $gibbonCourseClassID,\n 'assessmentID' => $assessmentID,\n 'recommendation' => $recommendation\n ]);\n } catch (PDOException $e) {\n error_log('Error storing recommendation: ' . $e->getMessage());\n return false;\n }\n}\n\n/**\n * Get AI recommendations for a student\n * @param PDO $connection2 The database connection\n * @param int $gibbonPersonID The student ID\n * @return array The recommendations\n */\nfunction getStudentRecommendations($connection2, $gibbonPersonID) {\n try {\n $sql = \"SELECT r.*, \n ia.name as assessment_name,\n c.name as course_name\n FROM gibbonChatBotRecommendations r\n JOIN gibbonInternalAssessment ia ON (ia.gibbonInternalAssessmentID = r.assessmentID)\n JOIN gibbonCourseClass cc ON (cc.gibbonCourseClassID = r.gibbonCourseClassID)\n JOIN gibbonCourse c ON (c.gibbonCourseID = cc.gibbonCourseID)\n WHERE r.gibbonPersonID = :gibbonPersonID\n ORDER BY r.timestamp DESC\";\n \n $result = $connection2->prepare($sql);\n $result->execute(['gibbonPersonID' => $gibbonPersonID]);\n return $result->fetchAll(PDO::FETCH_ASSOC);\n } catch (PDOException $e) {\n error_log('Error getting student recommendations: ' . $e->getMessage());\n return [];\n }\n}\n\n/**\n * Add training data to the ChatBot\n * @param PDO $connection2 The database connection\n * @param string $question The question\n * @param string $answer The answer\n * @param int $createdBy The user ID who created the entry\n * @param string|null $category The category (optional)\n * @return bool Whether the operation was successful\n */\nfunction addTrainingData($connection2, $question, $answer, $createdBy, $category = null) {\n try {\n $sql = \"INSERT INTO gibbonChatBotTraining \n (question, answer, category, created_by) \n VALUES \n (:question, :answer, :category, :created_by)\";\n \n $result = $connection2->prepare($sql);\n return $result->execute([\n 'question' => $question,\n 'answer' => $answer,\n 'category' => $category,\n 'created_by' => $createdBy\n ]);\n } catch (PDOException $e) {\n error_log('Error adding training data: ' . $e->getMessage());\n return false;\n }\n}\n\n/**\n * Get training data for the ChatBot\n * @param PDO $connection2 The database connection\n * @param string $category Optional category filter\n * @return array The training data\n */\nfunction getTrainingData($connection2, $category = null) {\n try {\n $sql = \"SELECT * FROM gibbonChatBotTraining\";\n $params = [];\n \n if ($category) {\n $sql .= \" WHERE category = :category\";\n $params['category'] = $category;\n }\n \n $sql .= \" ORDER BY timestampCreated DESC\";\n \n $result = $connection2->prepare($sql);\n $result->execute($params);\n return $result->fetchAll(PDO::FETCH_ASSOC);\n } catch (PDOException $e) {\n error_log('Error getting training data: ' . $e->getMessage());\n return [];\n }\n}\n\n/**\n * Delete training data\n * @param PDO $connection2 The database connection\n * @param int $id The training data ID\n * @return bool Whether the operation was successful\n */\nfunction deleteTrainingData($connection2, $id) {\n try {\n $sql = \"DELETE FROM gibbonChatBotTraining WHERE gibbonChatBotTrainingID = :id\";\n $result = $connection2->prepare($sql);\n return $result->execute(['id' => $id]);\n } catch (PDOException $e) {\n error_log('Error deleting training data: ' . $e->getMessage());\n return false;\n }\n}\n\n/**\n * Generate AI recommendations based on assessment data\n * @param array $assessment The assessment data\n * @param string $apiKey The DeepSeek API key\n * @return string|null The AI recommendation\n */\nfunction generateAssessmentRecommendation($assessment, $apiKey) {\n try {\n $prompt = \"Based on the following assessment data:\\n\";\n $prompt .= \"Subject: {$assessment['subject']}\\n\";\n $prompt .= \"Grade: {$assessment['grade']}\\n\";\n $prompt .= \"Teacher Feedback: {$assessment['feedback']}\\n\\n\";\n $prompt .= \"Please provide specific, actionable recommendations for improvement in this subject. Consider:\\n\";\n $prompt .= \"1. Study strategies\\n\";\n $prompt .= \"2. Practice exercises\\n\";\n $prompt .= \"3. Additional resources\\n\";\n $prompt .= \"4. Areas needing focus based on the assessment\\n\";\n $prompt .= \"5. Time management suggestions\";\n \n $data = [\n 'model' => 'deepseek-v4-flash',\n 'messages' => [\n [\n 'role' => 'system',\n 'content' => 'You are an educational AI assistant providing specific, actionable recommendations for academic improvement based on assessment data and teacher feedback.'\n ],\n [\n 'role' => 'user',\n 'content' => $prompt\n ]\n ],\n 'max_tokens' => 500\n ];\n \n $ch = curl_init();\n curl_setopt($ch, CURLOPT_URL, 'https://api.deepseek.com/v1/chat/completions');\n curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\n curl_setopt($ch, CURLOPT_POST, true);\n curl_setopt($ch, CURLOPT_HTTPHEADER, [\n 'Content-Type: application/json',\n 'Authorization: Bearer ' . $apiKey\n ]);\n \n curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));\n \n $response = curl_exec($ch);\n $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);\n curl_close($ch);\n \n if ($httpCode === 200) {\n $result = json_decode($response, true);\n return $result['choices'][0]['message']['content'];\n }\n } catch (Exception $e) {\n error_log('Error generating assessment recommendation: ' . $e->getMessage());\n }\n \n return null;\n}\n\n/**\n * Get student performance metrics\n * @param int $gibbonPersonID\n * @return string HTML output of performance metrics\n */\nfunction getStudentPerformanceMetrics($gibbonPersonID) {\n global $pdo;\n \n try {\n // Get assessment data\n $data = getStudentAssessmentData($gibbonPersonID);\n \n // Calculate metrics\n $metrics = [\n 'average_attainment' => calculateAverageAttainment($data),\n 'progress_indicators' => analyzeProgressTrends($data),\n 'subject_breakdown' => getSubjectPerformance($data)\n ];\n \n // Generate HTML output\n $output = '
';\n foreach ($metrics as $key => $value) {\n $output .= generateMetricCard($key, $value);\n }\n $output .= '
';\n \n return $output;\n } catch (Exception $e) {\n return \"Error generating performance metrics: \" . $e->getMessage();\n }\n}\n\n/**\n * Generate AI recommendations based on student data\n * @param int $gibbonPersonID\n * @return string HTML output of recommendations\n */\nfunction generateAIRecommendations($gibbonPersonID) {\n try {\n // Get comprehensive student data\n $assessmentData = getStudentAssessmentData($gibbonPersonID);\n $feedbackData = getTeacherFeedback($gibbonPersonID);\n $engagementData = getStudentEngagementData($gibbonPersonID);\n \n // Prepare data for AI analysis\n $analysisData = [\n 'assessment_history' => $assessmentData,\n 'teacher_feedback' => $feedbackData,\n 'engagement_metrics' => $engagementData,\n 'learning_patterns' => analyzeLearningPatterns($gibbonPersonID)\n ];\n \n // Get AI recommendations\n $recommendations = callDeepSeekAPI($analysisData);\n \n // Format recommendations\n return formatAIRecommendations($recommendations);\n } catch (Exception $e) {\n return \"Error generating AI recommendations: \" . $e->getMessage();\n }\n}\n\n/**\n * Process and store training data\n * @param array $data Training data array\n * @return bool Success status\n */\nfunction processTrainingData($data) {\n try {\n // Validate training data\n validateTrainingData($data);\n \n // Enrich data with institutional context\n $enrichedData = enrichTrainingData($data);\n \n // Store in database\n storeTrainingData($enrichedData);\n \n // Update AI model\n updateAIModel($enrichedData);\n \n return true;\n } catch (Exception $e) {\n error_log(\"Training data processing error: \" . $e->getMessage());\n return false;\n }\n}\n\n/**\n * Generate learning analytics\n * @param array $params Analytics parameters\n * @return array Analytics data\n */\nfunction generateLearningAnalytics($params) {\n try {\n // Gather data\n $performanceData = getPerformanceData($params);\n $engagementData = getEngagementData($params);\n $patternData = getPatternData($params);\n \n // Process analytics\n $analytics = [\n 'performance' => analyzePerformance($performanceData),\n 'engagement' => analyzeEngagement($engagementData),\n 'patterns' => analyzePatterns($patternData),\n 'recommendations' => generateRecommendations([\n 'performance' => $performanceData,\n 'engagement' => $engagementData,\n 'patterns' => $patternData\n ])\n ];\n \n return $analytics;\n } catch (Exception $e) {\n error_log(\"Analytics generation error: \" . $e->getMessage());\n return null;\n }\n}\n\n/**\n * Format AI recommendations for display\n * @param array $recommendations\n * @return string Formatted HTML\n */\nfunction formatAIRecommendations($recommendations) {\n $output = '
';\n \n // Format subject-specific recommendations\n if (isset($recommendations['subjects'])) {\n $output .= '
';\n $output .= '

Subject-Specific Recommendations

';\n foreach ($recommendations['subjects'] as $subject => $data) {\n $output .= formatSubjectRecommendation($subject, $data);\n }\n $output .= '
';\n }\n \n // Format learning strategy recommendations\n if (isset($recommendations['strategies'])) {\n $output .= '
';\n $output .= '

Learning Strategy Recommendations

';\n foreach ($recommendations['strategies'] as $strategy) {\n $output .= formatStrategyRecommendation($strategy);\n }\n $output .= '
';\n }\n \n // Format improvement suggestions\n if (isset($recommendations['improvements'])) {\n $output .= '
';\n $output .= '

Areas for Improvement

';\n foreach ($recommendations['improvements'] as $area => $suggestions) {\n $output .= formatImprovementSuggestions($area, $suggestions);\n }\n $output .= '
';\n }\n \n $output .= '
';\n return $output;\n}\n\n"}, "228065060bc341b101409b1b5a4415a502b48a3a": {"t": 1785756298.6214683, "n": 21952, "h": "6781a141c0d264fc", "c": ".\n*/\n\nuse Gibbon\\Domain\\System\\SettingGateway;\nuse Gibbon\\Forms\\Form;\nuse Gibbon\\Services\\Format as GibbonFormat;\nuse Gibbon\\Domain\\System\\ModuleGateway;\nuse Gibbon\\Domain\\DataSet;\nuse Gibbon\\Tables\\DataTable;\n\n// Include Gibbon core functions\nrequire_once __DIR__ . '/../../functions.php';\n\n/**\n * Get a setting value by scope\n * @param PDO $connection2 The database connection\n * @param string $scope The setting scope\n * @param string $name The setting name\n * @return string The setting value\n */\nfunction getSettingByScope($connection2, $scope, $name) {\n try {\n $sql = \"SELECT value FROM gibbonSetting WHERE scope=:scope AND name=:name\";\n $result = $connection2->prepare($sql);\n $result->execute(['scope' => $scope, 'name' => $name]);\n if ($result->rowCount() == 1) {\n $row = $result->fetch();\n return $row['value'];\n }\n } catch (PDOException $e) {\n return '';\n }\n return '';\n}\n\n/**\n * Check if a file is a valid PDF\n * @param string $file The file path\n * @return bool Whether the file is a valid PDF\n */\nfunction isValidPDF($file) {\n if (empty($file)) return false;\n \n // Check file extension\n $extension = strtolower(pathinfo($file, PATHINFO_EXTENSION));\n if ($extension !== 'pdf') return false;\n \n // Check file mime type\n $finfo = finfo_open(FILEINFO_MIME_TYPE);\n $mimeType = finfo_file($finfo, $file);\n finfo_close($finfo);\n \n return $mimeType === 'application/pdf';\n}\n\n/**\n * Extract text from PDF file\n * @param string $pdfFile The PDF file path\n * @return string The extracted text\n */\nfunction extractTextFromPDF($pdfFile) {\n if (!isValidPDF($pdfFile)) {\n throw new Exception('Invalid PDF file');\n }\n \n // Create temporary files\n $tempPdf = tempnam(sys_get_temp_dir(), 'pdf_');\n $tempTxt = tempnam(sys_get_temp_dir(), 'txt_');\n \n try {\n // Copy PDF to temp location\n if (!copy($pdfFile, $tempPdf)) {\n throw new Exception('Failed to copy PDF file');\n }\n \n // Convert PDF to text\n exec(\"pdftotext '{$tempPdf}' '{$tempTxt}' 2>&1\", $output, $returnVar);\n \n if ($returnVar !== 0) {\n throw new Exception('Failed to convert PDF: ' . implode(\"\\n\", $output));\n }\n \n // Read converted text\n $text = file_get_contents($tempTxt);\n \n return $text;\n \n } finally {\n // Clean up temp files\n if (file_exists($tempPdf)) unlink($tempPdf);\n if (file_exists($tempTxt)) unlink($tempTxt);\n }\n}\n\n/**\n * Store the API key securely\n * @param PDO $connection2 The database connection\n * @param string $apiKey The API key to store\n * @return bool Whether the operation was successful\n */\nfunction storeAPIKey($connection2, $apiKey) {\n try {\n $sql = \"INSERT INTO gibbonSetting (scope, name, nameDisplay, description, value) \n VALUES ('ChatBot', 'deepseek_api_key', 'DeepSeek API Key', 'API key for DeepSeek integration', :apiKey)\n ON DUPLICATE KEY UPDATE value = :apiKey\";\n \n $result = $connection2->prepare($sql);\n return $result->execute(['apiKey' => $apiKey]);\n } catch (PDOException $e) {\n error_log('Error storing API key: ' . $e->getMessage());\n return false;\n }\n}\n\n/**\n * Get the API key securely\n * @param PDO $connection2 The database connection\n * @return string The API key\n */\nfunction getSecureAPIKey($connection2) {\n try {\n $sql = \"SELECT value FROM gibbonSetting WHERE scope='ChatBot' AND name='deepseek_api_key'\";\n $result = $connection2->prepare($sql);\n $result->execute();\n if ($result->rowCount() == 1) {\n $row = $result->fetch();\n return $row['value'];\n }\n } catch (PDOException $e) {\n return '';\n }\n return '';\n}\n\n/**\n * Get student assessment data\n * @param PDO $connection2 The database connection\n * @param int $studentID The student ID\n * @return array The assessment data\n */\nfunction getStudentAssessmentData($connection2, $studentID) {\n try {\n $sql = \"SELECT \n ia.name as assessment_name,\n iac.attainmentValue,\n iac.effortValue,\n iac.comment as teacher_feedback,\n c.name as course_name\n FROM gibbonInternalAssessmentEntry iac\n JOIN gibbonInternalAssessmentColumn ia ON (ia.gibbonInternalAssessmentColumnID=iac.gibbonInternalAssessmentColumnID)\n JOIN gibbonCourse c ON (c.gibbonCourseID=ia.gibbonCourseID)\n WHERE iac.gibbonPersonID=:studentID\n ORDER BY ia.timestampCreated DESC\";\n \n $result = $connection2->prepare($sql);\n $result->execute(['studentID' => $studentID]);\n return $result->fetchAll(PDO::FETCH_ASSOC);\n } catch (Exception $e) {\n error_log('Error getting student assessment data: ' . $e->getMessage());\n return [];\n }\n}\n\n/**\n * Make a secure call to the OpenAI API\n * \n * @param string $apiKey\n * @param array $data\n * @return array|null\n */\nfunction callOpenAIAPI($apiKey, $data) {\n try {\n // Initialize cURL session\n $ch = curl_init('https://api.openai.com/v1/chat/completions');\n \n // Set cURL options\n curl_setopt_array($ch, [\n CURLOPT_RETURNTRANSFER => true,\n CURLOPT_POST => true,\n CURLOPT_HTTPHEADER => [\n 'Content-Type: application/json',\n 'Authorization: Bearer ' . $apiKey\n ],\n CURLOPT_POSTFIELDS => json_encode($data),\n CURLOPT_SSL_VERIFYPEER => true, // Verify SSL certificate\n CURLOPT_SSL_VERIFYHOST => 2, // Verify hostname\n CURLOPT_CONNECTTIMEOUT => 10, // Connection timeout\n CURLOPT_TIMEOUT => 30, // Response timeout\n CURLOPT_PROTOCOLS => CURLPROTO_HTTPS, // Only allow HTTPS\n ]);\n \n // Execute request\n $response = curl_exec($ch);\n $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);\n \n // Check for errors\n if ($httpCode !== 200) {\n error_log('OpenAI API error: HTTP ' . $httpCode);\n error_log('Response: ' . $response);\n return null;\n }\n \n // Close cURL session\n curl_close($ch);\n \n // Parse and return response\n return json_decode($response, true);\n \n } catch (Exception $e) {\n error_log('Error calling OpenAI API: ' . $e->getMessage());\n return null;\n }\n}\n\n// Only declare if not already defined\nif (!function_exists('isActionAccessible')) {\n /**\n * Check if a user has access to a specific action\n * @param mixed $guid The global unique identifier\n * @param PDO $connection2 The database connection\n * @param string $address The action address to check\n * @return bool Whether the user has access\n */\n function isActionAccessible($guid, $connection2, $address) {\n try {\n $data = array('gibbonRoleID' => $_SESSION[$guid]['gibbonRoleIDCurrent'], 'address' => $address);\n $sql = \"SELECT DISTINCT gibbonAction.gibbonActionID \n FROM gibbonAction \n JOIN gibbonPermission ON (gibbonAction.gibbonActionID=gibbonPermission.gibbonActionID) \n WHERE (gibbonPermission.gibbonRoleID=:gibbonRoleID AND gibbonAction.URLList LIKE CONCAT('%', :address, '%'))\";\n $result = $connection2->prepare($sql);\n $result->execute($data);\n return ($result->rowCount() > 0);\n } catch (PDOException $e) {\n return false;\n }\n }\n}\n\n/**\n * Helper functions for the ChatBot module\n */\n\n/**\n * Check if a user has access to the ChatBot module\n *\n * @param string $guid\n * @param PDO $connection2\n * @return bool\n */\nfunction chatbotCheckAccess($guid, $connection2) {\n return isActionAccessible($guid, $connection2, '/modules/ChatBot/chatbot.php');\n}\n\n/**\n * Get the DeepSeek API key from settings\n *\n * @param SettingGateway $settingGateway\n * @return string|null\n */\nfunction chatbotGetApiKey($settingGateway) {\n return $settingGateway->getSettingByScope('ChatBot', 'deepseek_api_key');\n}\n\n/**\n * Format chat messages for display\n *\n * @param array $messages\n * @return string\n */\nfunction chatbotFormatMessages($messages) {\n $output = '';\n foreach ($messages as $message) {\n $type = $message['type'];\n $content = $message['content'];\n $output .= \"
\";\n $output .= \"
{$content}
\";\n $output .= \"
\";\n }\n return $output;\n}\n\n/**\n * Validate chat message\n *\n * @param string $message\n * @return bool\n */\nfunction chatbotValidateMessage($message) {\n return !empty(trim($message));\n}\n\n/**\n * Sanitize chat message\n *\n * @param string $message\n * @return string\n */\nfunction chatbotSanitizeMessage($message) {\n return strip_tags(trim($message));\n}\n\n/**\n * Store AI recommendations for a student\n * @param PDO $connection2 The database connection\n * @param int $gibbonPersonID The student ID\n * @param int $gibbonCourseClassID The course class ID\n * @param int $assessmentID The assessment ID\n * @param string $recommendation The AI recommendation\n * @return bool Whether the operation was successful\n */\nfunction storeRecommendation($connection2, $gibbonPersonID, $gibbonCourseClassID, $assessmentID, $recommendation) {\n try {\n $sql = \"INSERT INTO gibbonChatBotRecommendations \n (gibbonPersonID, gibbonCourseClassID, assessmentID, recommendation) \n VALUES \n (:gibbonPersonID, :gibbonCourseClassID, :assessmentID, :recommendation)\";\n \n $result = $connection2->prepare($sql);\n return $result->execute([\n 'gibbonPersonID' => $gibbonPersonID,\n 'gibbonCourseClassID' => $gibbonCourseClassID,\n 'assessmentID' => $assessmentID,\n 'recommendation' => $recommendation\n ]);\n } catch (PDOException $e) {\n error_log('Error storing recommendation: ' . $e->getMessage());\n return false;\n }\n}\n\n/**\n * Get AI recommendations for a student\n * @param PDO $connection2 The database connection\n * @param int $gibbonPersonID The student ID\n * @return array The recommendations\n */\nfunction getStudentRecommendations($connection2, $gibbonPersonID) {\n try {\n $sql = \"SELECT r.*, \n ia.name as assessment_name,\n c.name as course_name\n FROM gibbonChatBotRecommendations r\n JOIN gibbonInternalAssessment ia ON (ia.gibbonInternalAssessmentID = r.assessmentID)\n JOIN gibbonCourseClass cc ON (cc.gibbonCourseClassID = r.gibbonCourseClassID)\n JOIN gibbonCourse c ON (c.gibbonCourseID = cc.gibbonCourseID)\n WHERE r.gibbonPersonID = :gibbonPersonID\n ORDER BY r.timestamp DESC\";\n \n $result = $connection2->prepare($sql);\n $result->execute(['gibbonPersonID' => $gibbonPersonID]);\n return $result->fetchAll(PDO::FETCH_ASSOC);\n } catch (PDOException $e) {\n error_log('Error getting student recommendations: ' . $e->getMessage());\n return [];\n }\n}\n\n/**\n * Add training data to the ChatBot\n * @param PDO $connection2 The database connection\n * @param string $question The question\n * @param string $answer The answer\n * @param int $createdBy The user ID who created the entry\n * @param string|null $category The category (optional)\n * @return bool Whether the operation was successful\n */\nfunction addTrainingData($connection2, $question, $answer, $createdBy, $category = null) {\n try {\n $sql = \"INSERT INTO gibbonChatBotTraining \n (question, answer, category, created_by) \n VALUES \n (:question, :answer, :category, :created_by)\";\n \n $result = $connection2->prepare($sql);\n return $result->execute([\n 'question' => $question,\n 'answer' => $answer,\n 'category' => $category,\n 'created_by' => $createdBy\n ]);\n } catch (PDOException $e) {\n error_log('Error adding training data: ' . $e->getMessage());\n return false;\n }\n}\n\n/**\n * Get training data for the ChatBot\n * @param PDO $connection2 The database connection\n * @param string $category Optional category filter\n * @return array The training data\n */\nfunction getTrainingData($connection2, $category = null) {\n try {\n $sql = \"SELECT * FROM gibbonChatBotTraining\";\n $params = [];\n \n if ($category) {\n $sql .= \" WHERE category = :category\";\n $params['category'] = $category;\n }\n \n $sql .= \" ORDER BY timestampCreated DESC\";\n \n $result = $connection2->prepare($sql);\n $result->execute($params);\n return $result->fetchAll(PDO::FETCH_ASSOC);\n } catch (PDOException $e) {\n error_log('Error getting training data: ' . $e->getMessage());\n return [];\n }\n}\n\n/**\n * Delete training data\n * @param PDO $connection2 The database connection\n * @param int $id The training data ID\n * @return bool Whether the operation was successful\n */\nfunction deleteTrainingData($connection2, $id) {\n try {\n $sql = \"DELETE FROM gibbonChatBotTraining WHERE gibbonChatBotTrainingID = :id\";\n $result = $connection2->prepare($sql);\n return $result->execute(['id' => $id]);\n } catch (PDOException $e) {\n error_log('Error deleting training data: ' . $e->getMessage());\n return false;\n }\n}\n\n/**\n * Generate AI recommendations based on assessment data\n * @param array $assessment The assessment data\n * @param string $apiKey The DeepSeek API key\n * @return string|null The AI recommendation\n */\nfunction generateAssessmentRecommendation($assessment, $apiKey) {\n try {\n $prompt = \"Based on the following assessment data:\\n\";\n $prompt .= \"Subject: {$assessment['subject']}\\n\";\n $prompt .= \"Grade: {$assessment['grade']}\\n\";\n $prompt .= \"Teacher Feedback: {$assessment['feedback']}\\n\\n\";\n $prompt .= \"Please provide specific, actionable recommendations for improvement in this subject. Consider:\\n\";\n $prompt .= \"1. Study strategies\\n\";\n $prompt .= \"2. Practice exercises\\n\";\n $prompt .= \"3. Additional resources\\n\";\n $prompt .= \"4. Areas needing focus based on the assessment\\n\";\n $prompt .= \"5. Time management suggestions\";\n \n $data = [\n 'model' => 'deepseek-v4-flash',\n 'messages' => [\n [\n 'role' => 'system',\n 'content' => 'You are an educational AI assistant providing specific, actionable recommendations for academic improvement based on assessment data and teacher feedback.'\n ],\n [\n 'role' => 'user',\n 'content' => $prompt\n ]\n ],\n 'max_tokens' => 500\n ];\n \n $ch = curl_init();\n curl_setopt($ch, CURLOPT_URL, 'https://api.deepseek.com/v1/chat/completions');\n curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\n curl_setopt($ch, CURLOPT_POST, true);\n curl_setopt($ch, CURLOPT_HTTPHEADER, [\n 'Content-Type: application/json',\n 'Authorization: Bearer ' . $apiKey\n ]);\n \n curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));\n \n $response = curl_exec($ch);\n $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);\n curl_close($ch);\n \n if ($httpCode === 200) {\n $result = json_decode($response, true);\n return $result['choices'][0]['message']['content'];\n }\n } catch (Exception $e) {\n error_log('Error generating assessment recommendation: ' . $e->getMessage());\n }\n \n return null;\n}\n\n/**\n * Get student performance metrics\n * @param int $gibbonPersonID\n * @return string HTML output of performance metrics\n */\nfunction getStudentPerformanceMetrics($gibbonPersonID) {\n global $pdo;\n \n try {\n // Get assessment data\n $data = getStudentAssessmentData($gibbonPersonID);\n \n // Calculate metrics\n $metrics = [\n 'average_attainment' => calculateAverageAttainment($data),\n 'progress_indicators' => analyzeProgressTrends($data),\n 'subject_breakdown' => getSubjectPerformance($data)\n ];\n \n // Generate HTML output\n $output = '
';\n foreach ($metrics as $key => $value) {\n $output .= generateMetricCard($key, $value);\n }\n $output .= '
';\n \n return $output;\n } catch (Exception $e) {\n return \"Error generating performance metrics: \" . $e->getMessage();\n }\n}\n\n/**\n * Generate AI recommendations based on student data\n * @param int $gibbonPersonID\n * @return string HTML output of recommendations\n */\nfunction generateAIRecommendations($gibbonPersonID) {\n try {\n // Get comprehensive student data\n $assessmentData = getStudentAssessmentData($gibbonPersonID);\n $feedbackData = getTeacherFeedback($gibbonPersonID);\n $engagementData = getStudentEngagementData($gibbonPersonID);\n \n // Prepare data for AI analysis\n $analysisData = [\n 'assessment_history' => $assessmentData,\n 'teacher_feedback' => $feedbackData,\n 'engagement_metrics' => $engagementData,\n 'learning_patterns' => analyzeLearningPatterns($gibbonPersonID)\n ];\n \n // Get AI recommendations\n $recommendations = callDeepSeekAPI($analysisData);\n \n // Format recommendations\n return formatAIRecommendations($recommendations);\n } catch (Exception $e) {\n return \"Error generating AI recommendations: \" . $e->getMessage();\n }\n}\n\n/**\n * Process and store training data\n * @param array $data Training data array\n * @return bool Success status\n */\nfunction processTrainingData($data) {\n try {\n // Validate training data\n validateTrainingData($data);\n \n // Enrich data with institutional context\n $enrichedData = enrichTrainingData($data);\n \n // Store in database\n storeTrainingData($enrichedData);\n \n // Update AI model\n updateAIModel($enrichedData);\n \n return true;\n } catch (Exception $e) {\n error_log(\"Training data processing error: \" . $e->getMessage());\n return false;\n }\n}\n\n/**\n * Generate learning analytics\n * @param array $params Analytics parameters\n * @return array Analytics data\n */\nfunction generateLearningAnalytics($params) {\n try {\n // Gather data\n $performanceData = getPerformanceData($params);\n $engagementData = getEngagementData($params);\n $patternData = getPatternData($params);\n \n // Process analytics\n $analytics = [\n 'performance' => analyzePerformance($performanceData),\n 'engagement' => analyzeEngagement($engagementData),\n 'patterns' => analyzePatterns($patternData),\n 'recommendations' => generateRecommendations([\n 'performance' => $performanceData,\n 'engagement' => $engagementData,\n 'patterns' => $patternData\n ])\n ];\n \n return $analytics;\n } catch (Exception $e) {\n error_log(\"Analytics generation error: \" . $e->getMessage());\n return null;\n }\n}\n\n/**\n * Format AI recommendations for display\n * @param array $recommendations\n * @return string Formatted HTML\n */\nfunction formatAIRecommendations($recommendations) {\n $output = '
';\n \n // Format subject-specific recommendations\n if (isset($recommendations['subjects'])) {\n $output .= '
';\n $output .= '

Subject-Specific Recommendations

';\n foreach ($recommendations['subjects'] as $subject => $data) {\n $output .= formatSubjectRecommendation($subject, $data);\n }\n $output .= '
';\n }\n \n // Format learning strategy recommendations\n if (isset($recommendations['strategies'])) {\n $output .= '
';\n $output .= '

Learning Strategy Recommendations

';\n foreach ($recommendations['strategies'] as $strategy) {\n $output .= formatStrategyRecommendation($strategy);\n }\n $output .= '
';\n }\n \n // Format improvement suggestions\n if (isset($recommendations['improvements'])) {\n $output .= '
';\n $output .= '

Areas for Improvement

';\n foreach ($recommendations['improvements'] as $area => $suggestions) {\n $output .= formatImprovementSuggestions($area, $suggestions);\n }\n $output .= '
';\n }\n \n $output .= '
';\n return $output;\n}\n\n"}, "030e63497fd5a0ea218107feedd91d649324421f": {"t": 1785756299.9833863, "n": 21948, "h": "4e3f7455eb0bcd79", "c": ".\n*/\n\nuse Gibbon\\Domain\\System\\SettingGateway;\nuse Gibbon\\Forms\\Form;\nuse Gibbon\\Services\\Format as GibbonFormat;\nuse Gibbon\\Domain\\System\\ModuleGateway;\nuse Gibbon\\Domain\\DataSet;\nuse Gibbon\\Tables\\DataTable;\n\n// Include Gibbon core functions\nrequire_once __DIR__ . '/../../functions.php';\n\n/**\n * Get a setting value by scope\n * @param PDO $connection2 The database connection\n * @param string $scope The setting scope\n * @param string $name The setting name\n * @return string The setting value\n */\nfunction getSettingByScope($connection2, $scope, $name) {\n try {\n $sql = \"SELECT value FROM gibbonSetting WHERE scope=:scope AND name=:name\";\n $result = $connection2->prepare($sql);\n $result->execute(['scope' => $scope, 'name' => $name]);\n if ($result->rowCount() == 1) {\n $row = $result->fetch();\n return $row['value'];\n }\n } catch (PDOException $e) {\n return '';\n }\n return '';\n}\n\n/**\n * Check if a file is a valid PDF\n * @param string $file The file path\n * @return bool Whether the file is a valid PDF\n */\nfunction isValidPDF($file) {\n if (empty($file)) return false;\n \n // Check file extension\n $extension = strtolower(pathinfo($file, PATHINFO_EXTENSION));\n if ($extension !== 'pdf') return false;\n \n // Check file mime type\n $finfo = finfo_open(FILEINFO_MIME_TYPE);\n $mimeType = finfo_file($finfo, $file);\n finfo_close($finfo);\n \n return $mimeType === 'application/pdf';\n}\n\n/**\n * Extract text from PDF file\n * @param string $pdfFile The PDF file path\n * @return string The extracted text\n */\nfunction extractTextFromPDF($pdfFile) {\n if (!isValidPDF($pdfFile)) {\n throw new Exception('Invalid PDF file');\n }\n \n // Create temporary files\n $tempPdf = tempnam(sys_get_temp_dir(), 'pdf_');\n $tempTxt = tempnam(sys_get_temp_dir(), 'txt_');\n \n try {\n // Copy PDF to temp location\n if (!copy($pdfFile, $tempPdf)) {\n throw new Exception('Failed to copy PDF file');\n }\n \n // Convert PDF to text\n exec(\"pdftotext '{$tempPdf}' '{$tempTxt}' 2>&1\", $output, $returnVar);\n \n if ($returnVar !== 0) {\n throw new Exception('Failed to convert PDF: ' . implode(\"\\n\", $output));\n }\n \n // Read converted text\n $text = file_get_contents($tempTxt);\n \n return $text;\n \n } finally {\n // Clean up temp files\n if (file_exists($tempPdf)) unlink($tempPdf);\n if (file_exists($tempTxt)) unlink($tempTxt);\n }\n}\n\n/**\n * Store the API key securely\n * @param PDO $connection2 The database connection\n * @param string $apiKey The API key to store\n * @return bool Whether the operation was successful\n */\nfunction storeAPIKey($connection2, $apiKey) {\n try {\n $sql = \"INSERT INTO gibbonSetting (scope, name, nameDisplay, description, value) \n VALUES ('ChatBot', 'deepseek_api_key', 'DeepSeek API Key', 'API key for DeepSeek integration', :apiKey)\n ON DUPLICATE KEY UPDATE value = :apiKey\";\n \n $result = $connection2->prepare($sql);\n return $result->execute(['apiKey' => $apiKey]);\n } catch (PDOException $e) {\n error_log('Error storing API key: ' . $e->getMessage());\n return false;\n }\n}\n\n/**\n * Get the API key securely\n * @param PDO $connection2 The database connection\n * @return string The API key\n */\nfunction getSecureAPIKey($connection2) {\n try {\n $sql = \"SELECT value FROM gibbonSetting WHERE scope='ChatBot' AND name='deepseek_api_key'\";\n $result = $connection2->prepare($sql);\n $result->execute();\n if ($result->rowCount() == 1) {\n $row = $result->fetch();\n return $row['value'];\n }\n } catch (PDOException $e) {\n return '';\n }\n return '';\n}\n\n/**\n * Get student assessment data\n * @param PDO $connection2 The database connection\n * @param int $studentID The student ID\n * @return array The assessment data\n */\nfunction getStudentAssessmentData($connection2, $studentID) {\n try {\n $sql = \"SELECT \n ia.name as assessment_name,\n iac.attainmentValue,\n iac.effortValue,\n iac.comment as teacher_feedback,\n c.name as course_name\n FROM gibbonInternalAssessmentEntry iac\n JOIN gibbonInternalAssessmentColumn ia ON (ia.gibbonInternalAssessmentColumnID=iac.gibbonInternalAssessmentColumnID)\n JOIN gibbonCourse c ON (c.gibbonCourseID=ia.gibbonCourseID)\n WHERE iac.gibbonPersonID=:studentID\n ORDER BY ia.timestampCreated DESC\";\n \n $result = $connection2->prepare($sql);\n $result->execute(['studentID' => $studentID]);\n return $result->fetchAll(PDO::FETCH_ASSOC);\n } catch (Exception $e) {\n error_log('Error getting student assessment data: ' . $e->getMessage());\n return [];\n }\n}\n\n/**\n * Make a secure call to the OpenAI API\n * \n * @param string $apiKey\n * @param array $data\n * @return array|null\n */\nfunction callOpenAIAPI($apiKey, $data) {\n try {\n // Initialize cURL session\n $ch = curl_init('https://api.openai.com/v1/chat/completions');\n \n // Set cURL options\n curl_setopt_array($ch, [\n CURLOPT_RETURNTRANSFER => true,\n CURLOPT_POST => true,\n CURLOPT_HTTPHEADER => [\n 'Content-Type: application/json',\n 'Authorization: Bearer ' . $apiKey\n ],\n CURLOPT_POSTFIELDS => json_encode($data),\n CURLOPT_SSL_VERIFYPEER => true, // Verify SSL certificate\n CURLOPT_SSL_VERIFYHOST => 2, // Verify hostname\n CURLOPT_CONNECTTIMEOUT => 10, // Connection timeout\n CURLOPT_TIMEOUT => 30, // Response timeout\n CURLOPT_PROTOCOLS => CURLPROTO_HTTPS, // Only allow HTTPS\n ]);\n \n // Execute request\n $response = curl_exec($ch);\n $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);\n \n // Check for errors\n if ($httpCode !== 200) {\n error_log('OpenAI API error: HTTP ' . $httpCode);\n error_log('Response: ' . $response);\n return null;\n }\n \n // Close cURL session\n curl_close($ch);\n \n // Parse and return response\n return json_decode($response, true);\n \n } catch (Exception $e) {\n error_log('Error calling OpenAI API: ' . $e->getMessage());\n return null;\n }\n}\n\n// Only declare if not already defined\nif (!function_exists('isActionAccessible')) {\n /**\n * Check if a user has access to a specific action\n * @param mixed $guid The global unique identifier\n * @param PDO $connection2 The database connection\n * @param string $address The action address to check\n * @return bool Whether the user has access\n */\n function isActionAccessible($guid, $connection2, $address) {\n try {\n $data = array('gibbonRoleID' => $_SESSION[$guid]['gibbonRoleIDCurrent'], 'address' => $address);\n $sql = \"SELECT DISTINCT gibbonAction.gibbonActionID \n FROM gibbonAction \n JOIN gibbonPermission ON (gibbonAction.gibbonActionID=gibbonPermission.gibbonActionID) \n WHERE (gibbonPermission.gibbonRoleID=:gibbonRoleID AND gibbonAction.URLList LIKE CONCAT('%', :address, '%'))\";\n $result = $connection2->prepare($sql);\n $result->execute($data);\n return ($result->rowCount() > 0);\n } catch (PDOException $e) {\n return false;\n }\n }\n}\n\n/**\n * Helper functions for the ChatBot module\n */\n\n/**\n * Check if a user has access to the ChatBot module\n *\n * @param string $guid\n * @param PDO $connection2\n * @return bool\n */\nfunction chatbotCheckAccess($guid, $connection2) {\n return isActionAccessible($guid, $connection2, '/modules/ChatBot/chatbot.php');\n}\n\n/**\n * Get the DeepSeek API key from settings\n *\n * @param SettingGateway $settingGateway\n * @return string|null\n */\nfunction chatbotGetApiKey($settingGateway) {\n return $settingGateway->getSettingByScope('ChatBot', 'deepseek_api_key');\n}\n\n/**\n * Format chat messages for display\n *\n * @param array $messages\n * @return string\n */\nfunction chatbotFormatMessages($messages) {\n $output = '';\n foreach ($messages as $message) {\n $type = $message['type'];\n $content = $message['content'];\n $output .= \"
\";\n $output .= \"
{$content}
\";\n $output .= \"
\";\n }\n return $output;\n}\n\n/**\n * Validate chat message\n *\n * @param string $message\n * @return bool\n */\nfunction chatbotValidateMessage($message) {\n return !empty(trim($message));\n}\n\n/**\n * Sanitize chat message\n *\n * @param string $message\n * @return string\n */\nfunction chatbotSanitizeMessage($message) {\n return strip_tags(trim($message));\n}\n\n/**\n * Store AI recommendations for a student\n * @param PDO $connection2 The database connection\n * @param int $gibbonPersonID The student ID\n * @param int $gibbonCourseClassID The course class ID\n * @param int $assessmentID The assessment ID\n * @param string $recommendation The AI recommendation\n * @return bool Whether the operation was successful\n */\nfunction storeRecommendation($connection2, $gibbonPersonID, $gibbonCourseClassID, $assessmentID, $recommendation) {\n try {\n $sql = \"INSERT INTO gibbonChatBotRecommendations \n (gibbonPersonID, gibbonCourseClassID, assessmentID, recommendation) \n VALUES \n (:gibbonPersonID, :gibbonCourseClassID, :assessmentID, :recommendation)\";\n \n $result = $connection2->prepare($sql);\n return $result->execute([\n 'gibbonPersonID' => $gibbonPersonID,\n 'gibbonCourseClassID' => $gibbonCourseClassID,\n 'assessmentID' => $assessmentID,\n 'recommendation' => $recommendation\n ]);\n } catch (PDOException $e) {\n error_log('Error storing recommendation: ' . $e->getMessage());\n return false;\n }\n}\n\n/**\n * Get AI recommendations for a student\n * @param PDO $connection2 The database connection\n * @param int $gibbonPersonID The student ID\n * @return array The recommendations\n */\nfunction getStudentRecommendations($connection2, $gibbonPersonID) {\n try {\n $sql = \"SELECT r.*, \n ia.name as assessment_name,\n c.name as course_name\n FROM gibbonChatBotRecommendations r\n JOIN gibbonInternalAssessment ia ON (ia.gibbonInternalAssessmentID = r.assessmentID)\n JOIN gibbonCourseClass cc ON (cc.gibbonCourseClassID = r.gibbonCourseClassID)\n JOIN gibbonCourse c ON (c.gibbonCourseID = cc.gibbonCourseID)\n WHERE r.gibbonPersonID = :gibbonPersonID\n ORDER BY r.timestamp DESC\";\n \n $result = $connection2->prepare($sql);\n $result->execute(['gibbonPersonID' => $gibbonPersonID]);\n return $result->fetchAll(PDO::FETCH_ASSOC);\n } catch (PDOException $e) {\n error_log('Error getting student recommendations: ' . $e->getMessage());\n return [];\n }\n}\n\n/**\n * Add training data to the ChatBot\n * @param PDO $connection2 The database connection\n * @param string $question The question\n * @param string $answer The answer\n * @param int $createdBy The user ID who created the entry\n * @param string|null $category The category (optional)\n * @return bool Whether the operation was successful\n */\nfunction addTrainingData($connection2, $question, $answer, $createdBy, $category = null) {\n try {\n $sql = \"INSERT INTO gibbonChatBotTraining \n (question, answer, category, created_by) \n VALUES \n (:question, :answer, :category, :created_by)\";\n \n $result = $connection2->prepare($sql);\n return $result->execute([\n 'question' => $question,\n 'answer' => $answer,\n 'category' => $category,\n 'created_by' => $createdBy\n ]);\n } catch (PDOException $e) {\n error_log('Error adding training data: ' . $e->getMessage());\n return false;\n }\n}\n\n/**\n * Get training data for the ChatBot\n * @param PDO $connection2 The database connection\n * @param string $category Optional category filter\n * @return array The training data\n */\nfunction getTrainingData($connection2, $category = null) {\n try {\n $sql = \"SELECT * FROM gibbonChatBotTraining\";\n $params = [];\n \n if ($category) {\n $sql .= \" WHERE category = :category\";\n $params['category'] = $category;\n }\n \n $sql .= \" ORDER BY timestampCreated DESC\";\n \n $result = $connection2->prepare($sql);\n $result->execute($params);\n return $result->fetchAll(PDO::FETCH_ASSOC);\n } catch (PDOException $e) {\n error_log('Error getting training data: ' . $e->getMessage());\n return [];\n }\n}\n\n/**\n * Delete training data\n * @param PDO $connection2 The database connection\n * @param int $id The training data ID\n * @return bool Whether the operation was successful\n */\nfunction deleteTrainingData($connection2, $id) {\n try {\n $sql = \"DELETE FROM gibbonChatBotTraining WHERE gibbonChatBotTrainingID = :id\";\n $result = $connection2->prepare($sql);\n return $result->execute(['id' => $id]);\n } catch (PDOException $e) {\n error_log('Error deleting training data: ' . $e->getMessage());\n return false;\n }\n}\n\n/**\n * Generate AI recommendations based on assessment data\n * @param array $assessment The assessment data\n * @param string $apiKey The DeepSeek API key\n * @return string|null The AI recommendation\n */\nfunction generateAssessmentRecommendation($assessment, $apiKey) {\n try {\n $prompt = \"Based on the following assessment data:\\n\";\n $prompt .= \"Subject: {$assessment['subject']}\\n\";\n $prompt .= \"Grade: {$assessment['grade']}\\n\";\n $prompt .= \"Teacher Feedback: {$assessment['feedback']}\\n\\n\";\n $prompt .= \"Please provide specific, actionable recommendations for improvement in this subject. Consider:\\n\";\n $prompt .= \"1. Study strategies\\n\";\n $prompt .= \"2. Practice exercises\\n\";\n $prompt .= \"3. Additional resources\\n\";\n $prompt .= \"4. Areas needing focus based on the assessment\\n\";\n $prompt .= \"5. Time management suggestions\";\n \n $data = [\n 'model' => 'deepseek-chat',\n 'messages' => [\n [\n 'role' => 'system',\n 'content' => 'You are an educational AI assistant providing specific, actionable recommendations for academic improvement based on assessment data and teacher feedback.'\n ],\n [\n 'role' => 'user',\n 'content' => $prompt\n ]\n ],\n 'max_tokens' => 500\n ];\n \n $ch = curl_init();\n curl_setopt($ch, CURLOPT_URL, 'https://api.deepseek.com/v1/chat/completions');\n curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\n curl_setopt($ch, CURLOPT_POST, true);\n curl_setopt($ch, CURLOPT_HTTPHEADER, [\n 'Content-Type: application/json',\n 'Authorization: Bearer ' . $apiKey\n ]);\n \n curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));\n \n $response = curl_exec($ch);\n $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);\n curl_close($ch);\n \n if ($httpCode === 200) {\n $result = json_decode($response, true);\n return $result['choices'][0]['message']['content'];\n }\n } catch (Exception $e) {\n error_log('Error generating assessment recommendation: ' . $e->getMessage());\n }\n \n return null;\n}\n\n/**\n * Get student performance metrics\n * @param int $gibbonPersonID\n * @return string HTML output of performance metrics\n */\nfunction getStudentPerformanceMetrics($gibbonPersonID) {\n global $pdo;\n \n try {\n // Get assessment data\n $data = getStudentAssessmentData($gibbonPersonID);\n \n // Calculate metrics\n $metrics = [\n 'average_attainment' => calculateAverageAttainment($data),\n 'progress_indicators' => analyzeProgressTrends($data),\n 'subject_breakdown' => getSubjectPerformance($data)\n ];\n \n // Generate HTML output\n $output = '
';\n foreach ($metrics as $key => $value) {\n $output .= generateMetricCard($key, $value);\n }\n $output .= '
';\n \n return $output;\n } catch (Exception $e) {\n return \"Error generating performance metrics: \" . $e->getMessage();\n }\n}\n\n/**\n * Generate AI recommendations based on student data\n * @param int $gibbonPersonID\n * @return string HTML output of recommendations\n */\nfunction generateAIRecommendations($gibbonPersonID) {\n try {\n // Get comprehensive student data\n $assessmentData = getStudentAssessmentData($gibbonPersonID);\n $feedbackData = getTeacherFeedback($gibbonPersonID);\n $engagementData = getStudentEngagementData($gibbonPersonID);\n \n // Prepare data for AI analysis\n $analysisData = [\n 'assessment_history' => $assessmentData,\n 'teacher_feedback' => $feedbackData,\n 'engagement_metrics' => $engagementData,\n 'learning_patterns' => analyzeLearningPatterns($gibbonPersonID)\n ];\n \n // Get AI recommendations\n $recommendations = callDeepSeekAPI($analysisData);\n \n // Format recommendations\n return formatAIRecommendations($recommendations);\n } catch (Exception $e) {\n return \"Error generating AI recommendations: \" . $e->getMessage();\n }\n}\n\n/**\n * Process and store training data\n * @param array $data Training data array\n * @return bool Success status\n */\nfunction processTrainingData($data) {\n try {\n // Validate training data\n validateTrainingData($data);\n \n // Enrich data with institutional context\n $enrichedData = enrichTrainingData($data);\n \n // Store in database\n storeTrainingData($enrichedData);\n \n // Update AI model\n updateAIModel($enrichedData);\n \n return true;\n } catch (Exception $e) {\n error_log(\"Training data processing error: \" . $e->getMessage());\n return false;\n }\n}\n\n/**\n * Generate learning analytics\n * @param array $params Analytics parameters\n * @return array Analytics data\n */\nfunction generateLearningAnalytics($params) {\n try {\n // Gather data\n $performanceData = getPerformanceData($params);\n $engagementData = getEngagementData($params);\n $patternData = getPatternData($params);\n \n // Process analytics\n $analytics = [\n 'performance' => analyzePerformance($performanceData),\n 'engagement' => analyzeEngagement($engagementData),\n 'patterns' => analyzePatterns($patternData),\n 'recommendations' => generateRecommendations([\n 'performance' => $performanceData,\n 'engagement' => $engagementData,\n 'patterns' => $patternData\n ])\n ];\n \n return $analytics;\n } catch (Exception $e) {\n error_log(\"Analytics generation error: \" . $e->getMessage());\n return null;\n }\n}\n\n/**\n * Format AI recommendations for display\n * @param array $recommendations\n * @return string Formatted HTML\n */\nfunction formatAIRecommendations($recommendations) {\n $output = '
';\n \n // Format subject-specific recommendations\n if (isset($recommendations['subjects'])) {\n $output .= '
';\n $output .= '

Subject-Specific Recommendations

';\n foreach ($recommendations['subjects'] as $subject => $data) {\n $output .= formatSubjectRecommendation($subject, $data);\n }\n $output .= '
';\n }\n \n // Format learning strategy recommendations\n if (isset($recommendations['strategies'])) {\n $output .= '
';\n $output .= '

Learning Strategy Recommendations

';\n foreach ($recommendations['strategies'] as $strategy) {\n $output .= formatStrategyRecommendation($strategy);\n }\n $output .= '
';\n }\n \n // Format improvement suggestions\n if (isset($recommendations['improvements'])) {\n $output .= '
';\n $output .= '

Areas for Improvement

';\n foreach ($recommendations['improvements'] as $area => $suggestions) {\n $output .= formatImprovementSuggestions($area, $suggestions);\n }\n $output .= '
';\n }\n \n $output .= '
';\n return $output;\n}\n\n"}, "ea2bf72c5d38ab35b13101b6131cf74ba37f78a3": {"t": 1786068313.289063, "n": 21952, "h": "6781a141c0d264fc", "c": ".\n*/\n\nuse Gibbon\\Domain\\System\\SettingGateway;\nuse Gibbon\\Forms\\Form;\nuse Gibbon\\Services\\Format as GibbonFormat;\nuse Gibbon\\Domain\\System\\ModuleGateway;\nuse Gibbon\\Domain\\DataSet;\nuse Gibbon\\Tables\\DataTable;\n\n// Include Gibbon core functions\nrequire_once __DIR__ . '/../../functions.php';\n\n/**\n * Get a setting value by scope\n * @param PDO $connection2 The database connection\n * @param string $scope The setting scope\n * @param string $name The setting name\n * @return string The setting value\n */\nfunction getSettingByScope($connection2, $scope, $name) {\n try {\n $sql = \"SELECT value FROM gibbonSetting WHERE scope=:scope AND name=:name\";\n $result = $connection2->prepare($sql);\n $result->execute(['scope' => $scope, 'name' => $name]);\n if ($result->rowCount() == 1) {\n $row = $result->fetch();\n return $row['value'];\n }\n } catch (PDOException $e) {\n return '';\n }\n return '';\n}\n\n/**\n * Check if a file is a valid PDF\n * @param string $file The file path\n * @return bool Whether the file is a valid PDF\n */\nfunction isValidPDF($file) {\n if (empty($file)) return false;\n \n // Check file extension\n $extension = strtolower(pathinfo($file, PATHINFO_EXTENSION));\n if ($extension !== 'pdf') return false;\n \n // Check file mime type\n $finfo = finfo_open(FILEINFO_MIME_TYPE);\n $mimeType = finfo_file($finfo, $file);\n finfo_close($finfo);\n \n return $mimeType === 'application/pdf';\n}\n\n/**\n * Extract text from PDF file\n * @param string $pdfFile The PDF file path\n * @return string The extracted text\n */\nfunction extractTextFromPDF($pdfFile) {\n if (!isValidPDF($pdfFile)) {\n throw new Exception('Invalid PDF file');\n }\n \n // Create temporary files\n $tempPdf = tempnam(sys_get_temp_dir(), 'pdf_');\n $tempTxt = tempnam(sys_get_temp_dir(), 'txt_');\n \n try {\n // Copy PDF to temp location\n if (!copy($pdfFile, $tempPdf)) {\n throw new Exception('Failed to copy PDF file');\n }\n \n // Convert PDF to text\n exec(\"pdftotext '{$tempPdf}' '{$tempTxt}' 2>&1\", $output, $returnVar);\n \n if ($returnVar !== 0) {\n throw new Exception('Failed to convert PDF: ' . implode(\"\\n\", $output));\n }\n \n // Read converted text\n $text = file_get_contents($tempTxt);\n \n return $text;\n \n } finally {\n // Clean up temp files\n if (file_exists($tempPdf)) unlink($tempPdf);\n if (file_exists($tempTxt)) unlink($tempTxt);\n }\n}\n\n/**\n * Store the API key securely\n * @param PDO $connection2 The database connection\n * @param string $apiKey The API key to store\n * @return bool Whether the operation was successful\n */\nfunction storeAPIKey($connection2, $apiKey) {\n try {\n $sql = \"INSERT INTO gibbonSetting (scope, name, nameDisplay, description, value) \n VALUES ('ChatBot', 'deepseek_api_key', 'DeepSeek API Key', 'API key for DeepSeek integration', :apiKey)\n ON DUPLICATE KEY UPDATE value = :apiKey\";\n \n $result = $connection2->prepare($sql);\n return $result->execute(['apiKey' => $apiKey]);\n } catch (PDOException $e) {\n error_log('Error storing API key: ' . $e->getMessage());\n return false;\n }\n}\n\n/**\n * Get the API key securely\n * @param PDO $connection2 The database connection\n * @return string The API key\n */\nfunction getSecureAPIKey($connection2) {\n try {\n $sql = \"SELECT value FROM gibbonSetting WHERE scope='ChatBot' AND name='deepseek_api_key'\";\n $result = $connection2->prepare($sql);\n $result->execute();\n if ($result->rowCount() == 1) {\n $row = $result->fetch();\n return $row['value'];\n }\n } catch (PDOException $e) {\n return '';\n }\n return '';\n}\n\n/**\n * Get student assessment data\n * @param PDO $connection2 The database connection\n * @param int $studentID The student ID\n * @return array The assessment data\n */\nfunction getStudentAssessmentData($connection2, $studentID) {\n try {\n $sql = \"SELECT \n ia.name as assessment_name,\n iac.attainmentValue,\n iac.effortValue,\n iac.comment as teacher_feedback,\n c.name as course_name\n FROM gibbonInternalAssessmentEntry iac\n JOIN gibbonInternalAssessmentColumn ia ON (ia.gibbonInternalAssessmentColumnID=iac.gibbonInternalAssessmentColumnID)\n JOIN gibbonCourse c ON (c.gibbonCourseID=ia.gibbonCourseID)\n WHERE iac.gibbonPersonID=:studentID\n ORDER BY ia.timestampCreated DESC\";\n \n $result = $connection2->prepare($sql);\n $result->execute(['studentID' => $studentID]);\n return $result->fetchAll(PDO::FETCH_ASSOC);\n } catch (Exception $e) {\n error_log('Error getting student assessment data: ' . $e->getMessage());\n return [];\n }\n}\n\n/**\n * Make a secure call to the OpenAI API\n * \n * @param string $apiKey\n * @param array $data\n * @return array|null\n */\nfunction callOpenAIAPI($apiKey, $data) {\n try {\n // Initialize cURL session\n $ch = curl_init('https://api.openai.com/v1/chat/completions');\n \n // Set cURL options\n curl_setopt_array($ch, [\n CURLOPT_RETURNTRANSFER => true,\n CURLOPT_POST => true,\n CURLOPT_HTTPHEADER => [\n 'Content-Type: application/json',\n 'Authorization: Bearer ' . $apiKey\n ],\n CURLOPT_POSTFIELDS => json_encode($data),\n CURLOPT_SSL_VERIFYPEER => true, // Verify SSL certificate\n CURLOPT_SSL_VERIFYHOST => 2, // Verify hostname\n CURLOPT_CONNECTTIMEOUT => 10, // Connection timeout\n CURLOPT_TIMEOUT => 30, // Response timeout\n CURLOPT_PROTOCOLS => CURLPROTO_HTTPS, // Only allow HTTPS\n ]);\n \n // Execute request\n $response = curl_exec($ch);\n $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);\n \n // Check for errors\n if ($httpCode !== 200) {\n error_log('OpenAI API error: HTTP ' . $httpCode);\n error_log('Response: ' . $response);\n return null;\n }\n \n // Close cURL session\n curl_close($ch);\n \n // Parse and return response\n return json_decode($response, true);\n \n } catch (Exception $e) {\n error_log('Error calling OpenAI API: ' . $e->getMessage());\n return null;\n }\n}\n\n// Only declare if not already defined\nif (!function_exists('isActionAccessible')) {\n /**\n * Check if a user has access to a specific action\n * @param mixed $guid The global unique identifier\n * @param PDO $connection2 The database connection\n * @param string $address The action address to check\n * @return bool Whether the user has access\n */\n function isActionAccessible($guid, $connection2, $address) {\n try {\n $data = array('gibbonRoleID' => $_SESSION[$guid]['gibbonRoleIDCurrent'], 'address' => $address);\n $sql = \"SELECT DISTINCT gibbonAction.gibbonActionID \n FROM gibbonAction \n JOIN gibbonPermission ON (gibbonAction.gibbonActionID=gibbonPermission.gibbonActionID) \n WHERE (gibbonPermission.gibbonRoleID=:gibbonRoleID AND gibbonAction.URLList LIKE CONCAT('%', :address, '%'))\";\n $result = $connection2->prepare($sql);\n $result->execute($data);\n return ($result->rowCount() > 0);\n } catch (PDOException $e) {\n return false;\n }\n }\n}\n\n/**\n * Helper functions for the ChatBot module\n */\n\n/**\n * Check if a user has access to the ChatBot module\n *\n * @param string $guid\n * @param PDO $connection2\n * @return bool\n */\nfunction chatbotCheckAccess($guid, $connection2) {\n return isActionAccessible($guid, $connection2, '/modules/ChatBot/chatbot.php');\n}\n\n/**\n * Get the DeepSeek API key from settings\n *\n * @param SettingGateway $settingGateway\n * @return string|null\n */\nfunction chatbotGetApiKey($settingGateway) {\n return $settingGateway->getSettingByScope('ChatBot', 'deepseek_api_key');\n}\n\n/**\n * Format chat messages for display\n *\n * @param array $messages\n * @return string\n */\nfunction chatbotFormatMessages($messages) {\n $output = '';\n foreach ($messages as $message) {\n $type = $message['type'];\n $content = $message['content'];\n $output .= \"
\";\n $output .= \"
{$content}
\";\n $output .= \"
\";\n }\n return $output;\n}\n\n/**\n * Validate chat message\n *\n * @param string $message\n * @return bool\n */\nfunction chatbotValidateMessage($message) {\n return !empty(trim($message));\n}\n\n/**\n * Sanitize chat message\n *\n * @param string $message\n * @return string\n */\nfunction chatbotSanitizeMessage($message) {\n return strip_tags(trim($message));\n}\n\n/**\n * Store AI recommendations for a student\n * @param PDO $connection2 The database connection\n * @param int $gibbonPersonID The student ID\n * @param int $gibbonCourseClassID The course class ID\n * @param int $assessmentID The assessment ID\n * @param string $recommendation The AI recommendation\n * @return bool Whether the operation was successful\n */\nfunction storeRecommendation($connection2, $gibbonPersonID, $gibbonCourseClassID, $assessmentID, $recommendation) {\n try {\n $sql = \"INSERT INTO gibbonChatBotRecommendations \n (gibbonPersonID, gibbonCourseClassID, assessmentID, recommendation) \n VALUES \n (:gibbonPersonID, :gibbonCourseClassID, :assessmentID, :recommendation)\";\n \n $result = $connection2->prepare($sql);\n return $result->execute([\n 'gibbonPersonID' => $gibbonPersonID,\n 'gibbonCourseClassID' => $gibbonCourseClassID,\n 'assessmentID' => $assessmentID,\n 'recommendation' => $recommendation\n ]);\n } catch (PDOException $e) {\n error_log('Error storing recommendation: ' . $e->getMessage());\n return false;\n }\n}\n\n/**\n * Get AI recommendations for a student\n * @param PDO $connection2 The database connection\n * @param int $gibbonPersonID The student ID\n * @return array The recommendations\n */\nfunction getStudentRecommendations($connection2, $gibbonPersonID) {\n try {\n $sql = \"SELECT r.*, \n ia.name as assessment_name,\n c.name as course_name\n FROM gibbonChatBotRecommendations r\n JOIN gibbonInternalAssessment ia ON (ia.gibbonInternalAssessmentID = r.assessmentID)\n JOIN gibbonCourseClass cc ON (cc.gibbonCourseClassID = r.gibbonCourseClassID)\n JOIN gibbonCourse c ON (c.gibbonCourseID = cc.gibbonCourseID)\n WHERE r.gibbonPersonID = :gibbonPersonID\n ORDER BY r.timestamp DESC\";\n \n $result = $connection2->prepare($sql);\n $result->execute(['gibbonPersonID' => $gibbonPersonID]);\n return $result->fetchAll(PDO::FETCH_ASSOC);\n } catch (PDOException $e) {\n error_log('Error getting student recommendations: ' . $e->getMessage());\n return [];\n }\n}\n\n/**\n * Add training data to the ChatBot\n * @param PDO $connection2 The database connection\n * @param string $question The question\n * @param string $answer The answer\n * @param int $createdBy The user ID who created the entry\n * @param string|null $category The category (optional)\n * @return bool Whether the operation was successful\n */\nfunction addTrainingData($connection2, $question, $answer, $createdBy, $category = null) {\n try {\n $sql = \"INSERT INTO gibbonChatBotTraining \n (question, answer, category, created_by) \n VALUES \n (:question, :answer, :category, :created_by)\";\n \n $result = $connection2->prepare($sql);\n return $result->execute([\n 'question' => $question,\n 'answer' => $answer,\n 'category' => $category,\n 'created_by' => $createdBy\n ]);\n } catch (PDOException $e) {\n error_log('Error adding training data: ' . $e->getMessage());\n return false;\n }\n}\n\n/**\n * Get training data for the ChatBot\n * @param PDO $connection2 The database connection\n * @param string $category Optional category filter\n * @return array The training data\n */\nfunction getTrainingData($connection2, $category = null) {\n try {\n $sql = \"SELECT * FROM gibbonChatBotTraining\";\n $params = [];\n \n if ($category) {\n $sql .= \" WHERE category = :category\";\n $params['category'] = $category;\n }\n \n $sql .= \" ORDER BY timestampCreated DESC\";\n \n $result = $connection2->prepare($sql);\n $result->execute($params);\n return $result->fetchAll(PDO::FETCH_ASSOC);\n } catch (PDOException $e) {\n error_log('Error getting training data: ' . $e->getMessage());\n return [];\n }\n}\n\n/**\n * Delete training data\n * @param PDO $connection2 The database connection\n * @param int $id The training data ID\n * @return bool Whether the operation was successful\n */\nfunction deleteTrainingData($connection2, $id) {\n try {\n $sql = \"DELETE FROM gibbonChatBotTraining WHERE gibbonChatBotTrainingID = :id\";\n $result = $connection2->prepare($sql);\n return $result->execute(['id' => $id]);\n } catch (PDOException $e) {\n error_log('Error deleting training data: ' . $e->getMessage());\n return false;\n }\n}\n\n/**\n * Generate AI recommendations based on assessment data\n * @param array $assessment The assessment data\n * @param string $apiKey The DeepSeek API key\n * @return string|null The AI recommendation\n */\nfunction generateAssessmentRecommendation($assessment, $apiKey) {\n try {\n $prompt = \"Based on the following assessment data:\\n\";\n $prompt .= \"Subject: {$assessment['subject']}\\n\";\n $prompt .= \"Grade: {$assessment['grade']}\\n\";\n $prompt .= \"Teacher Feedback: {$assessment['feedback']}\\n\\n\";\n $prompt .= \"Please provide specific, actionable recommendations for improvement in this subject. Consider:\\n\";\n $prompt .= \"1. Study strategies\\n\";\n $prompt .= \"2. Practice exercises\\n\";\n $prompt .= \"3. Additional resources\\n\";\n $prompt .= \"4. Areas needing focus based on the assessment\\n\";\n $prompt .= \"5. Time management suggestions\";\n \n $data = [\n 'model' => 'deepseek-v4-flash',\n 'messages' => [\n [\n 'role' => 'system',\n 'content' => 'You are an educational AI assistant providing specific, actionable recommendations for academic improvement based on assessment data and teacher feedback.'\n ],\n [\n 'role' => 'user',\n 'content' => $prompt\n ]\n ],\n 'max_tokens' => 500\n ];\n \n $ch = curl_init();\n curl_setopt($ch, CURLOPT_URL, 'https://api.deepseek.com/v1/chat/completions');\n curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\n curl_setopt($ch, CURLOPT_POST, true);\n curl_setopt($ch, CURLOPT_HTTPHEADER, [\n 'Content-Type: application/json',\n 'Authorization: Bearer ' . $apiKey\n ]);\n \n curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));\n \n $response = curl_exec($ch);\n $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);\n curl_close($ch);\n \n if ($httpCode === 200) {\n $result = json_decode($response, true);\n return $result['choices'][0]['message']['content'];\n }\n } catch (Exception $e) {\n error_log('Error generating assessment recommendation: ' . $e->getMessage());\n }\n \n return null;\n}\n\n/**\n * Get student performance metrics\n * @param int $gibbonPersonID\n * @return string HTML output of performance metrics\n */\nfunction getStudentPerformanceMetrics($gibbonPersonID) {\n global $pdo;\n \n try {\n // Get assessment data\n $data = getStudentAssessmentData($gibbonPersonID);\n \n // Calculate metrics\n $metrics = [\n 'average_attainment' => calculateAverageAttainment($data),\n 'progress_indicators' => analyzeProgressTrends($data),\n 'subject_breakdown' => getSubjectPerformance($data)\n ];\n \n // Generate HTML output\n $output = '
';\n foreach ($metrics as $key => $value) {\n $output .= generateMetricCard($key, $value);\n }\n $output .= '
';\n \n return $output;\n } catch (Exception $e) {\n return \"Error generating performance metrics: \" . $e->getMessage();\n }\n}\n\n/**\n * Generate AI recommendations based on student data\n * @param int $gibbonPersonID\n * @return string HTML output of recommendations\n */\nfunction generateAIRecommendations($gibbonPersonID) {\n try {\n // Get comprehensive student data\n $assessmentData = getStudentAssessmentData($gibbonPersonID);\n $feedbackData = getTeacherFeedback($gibbonPersonID);\n $engagementData = getStudentEngagementData($gibbonPersonID);\n \n // Prepare data for AI analysis\n $analysisData = [\n 'assessment_history' => $assessmentData,\n 'teacher_feedback' => $feedbackData,\n 'engagement_metrics' => $engagementData,\n 'learning_patterns' => analyzeLearningPatterns($gibbonPersonID)\n ];\n \n // Get AI recommendations\n $recommendations = callDeepSeekAPI($analysisData);\n \n // Format recommendations\n return formatAIRecommendations($recommendations);\n } catch (Exception $e) {\n return \"Error generating AI recommendations: \" . $e->getMessage();\n }\n}\n\n/**\n * Process and store training data\n * @param array $data Training data array\n * @return bool Success status\n */\nfunction processTrainingData($data) {\n try {\n // Validate training data\n validateTrainingData($data);\n \n // Enrich data with institutional context\n $enrichedData = enrichTrainingData($data);\n \n // Store in database\n storeTrainingData($enrichedData);\n \n // Update AI model\n updateAIModel($enrichedData);\n \n return true;\n } catch (Exception $e) {\n error_log(\"Training data processing error: \" . $e->getMessage());\n return false;\n }\n}\n\n/**\n * Generate learning analytics\n * @param array $params Analytics parameters\n * @return array Analytics data\n */\nfunction generateLearningAnalytics($params) {\n try {\n // Gather data\n $performanceData = getPerformanceData($params);\n $engagementData = getEngagementData($params);\n $patternData = getPatternData($params);\n \n // Process analytics\n $analytics = [\n 'performance' => analyzePerformance($performanceData),\n 'engagement' => analyzeEngagement($engagementData),\n 'patterns' => analyzePatterns($patternData),\n 'recommendations' => generateRecommendations([\n 'performance' => $performanceData,\n 'engagement' => $engagementData,\n 'patterns' => $patternData\n ])\n ];\n \n return $analytics;\n } catch (Exception $e) {\n error_log(\"Analytics generation error: \" . $e->getMessage());\n return null;\n }\n}\n\n/**\n * Format AI recommendations for display\n * @param array $recommendations\n * @return string Formatted HTML\n */\nfunction formatAIRecommendations($recommendations) {\n $output = '
';\n \n // Format subject-specific recommendations\n if (isset($recommendations['subjects'])) {\n $output .= '
';\n $output .= '

Subject-Specific Recommendations

';\n foreach ($recommendations['subjects'] as $subject => $data) {\n $output .= formatSubjectRecommendation($subject, $data);\n }\n $output .= '
';\n }\n \n // Format learning strategy recommendations\n if (isset($recommendations['strategies'])) {\n $output .= '
';\n $output .= '

Learning Strategy Recommendations

';\n foreach ($recommendations['strategies'] as $strategy) {\n $output .= formatStrategyRecommendation($strategy);\n }\n $output .= '
';\n }\n \n // Format improvement suggestions\n if (isset($recommendations['improvements'])) {\n $output .= '
';\n $output .= '

Areas for Improvement

';\n foreach ($recommendations['improvements'] as $area => $suggestions) {\n $output .= formatImprovementSuggestions($area, $suggestions);\n }\n $output .= '
';\n }\n \n $output .= '
';\n return $output;\n}\n\n"}}, "modules/aiTeacher/student_ai_tutor.php": {"5eacf702b1d14d58114e1990619ea4913b19ff59": {"t": 1785745766.6398969, "n": 9574, "h": "d1156eb6fff95766", "c": ".\n*/\n\n// Gibbon includes\nrequire_once __DIR__ . '/../../gibbon.php';\nrequire_once __DIR__ . '/../../functions.php';\n\n// Module includes\nrequire_once __DIR__ . '/moduleFunctions.php';\n\n$page->breadcrumbs->add(__('AI Tutor Chat'));\n\n// Check if user has access\nif (isActionAccessible($guid, $connection2, '/modules/aiTeacher/student_ai_tutor.php') == false) {\n $page->addError(__('You do not have access to this action.'));\n} else {\n // Get current user\n $gibbonPersonID = $gibbon->session->get('gibbonPersonID');\n $gibbonSchoolYearID = $gibbon->session->get('gibbonSchoolYearID');\n\n // Get user photo\n $sqlPerson = \"SELECT image_240 FROM gibbonPerson WHERE gibbonPersonID = :personID\";\n $resultPerson = $pdo->select($sqlPerson, ['personID' => $gibbonPersonID]);\n $person = $resultPerson->fetch();\n $userPhoto = $person['image_240'] ?? '';\n $absoluteURL = $gibbon->session->get('absoluteURL');\n\n // Check if resuming an existing session\n $sessionID = $_GET['sessionID'] ?? null;\n\n // If sessionID provided, verify it belongs to this user\n if ($sessionID) {\n $sql = \"SELECT sessionID FROM aiTeacherChatSessions\n WHERE sessionID = :sessionID AND gibbonPersonID = :personID\";\n $result = $pdo->executeQuery(['sessionID' => $sessionID, 'personID' => $gibbonPersonID], $sql);\n\n if ($result->rowCount() === 0) {\n // Invalid session, create new one\n $sessionID = getOrCreateChatSession($pdo, $gibbonPersonID, $gibbonSchoolYearID);\n }\n } else {\n // Get or create chat session\n $sessionID = getOrCreateChatSession($pdo, $gibbonPersonID, $gibbonSchoolYearID);\n }\n\n // Get AI settings to check if configured\n $settings = getAITeacherSettings($pdo);\n\n if (empty($settings['deepseek_api_key'])) {\n echo '
';\n echo __('AI Tutor is not configured. Please contact your administrator.');\n echo '
';\n return;\n }\n\n // Add CSS and JavaScript\n echo 'session->get('absoluteURL') . '/modules/aiTeacher/css/student_tutor.css\">';\n echo '';\n\n // Add MathJax for mathematical expressions\n echo '';\n echo '';\n\n // Get conversation history for current session\n $context = getConversationContext($pdo, $sessionID, 50);\n\n echo '
';\n\n // Header\n echo '
';\n echo '

' . __('AI Tutor') . '

';\n echo '

' . __('Ask me anything about your coursework. I\\'m here to help you learn!') . '

';\n echo '';\n echo '
';\n\n // Chat messages area\n echo '
';\n\n if (empty($context)) {\n // Welcome message\n echo '
';\n echo '
🤖
';\n echo '
';\n echo '

' . __('Hi! I\\'m your AI tutor. I can help you understand concepts, guide you through problems, and support your learning journey.') . '

';\n echo '

' . __('What would you like to learn about today?') . '

';\n echo '
';\n echo '
';\n } else {\n // Load previous messages\n foreach ($context as $msg) {\n $isStudent = ($msg['sender'] === 'student');\n $messageClass = $isStudent ? 'student-message' : 'ai-message';\n\n // Generate avatar HTML\n if ($isStudent) {\n // Use student's photo\n if (!empty($userPhoto)) {\n $photoPath = $absoluteURL . '/' . $userPhoto;\n $avatarHTML = '';\n } else {\n // Default placeholder if no photo\n $avatarHTML = '';\n }\n } else {\n // AI avatar - use robot emoji\n $avatarHTML = '🤖';\n }\n\n echo '
';\n if (!$isStudent) {\n echo '
' . $avatarHTML . '
';\n }\n echo '
';\n // Use markdown and math rendering for AI messages, simple formatting for student messages\n if ($isStudent) {\n echo '

' . nl2br(htmlspecialchars($msg['message'])) . '

';\n } else {\n echo renderMarkdownAndMath($msg['message']);\n }\n echo '' . date('g:i A', strtotime($msg['timestamp'])) . '';\n echo '
';\n if ($isStudent) {\n echo '
' . $avatarHTML . '
';\n }\n echo '
';\n }\n }\n\n echo '
';\n\n // Typing indicator (hidden by default)\n echo '
';\n echo '
🤖
';\n echo '
';\n echo '';\n echo '
';\n echo '
';\n\n // Input area\n echo '
';\n echo '
';\n echo '';\n echo '';\n echo '';\n echo '';\n echo '';\n\n echo '
';\n echo '';\n echo '';\n echo '
';\n\n echo '
';\n echo '0/500';\n echo '' . __('Press Enter to send, Shift+Enter for new line') . '';\n echo '
';\n\n echo '
';\n echo '
';\n\n // Rating and feedback area (shown after AI response)\n echo '
';\n echo '

' . __('Was this response helpful?') . '

';\n echo '
';\n echo '';\n echo '';\n echo '';\n echo '
';\n echo '
';\n\n echo '
'; // End container\n\n // Initialize JavaScript\n echo '';\n}\n"}, "efc3b33048fa694a8872e5c9517aaa2fbf59f204": {"t": 1785756299.189468, "n": 9574, "h": "d1156eb6fff95766", "c": ".\n*/\n\n// Gibbon includes\nrequire_once __DIR__ . '/../../gibbon.php';\nrequire_once __DIR__ . '/../../functions.php';\n\n// Module includes\nrequire_once __DIR__ . '/moduleFunctions.php';\n\n$page->breadcrumbs->add(__('AI Tutor Chat'));\n\n// Check if user has access\nif (isActionAccessible($guid, $connection2, '/modules/aiTeacher/student_ai_tutor.php') == false) {\n $page->addError(__('You do not have access to this action.'));\n} else {\n // Get current user\n $gibbonPersonID = $gibbon->session->get('gibbonPersonID');\n $gibbonSchoolYearID = $gibbon->session->get('gibbonSchoolYearID');\n\n // Get user photo\n $sqlPerson = \"SELECT image_240 FROM gibbonPerson WHERE gibbonPersonID = :personID\";\n $resultPerson = $pdo->select($sqlPerson, ['personID' => $gibbonPersonID]);\n $person = $resultPerson->fetch();\n $userPhoto = $person['image_240'] ?? '';\n $absoluteURL = $gibbon->session->get('absoluteURL');\n\n // Check if resuming an existing session\n $sessionID = $_GET['sessionID'] ?? null;\n\n // If sessionID provided, verify it belongs to this user\n if ($sessionID) {\n $sql = \"SELECT sessionID FROM aiTeacherChatSessions\n WHERE sessionID = :sessionID AND gibbonPersonID = :personID\";\n $result = $pdo->executeQuery(['sessionID' => $sessionID, 'personID' => $gibbonPersonID], $sql);\n\n if ($result->rowCount() === 0) {\n // Invalid session, create new one\n $sessionID = getOrCreateChatSession($pdo, $gibbonPersonID, $gibbonSchoolYearID);\n }\n } else {\n // Get or create chat session\n $sessionID = getOrCreateChatSession($pdo, $gibbonPersonID, $gibbonSchoolYearID);\n }\n\n // Get AI settings to check if configured\n $settings = getAITeacherSettings($pdo);\n\n if (empty($settings['deepseek_api_key'])) {\n echo '
';\n echo __('AI Tutor is not configured. Please contact your administrator.');\n echo '
';\n return;\n }\n\n // Add CSS and JavaScript\n echo 'session->get('absoluteURL') . '/modules/aiTeacher/css/student_tutor.css\">';\n echo '';\n\n // Add MathJax for mathematical expressions\n echo '';\n echo '';\n\n // Get conversation history for current session\n $context = getConversationContext($pdo, $sessionID, 50);\n\n echo '
';\n\n // Header\n echo '
';\n echo '

' . __('AI Tutor') . '

';\n echo '

' . __('Ask me anything about your coursework. I\\'m here to help you learn!') . '

';\n echo '';\n echo '
';\n\n // Chat messages area\n echo '
';\n\n if (empty($context)) {\n // Welcome message\n echo '
';\n echo '
🤖
';\n echo '
';\n echo '

' . __('Hi! I\\'m your AI tutor. I can help you understand concepts, guide you through problems, and support your learning journey.') . '

';\n echo '

' . __('What would you like to learn about today?') . '

';\n echo '
';\n echo '
';\n } else {\n // Load previous messages\n foreach ($context as $msg) {\n $isStudent = ($msg['sender'] === 'student');\n $messageClass = $isStudent ? 'student-message' : 'ai-message';\n\n // Generate avatar HTML\n if ($isStudent) {\n // Use student's photo\n if (!empty($userPhoto)) {\n $photoPath = $absoluteURL . '/' . $userPhoto;\n $avatarHTML = '';\n } else {\n // Default placeholder if no photo\n $avatarHTML = '';\n }\n } else {\n // AI avatar - use robot emoji\n $avatarHTML = '🤖';\n }\n\n echo '
';\n if (!$isStudent) {\n echo '
' . $avatarHTML . '
';\n }\n echo '
';\n // Use markdown and math rendering for AI messages, simple formatting for student messages\n if ($isStudent) {\n echo '

' . nl2br(htmlspecialchars($msg['message'])) . '

';\n } else {\n echo renderMarkdownAndMath($msg['message']);\n }\n echo '' . date('g:i A', strtotime($msg['timestamp'])) . '';\n echo '
';\n if ($isStudent) {\n echo '
' . $avatarHTML . '
';\n }\n echo '
';\n }\n }\n\n echo '
';\n\n // Typing indicator (hidden by default)\n echo '
';\n echo '
🤖
';\n echo '
';\n echo '';\n echo '
';\n echo '
';\n\n // Input area\n echo '
';\n echo '
';\n echo '';\n echo '';\n echo '';\n echo '';\n echo '';\n\n echo '
';\n echo '';\n echo '';\n echo '
';\n\n echo '
';\n echo '0/500';\n echo '' . __('Press Enter to send, Shift+Enter for new line') . '';\n echo '
';\n\n echo '
';\n echo '
';\n\n // Rating and feedback area (shown after AI response)\n echo '
';\n echo '

' . __('Was this response helpful?') . '

';\n echo '
';\n echo '';\n echo '';\n echo '';\n echo '
';\n echo '
';\n\n echo '
'; // End container\n\n // Initialize JavaScript\n echo '';\n}\n"}, "0b84281cff4dc2315def5fff6a77a093eaca5f52": {"t": 1785756300.6338928, "n": 8460, "h": "37090cb655bf8dca", "c": ".\n*/\n\n// Gibbon includes\nrequire_once __DIR__ . '/../../gibbon.php';\nrequire_once __DIR__ . '/../../functions.php';\n\n// Module includes\nrequire_once __DIR__ . '/moduleFunctions.php';\n\n$page->breadcrumbs->add(__('AI Tutor Chat'));\n\n// Check if user has access\nif (isActionAccessible($guid, $connection2, '/modules/aiTeacher/student_ai_tutor.php') == false) {\n $page->addError(__('You do not have access to this action.'));\n} else {\n // Get current user\n $gibbonPersonID = $gibbon->session->get('gibbonPersonID');\n $gibbonSchoolYearID = $gibbon->session->get('gibbonSchoolYearID');\n\n // Check if resuming an existing session\n $sessionID = $_GET['sessionID'] ?? null;\n\n // If sessionID provided, verify it belongs to this user\n if ($sessionID) {\n $sql = \"SELECT sessionID FROM aiTeacherChatSessions\n WHERE sessionID = :sessionID AND gibbonPersonID = :personID\";\n $result = $pdo->executeQuery(['sessionID' => $sessionID, 'personID' => $gibbonPersonID], $sql);\n\n if ($result->rowCount() === 0) {\n // Invalid session, create new one\n $sessionID = getOrCreateChatSession($pdo, $gibbonPersonID, $gibbonSchoolYearID);\n }\n } else {\n // Get or create chat session\n $sessionID = getOrCreateChatSession($pdo, $gibbonPersonID, $gibbonSchoolYearID);\n }\n\n // Get AI settings to check if configured\n $settings = getAITeacherSettings($pdo);\n\n if (empty($settings['deepseek_api_key'])) {\n echo '
';\n echo __('AI Tutor is not configured. Please contact your administrator.');\n echo '
';\n return;\n }\n\n // Add CSS and JavaScript\n echo 'session->get('absoluteURL') . '/modules/aiTeacher/css/student_tutor.css\">';\n echo '';\n\n // Add MathJax for mathematical expressions\n echo '';\n echo '';\n\n // Get conversation history for current session\n $context = getConversationContext($pdo, $sessionID, 50);\n\n echo '
';\n\n // Header\n echo '
';\n echo '

' . __('AI Tutor') . '

';\n echo '

' . __('Ask me anything about your coursework. I\\'m here to help you learn!') . '

';\n echo '';\n echo '
';\n\n // Chat messages area\n echo '
';\n\n if (empty($context)) {\n // Welcome message\n echo '
';\n echo '
🤖
';\n echo '
';\n echo '

' . __('Hi! I\\'m your AI tutor. I can help you understand concepts, guide you through problems, and support your learning journey.') . '

';\n echo '

' . __('What would you like to learn about today?') . '

';\n echo '
';\n echo '
';\n } else {\n // Load previous messages\n foreach ($context as $msg) {\n $isStudent = ($msg['sender'] === 'student');\n $messageClass = $isStudent ? 'student-message' : 'ai-message';\n $avatar = $isStudent ? '👤' : '🤖';\n\n echo '
';\n if (!$isStudent) {\n echo '
' . $avatar . '
';\n }\n echo '
';\n // Use markdown and math rendering for AI messages, simple formatting for student messages\n if ($isStudent) {\n echo '

' . nl2br(htmlspecialchars($msg['message'])) . '

';\n } else {\n echo renderMarkdownAndMath($msg['message']);\n }\n echo '' . date('g:i A', strtotime($msg['timestamp'])) . '';\n echo '
';\n if ($isStudent) {\n echo '
' . $avatar . '
';\n }\n echo '
';\n }\n }\n\n echo '
';\n\n // Typing indicator (hidden by default)\n echo '
';\n echo '
🤖
';\n echo '
';\n echo '';\n echo '
';\n echo '
';\n\n // Input area\n echo '
';\n echo '
';\n echo '';\n echo '';\n echo '';\n echo 'session->get('absoluteURL') . '\">';\n\n echo '
';\n echo '';\n echo '';\n echo '
';\n\n echo '
';\n echo '0/500';\n echo '' . __('Press Enter to send, Shift+Enter for new line') . '';\n echo '
';\n\n echo '
';\n echo '
';\n\n // Rating and feedback area (shown after AI response)\n echo '
';\n echo '

' . __('Was this response helpful?') . '

';\n echo '
';\n echo '';\n echo '';\n echo '';\n echo '
';\n echo '
';\n\n echo '
'; // End container\n\n // Initialize JavaScript\n echo '';\n}\n"}, "c10673e9ae9c5284dc569b8c256897f2c6cf1c24": {"t": 1785756301.8735542, "n": 7613, "h": "1311fef68474de45", "c": ".\n*/\n\n// Gibbon includes\nrequire_once __DIR__ . '/../../gibbon.php';\nrequire_once __DIR__ . '/../../functions.php';\n\n// Module includes\nrequire_once __DIR__ . '/moduleFunctions.php';\n\n$page->breadcrumbs->add(__('AI Tutor Chat'));\n\n// Check if user has access\nif (isActionAccessible($guid, $connection2, '/modules/aiTeacher/student_ai_tutor.php') == false) {\n $page->addError(__('You do not have access to this action.'));\n} else {\n // Get current user\n $gibbonPersonID = $gibbon->session->get('gibbonPersonID');\n $gibbonSchoolYearID = $gibbon->session->get('gibbonSchoolYearID');\n\n // Check if resuming an existing session\n $sessionID = $_GET['sessionID'] ?? null;\n\n // If sessionID provided, verify it belongs to this user\n if ($sessionID) {\n $sql = \"SELECT sessionID FROM aiTeacherChatSessions\n WHERE sessionID = :sessionID AND gibbonPersonID = :personID\";\n $result = $pdo->executeQuery(['sessionID' => $sessionID, 'personID' => $gibbonPersonID], $sql);\n\n if ($result->rowCount() === 0) {\n // Invalid session, create new one\n $sessionID = getOrCreateChatSession($pdo, $gibbonPersonID, $gibbonSchoolYearID);\n }\n } else {\n // Get or create chat session\n $sessionID = getOrCreateChatSession($pdo, $gibbonPersonID, $gibbonSchoolYearID);\n }\n\n // Get AI settings to check if configured\n $settings = getAITeacherSettings($pdo);\n\n if (empty($settings['deepseek_api_key'])) {\n echo '
';\n echo __('AI Tutor is not configured. Please contact your administrator.');\n echo '
';\n return;\n }\n\n // Add CSS and JavaScript\n echo 'session->get('absoluteURL') . '/modules/aiTeacher/css/student_tutor.css\">';\n echo '';\n\n // Get conversation history for current session\n $context = getConversationContext($pdo, $sessionID, 50);\n\n echo '
';\n\n // Header\n echo '
';\n echo '

' . __('AI Tutor') . '

';\n echo '

' . __('Ask me anything about your coursework. I\\'m here to help you learn!') . '

';\n echo '';\n echo '
';\n\n // Chat messages area\n echo '
';\n\n if (empty($context)) {\n // Welcome message\n echo '
';\n echo '
🤖
';\n echo '
';\n echo '

' . __('Hi! I\\'m your AI tutor. I can help you understand concepts, guide you through problems, and support your learning journey.') . '

';\n echo '

' . __('What would you like to learn about today?') . '

';\n echo '
';\n echo '
';\n } else {\n // Load previous messages\n foreach ($context as $msg) {\n $isStudent = ($msg['sender'] === 'student');\n $messageClass = $isStudent ? 'student-message' : 'ai-message';\n $avatar = $isStudent ? '👤' : '🤖';\n\n echo '
';\n if (!$isStudent) {\n echo '
' . $avatar . '
';\n }\n echo '
';\n echo '

' . nl2br(htmlspecialchars($msg['message'])) . '

';\n echo '' . date('g:i A', strtotime($msg['timestamp'])) . '';\n echo '
';\n if ($isStudent) {\n echo '
' . $avatar . '
';\n }\n echo '
';\n }\n }\n\n echo '
';\n\n // Typing indicator (hidden by default)\n echo '
';\n echo '
🤖
';\n echo '
';\n echo '';\n echo '
';\n echo '
';\n\n // Input area\n echo '
';\n echo '
';\n echo '';\n echo '';\n echo '';\n echo 'session->get('absoluteURL') . '\">';\n\n echo '
';\n echo '';\n echo '';\n echo '
';\n\n echo '
';\n echo '0/500';\n echo '' . __('Press Enter to send, Shift+Enter for new line') . '';\n echo '
';\n\n echo '
';\n echo '
';\n\n // Rating and feedback area (shown after AI response)\n echo '
';\n echo '

' . __('Was this response helpful?') . '

';\n echo '
';\n echo '';\n echo '';\n echo '';\n echo '
';\n echo '
';\n\n echo '
'; // End container\n\n // Initialize JavaScript\n echo '';\n}\n"}, "6e2315c09113c7dbb2d5f8416cf1903ce8878ec1": {"t": 1785756306.326054, "n": 6978, "h": "ca8cc39c9ce43bf7", "c": ".\n*/\n\n// Gibbon includes\nrequire_once __DIR__ . '/../../gibbon.php';\nrequire_once __DIR__ . '/../../functions.php';\n\n// Module includes\nrequire_once __DIR__ . '/moduleFunctions.php';\n\n$page->breadcrumbs->add(__('AI Tutor Chat'));\n\n// Check if user has access\nif (isActionAccessible($guid, $connection2, '/modules/aiTeacher/student_ai_tutor.php') == false) {\n $page->addError(__('You do not have access to this action.'));\n} else {\n // Get current user\n $gibbonPersonID = $gibbon->session->get('gibbonPersonID');\n $gibbonSchoolYearID = $gibbon->session->get('gibbonSchoolYearID');\n\n // Get or create chat session\n $sessionID = getOrCreateChatSession($pdo, $gibbonPersonID, $gibbonSchoolYearID);\n\n // Get AI settings to check if configured\n $settings = getAITeacherSettings($pdo);\n\n if (empty($settings['deepseek_api_key'])) {\n echo '
';\n echo __('AI Tutor is not configured. Please contact your administrator.');\n echo '
';\n return;\n }\n\n // Add CSS and JavaScript\n echo 'session->get('absoluteURL') . '/modules/aiTeacher/css/student_tutor.css\">';\n echo '';\n\n // Get conversation history for current session\n $context = getConversationContext($pdo, $sessionID, 50);\n\n echo '
';\n\n // Header\n echo '
';\n echo '

' . __('AI Tutor') . '

';\n echo '

' . __('Ask me anything about your coursework. I\\'m here to help you learn!') . '

';\n echo '';\n echo '
';\n\n // Chat messages area\n echo '
';\n\n if (empty($context)) {\n // Welcome message\n echo '
';\n echo '
🤖
';\n echo '
';\n echo '

' . __('Hi! I\\'m your AI tutor. I can help you understand concepts, guide you through problems, and support your learning journey.') . '

';\n echo '

' . __('What would you like to learn about today?') . '

';\n echo '
';\n echo '
';\n } else {\n // Load previous messages\n foreach ($context as $msg) {\n $isStudent = ($msg['sender'] === 'student');\n $messageClass = $isStudent ? 'student-message' : 'ai-message';\n $avatar = $isStudent ? '👤' : '🤖';\n\n echo '
';\n if (!$isStudent) {\n echo '
' . $avatar . '
';\n }\n echo '
';\n echo '

' . nl2br(htmlspecialchars($msg['message'])) . '

';\n echo '' . date('g:i A', strtotime($msg['timestamp'])) . '';\n echo '
';\n if ($isStudent) {\n echo '
' . $avatar . '
';\n }\n echo '
';\n }\n }\n\n echo '
';\n\n // Typing indicator (hidden by default)\n echo '
';\n echo '
🤖
';\n echo '
';\n echo '';\n echo '
';\n echo '
';\n\n // Input area\n echo '
';\n echo '
';\n echo '';\n echo '';\n echo '';\n echo 'session->get('absoluteURL') . '\">';\n\n echo '
';\n echo '';\n echo '';\n echo '
';\n\n echo '
';\n echo '0/500';\n echo '' . __('Press Enter to send, Shift+Enter for new line') . '';\n echo '
';\n\n echo '
';\n echo '
';\n\n // Rating and feedback area (shown after AI response)\n echo '
';\n echo '

' . __('Was this response helpful?') . '

';\n echo '
';\n echo '';\n echo '';\n echo '';\n echo '
';\n echo '
';\n\n echo '
'; // End container\n\n // Initialize JavaScript\n echo '';\n}\n"}, "c8048cb80b9e0ad166f7b5f116d905850d8813e8": {"t": 1785756308.0685246, "n": 7002, "h": "d7417093c5712175", "c": ".\n*/\n\n// Gibbon includes\nrequire_once __DIR__ . '/../../gibbon.php';\nrequire_once __DIR__ . '/../../functions.php';\n\n// Module includes\nrequire_once __DIR__ . '/moduleFunctions.php';\n\n$page->breadcrumbs->add(__('AI Tutor Chat'));\n\n// Check if user has access\nif (isActionAccessible($guid, $connection2, '/modules/aiTeacher/student_ai_tutor.php') == false) {\n $page->addError(__('You do not have access to this action.'));\n} else {\n // Get current user\n $gibbonPersonID = $gibbon->session->get('gibbonPersonID');\n $gibbonSchoolYearID = $gibbon->session->get('gibbonSchoolYearID');\n\n // Get or create chat session\n $sessionID = getOrCreateChatSession($connection2, $gibbonPersonID, $gibbonSchoolYearID);\n\n // Get AI settings to check if configured\n $settings = getAITeacherSettings($connection2);\n\n if (empty($settings['deepseek_api_key'])) {\n echo '
';\n echo __('AI Tutor is not configured. Please contact your administrator.');\n echo '
';\n return;\n }\n\n // Add CSS and JavaScript\n echo 'session->get('absoluteURL') . '/modules/aiTeacher/css/student_tutor.css\">';\n echo '';\n\n // Get conversation history for current session\n $context = getConversationContext($connection2, $sessionID, 50);\n\n echo '
';\n\n // Header\n echo '
';\n echo '

' . __('AI Tutor') . '

';\n echo '

' . __('Ask me anything about your coursework. I\\'m here to help you learn!') . '

';\n echo '';\n echo '
';\n\n // Chat messages area\n echo '
';\n\n if (empty($context)) {\n // Welcome message\n echo '
';\n echo '
🤖
';\n echo '
';\n echo '

' . __('Hi! I\\'m your AI tutor. I can help you understand concepts, guide you through problems, and support your learning journey.') . '

';\n echo '

' . __('What would you like to learn about today?') . '

';\n echo '
';\n echo '
';\n } else {\n // Load previous messages\n foreach ($context as $msg) {\n $isStudent = ($msg['sender'] === 'student');\n $messageClass = $isStudent ? 'student-message' : 'ai-message';\n $avatar = $isStudent ? '👤' : '🤖';\n\n echo '
';\n if (!$isStudent) {\n echo '
' . $avatar . '
';\n }\n echo '
';\n echo '

' . nl2br(htmlspecialchars($msg['message'])) . '

';\n echo '' . date('g:i A', strtotime($msg['timestamp'])) . '';\n echo '
';\n if ($isStudent) {\n echo '
' . $avatar . '
';\n }\n echo '
';\n }\n }\n\n echo '
';\n\n // Typing indicator (hidden by default)\n echo '
';\n echo '
🤖
';\n echo '
';\n echo '';\n echo '
';\n echo '
';\n\n // Input area\n echo '
';\n echo '
';\n echo '';\n echo '';\n echo '';\n echo 'session->get('absoluteURL') . '\">';\n\n echo '
';\n echo '';\n echo '';\n echo '
';\n\n echo '
';\n echo '0/500';\n echo '' . __('Press Enter to send, Shift+Enter for new line') . '';\n echo '
';\n\n echo '
';\n echo '
';\n\n // Rating and feedback area (shown after AI response)\n echo '
';\n echo '

' . __('Was this response helpful?') . '

';\n echo '
';\n echo '';\n echo '';\n echo '';\n echo '
';\n echo '
';\n\n echo '
'; // End container\n\n // Initialize JavaScript\n echo '';\n}\n"}, "0d2085f58265d908285062976c1dc44f6a37f817": {"t": 1785756309.3540063, "n": 6978, "h": "ca8cc39c9ce43bf7", "c": ".\n*/\n\n// Gibbon includes\nrequire_once __DIR__ . '/../../gibbon.php';\nrequire_once __DIR__ . '/../../functions.php';\n\n// Module includes\nrequire_once __DIR__ . '/moduleFunctions.php';\n\n$page->breadcrumbs->add(__('AI Tutor Chat'));\n\n// Check if user has access\nif (isActionAccessible($guid, $connection2, '/modules/aiTeacher/student_ai_tutor.php') == false) {\n $page->addError(__('You do not have access to this action.'));\n} else {\n // Get current user\n $gibbonPersonID = $gibbon->session->get('gibbonPersonID');\n $gibbonSchoolYearID = $gibbon->session->get('gibbonSchoolYearID');\n\n // Get or create chat session\n $sessionID = getOrCreateChatSession($pdo, $gibbonPersonID, $gibbonSchoolYearID);\n\n // Get AI settings to check if configured\n $settings = getAITeacherSettings($pdo);\n\n if (empty($settings['deepseek_api_key'])) {\n echo '
';\n echo __('AI Tutor is not configured. Please contact your administrator.');\n echo '
';\n return;\n }\n\n // Add CSS and JavaScript\n echo 'session->get('absoluteURL') . '/modules/aiTeacher/css/student_tutor.css\">';\n echo '';\n\n // Get conversation history for current session\n $context = getConversationContext($pdo, $sessionID, 50);\n\n echo '
';\n\n // Header\n echo '
';\n echo '

' . __('AI Tutor') . '

';\n echo '

' . __('Ask me anything about your coursework. I\\'m here to help you learn!') . '

';\n echo '';\n echo '
';\n\n // Chat messages area\n echo '
';\n\n if (empty($context)) {\n // Welcome message\n echo '
';\n echo '
🤖
';\n echo '
';\n echo '

' . __('Hi! I\\'m your AI tutor. I can help you understand concepts, guide you through problems, and support your learning journey.') . '

';\n echo '

' . __('What would you like to learn about today?') . '

';\n echo '
';\n echo '
';\n } else {\n // Load previous messages\n foreach ($context as $msg) {\n $isStudent = ($msg['sender'] === 'student');\n $messageClass = $isStudent ? 'student-message' : 'ai-message';\n $avatar = $isStudent ? '👤' : '🤖';\n\n echo '
';\n if (!$isStudent) {\n echo '
' . $avatar . '
';\n }\n echo '
';\n echo '

' . nl2br(htmlspecialchars($msg['message'])) . '

';\n echo '' . date('g:i A', strtotime($msg['timestamp'])) . '';\n echo '
';\n if ($isStudent) {\n echo '
' . $avatar . '
';\n }\n echo '
';\n }\n }\n\n echo '
';\n\n // Typing indicator (hidden by default)\n echo '
';\n echo '
🤖
';\n echo '
';\n echo '';\n echo '
';\n echo '
';\n\n // Input area\n echo '
';\n echo '
';\n echo '';\n echo '';\n echo '';\n echo 'session->get('absoluteURL') . '\">';\n\n echo '
';\n echo '';\n echo '';\n echo '
';\n\n echo '
';\n echo '0/500';\n echo '' . __('Press Enter to send, Shift+Enter for new line') . '';\n echo '
';\n\n echo '
';\n echo '
';\n\n // Rating and feedback area (shown after AI response)\n echo '
';\n echo '

' . __('Was this response helpful?') . '

';\n echo '
';\n echo '';\n echo '';\n echo '';\n echo '
';\n echo '
';\n\n echo '
'; // End container\n\n // Initialize JavaScript\n echo '';\n}\n"}, "ea2bf72c5d38ab35b13101b6131cf74ba37f78a3": {"t": 1786068313.4741411, "n": 9574, "h": "d1156eb6fff95766", "c": ".\n*/\n\n// Gibbon includes\nrequire_once __DIR__ . '/../../gibbon.php';\nrequire_once __DIR__ . '/../../functions.php';\n\n// Module includes\nrequire_once __DIR__ . '/moduleFunctions.php';\n\n$page->breadcrumbs->add(__('AI Tutor Chat'));\n\n// Check if user has access\nif (isActionAccessible($guid, $connection2, '/modules/aiTeacher/student_ai_tutor.php') == false) {\n $page->addError(__('You do not have access to this action.'));\n} else {\n // Get current user\n $gibbonPersonID = $gibbon->session->get('gibbonPersonID');\n $gibbonSchoolYearID = $gibbon->session->get('gibbonSchoolYearID');\n\n // Get user photo\n $sqlPerson = \"SELECT image_240 FROM gibbonPerson WHERE gibbonPersonID = :personID\";\n $resultPerson = $pdo->select($sqlPerson, ['personID' => $gibbonPersonID]);\n $person = $resultPerson->fetch();\n $userPhoto = $person['image_240'] ?? '';\n $absoluteURL = $gibbon->session->get('absoluteURL');\n\n // Check if resuming an existing session\n $sessionID = $_GET['sessionID'] ?? null;\n\n // If sessionID provided, verify it belongs to this user\n if ($sessionID) {\n $sql = \"SELECT sessionID FROM aiTeacherChatSessions\n WHERE sessionID = :sessionID AND gibbonPersonID = :personID\";\n $result = $pdo->executeQuery(['sessionID' => $sessionID, 'personID' => $gibbonPersonID], $sql);\n\n if ($result->rowCount() === 0) {\n // Invalid session, create new one\n $sessionID = getOrCreateChatSession($pdo, $gibbonPersonID, $gibbonSchoolYearID);\n }\n } else {\n // Get or create chat session\n $sessionID = getOrCreateChatSession($pdo, $gibbonPersonID, $gibbonSchoolYearID);\n }\n\n // Get AI settings to check if configured\n $settings = getAITeacherSettings($pdo);\n\n if (empty($settings['deepseek_api_key'])) {\n echo '
';\n echo __('AI Tutor is not configured. Please contact your administrator.');\n echo '
';\n return;\n }\n\n // Add CSS and JavaScript\n echo 'session->get('absoluteURL') . '/modules/aiTeacher/css/student_tutor.css\">';\n echo '';\n\n // Add MathJax for mathematical expressions\n echo '';\n echo '';\n\n // Get conversation history for current session\n $context = getConversationContext($pdo, $sessionID, 50);\n\n echo '
';\n\n // Header\n echo '
';\n echo '

' . __('AI Tutor') . '

';\n echo '

' . __('Ask me anything about your coursework. I\\'m here to help you learn!') . '

';\n echo '';\n echo '
';\n\n // Chat messages area\n echo '
';\n\n if (empty($context)) {\n // Welcome message\n echo '
';\n echo '
🤖
';\n echo '
';\n echo '

' . __('Hi! I\\'m your AI tutor. I can help you understand concepts, guide you through problems, and support your learning journey.') . '

';\n echo '

' . __('What would you like to learn about today?') . '

';\n echo '
';\n echo '
';\n } else {\n // Load previous messages\n foreach ($context as $msg) {\n $isStudent = ($msg['sender'] === 'student');\n $messageClass = $isStudent ? 'student-message' : 'ai-message';\n\n // Generate avatar HTML\n if ($isStudent) {\n // Use student's photo\n if (!empty($userPhoto)) {\n $photoPath = $absoluteURL . '/' . $userPhoto;\n $avatarHTML = '';\n } else {\n // Default placeholder if no photo\n $avatarHTML = '';\n }\n } else {\n // AI avatar - use robot emoji\n $avatarHTML = '🤖';\n }\n\n echo '
';\n if (!$isStudent) {\n echo '
' . $avatarHTML . '
';\n }\n echo '
';\n // Use markdown and math rendering for AI messages, simple formatting for student messages\n if ($isStudent) {\n echo '

' . nl2br(htmlspecialchars($msg['message'])) . '

';\n } else {\n echo renderMarkdownAndMath($msg['message']);\n }\n echo '' . date('g:i A', strtotime($msg['timestamp'])) . '';\n echo '
';\n if ($isStudent) {\n echo '
' . $avatarHTML . '
';\n }\n echo '
';\n }\n }\n\n echo '
';\n\n // Typing indicator (hidden by default)\n echo '
';\n echo '
🤖
';\n echo '
';\n echo '';\n echo '
';\n echo '
';\n\n // Input area\n echo '
';\n echo '
';\n echo '';\n echo '';\n echo '';\n echo '';\n echo '';\n\n echo '
';\n echo '';\n echo '';\n echo '
';\n\n echo '
';\n echo '0/500';\n echo '' . __('Press Enter to send, Shift+Enter for new line') . '';\n echo '
';\n\n echo '
';\n echo '
';\n\n // Rating and feedback area (shown after AI response)\n echo '
';\n echo '

' . __('Was this response helpful?') . '

';\n echo '
';\n echo '';\n echo '';\n echo '';\n echo '
';\n echo '
';\n\n echo '
'; // End container\n\n // Initialize JavaScript\n echo '';\n}\n"}}, "modules/ChatBot/api/fix_settings.php": {"5eacf702b1d14d58114e1990619ea4913b19ff59": {"t": 1785745772.03387, "n": 1663, "h": "6d6945a3eb52c9a1", "c": "query($sql);\n \n if ($result->rowCount() == 0) {\n // Setting doesn't exist, create it\n $sql = \"INSERT INTO gibbonSetting (scope, name, nameDisplay, description, value) \n VALUES ('ChatBot', 'deepseek_api_key', 'DeepSeek API Key', 'API key for DeepSeek AI service', '')\";\n $connection2->query($sql);\n echo \"Created DeepSeek API key setting
\";\n } else {\n echo \"DeepSeek API key setting exists
\";\n $setting = $result->fetch();\n if (empty($setting['value'])) {\n echo \"Warning: API key is not set
\";\n }\n }\n \n // Show all ChatBot settings\n $sql = \"SELECT * FROM gibbonSetting WHERE scope='ChatBot'\";\n $result = $connection2->query($sql);\n $settings = $result->fetchAll();\n \n echo \"
\";\n    echo \"Current ChatBot Settings:\\n\";\n    foreach ($settings as $setting) {\n        echo \"- {$setting['name']}: \" . (empty($setting['value']) ? 'Not set' : 'Set') . \"\\n\";\n        echo \"  Description: {$setting['description']}\\n\";\n    }\n    echo \"
\";\n \n // Add link to settings page\n echo \"

To update the API key, go to ChatBot Settings

\";\n \n} catch (Exception $e) {\n echo \"Error: \" . $e->getMessage() . \"
\";\n echo \"SQL State: \" . $e->getCode() . \"
\";\n error_log(\"ChatBot settings error: \" . $e->getMessage());\n} "}, "030e63497fd5a0ea218107feedd91d649324421f": {"t": 1785756301.333605, "n": 1663, "h": "6d6945a3eb52c9a1", "c": "query($sql);\n \n if ($result->rowCount() == 0) {\n // Setting doesn't exist, create it\n $sql = \"INSERT INTO gibbonSetting (scope, name, nameDisplay, description, value) \n VALUES ('ChatBot', 'deepseek_api_key', 'DeepSeek API Key', 'API key for DeepSeek AI service', '')\";\n $connection2->query($sql);\n echo \"Created DeepSeek API key setting
\";\n } else {\n echo \"DeepSeek API key setting exists
\";\n $setting = $result->fetch();\n if (empty($setting['value'])) {\n echo \"Warning: API key is not set
\";\n }\n }\n \n // Show all ChatBot settings\n $sql = \"SELECT * FROM gibbonSetting WHERE scope='ChatBot'\";\n $result = $connection2->query($sql);\n $settings = $result->fetchAll();\n \n echo \"
\";\n    echo \"Current ChatBot Settings:\\n\";\n    foreach ($settings as $setting) {\n        echo \"- {$setting['name']}: \" . (empty($setting['value']) ? 'Not set' : 'Set') . \"\\n\";\n        echo \"  Description: {$setting['description']}\\n\";\n    }\n    echo \"
\";\n \n // Add link to settings page\n echo \"

To update the API key, go to ChatBot Settings

\";\n \n} catch (Exception $e) {\n echo \"Error: \" . $e->getMessage() . \"
\";\n echo \"SQL State: \" . $e->getCode() . \"
\";\n error_log(\"ChatBot settings error: \" . $e->getMessage());\n} "}, "ea2bf72c5d38ab35b13101b6131cf74ba37f78a3": {"t": 1786068313.7532463, "n": 1663, "h": "6d6945a3eb52c9a1", "c": "query($sql);\n \n if ($result->rowCount() == 0) {\n // Setting doesn't exist, create it\n $sql = \"INSERT INTO gibbonSetting (scope, name, nameDisplay, description, value) \n VALUES ('ChatBot', 'deepseek_api_key', 'DeepSeek API Key', 'API key for DeepSeek AI service', '')\";\n $connection2->query($sql);\n echo \"Created DeepSeek API key setting
\";\n } else {\n echo \"DeepSeek API key setting exists
\";\n $setting = $result->fetch();\n if (empty($setting['value'])) {\n echo \"Warning: API key is not set
\";\n }\n }\n \n // Show all ChatBot settings\n $sql = \"SELECT * FROM gibbonSetting WHERE scope='ChatBot'\";\n $result = $connection2->query($sql);\n $settings = $result->fetchAll();\n \n echo \"
\";\n    echo \"Current ChatBot Settings:\\n\";\n    foreach ($settings as $setting) {\n        echo \"- {$setting['name']}: \" . (empty($setting['value']) ? 'Not set' : 'Set') . \"\\n\";\n        echo \"  Description: {$setting['description']}\\n\";\n    }\n    echo \"
\";\n \n // Add link to settings page\n echo \"

To update the API key, go to ChatBot Settings

\";\n \n} catch (Exception $e) {\n echo \"Error: \" . $e->getMessage() . \"
\";\n echo \"SQL State: \" . $e->getCode() . \"
\";\n error_log(\"ChatBot settings error: \" . $e->getMessage());\n} "}}, "modules/aiTeacher/index.php": {"5eacf702b1d14d58114e1990619ea4913b19ff59": {"t": 1785745772.5442655, "n": 6285, "h": "e8c8b26454c957d7", "c": ".\n*/\n\nuse Gibbon\\Module\\aiTeacher\\DeepSeekAPI;\n\n// Gibbon includes\nrequire_once __DIR__ . '/../../gibbon.php';\nrequire_once __DIR__ . '/../../functions.php';\n\n// Module includes\nrequire_once __DIR__ . '/moduleFunctions.php';\n\n$page->breadcrumbs->add(__('AI Teacher Assistance'));\n\n// Check if user has access\nif (isActionAccessible($guid, $connection2, '/modules/aiTeacher/index.php') == false) {\n // Access denied\n $page->addError(__('You do not have access to this action.'));\n} else {\n // Get module settings\n $settings = getAITeacherSettings($pdo);\n \n // Check if API key is configured\n if (empty($settings['deepseek_api_key'])) {\n $page->addError(__('DeepSeek API key is not configured. Please contact your administrator.'));\n } else {\n // Display dashboard\n echo '

' . __('AI Teacher Dashboard') . '

';\n \n // Quick actions\n echo '
';\n echo 'session->get('absoluteURL') . '/index.php?q=/modules/aiTeacher/curriculum_support.php\">' . __('Curriculum Support') . ' | ';\n echo 'session->get('absoluteURL') . '/index.php?q=/modules/aiTeacher/assessment_analysis.php\">' . __('Assessment Analysis') . ' | ';\n echo 'session->get('absoluteURL') . '/index.php?q=/modules/aiTeacher/resource_generator.php\">' . __('Resource Generator') . '';\n echo '
';\n \n // Recent activity\n echo '

' . __('Recent Activity') . '

';\n \n $sql = \"SELECT l.*, p.preferredName, p.surname \n FROM aiTeacherLogs l \n JOIN gibbonPerson p ON l.gibbonPersonID = p.gibbonPersonID \n ORDER BY l.timestamp DESC \n LIMIT 5\";\n $result = $pdo->executeQuery(array(), $sql);\n \n if ($result->rowCount() > 0) {\n echo '';\n echo '';\n echo '';\n echo '';\n echo '';\n echo '';\n echo '';\n \n while ($row = $result->fetch()) {\n echo '';\n echo '';\n echo '';\n echo '';\n echo '';\n echo '';\n }\n \n echo '
' . __('Date') . '' . __('User') . '' . __('Action') . '' . __('Subject') . '
' . date('Y-m-d H:i', strtotime($row['timestamp'])) . '' . $row['preferredName'] . ' ' . $row['surname'] . '' . __($row['action']) . '' . $row['subject'] . '
';\n } else {\n echo '

' . __('No recent activity found.') . '

';\n }\n \n // Quick tools\n echo '

' . __('Quick Tools') . '

';\n \n echo '
';\n echo 'session->get('absoluteURL') . '/index.php?q=/modules/aiTeacher/generate_lesson_plan.php\">' . __('Generate Lesson Plan') . ' | ';\n echo 'session->get('absoluteURL') . '/index.php?q=/modules/aiTeacher/generate_assessment.php\">' . __('Generate Assessment') . ' | ';\n echo 'session->get('absoluteURL') . '/index.php?q=/modules/aiTeacher/upload_resource.php\">' . __('Upload Resource') . '';\n echo '
';\n \n // System status\n echo '

' . __('System Status') . '

';\n \n try {\n $api = new DeepSeekAPI($settings['deepseek_api_key']);\n // Construct the data payload for the send() method\n // $data = [ // Old data structure for send()\n // 'model' => 'deepseek-chat', \n // 'messages' => [\n // ['role' => 'user', 'content' => 'Test connection']\n // ]\n // ];\n // $testResponse = $api->send($data); // Old problematic call\n \n $testContent = $api->generateResponse('Test connection'); // Corrected: Use public method\n \n // Check if the API call was successful and the response structure\n // if (isset($testResponse['success']) && $testResponse['success'] && isset($testResponse['response']['choices'])) { // Old check\n if ($testContent !== null) { // Corrected: generateResponse returns content string or null\n echo '

' . __('AI system is operational.') . '

';\n } else {\n // $errorMessage = __('AI system is not responding correctly.'); // Old error message\n // if (isset($testResponse['error'])) {\n // $errorMessage .= ' Error: ' . htmlspecialchars($testResponse['error']);\n // } else if (isset($testResponse['response']['error']['message'])) {\n // $errorMessage .= ' API Error: ' . htmlspecialchars($testResponse['response']['error']['message']);\n // }\n // echo '

' . $errorMessage . '

';\n $errorMessage = __('AI system is not responding correctly. Please check server logs for details.'); // Corrected error message\n echo '

' . $errorMessage . '

';\n }\n } catch (Exception $e) {\n echo '

' . __('AI system connection error: ') . htmlspecialchars($e->getMessage()) . '

';\n }\n }\n}"}, "030e63497fd5a0ea218107feedd91d649324421f": {"t": 1785756294.2456677, "n": 6285, "h": "e8c8b26454c957d7", "c": ".\n*/\n\nuse Gibbon\\Module\\aiTeacher\\DeepSeekAPI;\n\n// Gibbon includes\nrequire_once __DIR__ . '/../../gibbon.php';\nrequire_once __DIR__ . '/../../functions.php';\n\n// Module includes\nrequire_once __DIR__ . '/moduleFunctions.php';\n\n$page->breadcrumbs->add(__('AI Teacher Assistance'));\n\n// Check if user has access\nif (isActionAccessible($guid, $connection2, '/modules/aiTeacher/index.php') == false) {\n // Access denied\n $page->addError(__('You do not have access to this action.'));\n} else {\n // Get module settings\n $settings = getAITeacherSettings($pdo);\n \n // Check if API key is configured\n if (empty($settings['deepseek_api_key'])) {\n $page->addError(__('DeepSeek API key is not configured. Please contact your administrator.'));\n } else {\n // Display dashboard\n echo '

' . __('AI Teacher Dashboard') . '

';\n \n // Quick actions\n echo '
';\n echo 'session->get('absoluteURL') . '/index.php?q=/modules/aiTeacher/curriculum_support.php\">' . __('Curriculum Support') . ' | ';\n echo 'session->get('absoluteURL') . '/index.php?q=/modules/aiTeacher/assessment_analysis.php\">' . __('Assessment Analysis') . ' | ';\n echo 'session->get('absoluteURL') . '/index.php?q=/modules/aiTeacher/resource_generator.php\">' . __('Resource Generator') . '';\n echo '
';\n \n // Recent activity\n echo '

' . __('Recent Activity') . '

';\n \n $sql = \"SELECT l.*, p.preferredName, p.surname \n FROM aiTeacherLogs l \n JOIN gibbonPerson p ON l.gibbonPersonID = p.gibbonPersonID \n ORDER BY l.timestamp DESC \n LIMIT 5\";\n $result = $pdo->executeQuery(array(), $sql);\n \n if ($result->rowCount() > 0) {\n echo '';\n echo '';\n echo '';\n echo '';\n echo '';\n echo '';\n echo '';\n \n while ($row = $result->fetch()) {\n echo '';\n echo '';\n echo '';\n echo '';\n echo '';\n echo '';\n }\n \n echo '
' . __('Date') . '' . __('User') . '' . __('Action') . '' . __('Subject') . '
' . date('Y-m-d H:i', strtotime($row['timestamp'])) . '' . $row['preferredName'] . ' ' . $row['surname'] . '' . __($row['action']) . '' . $row['subject'] . '
';\n } else {\n echo '

' . __('No recent activity found.') . '

';\n }\n \n // Quick tools\n echo '

' . __('Quick Tools') . '

';\n \n echo '
';\n echo 'session->get('absoluteURL') . '/index.php?q=/modules/aiTeacher/generate_lesson_plan.php\">' . __('Generate Lesson Plan') . ' | ';\n echo 'session->get('absoluteURL') . '/index.php?q=/modules/aiTeacher/generate_assessment.php\">' . __('Generate Assessment') . ' | ';\n echo 'session->get('absoluteURL') . '/index.php?q=/modules/aiTeacher/upload_resource.php\">' . __('Upload Resource') . '';\n echo '
';\n \n // System status\n echo '

' . __('System Status') . '

';\n \n try {\n $api = new DeepSeekAPI($settings['deepseek_api_key']);\n // Construct the data payload for the send() method\n // $data = [ // Old data structure for send()\n // 'model' => 'deepseek-chat', \n // 'messages' => [\n // ['role' => 'user', 'content' => 'Test connection']\n // ]\n // ];\n // $testResponse = $api->send($data); // Old problematic call\n \n $testContent = $api->generateResponse('Test connection'); // Corrected: Use public method\n \n // Check if the API call was successful and the response structure\n // if (isset($testResponse['success']) && $testResponse['success'] && isset($testResponse['response']['choices'])) { // Old check\n if ($testContent !== null) { // Corrected: generateResponse returns content string or null\n echo '

' . __('AI system is operational.') . '

';\n } else {\n // $errorMessage = __('AI system is not responding correctly.'); // Old error message\n // if (isset($testResponse['error'])) {\n // $errorMessage .= ' Error: ' . htmlspecialchars($testResponse['error']);\n // } else if (isset($testResponse['response']['error']['message'])) {\n // $errorMessage .= ' API Error: ' . htmlspecialchars($testResponse['response']['error']['message']);\n // }\n // echo '

' . $errorMessage . '

';\n $errorMessage = __('AI system is not responding correctly. Please check server logs for details.'); // Corrected error message\n echo '

' . $errorMessage . '

';\n }\n } catch (Exception $e) {\n echo '

' . __('AI system connection error: ') . htmlspecialchars($e->getMessage()) . '

';\n }\n }\n}"}, "ea2bf72c5d38ab35b13101b6131cf74ba37f78a3": {"t": 1786068313.7465246, "n": 6285, "h": "e8c8b26454c957d7", "c": ".\n*/\n\nuse Gibbon\\Module\\aiTeacher\\DeepSeekAPI;\n\n// Gibbon includes\nrequire_once __DIR__ . '/../../gibbon.php';\nrequire_once __DIR__ . '/../../functions.php';\n\n// Module includes\nrequire_once __DIR__ . '/moduleFunctions.php';\n\n$page->breadcrumbs->add(__('AI Teacher Assistance'));\n\n// Check if user has access\nif (isActionAccessible($guid, $connection2, '/modules/aiTeacher/index.php') == false) {\n // Access denied\n $page->addError(__('You do not have access to this action.'));\n} else {\n // Get module settings\n $settings = getAITeacherSettings($pdo);\n \n // Check if API key is configured\n if (empty($settings['deepseek_api_key'])) {\n $page->addError(__('DeepSeek API key is not configured. Please contact your administrator.'));\n } else {\n // Display dashboard\n echo '

' . __('AI Teacher Dashboard') . '

';\n \n // Quick actions\n echo '
';\n echo 'session->get('absoluteURL') . '/index.php?q=/modules/aiTeacher/curriculum_support.php\">' . __('Curriculum Support') . ' | ';\n echo 'session->get('absoluteURL') . '/index.php?q=/modules/aiTeacher/assessment_analysis.php\">' . __('Assessment Analysis') . ' | ';\n echo 'session->get('absoluteURL') . '/index.php?q=/modules/aiTeacher/resource_generator.php\">' . __('Resource Generator') . '';\n echo '
';\n \n // Recent activity\n echo '

' . __('Recent Activity') . '

';\n \n $sql = \"SELECT l.*, p.preferredName, p.surname \n FROM aiTeacherLogs l \n JOIN gibbonPerson p ON l.gibbonPersonID = p.gibbonPersonID \n ORDER BY l.timestamp DESC \n LIMIT 5\";\n $result = $pdo->executeQuery(array(), $sql);\n \n if ($result->rowCount() > 0) {\n echo '';\n echo '';\n echo '';\n echo '';\n echo '';\n echo '';\n echo '';\n \n while ($row = $result->fetch()) {\n echo '';\n echo '';\n echo '';\n echo '';\n echo '';\n echo '';\n }\n \n echo '
' . __('Date') . '' . __('User') . '' . __('Action') . '' . __('Subject') . '
' . date('Y-m-d H:i', strtotime($row['timestamp'])) . '' . $row['preferredName'] . ' ' . $row['surname'] . '' . __($row['action']) . '' . $row['subject'] . '
';\n } else {\n echo '

' . __('No recent activity found.') . '

';\n }\n \n // Quick tools\n echo '

' . __('Quick Tools') . '

';\n \n echo '
';\n echo 'session->get('absoluteURL') . '/index.php?q=/modules/aiTeacher/generate_lesson_plan.php\">' . __('Generate Lesson Plan') . ' | ';\n echo 'session->get('absoluteURL') . '/index.php?q=/modules/aiTeacher/generate_assessment.php\">' . __('Generate Assessment') . ' | ';\n echo 'session->get('absoluteURL') . '/index.php?q=/modules/aiTeacher/upload_resource.php\">' . __('Upload Resource') . '';\n echo '
';\n \n // System status\n echo '

' . __('System Status') . '

';\n \n try {\n $api = new DeepSeekAPI($settings['deepseek_api_key']);\n // Construct the data payload for the send() method\n // $data = [ // Old data structure for send()\n // 'model' => 'deepseek-chat', \n // 'messages' => [\n // ['role' => 'user', 'content' => 'Test connection']\n // ]\n // ];\n // $testResponse = $api->send($data); // Old problematic call\n \n $testContent = $api->generateResponse('Test connection'); // Corrected: Use public method\n \n // Check if the API call was successful and the response structure\n // if (isset($testResponse['success']) && $testResponse['success'] && isset($testResponse['response']['choices'])) { // Old check\n if ($testContent !== null) { // Corrected: generateResponse returns content string or null\n echo '

' . __('AI system is operational.') . '

';\n } else {\n // $errorMessage = __('AI system is not responding correctly.'); // Old error message\n // if (isset($testResponse['error'])) {\n // $errorMessage .= ' Error: ' . htmlspecialchars($testResponse['error']);\n // } else if (isset($testResponse['response']['error']['message'])) {\n // $errorMessage .= ' API Error: ' . htmlspecialchars($testResponse['response']['error']['message']);\n // }\n // echo '

' . $errorMessage . '

';\n $errorMessage = __('AI system is not responding correctly. Please check server logs for details.'); // Corrected error message\n echo '

' . $errorMessage . '

';\n }\n } catch (Exception $e) {\n echo '

' . __('AI system connection error: ') . htmlspecialchars($e->getMessage()) . '

';\n }\n }\n}"}}, "modules/ChatBot/index.php": {"5eacf702b1d14d58114e1990619ea4913b19ff59": {"t": 1785745776.9634418, "n": 1756, "h": "bae2268379ab7726", "c": "addError($session->get('strAccessDenied'));\n} else {\n // Proceed!\n $page->breadcrumbs->add('AI Teaching Assistant');\n \n // Add module CSS and JS\n $page->stylesheets->add('chatbot', 'modules/ChatBot/css/chatbot.css');\n $page->scripts->add('chatbot', 'modules/ChatBot/js/chatbot.js');\n \n // Get API key from settings\n $apiKey = getSettingByScope($connection2, 'ChatBot', 'deepseek_api_key');\n \n ?>\n
\n
\n
\n

AI Teaching Assistant

\n
\n \n \n
\n
\n \n
\n
\n
\n \n \n
\n
\n
\n
\n \n \n "}, "030e63497fd5a0ea218107feedd91d649324421f": {"t": 1785756301.2722747, "n": 1756, "h": "bae2268379ab7726", "c": "addError($session->get('strAccessDenied'));\n} else {\n // Proceed!\n $page->breadcrumbs->add('AI Teaching Assistant');\n \n // Add module CSS and JS\n $page->stylesheets->add('chatbot', 'modules/ChatBot/css/chatbot.css');\n $page->scripts->add('chatbot', 'modules/ChatBot/js/chatbot.js');\n \n // Get API key from settings\n $apiKey = getSettingByScope($connection2, 'ChatBot', 'deepseek_api_key');\n \n ?>\n
\n
\n
\n

AI Teaching Assistant

\n
\n \n \n
\n
\n \n
\n
\n
\n \n \n
\n
\n
\n
\n \n \n "}, "ea2bf72c5d38ab35b13101b6131cf74ba37f78a3": {"t": 1786068314.5029423, "n": 1756, "h": "bae2268379ab7726", "c": "addError($session->get('strAccessDenied'));\n} else {\n // Proceed!\n $page->breadcrumbs->add('AI Teaching Assistant');\n \n // Add module CSS and JS\n $page->stylesheets->add('chatbot', 'modules/ChatBot/css/chatbot.css');\n $page->scripts->add('chatbot', 'modules/ChatBot/js/chatbot.js');\n \n // Get API key from settings\n $apiKey = getSettingByScope($connection2, 'ChatBot', 'deepseek_api_key');\n \n ?>\n
\n
\n
\n

AI Teaching Assistant

\n
\n \n \n
\n
\n \n
\n
\n
\n \n \n
\n
\n
\n
\n \n \n "}}, "modules/aiTeacher/settings.php": {"5eacf702b1d14d58114e1990619ea4913b19ff59": {"t": 1785745777.367991, "n": 5520, "h": "9c7ba028147a28a3", "c": ".\n*/\n\n// Gibbon includes\nrequire_once __DIR__ . '/../../gibbon.php';\nrequire_once __DIR__ . '/../../functions.php';\n\n// Module includes\nrequire_once __DIR__ . '/moduleFunctions.php';\n\n$page->breadcrumbs->add(__('AI Teacher Assistance'), 'index.php');\n$page->breadcrumbs->add(__('Settings'));\n\n// Check if user has access\nif (isActionAccessible($guid, $connection2, '/modules/aiTeacher/settings.php') == false) {\n // Access denied\n $page->addError(__('You do not have access to this action.'));\n} else {\n // Get module settings\n $settings = getAITeacherSettings($pdo); // This function might also need a slight tweak if it specifically looks for deepseek_api_key by name\n \n // Ensure settings rows exist\n $settingNames = [\n 'openai_api_key' => 'OpenAI API Key for AI integration',\n 'deepseek_api_key' => 'DeepSeek API Key for AI integration',\n 'upload_path' => 'Path for storing uploaded resources',\n 'score_threshold' => 'Threshold for student performance alerts (percentage)'\n ];\n\n foreach ($settingNames as $name => $desc) {\n $sqlCheck = \"SELECT COUNT(*) FROM aiTeacherSettings WHERE scope = 'aiTeacher' AND name = :name\";\n $result = $pdo->executeQuery(['name' => $name], $sqlCheck);\n if ($result->fetchColumn() == 0) {\n $insertSql = \"INSERT INTO aiTeacherSettings (scope, name, value, description) VALUES ('aiTeacher', :name, '', :desc)\";\n $pdo->executeQuery(['name' => $name, 'desc' => $desc], $insertSql);\n }\n }\n\n // Handle form submission\n if (isset($_POST['submit'])) {\n // Validate input\n $openai_api_key = $_POST['openai_api_key'] ?? ''; // Changed\n $upload_path = $_POST['upload_path'] ?? 'uploads/aiTeacher';\n $score_threshold = $_POST['score_threshold'] ?? 60;\n $deepseek_api_key = $_POST['deepseek_api_key'] ?? '';\n \n // Update settings\n $sql = \"UPDATE aiTeacherSettings SET value = :value WHERE scope = 'aiTeacher' AND name = :name\";\n \n $pdo->executeQuery(\n array('value' => $openai_api_key, 'name' => 'openai_api_key'), // Changed\n $sql\n );\n \n $pdo->executeQuery(\n array('value' => $upload_path, 'name' => 'upload_path'),\n $sql\n );\n \n $pdo->executeQuery(\n array('value' => $score_threshold, 'name' => 'score_threshold'),\n $sql\n );\n \n $pdo->executeQuery(\n array('value' => $deepseek_api_key, 'name' => 'deepseek_api_key'),\n $sql\n );\n \n $page->addSuccess(__('Settings updated successfully.'));\n }\n \n // Display form\n echo '
session->get('absoluteURL') . '/index.php?q=/modules/aiTeacher/settings.php\">';\n \n echo '';\n \n // OpenAI API Key // Changed comment\n echo '';\n echo '';\n echo '';\n echo '';\n \n // DeepSeek API Key\n echo '';\n echo '';\n echo '';\n echo '';\n \n // Upload Path\n echo '';\n echo '';\n echo '';\n echo '';\n \n // Score Threshold\n echo '';\n echo '';\n echo '';\n echo '';\n \n echo '
';\n echo '' . __('OpenAI API Key') . '
'; // Changed\n echo __('Enter your OpenAI API key for AI integration.') . '
'; // Changed\n echo '
';\n echo ''; // Changed name and settings key\n echo '
';\n echo '' . __('DeepSeek API Key') . '
';\n echo __('Enter your DeepSeek API key for AI integration.') . '
';\n echo '
';\n echo '';\n echo '
';\n echo '' . __('Upload Path') . '
';\n echo __('Path for storing uploaded resources.') . '
';\n echo '
';\n echo '';\n echo '
';\n echo '' . __('Score Threshold') . '
';\n echo __('Threshold for student performance alerts (percentage).') . '
';\n echo '
';\n echo '';\n echo '
';\n \n echo '
';\n echo '';\n echo '
';\n \n echo '
';\n}"}, "030e63497fd5a0ea218107feedd91d649324421f": {"t": 1785756300.3326864, "n": 5520, "h": "9c7ba028147a28a3", "c": ".\n*/\n\n// Gibbon includes\nrequire_once __DIR__ . '/../../gibbon.php';\nrequire_once __DIR__ . '/../../functions.php';\n\n// Module includes\nrequire_once __DIR__ . '/moduleFunctions.php';\n\n$page->breadcrumbs->add(__('AI Teacher Assistance'), 'index.php');\n$page->breadcrumbs->add(__('Settings'));\n\n// Check if user has access\nif (isActionAccessible($guid, $connection2, '/modules/aiTeacher/settings.php') == false) {\n // Access denied\n $page->addError(__('You do not have access to this action.'));\n} else {\n // Get module settings\n $settings = getAITeacherSettings($pdo); // This function might also need a slight tweak if it specifically looks for deepseek_api_key by name\n \n // Ensure settings rows exist\n $settingNames = [\n 'openai_api_key' => 'OpenAI API Key for AI integration',\n 'deepseek_api_key' => 'DeepSeek API Key for AI integration',\n 'upload_path' => 'Path for storing uploaded resources',\n 'score_threshold' => 'Threshold for student performance alerts (percentage)'\n ];\n\n foreach ($settingNames as $name => $desc) {\n $sqlCheck = \"SELECT COUNT(*) FROM aiTeacherSettings WHERE scope = 'aiTeacher' AND name = :name\";\n $result = $pdo->executeQuery(['name' => $name], $sqlCheck);\n if ($result->fetchColumn() == 0) {\n $insertSql = \"INSERT INTO aiTeacherSettings (scope, name, value, description) VALUES ('aiTeacher', :name, '', :desc)\";\n $pdo->executeQuery(['name' => $name, 'desc' => $desc], $insertSql);\n }\n }\n\n // Handle form submission\n if (isset($_POST['submit'])) {\n // Validate input\n $openai_api_key = $_POST['openai_api_key'] ?? ''; // Changed\n $upload_path = $_POST['upload_path'] ?? 'uploads/aiTeacher';\n $score_threshold = $_POST['score_threshold'] ?? 60;\n $deepseek_api_key = $_POST['deepseek_api_key'] ?? '';\n \n // Update settings\n $sql = \"UPDATE aiTeacherSettings SET value = :value WHERE scope = 'aiTeacher' AND name = :name\";\n \n $pdo->executeQuery(\n array('value' => $openai_api_key, 'name' => 'openai_api_key'), // Changed\n $sql\n );\n \n $pdo->executeQuery(\n array('value' => $upload_path, 'name' => 'upload_path'),\n $sql\n );\n \n $pdo->executeQuery(\n array('value' => $score_threshold, 'name' => 'score_threshold'),\n $sql\n );\n \n $pdo->executeQuery(\n array('value' => $deepseek_api_key, 'name' => 'deepseek_api_key'),\n $sql\n );\n \n $page->addSuccess(__('Settings updated successfully.'));\n }\n \n // Display form\n echo '
session->get('absoluteURL') . '/index.php?q=/modules/aiTeacher/settings.php\">';\n \n echo '';\n \n // OpenAI API Key // Changed comment\n echo '';\n echo '';\n echo '';\n echo '';\n \n // DeepSeek API Key\n echo '';\n echo '';\n echo '';\n echo '';\n \n // Upload Path\n echo '';\n echo '';\n echo '';\n echo '';\n \n // Score Threshold\n echo '';\n echo '';\n echo '';\n echo '';\n \n echo '
';\n echo '' . __('OpenAI API Key') . '
'; // Changed\n echo __('Enter your OpenAI API key for AI integration.') . '
'; // Changed\n echo '
';\n echo ''; // Changed name and settings key\n echo '
';\n echo '' . __('DeepSeek API Key') . '
';\n echo __('Enter your DeepSeek API key for AI integration.') . '
';\n echo '
';\n echo '';\n echo '
';\n echo '' . __('Upload Path') . '
';\n echo __('Path for storing uploaded resources.') . '
';\n echo '
';\n echo '';\n echo '
';\n echo '' . __('Score Threshold') . '
';\n echo __('Threshold for student performance alerts (percentage).') . '
';\n echo '
';\n echo '';\n echo '
';\n \n echo '
';\n echo '';\n echo '
';\n \n echo '
';\n}"}, "ea2bf72c5d38ab35b13101b6131cf74ba37f78a3": {"t": 1786068314.5222397, "n": 5520, "h": "9c7ba028147a28a3", "c": ".\n*/\n\n// Gibbon includes\nrequire_once __DIR__ . '/../../gibbon.php';\nrequire_once __DIR__ . '/../../functions.php';\n\n// Module includes\nrequire_once __DIR__ . '/moduleFunctions.php';\n\n$page->breadcrumbs->add(__('AI Teacher Assistance'), 'index.php');\n$page->breadcrumbs->add(__('Settings'));\n\n// Check if user has access\nif (isActionAccessible($guid, $connection2, '/modules/aiTeacher/settings.php') == false) {\n // Access denied\n $page->addError(__('You do not have access to this action.'));\n} else {\n // Get module settings\n $settings = getAITeacherSettings($pdo); // This function might also need a slight tweak if it specifically looks for deepseek_api_key by name\n \n // Ensure settings rows exist\n $settingNames = [\n 'openai_api_key' => 'OpenAI API Key for AI integration',\n 'deepseek_api_key' => 'DeepSeek API Key for AI integration',\n 'upload_path' => 'Path for storing uploaded resources',\n 'score_threshold' => 'Threshold for student performance alerts (percentage)'\n ];\n\n foreach ($settingNames as $name => $desc) {\n $sqlCheck = \"SELECT COUNT(*) FROM aiTeacherSettings WHERE scope = 'aiTeacher' AND name = :name\";\n $result = $pdo->executeQuery(['name' => $name], $sqlCheck);\n if ($result->fetchColumn() == 0) {\n $insertSql = \"INSERT INTO aiTeacherSettings (scope, name, value, description) VALUES ('aiTeacher', :name, '', :desc)\";\n $pdo->executeQuery(['name' => $name, 'desc' => $desc], $insertSql);\n }\n }\n\n // Handle form submission\n if (isset($_POST['submit'])) {\n // Validate input\n $openai_api_key = $_POST['openai_api_key'] ?? ''; // Changed\n $upload_path = $_POST['upload_path'] ?? 'uploads/aiTeacher';\n $score_threshold = $_POST['score_threshold'] ?? 60;\n $deepseek_api_key = $_POST['deepseek_api_key'] ?? '';\n \n // Update settings\n $sql = \"UPDATE aiTeacherSettings SET value = :value WHERE scope = 'aiTeacher' AND name = :name\";\n \n $pdo->executeQuery(\n array('value' => $openai_api_key, 'name' => 'openai_api_key'), // Changed\n $sql\n );\n \n $pdo->executeQuery(\n array('value' => $upload_path, 'name' => 'upload_path'),\n $sql\n );\n \n $pdo->executeQuery(\n array('value' => $score_threshold, 'name' => 'score_threshold'),\n $sql\n );\n \n $pdo->executeQuery(\n array('value' => $deepseek_api_key, 'name' => 'deepseek_api_key'),\n $sql\n );\n \n $page->addSuccess(__('Settings updated successfully.'));\n }\n \n // Display form\n echo '
session->get('absoluteURL') . '/index.php?q=/modules/aiTeacher/settings.php\">';\n \n echo '';\n \n // OpenAI API Key // Changed comment\n echo '';\n echo '';\n echo '';\n echo '';\n \n // DeepSeek API Key\n echo '';\n echo '';\n echo '';\n echo '';\n \n // Upload Path\n echo '';\n echo '';\n echo '';\n echo '';\n \n // Score Threshold\n echo '';\n echo '';\n echo '';\n echo '';\n \n echo '
';\n echo '' . __('OpenAI API Key') . '
'; // Changed\n echo __('Enter your OpenAI API key for AI integration.') . '
'; // Changed\n echo '
';\n echo ''; // Changed name and settings key\n echo '
';\n echo '' . __('DeepSeek API Key') . '
';\n echo __('Enter your DeepSeek API key for AI integration.') . '
';\n echo '
';\n echo '';\n echo '
';\n echo '' . __('Upload Path') . '
';\n echo __('Path for storing uploaded resources.') . '
';\n echo '
';\n echo '';\n echo '
';\n echo '' . __('Score Threshold') . '
';\n echo __('Threshold for student performance alerts (percentage).') . '
';\n echo '
';\n echo '';\n echo '
';\n \n echo '
';\n echo '';\n echo '
';\n \n echo '
';\n}"}}, "modules/ChatBot/api/chat.php": {"5eacf702b1d14d58114e1990619ea4913b19ff59": {"t": 1785745780.505213, "n": 8793, "h": "c14adba938f7da19", "c": ".\n*/\n\n// Set PHP configuration\nini_set('max_execution_time', 300);\nini_set('memory_limit', '256M');\nset_time_limit(300);\n\nuse Gibbon\\Contracts\\Database\\Connection;\nuse Gibbon\\Domain\\System\\SettingGateway;\nuse Gibbon\\Domain\\System\\ModuleGateway;\nuse Gibbon\\Module\\ChatBot\\Domain\\ChatGateway;\nuse Gibbon\\Module\\ChatBot\\DeepSeekAPI;\n\n// Include Gibbon core files\nrequire_once '../../../gibbon.php';\nrequire_once __DIR__ . '/../moduleFunctions.php';\nrequire_once __DIR__ . '/../src/Domain/ChatGateway.php';\nrequire_once __DIR__ . '/../src/DeepSeekAPI.php';\n\n// Set headers and enable error logging\nheader('Content-Type: application/json');\nerror_reporting(E_ALL);\nini_set('display_errors', 0);\nini_set('log_errors', 1);\n//ini_set('error_log', '/Applications/MAMP/logs/php_error.log');\n\ntry {\n // Check if user is logged in\n if (!$session->has('username')) {\n error_log(\"ChatBot: User not authenticated\");\n http_response_code(401);\n echo json_encode([\n 'error' => 'Please log in to use the AI Teaching Assistant.',\n 'timestamp' => date('Y-m-d H:i:s')\n ]);\n exit;\n }\n\n // Get the DeepSeek API key from settings\n $settingGateway = $container->get(SettingGateway::class);\n $apiKey = $settingGateway->getSettingByScope('ChatBot', 'deepseek_api_key');\n \n if (empty($apiKey)) {\n error_log(\"ChatBot: DeepSeek API key not configured\");\n http_response_code(500);\n echo json_encode([\n 'error' => 'The AI Teaching Assistant is not properly configured. Please contact your administrator.',\n 'timestamp' => date('Y-m-d H:i:s')\n ]);\n exit;\n }\n\n // Validate the incoming request\n $input = json_decode(file_get_contents('php://input'), true);\n if (json_last_error() !== JSON_ERROR_NONE) {\n error_log(\"ChatBot: Invalid JSON input - \" . json_last_error_msg());\n http_response_code(400);\n echo json_encode([\n 'error' => 'Invalid request format. Please try again.',\n 'timestamp' => date('Y-m-d H:i:s')\n ]);\n exit;\n }\n\n if (empty($input['message'])) {\n error_log(\"ChatBot: Empty message received\");\n http_response_code(400);\n echo json_encode([\n 'error' => 'Please enter a message.',\n 'timestamp' => date('Y-m-d H:i:s')\n ]);\n exit;\n }\n\n try {\n // Initialize the DeepSeek API\n $api = new DeepSeekAPI($apiKey);\n error_log(\"ChatBot: Processing request for user \" . $session->get('username'));\n\n // Get the response\n $response = $api->getResponse($input['message']);\n \n // Validate response format\n if (empty($response['answer'])) {\n error_log(\"ChatBot: Empty response from API\");\n throw new Exception(\"Invalid response format from AI service\");\n }\n\n // Log success and response content\n $answer = $response['answer'];\n $logContent = is_array($answer) ? json_encode($answer) : $answer;\n error_log(\"ChatBot: Successfully processed request. Response: \" . substr($logContent, 0, 100) . \"...\");\n\n // Return the response\n $jsonResponse = json_encode($response);\n if ($jsonResponse === false) {\n error_log(\"ChatBot: Failed to encode API response to JSON. Error: \" . json_last_error_msg());\n throw new Exception(\"Failed to encode response from AI service.\");\n }\n echo $jsonResponse;\n\n } catch (Exception $e) {\n error_log(\"ChatBot: Error processing request - \" . $e->getMessage());\n \n // Handle specific error cases\n if (strpos($e->getMessage(), 'Authentication Fails') !== false) {\n http_response_code(401);\n echo json_encode([\n 'error' => 'The AI Teaching Assistant is not properly configured. Please contact your administrator.',\n 'timestamp' => date('Y-m-d H:i:s')\n ]);\n } else if (strpos($e->getMessage(), 'timeout') !== false) {\n http_response_code(504);\n echo json_encode([\n 'error' => 'The AI Teaching Assistant is taking too long to respond. Please try again.',\n 'timestamp' => date('Y-m-d H:i:s')\n ]);\n } else {\n error_log(\"ChatBot Error Stack Trace: \" . $e->getTraceAsString());\n http_response_code(500);\n echo json_encode([\n 'error' => 'An unexpected error occurred: ' . htmlspecialchars($e->getMessage()),\n 'timestamp' => date('Y-m-d H:i:s')\n ]);\n }\n }\n\n} catch (PDOException $e) {\n // Database errors\n error_log('ChatBot Database Error: ' . $e->getMessage());\n error_log('ChatBot Database Error Stack Trace: ' . $e->getTraceAsString());\n http_response_code(500);\n echo json_encode([\n 'error' => 'A database error occurred. Please try again later.',\n 'details' => $e->getMessage()\n ]);\n \n} catch (Exception $e) {\n // General errors\n error_log('ChatBot Error: ' . $e->getMessage());\n error_log('ChatBot Error Stack Trace: ' . $e->getTraceAsString());\n http_response_code(500);\n echo json_encode([\n 'error' => $e->getMessage(),\n 'timestamp' => date('Y-m-d H:i:s')\n ]);\n}\n\nclass InternalAssessmentTrainer {\n private $pdo;\n \n public function __construct($pdo) {\n $this->pdo = $pdo;\n }\n \n public function getTeacherFeedback() {\n $sql = \"SELECT \n ia.gibbonInternalAssessmentID,\n ia.name as assessment_name,\n iac.comment as teacher_feedback,\n iac.attainmentValue,\n iac.effortValue,\n c.name as course_name,\n c.nameShort as course_code,\n student.preferredName as student_name,\n student.gibbonPersonID\n FROM gibbonInternalAssessmentColumn ia \n JOIN gibbonInternalAssessmentEntry iac ON iac.gibbonInternalAssessmentColumnID=ia.gibbonInternalAssessmentColumnID\n JOIN gibbonCourse c ON c.gibbonCourseID=ia.gibbonCourseID\n JOIN gibbonPerson student ON student.gibbonPersonID=iac.gibbonPersonID\n WHERE iac.comment IS NOT NULL AND iac.comment != ''\n ORDER BY ia.gibbonInternalAssessmentID DESC\";\n \n try {\n $result = $this->pdo->query($sql);\n return $result->fetchAll(PDO::FETCH_ASSOC);\n } catch (Exception $e) {\n error_log('Error fetching Internal Assessment feedback: ' . $e->getMessage());\n return [];\n }\n }\n \n public function formatTrainingData($feedbackData) {\n $trainingData = [];\n foreach ($feedbackData as $entry) {\n $trainingData[] = [\n 'context' => [\n 'course' => $entry['course_name'],\n 'assessment' => $entry['assessment_name'],\n 'attainment' => $entry['attainmentValue'],\n 'effort' => $entry['effortValue']\n ],\n 'feedback' => $entry['teacher_feedback'],\n 'metadata' => [\n 'course_code' => $entry['course_code'],\n 'student_id' => $entry['gibbonPersonID']\n ]\n ];\n }\n return $trainingData;\n }\n \n public function trainChatbot($trainingData) {\n // Format data for DeepSeek API\n $systemPrompt = \"You are an educational AI assistant trained on teacher feedback. Your role is to:\n1. Analyze patterns in student performance\n2. Identify common areas for improvement\n3. Suggest specific strategies for enhancement\n4. Provide constructive, encouraging guidance\n5. Reference real examples from teacher feedback\";\n \n $messages = [\n [\n 'role' => 'system',\n 'content' => $systemPrompt\n ],\n [\n 'role' => 'user',\n 'content' => 'Train using this feedback data: ' . json_encode($trainingData)\n ]\n ];\n \n return $messages;\n }\n} "}, "030e63497fd5a0ea218107feedd91d649324421f": {"t": 1785756293.4539733, "n": 8793, "h": "c14adba938f7da19", "c": ".\n*/\n\n// Set PHP configuration\nini_set('max_execution_time', 300);\nini_set('memory_limit', '256M');\nset_time_limit(300);\n\nuse Gibbon\\Contracts\\Database\\Connection;\nuse Gibbon\\Domain\\System\\SettingGateway;\nuse Gibbon\\Domain\\System\\ModuleGateway;\nuse Gibbon\\Module\\ChatBot\\Domain\\ChatGateway;\nuse Gibbon\\Module\\ChatBot\\DeepSeekAPI;\n\n// Include Gibbon core files\nrequire_once '../../../gibbon.php';\nrequire_once __DIR__ . '/../moduleFunctions.php';\nrequire_once __DIR__ . '/../src/Domain/ChatGateway.php';\nrequire_once __DIR__ . '/../src/DeepSeekAPI.php';\n\n// Set headers and enable error logging\nheader('Content-Type: application/json');\nerror_reporting(E_ALL);\nini_set('display_errors', 0);\nini_set('log_errors', 1);\n//ini_set('error_log', '/Applications/MAMP/logs/php_error.log');\n\ntry {\n // Check if user is logged in\n if (!$session->has('username')) {\n error_log(\"ChatBot: User not authenticated\");\n http_response_code(401);\n echo json_encode([\n 'error' => 'Please log in to use the AI Teaching Assistant.',\n 'timestamp' => date('Y-m-d H:i:s')\n ]);\n exit;\n }\n\n // Get the DeepSeek API key from settings\n $settingGateway = $container->get(SettingGateway::class);\n $apiKey = $settingGateway->getSettingByScope('ChatBot', 'deepseek_api_key');\n \n if (empty($apiKey)) {\n error_log(\"ChatBot: DeepSeek API key not configured\");\n http_response_code(500);\n echo json_encode([\n 'error' => 'The AI Teaching Assistant is not properly configured. Please contact your administrator.',\n 'timestamp' => date('Y-m-d H:i:s')\n ]);\n exit;\n }\n\n // Validate the incoming request\n $input = json_decode(file_get_contents('php://input'), true);\n if (json_last_error() !== JSON_ERROR_NONE) {\n error_log(\"ChatBot: Invalid JSON input - \" . json_last_error_msg());\n http_response_code(400);\n echo json_encode([\n 'error' => 'Invalid request format. Please try again.',\n 'timestamp' => date('Y-m-d H:i:s')\n ]);\n exit;\n }\n\n if (empty($input['message'])) {\n error_log(\"ChatBot: Empty message received\");\n http_response_code(400);\n echo json_encode([\n 'error' => 'Please enter a message.',\n 'timestamp' => date('Y-m-d H:i:s')\n ]);\n exit;\n }\n\n try {\n // Initialize the DeepSeek API\n $api = new DeepSeekAPI($apiKey);\n error_log(\"ChatBot: Processing request for user \" . $session->get('username'));\n\n // Get the response\n $response = $api->getResponse($input['message']);\n \n // Validate response format\n if (empty($response['answer'])) {\n error_log(\"ChatBot: Empty response from API\");\n throw new Exception(\"Invalid response format from AI service\");\n }\n\n // Log success and response content\n $answer = $response['answer'];\n $logContent = is_array($answer) ? json_encode($answer) : $answer;\n error_log(\"ChatBot: Successfully processed request. Response: \" . substr($logContent, 0, 100) . \"...\");\n\n // Return the response\n $jsonResponse = json_encode($response);\n if ($jsonResponse === false) {\n error_log(\"ChatBot: Failed to encode API response to JSON. Error: \" . json_last_error_msg());\n throw new Exception(\"Failed to encode response from AI service.\");\n }\n echo $jsonResponse;\n\n } catch (Exception $e) {\n error_log(\"ChatBot: Error processing request - \" . $e->getMessage());\n \n // Handle specific error cases\n if (strpos($e->getMessage(), 'Authentication Fails') !== false) {\n http_response_code(401);\n echo json_encode([\n 'error' => 'The AI Teaching Assistant is not properly configured. Please contact your administrator.',\n 'timestamp' => date('Y-m-d H:i:s')\n ]);\n } else if (strpos($e->getMessage(), 'timeout') !== false) {\n http_response_code(504);\n echo json_encode([\n 'error' => 'The AI Teaching Assistant is taking too long to respond. Please try again.',\n 'timestamp' => date('Y-m-d H:i:s')\n ]);\n } else {\n error_log(\"ChatBot Error Stack Trace: \" . $e->getTraceAsString());\n http_response_code(500);\n echo json_encode([\n 'error' => 'An unexpected error occurred: ' . htmlspecialchars($e->getMessage()),\n 'timestamp' => date('Y-m-d H:i:s')\n ]);\n }\n }\n\n} catch (PDOException $e) {\n // Database errors\n error_log('ChatBot Database Error: ' . $e->getMessage());\n error_log('ChatBot Database Error Stack Trace: ' . $e->getTraceAsString());\n http_response_code(500);\n echo json_encode([\n 'error' => 'A database error occurred. Please try again later.',\n 'details' => $e->getMessage()\n ]);\n \n} catch (Exception $e) {\n // General errors\n error_log('ChatBot Error: ' . $e->getMessage());\n error_log('ChatBot Error Stack Trace: ' . $e->getTraceAsString());\n http_response_code(500);\n echo json_encode([\n 'error' => $e->getMessage(),\n 'timestamp' => date('Y-m-d H:i:s')\n ]);\n}\n\nclass InternalAssessmentTrainer {\n private $pdo;\n \n public function __construct($pdo) {\n $this->pdo = $pdo;\n }\n \n public function getTeacherFeedback() {\n $sql = \"SELECT \n ia.gibbonInternalAssessmentID,\n ia.name as assessment_name,\n iac.comment as teacher_feedback,\n iac.attainmentValue,\n iac.effortValue,\n c.name as course_name,\n c.nameShort as course_code,\n student.preferredName as student_name,\n student.gibbonPersonID\n FROM gibbonInternalAssessmentColumn ia \n JOIN gibbonInternalAssessmentEntry iac ON iac.gibbonInternalAssessmentColumnID=ia.gibbonInternalAssessmentColumnID\n JOIN gibbonCourse c ON c.gibbonCourseID=ia.gibbonCourseID\n JOIN gibbonPerson student ON student.gibbonPersonID=iac.gibbonPersonID\n WHERE iac.comment IS NOT NULL AND iac.comment != ''\n ORDER BY ia.gibbonInternalAssessmentID DESC\";\n \n try {\n $result = $this->pdo->query($sql);\n return $result->fetchAll(PDO::FETCH_ASSOC);\n } catch (Exception $e) {\n error_log('Error fetching Internal Assessment feedback: ' . $e->getMessage());\n return [];\n }\n }\n \n public function formatTrainingData($feedbackData) {\n $trainingData = [];\n foreach ($feedbackData as $entry) {\n $trainingData[] = [\n 'context' => [\n 'course' => $entry['course_name'],\n 'assessment' => $entry['assessment_name'],\n 'attainment' => $entry['attainmentValue'],\n 'effort' => $entry['effortValue']\n ],\n 'feedback' => $entry['teacher_feedback'],\n 'metadata' => [\n 'course_code' => $entry['course_code'],\n 'student_id' => $entry['gibbonPersonID']\n ]\n ];\n }\n return $trainingData;\n }\n \n public function trainChatbot($trainingData) {\n // Format data for DeepSeek API\n $systemPrompt = \"You are an educational AI assistant trained on teacher feedback. Your role is to:\n1. Analyze patterns in student performance\n2. Identify common areas for improvement\n3. Suggest specific strategies for enhancement\n4. Provide constructive, encouraging guidance\n5. Reference real examples from teacher feedback\";\n \n $messages = [\n [\n 'role' => 'system',\n 'content' => $systemPrompt\n ],\n [\n 'role' => 'user',\n 'content' => 'Train using this feedback data: ' . json_encode($trainingData)\n ]\n ];\n \n return $messages;\n }\n} "}, "ea2bf72c5d38ab35b13101b6131cf74ba37f78a3": {"t": 1786068314.7174685, "n": 8793, "h": "c14adba938f7da19", "c": ".\n*/\n\n// Set PHP configuration\nini_set('max_execution_time', 300);\nini_set('memory_limit', '256M');\nset_time_limit(300);\n\nuse Gibbon\\Contracts\\Database\\Connection;\nuse Gibbon\\Domain\\System\\SettingGateway;\nuse Gibbon\\Domain\\System\\ModuleGateway;\nuse Gibbon\\Module\\ChatBot\\Domain\\ChatGateway;\nuse Gibbon\\Module\\ChatBot\\DeepSeekAPI;\n\n// Include Gibbon core files\nrequire_once '../../../gibbon.php';\nrequire_once __DIR__ . '/../moduleFunctions.php';\nrequire_once __DIR__ . '/../src/Domain/ChatGateway.php';\nrequire_once __DIR__ . '/../src/DeepSeekAPI.php';\n\n// Set headers and enable error logging\nheader('Content-Type: application/json');\nerror_reporting(E_ALL);\nini_set('display_errors', 0);\nini_set('log_errors', 1);\n//ini_set('error_log', '/Applications/MAMP/logs/php_error.log');\n\ntry {\n // Check if user is logged in\n if (!$session->has('username')) {\n error_log(\"ChatBot: User not authenticated\");\n http_response_code(401);\n echo json_encode([\n 'error' => 'Please log in to use the AI Teaching Assistant.',\n 'timestamp' => date('Y-m-d H:i:s')\n ]);\n exit;\n }\n\n // Get the DeepSeek API key from settings\n $settingGateway = $container->get(SettingGateway::class);\n $apiKey = $settingGateway->getSettingByScope('ChatBot', 'deepseek_api_key');\n \n if (empty($apiKey)) {\n error_log(\"ChatBot: DeepSeek API key not configured\");\n http_response_code(500);\n echo json_encode([\n 'error' => 'The AI Teaching Assistant is not properly configured. Please contact your administrator.',\n 'timestamp' => date('Y-m-d H:i:s')\n ]);\n exit;\n }\n\n // Validate the incoming request\n $input = json_decode(file_get_contents('php://input'), true);\n if (json_last_error() !== JSON_ERROR_NONE) {\n error_log(\"ChatBot: Invalid JSON input - \" . json_last_error_msg());\n http_response_code(400);\n echo json_encode([\n 'error' => 'Invalid request format. Please try again.',\n 'timestamp' => date('Y-m-d H:i:s')\n ]);\n exit;\n }\n\n if (empty($input['message'])) {\n error_log(\"ChatBot: Empty message received\");\n http_response_code(400);\n echo json_encode([\n 'error' => 'Please enter a message.',\n 'timestamp' => date('Y-m-d H:i:s')\n ]);\n exit;\n }\n\n try {\n // Initialize the DeepSeek API\n $api = new DeepSeekAPI($apiKey);\n error_log(\"ChatBot: Processing request for user \" . $session->get('username'));\n\n // Get the response\n $response = $api->getResponse($input['message']);\n \n // Validate response format\n if (empty($response['answer'])) {\n error_log(\"ChatBot: Empty response from API\");\n throw new Exception(\"Invalid response format from AI service\");\n }\n\n // Log success and response content\n $answer = $response['answer'];\n $logContent = is_array($answer) ? json_encode($answer) : $answer;\n error_log(\"ChatBot: Successfully processed request. Response: \" . substr($logContent, 0, 100) . \"...\");\n\n // Return the response\n $jsonResponse = json_encode($response);\n if ($jsonResponse === false) {\n error_log(\"ChatBot: Failed to encode API response to JSON. Error: \" . json_last_error_msg());\n throw new Exception(\"Failed to encode response from AI service.\");\n }\n echo $jsonResponse;\n\n } catch (Exception $e) {\n error_log(\"ChatBot: Error processing request - \" . $e->getMessage());\n \n // Handle specific error cases\n if (strpos($e->getMessage(), 'Authentication Fails') !== false) {\n http_response_code(401);\n echo json_encode([\n 'error' => 'The AI Teaching Assistant is not properly configured. Please contact your administrator.',\n 'timestamp' => date('Y-m-d H:i:s')\n ]);\n } else if (strpos($e->getMessage(), 'timeout') !== false) {\n http_response_code(504);\n echo json_encode([\n 'error' => 'The AI Teaching Assistant is taking too long to respond. Please try again.',\n 'timestamp' => date('Y-m-d H:i:s')\n ]);\n } else {\n error_log(\"ChatBot Error Stack Trace: \" . $e->getTraceAsString());\n http_response_code(500);\n echo json_encode([\n 'error' => 'An unexpected error occurred: ' . htmlspecialchars($e->getMessage()),\n 'timestamp' => date('Y-m-d H:i:s')\n ]);\n }\n }\n\n} catch (PDOException $e) {\n // Database errors\n error_log('ChatBot Database Error: ' . $e->getMessage());\n error_log('ChatBot Database Error Stack Trace: ' . $e->getTraceAsString());\n http_response_code(500);\n echo json_encode([\n 'error' => 'A database error occurred. Please try again later.',\n 'details' => $e->getMessage()\n ]);\n \n} catch (Exception $e) {\n // General errors\n error_log('ChatBot Error: ' . $e->getMessage());\n error_log('ChatBot Error Stack Trace: ' . $e->getTraceAsString());\n http_response_code(500);\n echo json_encode([\n 'error' => $e->getMessage(),\n 'timestamp' => date('Y-m-d H:i:s')\n ]);\n}\n\nclass InternalAssessmentTrainer {\n private $pdo;\n \n public function __construct($pdo) {\n $this->pdo = $pdo;\n }\n \n public function getTeacherFeedback() {\n $sql = \"SELECT \n ia.gibbonInternalAssessmentID,\n ia.name as assessment_name,\n iac.comment as teacher_feedback,\n iac.attainmentValue,\n iac.effortValue,\n c.name as course_name,\n c.nameShort as course_code,\n student.preferredName as student_name,\n student.gibbonPersonID\n FROM gibbonInternalAssessmentColumn ia \n JOIN gibbonInternalAssessmentEntry iac ON iac.gibbonInternalAssessmentColumnID=ia.gibbonInternalAssessmentColumnID\n JOIN gibbonCourse c ON c.gibbonCourseID=ia.gibbonCourseID\n JOIN gibbonPerson student ON student.gibbonPersonID=iac.gibbonPersonID\n WHERE iac.comment IS NOT NULL AND iac.comment != ''\n ORDER BY ia.gibbonInternalAssessmentID DESC\";\n \n try {\n $result = $this->pdo->query($sql);\n return $result->fetchAll(PDO::FETCH_ASSOC);\n } catch (Exception $e) {\n error_log('Error fetching Internal Assessment feedback: ' . $e->getMessage());\n return [];\n }\n }\n \n public function formatTrainingData($feedbackData) {\n $trainingData = [];\n foreach ($feedbackData as $entry) {\n $trainingData[] = [\n 'context' => [\n 'course' => $entry['course_name'],\n 'assessment' => $entry['assessment_name'],\n 'attainment' => $entry['attainmentValue'],\n 'effort' => $entry['effortValue']\n ],\n 'feedback' => $entry['teacher_feedback'],\n 'metadata' => [\n 'course_code' => $entry['course_code'],\n 'student_id' => $entry['gibbonPersonID']\n ]\n ];\n }\n return $trainingData;\n }\n \n public function trainChatbot($trainingData) {\n // Format data for DeepSeek API\n $systemPrompt = \"You are an educational AI assistant trained on teacher feedback. Your role is to:\n1. Analyze patterns in student performance\n2. Identify common areas for improvement\n3. Suggest specific strategies for enhancement\n4. Provide constructive, encouraging guidance\n5. Reference real examples from teacher feedback\";\n \n $messages = [\n [\n 'role' => 'system',\n 'content' => $systemPrompt\n ],\n [\n 'role' => 'user',\n 'content' => 'Train using this feedback data: ' . json_encode($trainingData)\n ]\n ];\n \n return $messages;\n }\n} "}}, "modules/ChatBot/api_test.php": {"5eacf702b1d14d58114e1990619ea4913b19ff59": {"t": 1785745781.4736953, "n": 10505, "h": "599d29ab3871d6ad", "c": "get('db');\n\necho \"DeepSeek API Test\";\necho \"\";\necho \"\";\necho \"

DeepSeek API Connection Test

\";\n\n// PART 1: Database Connection and API Key Retrieval\necho \"
\";\necho \"

1. Database Connection

\";\n\ntry {\n // Get the API key from the database using Gibbon's connection\n $query = $connection2->prepare(\"SELECT value FROM gibbonSetting WHERE scope='ChatBot' AND name='deepseek_api_key'\");\n $query->execute();\n $result = $query->fetch(PDO::FETCH_ASSOC);\n $apiKey = $result ? $result['value'] : '';\n \n if (empty($apiKey)) {\n echo \"

❌ No API key found in the database.

\";\n echo \"

Please configure the DeepSeek API key in the ChatBot settings.

\";\n } else {\n $maskedKey = substr($apiKey, 0, 4) . str_repeat('*', strlen($apiKey) - 8) . substr($apiKey, -4);\n echo \"

✅ API key found in database: $maskedKey

\";\n \n // Get model name\n $modelQuery = $connection2->prepare(\"SELECT value FROM gibbonSetting WHERE scope='ChatBot' AND name='model_name'\");\n $modelQuery->execute();\n $modelResult = $modelQuery->fetch(PDO::FETCH_ASSOC);\n $model = $modelResult ? $modelResult['value'] : 'deepseek-chat';\n \n // Get max tokens\n $tokensQuery = $connection2->prepare(\"SELECT value FROM gibbonSetting WHERE scope='ChatBot' AND name='max_tokens'\");\n $tokensQuery->execute();\n $tokensResult = $tokensQuery->fetch(PDO::FETCH_ASSOC);\n $maxTokens = $tokensResult ? $tokensResult['value'] : '2000';\n \n echo \"

Model: $model

\";\n echo \"

Max Tokens: $maxTokens

\";\n }\n} catch (PDOException $e) {\n echo \"

❌ Database Error: \" . $e->getMessage() . \"

\";\n $apiKey = '';\n}\necho \"
\";\n\n// PART 2: API Connection Test\nif (!empty($apiKey)) {\n echo \"
\";\n echo \"

2. DeepSeek API Connection Test

\";\n echo \"

Testing connection to DeepSeek API...

\";\n \n // Create a basic curl request to test the API key\n $ch = curl_init('https://api.deepseek.com/v1/chat/completions');\n curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\n curl_setopt($ch, CURLOPT_POST, true);\n curl_setopt($ch, CURLOPT_HTTPHEADER, [\n 'Content-Type: application/json',\n 'Authorization: Bearer ' . $apiKey\n ]);\n \n $data = [\n 'model' => $model,\n 'messages' => [\n ['role' => 'system', 'content' => 'You are a helpful assistant.'],\n ['role' => 'user', 'content' => 'Say hello!']\n ],\n 'max_tokens' => intval($maxTokens)\n ];\n \n curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));\n curl_setopt($ch, CURLOPT_TIMEOUT, 30);\n \n $response = curl_exec($ch);\n $info = curl_getinfo($ch);\n $error = curl_error($ch);\n curl_close($ch);\n \n // Display cURL info\n echo \"

cURL Info:

\";\n echo \"
\";\n    echo \"HTTP Status: \" . $info['http_code'] . \"\\n\";\n    echo \"Total time: \" . $info['total_time'] . \" seconds\\n\";\n    echo \"Connect time: \" . $info['connect_time'] . \" seconds\\n\";\n    echo \"Size download: \" . $info['size_download'] . \" bytes\\n\";\n    echo \"
\";\n \n if ($error) {\n echo \"

❌ API Connection Error: \" . htmlspecialchars($error) . \"

\";\n } elseif ($info['http_code'] >= 400) {\n echo \"

❌ API Error: HTTP Status \" . $info['http_code'] . \"

\";\n $responseData = json_decode($response, true);\n if ($responseData && isset($responseData['error'])) {\n echo \"

Error Message: \" . htmlspecialchars($responseData['error']['message']) . \"

\";\n if (isset($responseData['error']['type'])) {\n echo \"

Error Type: \" . htmlspecialchars($responseData['error']['type']) . \"

\";\n }\n }\n echo \"

Raw Response:

\";\n echo \"
\" . htmlspecialchars($response) . \"
\";\n } else {\n echo \"

✅ API Connection Successful! HTTP Status \" . $info['http_code'] . \"

\";\n $responseData = json_decode($response, true);\n if ($responseData) {\n echo \"

Response received from DeepSeek API!

\";\n if (isset($responseData['choices'][0]['message']['content'])) {\n $aiResponse = $responseData['choices'][0]['message']['content'];\n echo \"

AI Response:

\";\n echo \"
\" . htmlspecialchars($aiResponse) . \"
\";\n }\n if (isset($responseData['usage'])) {\n echo \"

Token Usage:

\";\n echo \"
\";\n                echo \"Prompt tokens: \" . $responseData['usage']['prompt_tokens'] . \"\\n\";\n                echo \"Completion tokens: \" . $responseData['usage']['completion_tokens'] . \"\\n\";\n                echo \"Total tokens: \" . $responseData['usage']['total_tokens'] . \"\\n\";\n                echo \"
\";\n }\n }\n }\n echo \"
\";\n \n // PART 3: JavaScript Integration Test\n echo \"
\";\n echo \"

3. JavaScript Integration Test

\";\n echo \"

This test will attempt to use the same API to send a message using JavaScript (similar to how the chatbot works).

\";\n echo \"
\";\n echo \"
\";\n echo \"\";\n echo \"
\";\n echo \"
\";\n \n echo \"\";\n echo \"
\";\n}\n\n// PART 4: Troubleshooting and Recommendations\necho \"
\";\necho \"

4. Troubleshooting

\";\n\necho \"

Common Issues:

\";\necho \"\";\n\necho \"

Next Steps:

\";\necho \"\";\necho \"
\";\n\necho \"\";\n?> "}, "030e63497fd5a0ea218107feedd91d649324421f": {"t": 1785756300.619957, "n": 10505, "h": "599d29ab3871d6ad", "c": "get('db');\n\necho \"DeepSeek API Test\";\necho \"\";\necho \"\";\necho \"

DeepSeek API Connection Test

\";\n\n// PART 1: Database Connection and API Key Retrieval\necho \"
\";\necho \"

1. Database Connection

\";\n\ntry {\n // Get the API key from the database using Gibbon's connection\n $query = $connection2->prepare(\"SELECT value FROM gibbonSetting WHERE scope='ChatBot' AND name='deepseek_api_key'\");\n $query->execute();\n $result = $query->fetch(PDO::FETCH_ASSOC);\n $apiKey = $result ? $result['value'] : '';\n \n if (empty($apiKey)) {\n echo \"

❌ No API key found in the database.

\";\n echo \"

Please configure the DeepSeek API key in the ChatBot settings.

\";\n } else {\n $maskedKey = substr($apiKey, 0, 4) . str_repeat('*', strlen($apiKey) - 8) . substr($apiKey, -4);\n echo \"

✅ API key found in database: $maskedKey

\";\n \n // Get model name\n $modelQuery = $connection2->prepare(\"SELECT value FROM gibbonSetting WHERE scope='ChatBot' AND name='model_name'\");\n $modelQuery->execute();\n $modelResult = $modelQuery->fetch(PDO::FETCH_ASSOC);\n $model = $modelResult ? $modelResult['value'] : 'deepseek-chat';\n \n // Get max tokens\n $tokensQuery = $connection2->prepare(\"SELECT value FROM gibbonSetting WHERE scope='ChatBot' AND name='max_tokens'\");\n $tokensQuery->execute();\n $tokensResult = $tokensQuery->fetch(PDO::FETCH_ASSOC);\n $maxTokens = $tokensResult ? $tokensResult['value'] : '2000';\n \n echo \"

Model: $model

\";\n echo \"

Max Tokens: $maxTokens

\";\n }\n} catch (PDOException $e) {\n echo \"

❌ Database Error: \" . $e->getMessage() . \"

\";\n $apiKey = '';\n}\necho \"
\";\n\n// PART 2: API Connection Test\nif (!empty($apiKey)) {\n echo \"
\";\n echo \"

2. DeepSeek API Connection Test

\";\n echo \"

Testing connection to DeepSeek API...

\";\n \n // Create a basic curl request to test the API key\n $ch = curl_init('https://api.deepseek.com/v1/chat/completions');\n curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\n curl_setopt($ch, CURLOPT_POST, true);\n curl_setopt($ch, CURLOPT_HTTPHEADER, [\n 'Content-Type: application/json',\n 'Authorization: Bearer ' . $apiKey\n ]);\n \n $data = [\n 'model' => $model,\n 'messages' => [\n ['role' => 'system', 'content' => 'You are a helpful assistant.'],\n ['role' => 'user', 'content' => 'Say hello!']\n ],\n 'max_tokens' => intval($maxTokens)\n ];\n \n curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));\n curl_setopt($ch, CURLOPT_TIMEOUT, 30);\n \n $response = curl_exec($ch);\n $info = curl_getinfo($ch);\n $error = curl_error($ch);\n curl_close($ch);\n \n // Display cURL info\n echo \"

cURL Info:

\";\n echo \"
\";\n    echo \"HTTP Status: \" . $info['http_code'] . \"\\n\";\n    echo \"Total time: \" . $info['total_time'] . \" seconds\\n\";\n    echo \"Connect time: \" . $info['connect_time'] . \" seconds\\n\";\n    echo \"Size download: \" . $info['size_download'] . \" bytes\\n\";\n    echo \"
\";\n \n if ($error) {\n echo \"

❌ API Connection Error: \" . htmlspecialchars($error) . \"

\";\n } elseif ($info['http_code'] >= 400) {\n echo \"

❌ API Error: HTTP Status \" . $info['http_code'] . \"

\";\n $responseData = json_decode($response, true);\n if ($responseData && isset($responseData['error'])) {\n echo \"

Error Message: \" . htmlspecialchars($responseData['error']['message']) . \"

\";\n if (isset($responseData['error']['type'])) {\n echo \"

Error Type: \" . htmlspecialchars($responseData['error']['type']) . \"

\";\n }\n }\n echo \"

Raw Response:

\";\n echo \"
\" . htmlspecialchars($response) . \"
\";\n } else {\n echo \"

✅ API Connection Successful! HTTP Status \" . $info['http_code'] . \"

\";\n $responseData = json_decode($response, true);\n if ($responseData) {\n echo \"

Response received from DeepSeek API!

\";\n if (isset($responseData['choices'][0]['message']['content'])) {\n $aiResponse = $responseData['choices'][0]['message']['content'];\n echo \"

AI Response:

\";\n echo \"
\" . htmlspecialchars($aiResponse) . \"
\";\n }\n if (isset($responseData['usage'])) {\n echo \"

Token Usage:

\";\n echo \"
\";\n                echo \"Prompt tokens: \" . $responseData['usage']['prompt_tokens'] . \"\\n\";\n                echo \"Completion tokens: \" . $responseData['usage']['completion_tokens'] . \"\\n\";\n                echo \"Total tokens: \" . $responseData['usage']['total_tokens'] . \"\\n\";\n                echo \"
\";\n }\n }\n }\n echo \"
\";\n \n // PART 3: JavaScript Integration Test\n echo \"
\";\n echo \"

3. JavaScript Integration Test

\";\n echo \"

This test will attempt to use the same API to send a message using JavaScript (similar to how the chatbot works).

\";\n echo \"
\";\n echo \"
\";\n echo \"\";\n echo \"
\";\n echo \"
\";\n \n echo \"\";\n echo \"
\";\n}\n\n// PART 4: Troubleshooting and Recommendations\necho \"
\";\necho \"

4. Troubleshooting

\";\n\necho \"

Common Issues:

\";\necho \"\";\n\necho \"

Next Steps:

\";\necho \"\";\necho \"
\";\n\necho \"\";\n?> "}, "ea2bf72c5d38ab35b13101b6131cf74ba37f78a3": {"t": 1786068314.3560824, "n": 10505, "h": "599d29ab3871d6ad", "c": "get('db');\n\necho \"DeepSeek API Test\";\necho \"\";\necho \"\";\necho \"

DeepSeek API Connection Test

\";\n\n// PART 1: Database Connection and API Key Retrieval\necho \"
\";\necho \"

1. Database Connection

\";\n\ntry {\n // Get the API key from the database using Gibbon's connection\n $query = $connection2->prepare(\"SELECT value FROM gibbonSetting WHERE scope='ChatBot' AND name='deepseek_api_key'\");\n $query->execute();\n $result = $query->fetch(PDO::FETCH_ASSOC);\n $apiKey = $result ? $result['value'] : '';\n \n if (empty($apiKey)) {\n echo \"

❌ No API key found in the database.

\";\n echo \"

Please configure the DeepSeek API key in the ChatBot settings.

\";\n } else {\n $maskedKey = substr($apiKey, 0, 4) . str_repeat('*', strlen($apiKey) - 8) . substr($apiKey, -4);\n echo \"

✅ API key found in database: $maskedKey

\";\n \n // Get model name\n $modelQuery = $connection2->prepare(\"SELECT value FROM gibbonSetting WHERE scope='ChatBot' AND name='model_name'\");\n $modelQuery->execute();\n $modelResult = $modelQuery->fetch(PDO::FETCH_ASSOC);\n $model = $modelResult ? $modelResult['value'] : 'deepseek-chat';\n \n // Get max tokens\n $tokensQuery = $connection2->prepare(\"SELECT value FROM gibbonSetting WHERE scope='ChatBot' AND name='max_tokens'\");\n $tokensQuery->execute();\n $tokensResult = $tokensQuery->fetch(PDO::FETCH_ASSOC);\n $maxTokens = $tokensResult ? $tokensResult['value'] : '2000';\n \n echo \"

Model: $model

\";\n echo \"

Max Tokens: $maxTokens

\";\n }\n} catch (PDOException $e) {\n echo \"

❌ Database Error: \" . $e->getMessage() . \"

\";\n $apiKey = '';\n}\necho \"
\";\n\n// PART 2: API Connection Test\nif (!empty($apiKey)) {\n echo \"
\";\n echo \"

2. DeepSeek API Connection Test

\";\n echo \"

Testing connection to DeepSeek API...

\";\n \n // Create a basic curl request to test the API key\n $ch = curl_init('https://api.deepseek.com/v1/chat/completions');\n curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\n curl_setopt($ch, CURLOPT_POST, true);\n curl_setopt($ch, CURLOPT_HTTPHEADER, [\n 'Content-Type: application/json',\n 'Authorization: Bearer ' . $apiKey\n ]);\n \n $data = [\n 'model' => $model,\n 'messages' => [\n ['role' => 'system', 'content' => 'You are a helpful assistant.'],\n ['role' => 'user', 'content' => 'Say hello!']\n ],\n 'max_tokens' => intval($maxTokens)\n ];\n \n curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));\n curl_setopt($ch, CURLOPT_TIMEOUT, 30);\n \n $response = curl_exec($ch);\n $info = curl_getinfo($ch);\n $error = curl_error($ch);\n curl_close($ch);\n \n // Display cURL info\n echo \"

cURL Info:

\";\n echo \"
\";\n    echo \"HTTP Status: \" . $info['http_code'] . \"\\n\";\n    echo \"Total time: \" . $info['total_time'] . \" seconds\\n\";\n    echo \"Connect time: \" . $info['connect_time'] . \" seconds\\n\";\n    echo \"Size download: \" . $info['size_download'] . \" bytes\\n\";\n    echo \"
\";\n \n if ($error) {\n echo \"

❌ API Connection Error: \" . htmlspecialchars($error) . \"

\";\n } elseif ($info['http_code'] >= 400) {\n echo \"

❌ API Error: HTTP Status \" . $info['http_code'] . \"

\";\n $responseData = json_decode($response, true);\n if ($responseData && isset($responseData['error'])) {\n echo \"

Error Message: \" . htmlspecialchars($responseData['error']['message']) . \"

\";\n if (isset($responseData['error']['type'])) {\n echo \"

Error Type: \" . htmlspecialchars($responseData['error']['type']) . \"

\";\n }\n }\n echo \"

Raw Response:

\";\n echo \"
\" . htmlspecialchars($response) . \"
\";\n } else {\n echo \"

✅ API Connection Successful! HTTP Status \" . $info['http_code'] . \"

\";\n $responseData = json_decode($response, true);\n if ($responseData) {\n echo \"

Response received from DeepSeek API!

\";\n if (isset($responseData['choices'][0]['message']['content'])) {\n $aiResponse = $responseData['choices'][0]['message']['content'];\n echo \"

AI Response:

\";\n echo \"
\" . htmlspecialchars($aiResponse) . \"
\";\n }\n if (isset($responseData['usage'])) {\n echo \"

Token Usage:

\";\n echo \"
\";\n                echo \"Prompt tokens: \" . $responseData['usage']['prompt_tokens'] . \"\\n\";\n                echo \"Completion tokens: \" . $responseData['usage']['completion_tokens'] . \"\\n\";\n                echo \"Total tokens: \" . $responseData['usage']['total_tokens'] . \"\\n\";\n                echo \"
\";\n }\n }\n }\n echo \"
\";\n \n // PART 3: JavaScript Integration Test\n echo \"
\";\n echo \"

3. JavaScript Integration Test

\";\n echo \"

This test will attempt to use the same API to send a message using JavaScript (similar to how the chatbot works).

\";\n echo \"
\";\n echo \"
\";\n echo \"\";\n echo \"
\";\n echo \"
\";\n \n echo \"\";\n echo \"
\";\n}\n\n// PART 4: Troubleshooting and Recommendations\necho \"
\";\necho \"

4. Troubleshooting

\";\n\necho \"

Common Issues:

\";\necho \"\";\n\necho \"

Next Steps:

\";\necho \"\";\necho \"
\";\n\necho \"\";\n?> "}}, "modules/aiTeacher/assessment_analysis.php": {"5eacf702b1d14d58114e1990619ea4913b19ff59": {"t": 1785745784.1736329, "n": 10762, "h": "8bca3e2420c62479", "c": ".\n*/\n\n// Gibbon includes\nrequire_once __DIR__ . '/../../gibbon.php';\nrequire_once __DIR__ . '/../../functions.php';\n\n// Initialize PDO connection\nglobal $pdo;\nif (!isset($pdo)) {\n $pdo = $connection2 ?? new PDO($dsn, $username, $password);\n}\n\n// Module includes\nrequire_once __DIR__ . '/moduleFunctions.php';\nrequire_once __DIR__ . '/src/DeepSeekAPI.php';\n\n$page->breadcrumbs->add(__('AI Teacher Assistance'), 'index.php');\n$page->breadcrumbs->add(__('Assessment Analysis'));\n\n// Check if user has access\nif (isActionAccessible($guid, $connection2, '/modules/aiTeacher/assessment_analysis.php') == false) {\n // Access denied\n $page->addError(__('You do not have access to this action.'));\n} else {\n // Get module settings\n $settings = getAITeacherSettings($pdo);\n $aiEnabled = !empty($settings['deepseek_api_key']);\n $threshold = isset($_GET['threshold']) ? intval($_GET['threshold']) : 60;\n $selectedCourse = $_GET['course'] ?? '';\n $selectedAssessmentName = $_GET['assessment_name'] ?? ''; // New: Get selected assessment name\n\n // Get list of courses\n $courses = $pdo->executeQuery([], \"SELECT gibbonCourseID, name FROM gibbonCourse ORDER BY name\");\n\n // New: Get list of distinct assessment names\n $assessmentNamesResult = $pdo->executeQuery([], \"SELECT DISTINCT name FROM gibbonInternalAssessmentColumn WHERE name IS NOT NULL AND name <> '' ORDER BY name ASC\");\n $assessmentNames = $assessmentNamesResult->fetchAll(\\PDO::FETCH_COLUMN);\n\n\n // Add some CSS for better visibility\n echo <<\n .filter-form-container {\n margin-bottom: 20px;\n display: flex;\n align-items: center;\n gap: 15px; /* Adds space between form elements */\n }\n .filter-form-container label {\n display: flex;\n align-items: center;\n gap: 5px; /* Space between label text and input */\n }\n .filter-form-container input[type=\"number\"] {\n width: 80px; /* Increased width for threshold */\n padding: 5px;\n border: 1px solid #ccc;\n border-radius: 4px;\n }\n .filter-form-container .button,\n .filter-form-container input[type=\"submit\"] { /* Target Gibbon's default button class and the submit input */\n padding: 6px 12px;\n background-color: #007bff; /* A visible background color */\n color: white;\n border: 1px solid #007bff;\n border-radius: 4px;\n cursor: pointer;\n text-decoration: none; /* If it's an styled as a button */\n display: inline-block; /* Ensure proper rendering */\n }\n .filter-form-container .button:hover,\n .filter-form-container input[type=\"submit\"]:hover {\n background-color: #0056b3;\n border-color: #0056b3;\n }\n\n /* Style for the \"Get AI Recommendation\" button in the table */\n .get-ai-recommendation {\n padding: 6px 12px;\n background-color: #28a745; /* Green background, or choose another distinct color */\n color: white;\n border: 1px solid #28a745;\n border-radius: 4px;\n cursor: pointer;\n text-decoration: none;\n display: inline-block;\n font-size: 0.9em; /* Match other button font size if desired */\n }\n .get-ai-recommendation:hover {\n background-color: #218838; /* Darker green on hover */\n border-color: #1e7e34;\n }\n\nCSS;\n\n // Filter form\n echo '
session->get('absoluteURL') . '/index.php\" class=\"filter-form-container\">'; // Added a class for styling\n echo '';\n echo ''; // Removed trailing space\n\n // New: Assessment Name Filter Dropdown\n echo '';\n\n echo ''; // Removed trailing space\n echo '';\n echo '
';\n\n if ($selectedCourse) {\n // Query students in the selected course with their latest assessment\n $sql = \"\n SELECT\n s.gibbonPersonID,\n s.preferredName,\n s.surname,\n c.gibbonCourseID,\n c.name AS courseName,\n iac.name AS assessmentColumnName, -- Added for clarity, can be used if needed\n iae.attainmentValue\n FROM\n gibbonInternalAssessmentEntry iae\n JOIN gibbonInternalAssessmentColumn iac ON iae.gibbonInternalAssessmentColumnID = iac.gibbonInternalAssessmentColumnID\n JOIN gibbonCourseClass ccc ON iac.gibbonCourseClassID = ccc.gibbonCourseClassID\n JOIN gibbonCourse c ON ccc.gibbonCourseID = c.gibbonCourseID\n JOIN gibbonPerson s ON iae.gibbonPersonIDStudent = s.gibbonPersonID\n WHERE\n c.gibbonCourseID = :courseID\";\n\n $params = ['courseID' => $selectedCourse];\n\n if (!empty($selectedAssessmentName)) {\n $sql .= \" AND iac.name = :assessmentName\";\n $params['assessmentName'] = $selectedAssessmentName;\n }\n\n $sql .= \" ORDER BY s.surname, s.preferredName, iac.name\"; // Added iac.name to ordering for consistency if multiple assessments per student\n \n $result = $pdo->executeQuery($params, $sql);\n $students = $result->fetchAll();\n \n // Filter only failing students\n $failingStudents = array_filter($students, function($student) use ($threshold) {\n return floatval($student['attainmentValue']) < $threshold;\n });\n \n if (count($failingStudents) > 0) {\n echo '';\n echo '';\n echo '';\n echo '';\n echo '';\n echo '';\n echo '';\n \n foreach ($failingStudents as $student) {\n $studentId = htmlspecialchars($student['gibbonPersonID']);\n $studentName = htmlspecialchars($student['preferredName'] . ' ' . $student['surname']);\n $courseName = htmlspecialchars($student['courseName']);\n $score = htmlspecialchars($student['attainmentValue']);\n echo '';\n echo '';\n echo '';\n echo '';\n echo '';\n echo '';\n }\n echo '
' . __('Student') . '' . __('Course') . '' . __('Score') . '' . __('AI Recommendation') . '
' . $studentName . '' . $courseName . '' . $score . '';\n echo '';\n echo '
';\n echo '
';\n \n // Define the URL for JavaScript\n $getRecommendationURL = $gibbon->session->get('absoluteURL').'/modules/aiTeacher/get_ai_recommendation.php';\n ?>\n \n ' . __('No failing students found for this course.') . '

';\n }\n } else {\n echo '

' . __('Please select a course to view students.') . '

';\n }\n} // Closing brace for the isActionAccessible check"}, "030e63497fd5a0ea218107feedd91d649324421f": {"t": 1785756301.1779785, "n": 10762, "h": "8bca3e2420c62479", "c": ".\n*/\n\n// Gibbon includes\nrequire_once __DIR__ . '/../../gibbon.php';\nrequire_once __DIR__ . '/../../functions.php';\n\n// Initialize PDO connection\nglobal $pdo;\nif (!isset($pdo)) {\n $pdo = $connection2 ?? new PDO($dsn, $username, $password);\n}\n\n// Module includes\nrequire_once __DIR__ . '/moduleFunctions.php';\nrequire_once __DIR__ . '/src/DeepSeekAPI.php';\n\n$page->breadcrumbs->add(__('AI Teacher Assistance'), 'index.php');\n$page->breadcrumbs->add(__('Assessment Analysis'));\n\n// Check if user has access\nif (isActionAccessible($guid, $connection2, '/modules/aiTeacher/assessment_analysis.php') == false) {\n // Access denied\n $page->addError(__('You do not have access to this action.'));\n} else {\n // Get module settings\n $settings = getAITeacherSettings($pdo);\n $aiEnabled = !empty($settings['deepseek_api_key']);\n $threshold = isset($_GET['threshold']) ? intval($_GET['threshold']) : 60;\n $selectedCourse = $_GET['course'] ?? '';\n $selectedAssessmentName = $_GET['assessment_name'] ?? ''; // New: Get selected assessment name\n\n // Get list of courses\n $courses = $pdo->executeQuery([], \"SELECT gibbonCourseID, name FROM gibbonCourse ORDER BY name\");\n\n // New: Get list of distinct assessment names\n $assessmentNamesResult = $pdo->executeQuery([], \"SELECT DISTINCT name FROM gibbonInternalAssessmentColumn WHERE name IS NOT NULL AND name <> '' ORDER BY name ASC\");\n $assessmentNames = $assessmentNamesResult->fetchAll(\\PDO::FETCH_COLUMN);\n\n\n // Add some CSS for better visibility\n echo <<\n .filter-form-container {\n margin-bottom: 20px;\n display: flex;\n align-items: center;\n gap: 15px; /* Adds space between form elements */\n }\n .filter-form-container label {\n display: flex;\n align-items: center;\n gap: 5px; /* Space between label text and input */\n }\n .filter-form-container input[type=\"number\"] {\n width: 80px; /* Increased width for threshold */\n padding: 5px;\n border: 1px solid #ccc;\n border-radius: 4px;\n }\n .filter-form-container .button,\n .filter-form-container input[type=\"submit\"] { /* Target Gibbon's default button class and the submit input */\n padding: 6px 12px;\n background-color: #007bff; /* A visible background color */\n color: white;\n border: 1px solid #007bff;\n border-radius: 4px;\n cursor: pointer;\n text-decoration: none; /* If it's an
styled as a button */\n display: inline-block; /* Ensure proper rendering */\n }\n .filter-form-container .button:hover,\n .filter-form-container input[type=\"submit\"]:hover {\n background-color: #0056b3;\n border-color: #0056b3;\n }\n\n /* Style for the \"Get AI Recommendation\" button in the table */\n .get-ai-recommendation {\n padding: 6px 12px;\n background-color: #28a745; /* Green background, or choose another distinct color */\n color: white;\n border: 1px solid #28a745;\n border-radius: 4px;\n cursor: pointer;\n text-decoration: none;\n display: inline-block;\n font-size: 0.9em; /* Match other button font size if desired */\n }\n .get-ai-recommendation:hover {\n background-color: #218838; /* Darker green on hover */\n border-color: #1e7e34;\n }\n\nCSS;\n\n // Filter form\n echo '
session->get('absoluteURL') . '/index.php\" class=\"filter-form-container\">'; // Added a class for styling\n echo '';\n echo ''; // Removed trailing space\n\n // New: Assessment Name Filter Dropdown\n echo '';\n\n echo ''; // Removed trailing space\n echo '';\n echo '
';\n\n if ($selectedCourse) {\n // Query students in the selected course with their latest assessment\n $sql = \"\n SELECT\n s.gibbonPersonID,\n s.preferredName,\n s.surname,\n c.gibbonCourseID,\n c.name AS courseName,\n iac.name AS assessmentColumnName, -- Added for clarity, can be used if needed\n iae.attainmentValue\n FROM\n gibbonInternalAssessmentEntry iae\n JOIN gibbonInternalAssessmentColumn iac ON iae.gibbonInternalAssessmentColumnID = iac.gibbonInternalAssessmentColumnID\n JOIN gibbonCourseClass ccc ON iac.gibbonCourseClassID = ccc.gibbonCourseClassID\n JOIN gibbonCourse c ON ccc.gibbonCourseID = c.gibbonCourseID\n JOIN gibbonPerson s ON iae.gibbonPersonIDStudent = s.gibbonPersonID\n WHERE\n c.gibbonCourseID = :courseID\";\n\n $params = ['courseID' => $selectedCourse];\n\n if (!empty($selectedAssessmentName)) {\n $sql .= \" AND iac.name = :assessmentName\";\n $params['assessmentName'] = $selectedAssessmentName;\n }\n\n $sql .= \" ORDER BY s.surname, s.preferredName, iac.name\"; // Added iac.name to ordering for consistency if multiple assessments per student\n \n $result = $pdo->executeQuery($params, $sql);\n $students = $result->fetchAll();\n \n // Filter only failing students\n $failingStudents = array_filter($students, function($student) use ($threshold) {\n return floatval($student['attainmentValue']) < $threshold;\n });\n \n if (count($failingStudents) > 0) {\n echo '';\n echo '';\n echo '';\n echo '';\n echo '';\n echo '';\n echo '';\n \n foreach ($failingStudents as $student) {\n $studentId = htmlspecialchars($student['gibbonPersonID']);\n $studentName = htmlspecialchars($student['preferredName'] . ' ' . $student['surname']);\n $courseName = htmlspecialchars($student['courseName']);\n $score = htmlspecialchars($student['attainmentValue']);\n echo '';\n echo '';\n echo '';\n echo '';\n echo '';\n echo '';\n }\n echo '
' . __('Student') . '' . __('Course') . '' . __('Score') . '' . __('AI Recommendation') . '
' . $studentName . '' . $courseName . '' . $score . '';\n echo '';\n echo '
';\n echo '
';\n \n // Define the URL for JavaScript\n $getRecommendationURL = $gibbon->session->get('absoluteURL').'/modules/aiTeacher/get_ai_recommendation.php';\n ?>\n \n ' . __('No failing students found for this course.') . '

';\n }\n } else {\n echo '

' . __('Please select a course to view students.') . '

';\n }\n} // Closing brace for the isActionAccessible check"}, "ea2bf72c5d38ab35b13101b6131cf74ba37f78a3": {"t": 1786068315.3297265, "n": 10762, "h": "8bca3e2420c62479", "c": ".\n*/\n\n// Gibbon includes\nrequire_once __DIR__ . '/../../gibbon.php';\nrequire_once __DIR__ . '/../../functions.php';\n\n// Initialize PDO connection\nglobal $pdo;\nif (!isset($pdo)) {\n $pdo = $connection2 ?? new PDO($dsn, $username, $password);\n}\n\n// Module includes\nrequire_once __DIR__ . '/moduleFunctions.php';\nrequire_once __DIR__ . '/src/DeepSeekAPI.php';\n\n$page->breadcrumbs->add(__('AI Teacher Assistance'), 'index.php');\n$page->breadcrumbs->add(__('Assessment Analysis'));\n\n// Check if user has access\nif (isActionAccessible($guid, $connection2, '/modules/aiTeacher/assessment_analysis.php') == false) {\n // Access denied\n $page->addError(__('You do not have access to this action.'));\n} else {\n // Get module settings\n $settings = getAITeacherSettings($pdo);\n $aiEnabled = !empty($settings['deepseek_api_key']);\n $threshold = isset($_GET['threshold']) ? intval($_GET['threshold']) : 60;\n $selectedCourse = $_GET['course'] ?? '';\n $selectedAssessmentName = $_GET['assessment_name'] ?? ''; // New: Get selected assessment name\n\n // Get list of courses\n $courses = $pdo->executeQuery([], \"SELECT gibbonCourseID, name FROM gibbonCourse ORDER BY name\");\n\n // New: Get list of distinct assessment names\n $assessmentNamesResult = $pdo->executeQuery([], \"SELECT DISTINCT name FROM gibbonInternalAssessmentColumn WHERE name IS NOT NULL AND name <> '' ORDER BY name ASC\");\n $assessmentNames = $assessmentNamesResult->fetchAll(\\PDO::FETCH_COLUMN);\n\n\n // Add some CSS for better visibility\n echo <<\n .filter-form-container {\n margin-bottom: 20px;\n display: flex;\n align-items: center;\n gap: 15px; /* Adds space between form elements */\n }\n .filter-form-container label {\n display: flex;\n align-items: center;\n gap: 5px; /* Space between label text and input */\n }\n .filter-form-container input[type=\"number\"] {\n width: 80px; /* Increased width for threshold */\n padding: 5px;\n border: 1px solid #ccc;\n border-radius: 4px;\n }\n .filter-form-container .button,\n .filter-form-container input[type=\"submit\"] { /* Target Gibbon's default button class and the submit input */\n padding: 6px 12px;\n background-color: #007bff; /* A visible background color */\n color: white;\n border: 1px solid #007bff;\n border-radius: 4px;\n cursor: pointer;\n text-decoration: none; /* If it's an
styled as a button */\n display: inline-block; /* Ensure proper rendering */\n }\n .filter-form-container .button:hover,\n .filter-form-container input[type=\"submit\"]:hover {\n background-color: #0056b3;\n border-color: #0056b3;\n }\n\n /* Style for the \"Get AI Recommendation\" button in the table */\n .get-ai-recommendation {\n padding: 6px 12px;\n background-color: #28a745; /* Green background, or choose another distinct color */\n color: white;\n border: 1px solid #28a745;\n border-radius: 4px;\n cursor: pointer;\n text-decoration: none;\n display: inline-block;\n font-size: 0.9em; /* Match other button font size if desired */\n }\n .get-ai-recommendation:hover {\n background-color: #218838; /* Darker green on hover */\n border-color: #1e7e34;\n }\n\nCSS;\n\n // Filter form\n echo '
session->get('absoluteURL') . '/index.php\" class=\"filter-form-container\">'; // Added a class for styling\n echo '';\n echo ''; // Removed trailing space\n\n // New: Assessment Name Filter Dropdown\n echo '';\n\n echo ''; // Removed trailing space\n echo '';\n echo '
';\n\n if ($selectedCourse) {\n // Query students in the selected course with their latest assessment\n $sql = \"\n SELECT\n s.gibbonPersonID,\n s.preferredName,\n s.surname,\n c.gibbonCourseID,\n c.name AS courseName,\n iac.name AS assessmentColumnName, -- Added for clarity, can be used if needed\n iae.attainmentValue\n FROM\n gibbonInternalAssessmentEntry iae\n JOIN gibbonInternalAssessmentColumn iac ON iae.gibbonInternalAssessmentColumnID = iac.gibbonInternalAssessmentColumnID\n JOIN gibbonCourseClass ccc ON iac.gibbonCourseClassID = ccc.gibbonCourseClassID\n JOIN gibbonCourse c ON ccc.gibbonCourseID = c.gibbonCourseID\n JOIN gibbonPerson s ON iae.gibbonPersonIDStudent = s.gibbonPersonID\n WHERE\n c.gibbonCourseID = :courseID\";\n\n $params = ['courseID' => $selectedCourse];\n\n if (!empty($selectedAssessmentName)) {\n $sql .= \" AND iac.name = :assessmentName\";\n $params['assessmentName'] = $selectedAssessmentName;\n }\n\n $sql .= \" ORDER BY s.surname, s.preferredName, iac.name\"; // Added iac.name to ordering for consistency if multiple assessments per student\n \n $result = $pdo->executeQuery($params, $sql);\n $students = $result->fetchAll();\n \n // Filter only failing students\n $failingStudents = array_filter($students, function($student) use ($threshold) {\n return floatval($student['attainmentValue']) < $threshold;\n });\n \n if (count($failingStudents) > 0) {\n echo '';\n echo '';\n echo '';\n echo '';\n echo '';\n echo '';\n echo '';\n \n foreach ($failingStudents as $student) {\n $studentId = htmlspecialchars($student['gibbonPersonID']);\n $studentName = htmlspecialchars($student['preferredName'] . ' ' . $student['surname']);\n $courseName = htmlspecialchars($student['courseName']);\n $score = htmlspecialchars($student['attainmentValue']);\n echo '';\n echo '';\n echo '';\n echo '';\n echo '';\n echo '';\n }\n echo '
' . __('Student') . '' . __('Course') . '' . __('Score') . '' . __('AI Recommendation') . '
' . $studentName . '' . $courseName . '' . $score . '';\n echo '';\n echo '
';\n echo '
';\n \n // Define the URL for JavaScript\n $getRecommendationURL = $gibbon->session->get('absoluteURL').'/modules/aiTeacher/get_ai_recommendation.php';\n ?>\n \n ' . __('No failing students found for this course.') . '

';\n }\n } else {\n echo '

' . __('Please select a course to view students.') . '

';\n }\n} // Closing brace for the isActionAccessible check"}}, "modules/ChatBot/manifest.php": {"5eacf702b1d14d58114e1990619ea4913b19ff59": {"t": 1785745791.6899595, "n": 10090, "h": "c3d9401b445c4b16", "c": ".\n*/\n\n// Basic variables\n$name = 'ChatBot';\n$description = 'AI-powered teaching assistant for teachers';\n$entryURL = 'chatbot.php';\n$type = 'Additional';\n$category = 'Learn';\n$version = '1.0.00';\n$author = 'Asley Smith';\n$url = '';\n\n// Module tables\n$moduleTables = array();\n\n$moduleTables[] = \"\nCREATE TABLE `gibbonChatBotFeedback` (\n `id` int(10) unsigned zerofill NOT NULL AUTO_INCREMENT,\n `user_message` text NOT NULL,\n `ai_response` text NOT NULL,\n `feedback_type` enum('like','dislike') NOT NULL,\n `feedback_text` text,\n `timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\";\n\n$moduleTables[] = \"\nCREATE TABLE `gibbonChatBotTraining` (\n `gibbonChatBotTrainingID` int(10) unsigned zerofill NOT NULL AUTO_INCREMENT,\n `question` text NOT NULL,\n `answer` text NOT NULL,\n `approved` tinyint(1) NOT NULL DEFAULT '0',\n `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n PRIMARY KEY (`gibbonChatBotTrainingID`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\";\n\n$moduleTables[] = \"\nCREATE TABLE `gibbonChatBotCourseMaterials` (\n `gibbonChatBotCourseMaterialsID` int(10) unsigned zerofill NOT NULL AUTO_INCREMENT,\n `title` varchar(255) NOT NULL,\n `type` varchar(50) NOT NULL,\n `description` text NULL,\n `filePath` varchar(255) NULL,\n `gibbonCourseID` int(8) unsigned zerofill NOT NULL,\n `gibbonSchoolYearID` int(3) unsigned zerofill NOT NULL,\n `dateAdded` date NOT NULL,\n `gibbonPersonIDCreator` int(10) unsigned zerofill NOT NULL,\n PRIMARY KEY (`gibbonChatBotCourseMaterialsID`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\";\n\n$moduleTables[] = \"\nCREATE TABLE `gibbonChatBotStudentProgress` (\n `gibbonChatBotStudentProgressID` int(10) unsigned zerofill NOT NULL AUTO_INCREMENT,\n `gibbonPersonID` int(10) unsigned zerofill NOT NULL,\n `gibbonCourseID` int(8) unsigned zerofill NOT NULL,\n `gibbonSchoolYearID` int(3) unsigned zerofill NOT NULL,\n `progress` decimal(5,2) NOT NULL DEFAULT '0.00',\n `lastActivity` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n PRIMARY KEY (`gibbonChatBotStudentProgressID`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\";\n\n$moduleTables[] = \"\nCREATE TABLE `gibbonChatBotStudentAnalytics` (\n `gibbonChatBotStudentAnalyticsID` int(10) unsigned zerofill NOT NULL AUTO_INCREMENT,\n `gibbonPersonID` int(10) unsigned zerofill NOT NULL,\n `gibbonCourseID` int(8) unsigned zerofill NOT NULL,\n `gibbonSchoolYearID` int(3) unsigned zerofill NOT NULL,\n `analyticsData` text NOT NULL,\n `dateGenerated` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n PRIMARY KEY (`gibbonChatBotStudentAnalyticsID`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\";\n\n$moduleTables[] = \"\nCREATE TABLE `gibbonChatBotInterventions` (\n `gibbonChatBotInterventionsID` int(10) unsigned zerofill NOT NULL AUTO_INCREMENT,\n `gibbonPersonID` int(10) unsigned zerofill NOT NULL,\n `gibbonCourseID` int(8) unsigned zerofill NOT NULL,\n `gibbonSchoolYearID` int(3) unsigned zerofill NOT NULL,\n `interventionType` varchar(50) NOT NULL,\n `description` text NOT NULL,\n `status` enum('Active','Completed','Cancelled') NOT NULL DEFAULT 'Active',\n `dateCreated` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n `dateModified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n PRIMARY KEY (`gibbonChatBotInterventionsID`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\";\n\n// Module settings\n$gibbonSetting = array();\n$gibbonSetting[] = \"INSERT INTO gibbonSetting (scope, name, nameDisplay, description, value) VALUES ('ChatBot', 'deepseek_api_key', 'DeepSeek API Key', 'API key for DeepSeek AI service', '');\";\n$gibbonSetting[] = \"INSERT INTO gibbonSetting (scope, name, nameDisplay, description, value) VALUES ('ChatBot', 'model_name', 'Model Name', 'DeepSeek model name', 'deepseek-v4-flash');\";\n$gibbonSetting[] = \"INSERT INTO gibbonSetting (scope, name, nameDisplay, description, value) VALUES ('ChatBot', 'max_tokens', 'Max Tokens', 'Maximum number of tokens to generate in responses', '4000');\";\n\n// Action rows\n$actionRows = array();\n\n$actionRows[] = [\n 'name' => 'AI Teaching Assistant',\n 'precedence' => '0',\n 'category' => 'Learning',\n 'description' => 'Access the AI teaching assistant',\n 'URLList' => 'chatbot.php',\n 'entryURL' => 'chatbot.php',\n 'defaultPermissionAdmin' => 'Y',\n 'defaultPermissionTeacher' => 'Y',\n 'defaultPermissionStudent' => 'Y',\n 'defaultPermissionParent' => 'N',\n 'defaultPermissionSupport' => 'Y',\n 'categoryPermissionStaff' => 'Y',\n 'categoryPermissionStudent' => 'Y',\n 'categoryPermissionParent' => 'N',\n 'categoryPermissionOther' => 'N'\n];\n\n$actionRows[] = [\n 'name' => 'View Student Assessments',\n 'precedence' => '1',\n 'category' => 'Learning',\n 'description' => 'View and analyze student assessment data',\n 'URLList' => 'assessment_integration.php',\n 'entryURL' => 'assessment_integration.php',\n 'defaultPermissionAdmin' => 'Y',\n 'defaultPermissionTeacher' => 'Y',\n 'defaultPermissionStudent' => 'N',\n 'defaultPermissionParent' => 'N',\n 'defaultPermissionSupport' => 'Y',\n 'categoryPermissionStaff' => 'Y',\n 'categoryPermissionStudent' => 'N',\n 'categoryPermissionParent' => 'N',\n 'categoryPermissionOther' => 'N'\n];\n\n$actionRows[] = [\n 'name' => 'Manage Training',\n 'precedence' => '2',\n 'category' => 'Admin',\n 'description' => 'Manage ChatBot training data',\n 'URLList' => 'training.php,upload.php',\n 'entryURL' => 'training.php',\n 'defaultPermissionAdmin' => 'Y',\n 'defaultPermissionTeacher' => 'Y',\n 'defaultPermissionStudent' => 'N',\n 'defaultPermissionParent' => 'N',\n 'defaultPermissionSupport' => 'Y',\n 'categoryPermissionStaff' => 'Y',\n 'categoryPermissionStudent' => 'N',\n 'categoryPermissionParent' => 'N',\n 'categoryPermissionOther' => 'N'\n];\n\n$actionRows[] = [\n 'name' => 'Manage ChatBot',\n 'precedence' => '3',\n 'category' => 'Admin',\n 'description' => 'Configure ChatBot settings',\n 'URLList' => 'settings.php',\n 'entryURL' => 'settings.php',\n 'defaultPermissionAdmin' => 'Y',\n 'defaultPermissionTeacher' => 'N',\n 'defaultPermissionStudent' => 'N',\n 'defaultPermissionParent' => 'N',\n 'defaultPermissionSupport' => 'N',\n 'categoryPermissionStaff' => 'Y',\n 'categoryPermissionStudent' => 'N',\n 'categoryPermissionParent' => 'N',\n 'categoryPermissionOther' => 'N'\n];\n\n$actionRows[] = [\n 'name' => 'Learning Management',\n 'precedence' => '4',\n 'category' => 'Learning',\n 'description' => 'Manage learning resources and content',\n 'URLList' => 'learning_management.php,learning_management_addProcess.php,learning_management_edit.php,learning_management_delete.php',\n 'entryURL' => 'learning_management.php',\n 'defaultPermissionAdmin' => 'Y',\n 'defaultPermissionTeacher' => 'Y',\n 'defaultPermissionStudent' => 'N',\n 'defaultPermissionParent' => 'N',\n 'defaultPermissionSupport' => 'Y',\n 'categoryPermissionStaff' => 'Y',\n 'categoryPermissionStudent' => 'N',\n 'categoryPermissionParent' => 'N',\n 'categoryPermissionOther' => 'N'\n];\n\n$actionRows[] = [\n 'name' => 'AI Learning & Feedback Analytics',\n 'precedence' => '5',\n 'category' => 'Admin',\n 'description' => 'View and analyze ChatBot feedback and AI learning data',\n 'URLList' => 'ai_learning.php',\n 'entryURL' => 'ai_learning.php',\n 'defaultPermissionAdmin' => 'Y',\n 'defaultPermissionTeacher' => 'Y',\n 'defaultPermissionStudent' => 'N',\n 'defaultPermissionParent' => 'N',\n 'defaultPermissionSupport' => 'Y',\n 'categoryPermissionStaff' => 'Y',\n 'categoryPermissionStudent' => 'N',\n 'categoryPermissionParent' => 'N',\n 'categoryPermissionOther' => 'N'\n];\n\n$actionRows[] = [\n 'name' => 'Feedback Analytics',\n 'precedence' => '6',\n 'category' => 'Admin',\n 'description' => 'View and analyze ChatBot feedback data',\n 'URLList' => 'feedback.php,api/feedback.php',\n 'entryURL' => 'feedback.php',\n 'defaultPermissionAdmin' => 'Y',\n 'defaultPermissionTeacher' => 'Y',\n 'defaultPermissionStudent' => 'N',\n 'defaultPermissionParent' => 'N',\n 'defaultPermissionSupport' => 'Y',\n 'categoryPermissionStaff' => 'Y',\n 'categoryPermissionStudent' => 'N',\n 'categoryPermissionParent' => 'N',\n 'categoryPermissionOther' => 'N'\n];\n\n$actionRows[] = [\n 'name' => 'Check Feedback Database',\n 'precedence' => '7',\n 'category' => 'Admin',\n 'description' => 'Check and verify ChatBot feedback database status',\n 'URLList' => 'db_check_feedback.php',\n 'entryURL' => 'db_check_feedback.php',\n 'defaultPermissionAdmin' => 'Y',\n 'defaultPermissionTeacher' => 'Y',\n 'defaultPermissionStudent' => 'N',\n 'defaultPermissionParent' => 'N',\n 'defaultPermissionSupport' => 'Y',\n 'categoryPermissionStaff' => 'Y',\n 'categoryPermissionStudent' => 'N',\n 'categoryPermissionParent' => 'N',\n 'categoryPermissionOther' => 'N'\n];\n\n// Return module configuration\nreturn [\n 'name' => $name,\n 'version' => $version,\n 'description' => $description,\n 'author' => $author,\n 'url' => $url,\n 'entryURL' => $entryURL,\n 'type' => $type,\n 'category' => $category,\n 'tables' => $moduleTables,\n 'gibbonSettings' => $gibbonSetting,\n 'actionRows' => $actionRows\n];\n"}, "228065060bc341b101409b1b5a4415a502b48a3a": {"t": 1785756293.9305758, "n": 10090, "h": "c3d9401b445c4b16", "c": ".\n*/\n\n// Basic variables\n$name = 'ChatBot';\n$description = 'AI-powered teaching assistant for teachers';\n$entryURL = 'chatbot.php';\n$type = 'Additional';\n$category = 'Learn';\n$version = '1.0.00';\n$author = 'Asley Smith';\n$url = '';\n\n// Module tables\n$moduleTables = array();\n\n$moduleTables[] = \"\nCREATE TABLE `gibbonChatBotFeedback` (\n `id` int(10) unsigned zerofill NOT NULL AUTO_INCREMENT,\n `user_message` text NOT NULL,\n `ai_response` text NOT NULL,\n `feedback_type` enum('like','dislike') NOT NULL,\n `feedback_text` text,\n `timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\";\n\n$moduleTables[] = \"\nCREATE TABLE `gibbonChatBotTraining` (\n `gibbonChatBotTrainingID` int(10) unsigned zerofill NOT NULL AUTO_INCREMENT,\n `question` text NOT NULL,\n `answer` text NOT NULL,\n `approved` tinyint(1) NOT NULL DEFAULT '0',\n `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n PRIMARY KEY (`gibbonChatBotTrainingID`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\";\n\n$moduleTables[] = \"\nCREATE TABLE `gibbonChatBotCourseMaterials` (\n `gibbonChatBotCourseMaterialsID` int(10) unsigned zerofill NOT NULL AUTO_INCREMENT,\n `title` varchar(255) NOT NULL,\n `type` varchar(50) NOT NULL,\n `description` text NULL,\n `filePath` varchar(255) NULL,\n `gibbonCourseID` int(8) unsigned zerofill NOT NULL,\n `gibbonSchoolYearID` int(3) unsigned zerofill NOT NULL,\n `dateAdded` date NOT NULL,\n `gibbonPersonIDCreator` int(10) unsigned zerofill NOT NULL,\n PRIMARY KEY (`gibbonChatBotCourseMaterialsID`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\";\n\n$moduleTables[] = \"\nCREATE TABLE `gibbonChatBotStudentProgress` (\n `gibbonChatBotStudentProgressID` int(10) unsigned zerofill NOT NULL AUTO_INCREMENT,\n `gibbonPersonID` int(10) unsigned zerofill NOT NULL,\n `gibbonCourseID` int(8) unsigned zerofill NOT NULL,\n `gibbonSchoolYearID` int(3) unsigned zerofill NOT NULL,\n `progress` decimal(5,2) NOT NULL DEFAULT '0.00',\n `lastActivity` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n PRIMARY KEY (`gibbonChatBotStudentProgressID`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\";\n\n$moduleTables[] = \"\nCREATE TABLE `gibbonChatBotStudentAnalytics` (\n `gibbonChatBotStudentAnalyticsID` int(10) unsigned zerofill NOT NULL AUTO_INCREMENT,\n `gibbonPersonID` int(10) unsigned zerofill NOT NULL,\n `gibbonCourseID` int(8) unsigned zerofill NOT NULL,\n `gibbonSchoolYearID` int(3) unsigned zerofill NOT NULL,\n `analyticsData` text NOT NULL,\n `dateGenerated` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n PRIMARY KEY (`gibbonChatBotStudentAnalyticsID`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\";\n\n$moduleTables[] = \"\nCREATE TABLE `gibbonChatBotInterventions` (\n `gibbonChatBotInterventionsID` int(10) unsigned zerofill NOT NULL AUTO_INCREMENT,\n `gibbonPersonID` int(10) unsigned zerofill NOT NULL,\n `gibbonCourseID` int(8) unsigned zerofill NOT NULL,\n `gibbonSchoolYearID` int(3) unsigned zerofill NOT NULL,\n `interventionType` varchar(50) NOT NULL,\n `description` text NOT NULL,\n `status` enum('Active','Completed','Cancelled') NOT NULL DEFAULT 'Active',\n `dateCreated` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n `dateModified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n PRIMARY KEY (`gibbonChatBotInterventionsID`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\";\n\n// Module settings\n$gibbonSetting = array();\n$gibbonSetting[] = \"INSERT INTO gibbonSetting (scope, name, nameDisplay, description, value) VALUES ('ChatBot', 'deepseek_api_key', 'DeepSeek API Key', 'API key for DeepSeek AI service', '');\";\n$gibbonSetting[] = \"INSERT INTO gibbonSetting (scope, name, nameDisplay, description, value) VALUES ('ChatBot', 'model_name', 'Model Name', 'DeepSeek model name', 'deepseek-v4-flash');\";\n$gibbonSetting[] = \"INSERT INTO gibbonSetting (scope, name, nameDisplay, description, value) VALUES ('ChatBot', 'max_tokens', 'Max Tokens', 'Maximum number of tokens to generate in responses', '4000');\";\n\n// Action rows\n$actionRows = array();\n\n$actionRows[] = [\n 'name' => 'AI Teaching Assistant',\n 'precedence' => '0',\n 'category' => 'Learning',\n 'description' => 'Access the AI teaching assistant',\n 'URLList' => 'chatbot.php',\n 'entryURL' => 'chatbot.php',\n 'defaultPermissionAdmin' => 'Y',\n 'defaultPermissionTeacher' => 'Y',\n 'defaultPermissionStudent' => 'Y',\n 'defaultPermissionParent' => 'N',\n 'defaultPermissionSupport' => 'Y',\n 'categoryPermissionStaff' => 'Y',\n 'categoryPermissionStudent' => 'Y',\n 'categoryPermissionParent' => 'N',\n 'categoryPermissionOther' => 'N'\n];\n\n$actionRows[] = [\n 'name' => 'View Student Assessments',\n 'precedence' => '1',\n 'category' => 'Learning',\n 'description' => 'View and analyze student assessment data',\n 'URLList' => 'assessment_integration.php',\n 'entryURL' => 'assessment_integration.php',\n 'defaultPermissionAdmin' => 'Y',\n 'defaultPermissionTeacher' => 'Y',\n 'defaultPermissionStudent' => 'N',\n 'defaultPermissionParent' => 'N',\n 'defaultPermissionSupport' => 'Y',\n 'categoryPermissionStaff' => 'Y',\n 'categoryPermissionStudent' => 'N',\n 'categoryPermissionParent' => 'N',\n 'categoryPermissionOther' => 'N'\n];\n\n$actionRows[] = [\n 'name' => 'Manage Training',\n 'precedence' => '2',\n 'category' => 'Admin',\n 'description' => 'Manage ChatBot training data',\n 'URLList' => 'training.php,upload.php',\n 'entryURL' => 'training.php',\n 'defaultPermissionAdmin' => 'Y',\n 'defaultPermissionTeacher' => 'Y',\n 'defaultPermissionStudent' => 'N',\n 'defaultPermissionParent' => 'N',\n 'defaultPermissionSupport' => 'Y',\n 'categoryPermissionStaff' => 'Y',\n 'categoryPermissionStudent' => 'N',\n 'categoryPermissionParent' => 'N',\n 'categoryPermissionOther' => 'N'\n];\n\n$actionRows[] = [\n 'name' => 'Manage ChatBot',\n 'precedence' => '3',\n 'category' => 'Admin',\n 'description' => 'Configure ChatBot settings',\n 'URLList' => 'settings.php',\n 'entryURL' => 'settings.php',\n 'defaultPermissionAdmin' => 'Y',\n 'defaultPermissionTeacher' => 'N',\n 'defaultPermissionStudent' => 'N',\n 'defaultPermissionParent' => 'N',\n 'defaultPermissionSupport' => 'N',\n 'categoryPermissionStaff' => 'Y',\n 'categoryPermissionStudent' => 'N',\n 'categoryPermissionParent' => 'N',\n 'categoryPermissionOther' => 'N'\n];\n\n$actionRows[] = [\n 'name' => 'Learning Management',\n 'precedence' => '4',\n 'category' => 'Learning',\n 'description' => 'Manage learning resources and content',\n 'URLList' => 'learning_management.php,learning_management_addProcess.php,learning_management_edit.php,learning_management_delete.php',\n 'entryURL' => 'learning_management.php',\n 'defaultPermissionAdmin' => 'Y',\n 'defaultPermissionTeacher' => 'Y',\n 'defaultPermissionStudent' => 'N',\n 'defaultPermissionParent' => 'N',\n 'defaultPermissionSupport' => 'Y',\n 'categoryPermissionStaff' => 'Y',\n 'categoryPermissionStudent' => 'N',\n 'categoryPermissionParent' => 'N',\n 'categoryPermissionOther' => 'N'\n];\n\n$actionRows[] = [\n 'name' => 'AI Learning & Feedback Analytics',\n 'precedence' => '5',\n 'category' => 'Admin',\n 'description' => 'View and analyze ChatBot feedback and AI learning data',\n 'URLList' => 'ai_learning.php',\n 'entryURL' => 'ai_learning.php',\n 'defaultPermissionAdmin' => 'Y',\n 'defaultPermissionTeacher' => 'Y',\n 'defaultPermissionStudent' => 'N',\n 'defaultPermissionParent' => 'N',\n 'defaultPermissionSupport' => 'Y',\n 'categoryPermissionStaff' => 'Y',\n 'categoryPermissionStudent' => 'N',\n 'categoryPermissionParent' => 'N',\n 'categoryPermissionOther' => 'N'\n];\n\n$actionRows[] = [\n 'name' => 'Feedback Analytics',\n 'precedence' => '6',\n 'category' => 'Admin',\n 'description' => 'View and analyze ChatBot feedback data',\n 'URLList' => 'feedback.php,api/feedback.php',\n 'entryURL' => 'feedback.php',\n 'defaultPermissionAdmin' => 'Y',\n 'defaultPermissionTeacher' => 'Y',\n 'defaultPermissionStudent' => 'N',\n 'defaultPermissionParent' => 'N',\n 'defaultPermissionSupport' => 'Y',\n 'categoryPermissionStaff' => 'Y',\n 'categoryPermissionStudent' => 'N',\n 'categoryPermissionParent' => 'N',\n 'categoryPermissionOther' => 'N'\n];\n\n$actionRows[] = [\n 'name' => 'Check Feedback Database',\n 'precedence' => '7',\n 'category' => 'Admin',\n 'description' => 'Check and verify ChatBot feedback database status',\n 'URLList' => 'db_check_feedback.php',\n 'entryURL' => 'db_check_feedback.php',\n 'defaultPermissionAdmin' => 'Y',\n 'defaultPermissionTeacher' => 'Y',\n 'defaultPermissionStudent' => 'N',\n 'defaultPermissionParent' => 'N',\n 'defaultPermissionSupport' => 'Y',\n 'categoryPermissionStaff' => 'Y',\n 'categoryPermissionStudent' => 'N',\n 'categoryPermissionParent' => 'N',\n 'categoryPermissionOther' => 'N'\n];\n\n// Return module configuration\nreturn [\n 'name' => $name,\n 'version' => $version,\n 'description' => $description,\n 'author' => $author,\n 'url' => $url,\n 'entryURL' => $entryURL,\n 'type' => $type,\n 'category' => $category,\n 'tables' => $moduleTables,\n 'gibbonSettings' => $gibbonSetting,\n 'actionRows' => $actionRows\n];\n"}, "030e63497fd5a0ea218107feedd91d649324421f": {"t": 1785756297.511358, "n": 10108, "h": "209bb5889c501a41", "c": ".\n*/\n\n// Basic variables\n$name = 'ChatBot';\n$description = 'AI-powered teaching assistant for teachers';\n$entryURL = 'chatbot.php';\n$type = 'Additional';\n$category = 'Learn';\n$version = '1.0.00';\n$author = 'Asley Smith';\n$url = '';\n\n// Module tables\n$moduleTables = array();\n\n$moduleTables[] = \"\nCREATE TABLE `gibbonChatBotFeedback` (\n `id` int(10) unsigned zerofill NOT NULL AUTO_INCREMENT,\n `user_message` text NOT NULL,\n `ai_response` text NOT NULL,\n `feedback_type` enum('like','dislike') NOT NULL,\n `feedback_text` text,\n `timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\";\n\n$moduleTables[] = \"\nCREATE TABLE `gibbonChatBotTraining` (\n `gibbonChatBotTrainingID` int(10) unsigned zerofill NOT NULL AUTO_INCREMENT,\n `question` text NOT NULL,\n `answer` text NOT NULL,\n `approved` tinyint(1) NOT NULL DEFAULT '0',\n `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n PRIMARY KEY (`gibbonChatBotTrainingID`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\";\n\n$moduleTables[] = \"\nCREATE TABLE `gibbonChatBotCourseMaterials` (\n `gibbonChatBotCourseMaterialsID` int(10) unsigned zerofill NOT NULL AUTO_INCREMENT,\n `title` varchar(255) NOT NULL,\n `type` varchar(50) NOT NULL,\n `description` text NULL,\n `filePath` varchar(255) NULL,\n `gibbonCourseID` int(8) unsigned zerofill NOT NULL,\n `gibbonSchoolYearID` int(3) unsigned zerofill NOT NULL,\n `dateAdded` date NOT NULL,\n `gibbonPersonIDCreator` int(10) unsigned zerofill NOT NULL,\n PRIMARY KEY (`gibbonChatBotCourseMaterialsID`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\";\n\n$moduleTables[] = \"\nCREATE TABLE `gibbonChatBotStudentProgress` (\n `gibbonChatBotStudentProgressID` int(10) unsigned zerofill NOT NULL AUTO_INCREMENT,\n `gibbonPersonID` int(10) unsigned zerofill NOT NULL,\n `gibbonCourseID` int(8) unsigned zerofill NOT NULL,\n `gibbonSchoolYearID` int(3) unsigned zerofill NOT NULL,\n `progress` decimal(5,2) NOT NULL DEFAULT '0.00',\n `lastActivity` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n PRIMARY KEY (`gibbonChatBotStudentProgressID`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\";\n\n$moduleTables[] = \"\nCREATE TABLE `gibbonChatBotStudentAnalytics` (\n `gibbonChatBotStudentAnalyticsID` int(10) unsigned zerofill NOT NULL AUTO_INCREMENT,\n `gibbonPersonID` int(10) unsigned zerofill NOT NULL,\n `gibbonCourseID` int(8) unsigned zerofill NOT NULL,\n `gibbonSchoolYearID` int(3) unsigned zerofill NOT NULL,\n `analyticsData` text NOT NULL,\n `dateGenerated` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n PRIMARY KEY (`gibbonChatBotStudentAnalyticsID`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\";\n\n$moduleTables[] = \"\nCREATE TABLE `gibbonChatBotInterventions` (\n `gibbonChatBotInterventionsID` int(10) unsigned zerofill NOT NULL AUTO_INCREMENT,\n `gibbonPersonID` int(10) unsigned zerofill NOT NULL,\n `gibbonCourseID` int(8) unsigned zerofill NOT NULL,\n `gibbonSchoolYearID` int(3) unsigned zerofill NOT NULL,\n `interventionType` varchar(50) NOT NULL,\n `description` text NOT NULL,\n `status` enum('Active','Completed','Cancelled') NOT NULL DEFAULT 'Active',\n `dateCreated` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n `dateModified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n PRIMARY KEY (`gibbonChatBotInterventionsID`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\";\n\n// Module settings\n$gibbonSetting = array();\n$gibbonSetting[] = \"INSERT INTO gibbonSetting (scope, name, nameDisplay, description, value) VALUES ('ChatBot', 'deepseek_api_key', 'DeepSeek API Key', 'API key for DeepSeek AI service', '');\";\n$gibbonSetting[] = \"INSERT INTO gibbonSetting (scope, name, nameDisplay, description, value) VALUES ('ChatBot', 'model_name', 'Model Name', 'DeepSeek model name (e.g., deepseek-chat)', 'deepseek-chat');\";\n$gibbonSetting[] = \"INSERT INTO gibbonSetting (scope, name, nameDisplay, description, value) VALUES ('ChatBot', 'max_tokens', 'Max Tokens', 'Maximum number of tokens to generate in responses', '4000');\";\n\n// Action rows\n$actionRows = array();\n\n$actionRows[] = [\n 'name' => 'AI Teaching Assistant',\n 'precedence' => '0',\n 'category' => 'Learning',\n 'description' => 'Access the AI teaching assistant',\n 'URLList' => 'chatbot.php',\n 'entryURL' => 'chatbot.php',\n 'defaultPermissionAdmin' => 'Y',\n 'defaultPermissionTeacher' => 'Y',\n 'defaultPermissionStudent' => 'Y',\n 'defaultPermissionParent' => 'N',\n 'defaultPermissionSupport' => 'Y',\n 'categoryPermissionStaff' => 'Y',\n 'categoryPermissionStudent' => 'Y',\n 'categoryPermissionParent' => 'N',\n 'categoryPermissionOther' => 'N'\n];\n\n$actionRows[] = [\n 'name' => 'View Student Assessments',\n 'precedence' => '1',\n 'category' => 'Learning',\n 'description' => 'View and analyze student assessment data',\n 'URLList' => 'assessment_integration.php',\n 'entryURL' => 'assessment_integration.php',\n 'defaultPermissionAdmin' => 'Y',\n 'defaultPermissionTeacher' => 'Y',\n 'defaultPermissionStudent' => 'N',\n 'defaultPermissionParent' => 'N',\n 'defaultPermissionSupport' => 'Y',\n 'categoryPermissionStaff' => 'Y',\n 'categoryPermissionStudent' => 'N',\n 'categoryPermissionParent' => 'N',\n 'categoryPermissionOther' => 'N'\n];\n\n$actionRows[] = [\n 'name' => 'Manage Training',\n 'precedence' => '2',\n 'category' => 'Admin',\n 'description' => 'Manage ChatBot training data',\n 'URLList' => 'training.php,upload.php',\n 'entryURL' => 'training.php',\n 'defaultPermissionAdmin' => 'Y',\n 'defaultPermissionTeacher' => 'Y',\n 'defaultPermissionStudent' => 'N',\n 'defaultPermissionParent' => 'N',\n 'defaultPermissionSupport' => 'Y',\n 'categoryPermissionStaff' => 'Y',\n 'categoryPermissionStudent' => 'N',\n 'categoryPermissionParent' => 'N',\n 'categoryPermissionOther' => 'N'\n];\n\n$actionRows[] = [\n 'name' => 'Manage ChatBot',\n 'precedence' => '3',\n 'category' => 'Admin',\n 'description' => 'Configure ChatBot settings',\n 'URLList' => 'settings.php',\n 'entryURL' => 'settings.php',\n 'defaultPermissionAdmin' => 'Y',\n 'defaultPermissionTeacher' => 'N',\n 'defaultPermissionStudent' => 'N',\n 'defaultPermissionParent' => 'N',\n 'defaultPermissionSupport' => 'N',\n 'categoryPermissionStaff' => 'Y',\n 'categoryPermissionStudent' => 'N',\n 'categoryPermissionParent' => 'N',\n 'categoryPermissionOther' => 'N'\n];\n\n$actionRows[] = [\n 'name' => 'Learning Management',\n 'precedence' => '4',\n 'category' => 'Learning',\n 'description' => 'Manage learning resources and content',\n 'URLList' => 'learning_management.php,learning_management_addProcess.php,learning_management_edit.php,learning_management_delete.php',\n 'entryURL' => 'learning_management.php',\n 'defaultPermissionAdmin' => 'Y',\n 'defaultPermissionTeacher' => 'Y',\n 'defaultPermissionStudent' => 'N',\n 'defaultPermissionParent' => 'N',\n 'defaultPermissionSupport' => 'Y',\n 'categoryPermissionStaff' => 'Y',\n 'categoryPermissionStudent' => 'N',\n 'categoryPermissionParent' => 'N',\n 'categoryPermissionOther' => 'N'\n];\n\n$actionRows[] = [\n 'name' => 'AI Learning & Feedback Analytics',\n 'precedence' => '5',\n 'category' => 'Admin',\n 'description' => 'View and analyze ChatBot feedback and AI learning data',\n 'URLList' => 'ai_learning.php',\n 'entryURL' => 'ai_learning.php',\n 'defaultPermissionAdmin' => 'Y',\n 'defaultPermissionTeacher' => 'Y',\n 'defaultPermissionStudent' => 'N',\n 'defaultPermissionParent' => 'N',\n 'defaultPermissionSupport' => 'Y',\n 'categoryPermissionStaff' => 'Y',\n 'categoryPermissionStudent' => 'N',\n 'categoryPermissionParent' => 'N',\n 'categoryPermissionOther' => 'N'\n];\n\n$actionRows[] = [\n 'name' => 'Feedback Analytics',\n 'precedence' => '6',\n 'category' => 'Admin',\n 'description' => 'View and analyze ChatBot feedback data',\n 'URLList' => 'feedback.php,api/feedback.php',\n 'entryURL' => 'feedback.php',\n 'defaultPermissionAdmin' => 'Y',\n 'defaultPermissionTeacher' => 'Y',\n 'defaultPermissionStudent' => 'N',\n 'defaultPermissionParent' => 'N',\n 'defaultPermissionSupport' => 'Y',\n 'categoryPermissionStaff' => 'Y',\n 'categoryPermissionStudent' => 'N',\n 'categoryPermissionParent' => 'N',\n 'categoryPermissionOther' => 'N'\n];\n\n$actionRows[] = [\n 'name' => 'Check Feedback Database',\n 'precedence' => '7',\n 'category' => 'Admin',\n 'description' => 'Check and verify ChatBot feedback database status',\n 'URLList' => 'db_check_feedback.php',\n 'entryURL' => 'db_check_feedback.php',\n 'defaultPermissionAdmin' => 'Y',\n 'defaultPermissionTeacher' => 'Y',\n 'defaultPermissionStudent' => 'N',\n 'defaultPermissionParent' => 'N',\n 'defaultPermissionSupport' => 'Y',\n 'categoryPermissionStaff' => 'Y',\n 'categoryPermissionStudent' => 'N',\n 'categoryPermissionParent' => 'N',\n 'categoryPermissionOther' => 'N'\n];\n\n// Return module configuration\nreturn [\n 'name' => $name,\n 'version' => $version,\n 'description' => $description,\n 'author' => $author,\n 'url' => $url,\n 'entryURL' => $entryURL,\n 'type' => $type,\n 'category' => $category,\n 'tables' => $moduleTables,\n 'gibbonSettings' => $gibbonSetting,\n 'actionRows' => $actionRows\n];\n"}, "ea2bf72c5d38ab35b13101b6131cf74ba37f78a3": {"t": 1786068315.5513947, "n": 10090, "h": "c3d9401b445c4b16", "c": ".\n*/\n\n// Basic variables\n$name = 'ChatBot';\n$description = 'AI-powered teaching assistant for teachers';\n$entryURL = 'chatbot.php';\n$type = 'Additional';\n$category = 'Learn';\n$version = '1.0.00';\n$author = 'Asley Smith';\n$url = '';\n\n// Module tables\n$moduleTables = array();\n\n$moduleTables[] = \"\nCREATE TABLE `gibbonChatBotFeedback` (\n `id` int(10) unsigned zerofill NOT NULL AUTO_INCREMENT,\n `user_message` text NOT NULL,\n `ai_response` text NOT NULL,\n `feedback_type` enum('like','dislike') NOT NULL,\n `feedback_text` text,\n `timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\";\n\n$moduleTables[] = \"\nCREATE TABLE `gibbonChatBotTraining` (\n `gibbonChatBotTrainingID` int(10) unsigned zerofill NOT NULL AUTO_INCREMENT,\n `question` text NOT NULL,\n `answer` text NOT NULL,\n `approved` tinyint(1) NOT NULL DEFAULT '0',\n `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n PRIMARY KEY (`gibbonChatBotTrainingID`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\";\n\n$moduleTables[] = \"\nCREATE TABLE `gibbonChatBotCourseMaterials` (\n `gibbonChatBotCourseMaterialsID` int(10) unsigned zerofill NOT NULL AUTO_INCREMENT,\n `title` varchar(255) NOT NULL,\n `type` varchar(50) NOT NULL,\n `description` text NULL,\n `filePath` varchar(255) NULL,\n `gibbonCourseID` int(8) unsigned zerofill NOT NULL,\n `gibbonSchoolYearID` int(3) unsigned zerofill NOT NULL,\n `dateAdded` date NOT NULL,\n `gibbonPersonIDCreator` int(10) unsigned zerofill NOT NULL,\n PRIMARY KEY (`gibbonChatBotCourseMaterialsID`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\";\n\n$moduleTables[] = \"\nCREATE TABLE `gibbonChatBotStudentProgress` (\n `gibbonChatBotStudentProgressID` int(10) unsigned zerofill NOT NULL AUTO_INCREMENT,\n `gibbonPersonID` int(10) unsigned zerofill NOT NULL,\n `gibbonCourseID` int(8) unsigned zerofill NOT NULL,\n `gibbonSchoolYearID` int(3) unsigned zerofill NOT NULL,\n `progress` decimal(5,2) NOT NULL DEFAULT '0.00',\n `lastActivity` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n PRIMARY KEY (`gibbonChatBotStudentProgressID`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\";\n\n$moduleTables[] = \"\nCREATE TABLE `gibbonChatBotStudentAnalytics` (\n `gibbonChatBotStudentAnalyticsID` int(10) unsigned zerofill NOT NULL AUTO_INCREMENT,\n `gibbonPersonID` int(10) unsigned zerofill NOT NULL,\n `gibbonCourseID` int(8) unsigned zerofill NOT NULL,\n `gibbonSchoolYearID` int(3) unsigned zerofill NOT NULL,\n `analyticsData` text NOT NULL,\n `dateGenerated` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n PRIMARY KEY (`gibbonChatBotStudentAnalyticsID`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\";\n\n$moduleTables[] = \"\nCREATE TABLE `gibbonChatBotInterventions` (\n `gibbonChatBotInterventionsID` int(10) unsigned zerofill NOT NULL AUTO_INCREMENT,\n `gibbonPersonID` int(10) unsigned zerofill NOT NULL,\n `gibbonCourseID` int(8) unsigned zerofill NOT NULL,\n `gibbonSchoolYearID` int(3) unsigned zerofill NOT NULL,\n `interventionType` varchar(50) NOT NULL,\n `description` text NOT NULL,\n `status` enum('Active','Completed','Cancelled') NOT NULL DEFAULT 'Active',\n `dateCreated` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n `dateModified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n PRIMARY KEY (`gibbonChatBotInterventionsID`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\";\n\n// Module settings\n$gibbonSetting = array();\n$gibbonSetting[] = \"INSERT INTO gibbonSetting (scope, name, nameDisplay, description, value) VALUES ('ChatBot', 'deepseek_api_key', 'DeepSeek API Key', 'API key for DeepSeek AI service', '');\";\n$gibbonSetting[] = \"INSERT INTO gibbonSetting (scope, name, nameDisplay, description, value) VALUES ('ChatBot', 'model_name', 'Model Name', 'DeepSeek model name', 'deepseek-v4-flash');\";\n$gibbonSetting[] = \"INSERT INTO gibbonSetting (scope, name, nameDisplay, description, value) VALUES ('ChatBot', 'max_tokens', 'Max Tokens', 'Maximum number of tokens to generate in responses', '4000');\";\n\n// Action rows\n$actionRows = array();\n\n$actionRows[] = [\n 'name' => 'AI Teaching Assistant',\n 'precedence' => '0',\n 'category' => 'Learning',\n 'description' => 'Access the AI teaching assistant',\n 'URLList' => 'chatbot.php',\n 'entryURL' => 'chatbot.php',\n 'defaultPermissionAdmin' => 'Y',\n 'defaultPermissionTeacher' => 'Y',\n 'defaultPermissionStudent' => 'Y',\n 'defaultPermissionParent' => 'N',\n 'defaultPermissionSupport' => 'Y',\n 'categoryPermissionStaff' => 'Y',\n 'categoryPermissionStudent' => 'Y',\n 'categoryPermissionParent' => 'N',\n 'categoryPermissionOther' => 'N'\n];\n\n$actionRows[] = [\n 'name' => 'View Student Assessments',\n 'precedence' => '1',\n 'category' => 'Learning',\n 'description' => 'View and analyze student assessment data',\n 'URLList' => 'assessment_integration.php',\n 'entryURL' => 'assessment_integration.php',\n 'defaultPermissionAdmin' => 'Y',\n 'defaultPermissionTeacher' => 'Y',\n 'defaultPermissionStudent' => 'N',\n 'defaultPermissionParent' => 'N',\n 'defaultPermissionSupport' => 'Y',\n 'categoryPermissionStaff' => 'Y',\n 'categoryPermissionStudent' => 'N',\n 'categoryPermissionParent' => 'N',\n 'categoryPermissionOther' => 'N'\n];\n\n$actionRows[] = [\n 'name' => 'Manage Training',\n 'precedence' => '2',\n 'category' => 'Admin',\n 'description' => 'Manage ChatBot training data',\n 'URLList' => 'training.php,upload.php',\n 'entryURL' => 'training.php',\n 'defaultPermissionAdmin' => 'Y',\n 'defaultPermissionTeacher' => 'Y',\n 'defaultPermissionStudent' => 'N',\n 'defaultPermissionParent' => 'N',\n 'defaultPermissionSupport' => 'Y',\n 'categoryPermissionStaff' => 'Y',\n 'categoryPermissionStudent' => 'N',\n 'categoryPermissionParent' => 'N',\n 'categoryPermissionOther' => 'N'\n];\n\n$actionRows[] = [\n 'name' => 'Manage ChatBot',\n 'precedence' => '3',\n 'category' => 'Admin',\n 'description' => 'Configure ChatBot settings',\n 'URLList' => 'settings.php',\n 'entryURL' => 'settings.php',\n 'defaultPermissionAdmin' => 'Y',\n 'defaultPermissionTeacher' => 'N',\n 'defaultPermissionStudent' => 'N',\n 'defaultPermissionParent' => 'N',\n 'defaultPermissionSupport' => 'N',\n 'categoryPermissionStaff' => 'Y',\n 'categoryPermissionStudent' => 'N',\n 'categoryPermissionParent' => 'N',\n 'categoryPermissionOther' => 'N'\n];\n\n$actionRows[] = [\n 'name' => 'Learning Management',\n 'precedence' => '4',\n 'category' => 'Learning',\n 'description' => 'Manage learning resources and content',\n 'URLList' => 'learning_management.php,learning_management_addProcess.php,learning_management_edit.php,learning_management_delete.php',\n 'entryURL' => 'learning_management.php',\n 'defaultPermissionAdmin' => 'Y',\n 'defaultPermissionTeacher' => 'Y',\n 'defaultPermissionStudent' => 'N',\n 'defaultPermissionParent' => 'N',\n 'defaultPermissionSupport' => 'Y',\n 'categoryPermissionStaff' => 'Y',\n 'categoryPermissionStudent' => 'N',\n 'categoryPermissionParent' => 'N',\n 'categoryPermissionOther' => 'N'\n];\n\n$actionRows[] = [\n 'name' => 'AI Learning & Feedback Analytics',\n 'precedence' => '5',\n 'category' => 'Admin',\n 'description' => 'View and analyze ChatBot feedback and AI learning data',\n 'URLList' => 'ai_learning.php',\n 'entryURL' => 'ai_learning.php',\n 'defaultPermissionAdmin' => 'Y',\n 'defaultPermissionTeacher' => 'Y',\n 'defaultPermissionStudent' => 'N',\n 'defaultPermissionParent' => 'N',\n 'defaultPermissionSupport' => 'Y',\n 'categoryPermissionStaff' => 'Y',\n 'categoryPermissionStudent' => 'N',\n 'categoryPermissionParent' => 'N',\n 'categoryPermissionOther' => 'N'\n];\n\n$actionRows[] = [\n 'name' => 'Feedback Analytics',\n 'precedence' => '6',\n 'category' => 'Admin',\n 'description' => 'View and analyze ChatBot feedback data',\n 'URLList' => 'feedback.php,api/feedback.php',\n 'entryURL' => 'feedback.php',\n 'defaultPermissionAdmin' => 'Y',\n 'defaultPermissionTeacher' => 'Y',\n 'defaultPermissionStudent' => 'N',\n 'defaultPermissionParent' => 'N',\n 'defaultPermissionSupport' => 'Y',\n 'categoryPermissionStaff' => 'Y',\n 'categoryPermissionStudent' => 'N',\n 'categoryPermissionParent' => 'N',\n 'categoryPermissionOther' => 'N'\n];\n\n$actionRows[] = [\n 'name' => 'Check Feedback Database',\n 'precedence' => '7',\n 'category' => 'Admin',\n 'description' => 'Check and verify ChatBot feedback database status',\n 'URLList' => 'db_check_feedback.php',\n 'entryURL' => 'db_check_feedback.php',\n 'defaultPermissionAdmin' => 'Y',\n 'defaultPermissionTeacher' => 'Y',\n 'defaultPermissionStudent' => 'N',\n 'defaultPermissionParent' => 'N',\n 'defaultPermissionSupport' => 'Y',\n 'categoryPermissionStaff' => 'Y',\n 'categoryPermissionStudent' => 'N',\n 'categoryPermissionParent' => 'N',\n 'categoryPermissionOther' => 'N'\n];\n\n// Return module configuration\nreturn [\n 'name' => $name,\n 'version' => $version,\n 'description' => $description,\n 'author' => $author,\n 'url' => $url,\n 'entryURL' => $entryURL,\n 'type' => $type,\n 'category' => $category,\n 'tables' => $moduleTables,\n 'gibbonSettings' => $gibbonSetting,\n 'actionRows' => $actionRows\n];\n"}}, "modules/ChatBot/simple_api_test.php": {"5eacf702b1d14d58114e1990619ea4913b19ff59": {"t": 1785745793.53718, "n": 7183, "h": "0889cbc57bdce8a3", "c": ".\n*/\n\n// Module includes\nrequire_once '../../gibbon.php';\n\n// Check if core Gibbon functions are available\nif (!function_exists('__') || !function_exists('isActionAccessible')) {\n die('Fatal Error: Gibbon core functions not loaded');\n}\n\n// Import required classes\nuse Gibbon\\Domain\\System\\SettingGateway;\nuse Gibbon\\View\\Page;\n\n// Basic initialization\nif (!isset($container)) {\n die('Fatal Error: Gibbon container not initialized');\n}\n\nif (!isset($guid) || !isset($connection2)) {\n die('Fatal Error: Gibbon core variables not initialized');\n}\n\n// Setup routes - moved outside try block to ensure it's available in catch blocks\n$page = new Page($container, ['address' => $_GET['q'] ?? '']);\n\nif (!$page instanceof Page) {\n die('Fatal Error: Failed to initialize Page object');\n}\n\ntry {\n // Check access\n if (!isActionAccessible($guid, $connection2, '/modules/ChatBot/simple_api_test.php')) {\n // Access denied\n $page->addWarning(__('You do not have access to this action.'));\n return;\n }\n\n // Get session\n $session = $container->get('session');\n\n // Set page breadcrumb\n $page->breadcrumbs\n ->add(__('ChatBot'), 'chatbot.php')\n ->add(__('API Test'));\n\n // Get the API key from settings\n try {\n $settingGateway = $container->get(SettingGateway::class);\n $apiKey = $settingGateway->getSettingByScope('ChatBot', 'deepseek_api_key');\n } catch (Exception $e) {\n $page->addError('Failed to load API key: ' . $e->getMessage());\n return;\n }\n} catch (Exception $e) {\n die('Fatal Error: ' . $e->getMessage());\n}\n\n// Add CSS\necho \"\";\n\n// Page title\necho \"

\" . __('API Test') . \"

\";\n\n// Add sidebar menu\n$page->addSidebarExtra('\n
\n

' . __('ChatBot Menu') . '

\n
\n
\n');\n\n// Create the test form\n$form = Gibbon\\Forms\\Form::create('apiTest', $session->get('absoluteURL') . '/modules/ChatBot/api_test_process.php');\n$form->setTitle('API Key Test');\n\n$form->addHiddenValue('address', $session->get('address'));\n$form->addHiddenValue('gibbonCSRFToken', $session->get('gibbonCSRFToken'));\n\n$row = $form->addRow();\n$row->addLabel('testMessage', __('Test Message'))\n ->description(__('Enter a message to test the API connection.'));\n$row->addTextArea('testMessage')\n ->setRows(3)\n ->setValue('What is 2+2?')\n ->required();\n\n$row = $form->addRow();\n$row->addFooter();\n$row->addSubmit('Test API');\n\necho $form->getOutput();\n\n// Add results container\necho '
\n

' . __('Test Results') . '

\n
\n
';\n\n// Add JavaScript for form handling\n?>\n\n\n\n?>"}, "030e63497fd5a0ea218107feedd91d649324421f": {"t": 1785756291.3493762, "n": 7183, "h": "0889cbc57bdce8a3", "c": ".\n*/\n\n// Module includes\nrequire_once '../../gibbon.php';\n\n// Check if core Gibbon functions are available\nif (!function_exists('__') || !function_exists('isActionAccessible')) {\n die('Fatal Error: Gibbon core functions not loaded');\n}\n\n// Import required classes\nuse Gibbon\\Domain\\System\\SettingGateway;\nuse Gibbon\\View\\Page;\n\n// Basic initialization\nif (!isset($container)) {\n die('Fatal Error: Gibbon container not initialized');\n}\n\nif (!isset($guid) || !isset($connection2)) {\n die('Fatal Error: Gibbon core variables not initialized');\n}\n\n// Setup routes - moved outside try block to ensure it's available in catch blocks\n$page = new Page($container, ['address' => $_GET['q'] ?? '']);\n\nif (!$page instanceof Page) {\n die('Fatal Error: Failed to initialize Page object');\n}\n\ntry {\n // Check access\n if (!isActionAccessible($guid, $connection2, '/modules/ChatBot/simple_api_test.php')) {\n // Access denied\n $page->addWarning(__('You do not have access to this action.'));\n return;\n }\n\n // Get session\n $session = $container->get('session');\n\n // Set page breadcrumb\n $page->breadcrumbs\n ->add(__('ChatBot'), 'chatbot.php')\n ->add(__('API Test'));\n\n // Get the API key from settings\n try {\n $settingGateway = $container->get(SettingGateway::class);\n $apiKey = $settingGateway->getSettingByScope('ChatBot', 'deepseek_api_key');\n } catch (Exception $e) {\n $page->addError('Failed to load API key: ' . $e->getMessage());\n return;\n }\n} catch (Exception $e) {\n die('Fatal Error: ' . $e->getMessage());\n}\n\n// Add CSS\necho \"\";\n\n// Page title\necho \"

\" . __('API Test') . \"

\";\n\n// Add sidebar menu\n$page->addSidebarExtra('\n
\n

' . __('ChatBot Menu') . '

\n \n
\n');\n\n// Create the test form\n$form = Gibbon\\Forms\\Form::create('apiTest', $session->get('absoluteURL') . '/modules/ChatBot/api_test_process.php');\n$form->setTitle('API Key Test');\n\n$form->addHiddenValue('address', $session->get('address'));\n$form->addHiddenValue('gibbonCSRFToken', $session->get('gibbonCSRFToken'));\n\n$row = $form->addRow();\n$row->addLabel('testMessage', __('Test Message'))\n ->description(__('Enter a message to test the API connection.'));\n$row->addTextArea('testMessage')\n ->setRows(3)\n ->setValue('What is 2+2?')\n ->required();\n\n$row = $form->addRow();\n$row->addFooter();\n$row->addSubmit('Test API');\n\necho $form->getOutput();\n\n// Add results container\necho '
\n

' . __('Test Results') . '

\n
\n
';\n\n// Add JavaScript for form handling\n?>\n\n\n\n?>"}, "ea2bf72c5d38ab35b13101b6131cf74ba37f78a3": {"t": 1786068318.2234674, "n": 7183, "h": "0889cbc57bdce8a3", "c": ".\n*/\n\n// Module includes\nrequire_once '../../gibbon.php';\n\n// Check if core Gibbon functions are available\nif (!function_exists('__') || !function_exists('isActionAccessible')) {\n die('Fatal Error: Gibbon core functions not loaded');\n}\n\n// Import required classes\nuse Gibbon\\Domain\\System\\SettingGateway;\nuse Gibbon\\View\\Page;\n\n// Basic initialization\nif (!isset($container)) {\n die('Fatal Error: Gibbon container not initialized');\n}\n\nif (!isset($guid) || !isset($connection2)) {\n die('Fatal Error: Gibbon core variables not initialized');\n}\n\n// Setup routes - moved outside try block to ensure it's available in catch blocks\n$page = new Page($container, ['address' => $_GET['q'] ?? '']);\n\nif (!$page instanceof Page) {\n die('Fatal Error: Failed to initialize Page object');\n}\n\ntry {\n // Check access\n if (!isActionAccessible($guid, $connection2, '/modules/ChatBot/simple_api_test.php')) {\n // Access denied\n $page->addWarning(__('You do not have access to this action.'));\n return;\n }\n\n // Get session\n $session = $container->get('session');\n\n // Set page breadcrumb\n $page->breadcrumbs\n ->add(__('ChatBot'), 'chatbot.php')\n ->add(__('API Test'));\n\n // Get the API key from settings\n try {\n $settingGateway = $container->get(SettingGateway::class);\n $apiKey = $settingGateway->getSettingByScope('ChatBot', 'deepseek_api_key');\n } catch (Exception $e) {\n $page->addError('Failed to load API key: ' . $e->getMessage());\n return;\n }\n} catch (Exception $e) {\n die('Fatal Error: ' . $e->getMessage());\n}\n\n// Add CSS\necho \"\";\n\n// Page title\necho \"

\" . __('API Test') . \"

\";\n\n// Add sidebar menu\n$page->addSidebarExtra('\n
\n

' . __('ChatBot Menu') . '

\n \n
\n');\n\n// Create the test form\n$form = Gibbon\\Forms\\Form::create('apiTest', $session->get('absoluteURL') . '/modules/ChatBot/api_test_process.php');\n$form->setTitle('API Key Test');\n\n$form->addHiddenValue('address', $session->get('address'));\n$form->addHiddenValue('gibbonCSRFToken', $session->get('gibbonCSRFToken'));\n\n$row = $form->addRow();\n$row->addLabel('testMessage', __('Test Message'))\n ->description(__('Enter a message to test the API connection.'));\n$row->addTextArea('testMessage')\n ->setRows(3)\n ->setValue('What is 2+2?')\n ->required();\n\n$row = $form->addRow();\n$row->addFooter();\n$row->addSubmit('Test API');\n\necho $form->getOutput();\n\n// Add results container\necho '
\n

' . __('Test Results') . '

\n
\n
';\n\n// Add JavaScript for form handling\n?>\n\n\n\n?>"}}, "modules/aiTeacher/script.php": {"5eacf702b1d14d58114e1990619ea4913b19ff59": {"t": 1785746143.996883, "n": 738, "h": "77633d6e9e2fbdd5", "c": "getSettingByScope('ChatBot', 'deepseek_api_key');\n\n$response = $httpClient->post('https://api.deepseek.com/v1/chat/completions', [\n 'headers' => [\n 'Authorization' => 'Bearer ' . $apiKey,\n 'Content-Type' => 'application/json',\n ],\n 'json' => [\n 'model' => 'deepseek-chat',\n 'messages' => [['role' => 'user', 'content' => $userMessage]]\n]);\n//Ended\n\n\n\n public function __construct($apiKey = null, $connection = null, $container = null)\n {\n if ($apiKey === null) {\n // Try to get API key from Gibbon settings\n try {\n // Try to use container if available (preferred method)\n if (isset($container) && $container->has(SettingGateway::class)) {\n $settingGateway = $container->get(SettingGateway::class);\n $apiKey = $settingGateway->getSettingByScope('ChatBot', 'deepseek_api_key');\n \n if (empty($apiKey)) {\n throw new \\Exception('DeepSeek API key is empty in settings');\n }\n }\n // Fallback to using provided database connection\n else if ($connection instanceof Connection) {\n $sql = \"SELECT value FROM gibbonSetting WHERE scope='ChatBot' AND name='deepseek_api_key'\";\n $result = $connection->select($sql);\n \n if (empty($result)) {\n throw new \\Exception('DeepSeek API key not found in settings');\n }\n \n $apiKey = $result[0]['value'];\n if (empty($apiKey)) {\n throw new \\Exception('DeepSeek API key is empty');\n }\n }\n // Last resort: try to get database connection from globals\n else if (isset($GLOBALS['container']) && $GLOBALS['container']->has(SettingGateway::class)) {\n $settingGateway = $GLOBALS['container']->get(SettingGateway::class);\n $apiKey = $settingGateway->getSettingByScope('ChatBot', 'deepseek_api_key');\n \n if (empty($apiKey)) {\n throw new \\Exception('DeepSeek API key is empty in settings');\n }\n } \n else {\n throw new \\Exception('No valid connection or container available');\n }\n } catch (\\Exception $e) {\n error_log('DeepSeekAPI Database Error: ' . $e->getMessage());\n throw new \\Exception('Failed to retrieve API key from database: ' . $e->getMessage());\n }\n }\n \n $this->apiKey = $apiKey;\n \n // Try to get model settings if available\n if (isset($container) && $container->has(SettingGateway::class)) {\n $settingGateway = $container->get(SettingGateway::class);\n $modelName = $settingGateway->getSettingByScope('ChatBot', 'model_name');\n $maxTokens = $settingGateway->getSettingByScope('ChatBot', 'max_tokens');\n \n if (!empty($modelName)) $this->modelName = $modelName;\n if (!empty($maxTokens)) $this->maxTokens = intval($maxTokens);\n }\n }\n\n /**\n * Get a response from the DeepSeek API with rate limiting and exponential backoff\n *\n * @param string $message The user's message\n * @param bool $isTrainingMode Whether training mode is enabled\n * @return array The AI's response\n * @throws \\Exception\n */\n public function getResponse($message, $isTrainingMode = false)\n {\n error_log('DeepSeekAPI: Preparing request for message: ' . substr($message, 0, 100) . '...');\n \n // Validate input\n if (empty($message)) {\n error_log('DeepSeekAPI Error: Empty message provided');\n throw new \\Exception('Message cannot be empty');\n }\n\n // Define system prompt based on mode\n $systemPrompt = $this->getSystemPrompt($isTrainingMode);\n\n // Prepare request data\n $data = [\n 'model' => $this->modelName,\n 'messages' => [\n ['role' => 'system', 'content' => $systemPrompt],\n ['role' => 'user', 'content' => $message]\n ],\n 'max_tokens' => $this->maxTokens,\n 'temperature' => $isTrainingMode ? 0.7 : 0.5,\n 'stream' => false\n ];\n\n error_log('DeepSeekAPI: Request data prepared: ' . json_encode($data));\n\n // Try up to maxRetries times with exponential backoff\n $lastError = null;\n for ($attempt = 1; $attempt <= $this->maxRetries; $attempt++) {\n try {\n error_log(\"DeepSeekAPI: Attempt $attempt of {$this->maxRetries}\");\n \n // Apply rate limiting delay if not first attempt\n if ($attempt > 1) {\n $delay = $this->rateLimitDelay * pow(2, $attempt - 1); // Exponential backoff\n error_log(\"DeepSeekAPI: Waiting {$delay} seconds before retry\");\n sleep($delay);\n }\n \n // Initialize cURL\n $ch = curl_init($this->apiEndpoint);\n if ($ch === false) {\n throw new \\Exception('Failed to initialize cURL');\n }\n\n // Set cURL options\n curl_setopt_array($ch, [\n CURLOPT_RETURNTRANSFER => true,\n CURLOPT_POST => true,\n CURLOPT_POSTFIELDS => json_encode($data),\n CURLOPT_HTTPHEADER => [\n 'Content-Type: application/json',\n 'Authorization: Bearer ' . $this->apiKey\n ],\n CURLOPT_TIMEOUT => $this->timeout,\n CURLOPT_SSL_VERIFYPEER => true\n ]);\n\n error_log('DeepSeekAPI: Sending request to ' . $this->apiEndpoint);\n\n // Execute request\n $response = curl_exec($ch);\n $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);\n $curlError = curl_error($ch);\n \n error_log('DeepSeekAPI: Response received - HTTP Code: ' . $httpCode);\n error_log('DeepSeekAPI: Raw response: ' . substr($response, 0, 1000));\n\n curl_close($ch);\n\n // Handle cURL errors\n if ($response === false) {\n throw new \\Exception('Failed to connect to AI service: ' . $curlError);\n }\n\n // Handle rate limiting\n if ($httpCode === 429) {\n $retryAfter = $this->getRetryAfterHeader($response) ?? $this->rateLimitDelay * pow(2, $attempt);\n error_log(\"DeepSeekAPI: Rate limited. Waiting {$retryAfter} seconds.\");\n sleep($retryAfter);\n continue;\n }\n\n // Handle other HTTP errors\n if ($httpCode !== 200) {\n $errorData = json_decode($response, true);\n $errorMessage = isset($errorData['error']['message']) ? $errorData['error']['message'] : 'Unknown error';\n throw new \\Exception('AI service error: ' . $errorMessage);\n }\n\n // Parse response\n $responseData = json_decode($response, true);\n if (json_last_error() !== JSON_ERROR_NONE) {\n throw new \\Exception('Invalid response from AI service: ' . json_last_error_msg());\n }\n\n // Extract message content\n if (!isset($responseData['choices'][0]['message']['content'])) {\n throw new \\Exception('Unexpected response format from AI service');\n }\n\n $content = $responseData['choices'][0]['message']['content'];\n error_log('DeepSeekAPI: Successfully received content: ' . substr($content, 0, 100) . '...');\n\n return [\n 'success' => true,\n 'answer' => $content,\n 'timestamp' => date('Y-m-d H:i:s'),\n 'usage' => $responseData['usage'] ?? null\n ];\n\n } catch (\\Exception $e) {\n $lastError = $e;\n error_log('DeepSeekAPI Error on attempt ' . $attempt . ': ' . $e->getMessage());\n \n // Don't retry on certain errors\n if ($this->isNonRetryableError($e)) {\n throw $e;\n }\n \n if ($attempt < $this->maxRetries) {\n continue;\n }\n }\n }\n\n // If we get here, all retries failed\n throw $lastError ?? new \\Exception('Failed to get response from AI service after ' . $this->maxRetries . ' attempts');\n }\n\n /**\n * Get the system prompt based on mode\n *\n * @param bool $trainingMode\n * @return string\n */\n private function getSystemPrompt($trainingMode)\n {\n if ($trainingMode) {\n return \"You are an AI teaching assistant trained to provide detailed, comprehensive educational guidance. \" .\n \"Focus on clear explanations, practical examples, and constructive feedback. \" .\n \"Use markdown formatting for better readability:\\n\" .\n \"- Use # for main headings\\n\" .\n \"- Use ## for subheadings\\n\" .\n \"- Use **bold** for emphasis\\n\" .\n \"- Use `code` for technical terms\\n\" .\n \"- Use numbered lists for steps\\n\" .\n \"- Use bullet points for key concepts\";\n }\n\n return \"You are a helpful AI teaching assistant. Provide clear, concise answers to educational questions. \" .\n \"Focus on being informative and supportive while maintaining a professional tone.\";\n }\n\n /**\n * Test the API connection\n *\n * @return bool\n * @throws \\Exception\n */\n public function testConnection()\n {\n try {\n // Check DNS resolution first\n $host = parse_url($this->apiEndpoint, PHP_URL_HOST);\n if (!$host) {\n throw new \\Exception('Invalid API endpoint URL');\n }\n\n $dnsCheck = dns_get_record($host, DNS_A);\n if (empty($dnsCheck)) {\n throw new \\Exception('Could not resolve API host');\n }\n\n error_log('DeepSeek API Test: DNS resolution successful for ' . $host);\n error_log('DeepSeek API Test: IP addresses - ' . implode(', ', array_column($dnsCheck, 'ip')));\n\n $response = $this->getResponse('Test connection');\n return true;\n } catch (\\Exception $e) {\n error_log('DeepSeek API Test Connection Failed: ' . $e->getMessage());\n throw new \\Exception('API connection test failed: ' . $e->getMessage());\n }\n }\n\n /**\n * Get retry-after value from response headers\n *\n * @param string $response The raw response\n * @return int|null The number of seconds to wait\n */\n private function getRetryAfterHeader($response)\n {\n $headers = [];\n $rawHeaders = substr($response, 0, strpos($response, \"\\r\\n\\r\\n\"));\n foreach (explode(\"\\r\\n\", $rawHeaders) as $line) {\n if (strpos($line, ':') !== false) {\n list($key, $value) = explode(':', $line, 2);\n $headers[strtolower(trim($key))] = trim($value);\n }\n }\n \n return isset($headers['retry-after']) ? (int)$headers['retry-after'] : null;\n }\n\n /**\n * Check if an error should not be retried\n *\n * @param \\Exception $error The error to check\n * @return bool\n */\n private function isNonRetryableError(\\Exception $error)\n {\n $nonRetryableMessages = [\n 'API key not found',\n 'Invalid API key',\n 'Permission denied',\n 'Invalid request format'\n ];\n \n foreach ($nonRetryableMessages as $message) {\n if (stripos($error->getMessage(), $message) !== false) {\n return true;\n }\n }\n \n return false;\n }\n}"}, "030e63497fd5a0ea218107feedd91d649324421f": {"t": 1785756299.305897, "n": 12992, "h": "2c6e27267d38d384", "c": " getSettingByScope('ChatBot', 'deepseek_api_key');\n\n$response = $httpClient->post('https://api.deepseek.com/v1/chat/completions', [\n 'headers' => [\n 'Authorization' => 'Bearer ' . $apiKey,\n 'Content-Type' => 'application/json',\n ],\n 'json' => [\n 'model' => 'deepseek-chat',\n 'messages' => [['role' => 'user', 'content' => $userMessage]]\n]);\n//Ended\n\n\n\n public function __construct($apiKey = null, $connection = null, $container = null)\n {\n if ($apiKey === null) {\n // Try to get API key from Gibbon settings\n try {\n // Try to use container if available (preferred method)\n if (isset($container) && $container->has(SettingGateway::class)) {\n $settingGateway = $container->get(SettingGateway::class);\n $apiKey = $settingGateway->getSettingByScope('ChatBot', 'deepseek_api_key');\n \n if (empty($apiKey)) {\n throw new \\Exception('DeepSeek API key is empty in settings');\n }\n }\n // Fallback to using provided database connection\n else if ($connection instanceof Connection) {\n $sql = \"SELECT value FROM gibbonSetting WHERE scope='ChatBot' AND name='deepseek_api_key'\";\n $result = $connection->select($sql);\n \n if (empty($result)) {\n throw new \\Exception('DeepSeek API key not found in settings');\n }\n \n $apiKey = $result[0]['value'];\n if (empty($apiKey)) {\n throw new \\Exception('DeepSeek API key is empty');\n }\n }\n // Last resort: try to get database connection from globals\n else if (isset($GLOBALS['container']) && $GLOBALS['container']->has(SettingGateway::class)) {\n $settingGateway = $GLOBALS['container']->get(SettingGateway::class);\n $apiKey = $settingGateway->getSettingByScope('ChatBot', 'deepseek_api_key');\n \n if (empty($apiKey)) {\n throw new \\Exception('DeepSeek API key is empty in settings');\n }\n } \n else {\n throw new \\Exception('No valid connection or container available');\n }\n } catch (\\Exception $e) {\n error_log('DeepSeekAPI Database Error: ' . $e->getMessage());\n throw new \\Exception('Failed to retrieve API key from database: ' . $e->getMessage());\n }\n }\n \n $this->apiKey = $apiKey;\n \n // Try to get model settings if available\n if (isset($container) && $container->has(SettingGateway::class)) {\n $settingGateway = $container->get(SettingGateway::class);\n $modelName = $settingGateway->getSettingByScope('ChatBot', 'model_name');\n $maxTokens = $settingGateway->getSettingByScope('ChatBot', 'max_tokens');\n \n if (!empty($modelName)) $this->modelName = $modelName;\n if (!empty($maxTokens)) $this->maxTokens = intval($maxTokens);\n }\n }\n\n /**\n * Get a response from the DeepSeek API with rate limiting and exponential backoff\n *\n * @param string $message The user's message\n * @param bool $isTrainingMode Whether training mode is enabled\n * @return array The AI's response\n * @throws \\Exception\n */\n public function getResponse($message, $isTrainingMode = false)\n {\n error_log('DeepSeekAPI: Preparing request for message: ' . substr($message, 0, 100) . '...');\n \n // Validate input\n if (empty($message)) {\n error_log('DeepSeekAPI Error: Empty message provided');\n throw new \\Exception('Message cannot be empty');\n }\n\n // Define system prompt based on mode\n $systemPrompt = $this->getSystemPrompt($isTrainingMode);\n\n // Prepare request data\n $data = [\n 'model' => $this->modelName,\n 'messages' => [\n ['role' => 'system', 'content' => $systemPrompt],\n ['role' => 'user', 'content' => $message]\n ],\n 'max_tokens' => $this->maxTokens,\n 'temperature' => $isTrainingMode ? 0.7 : 0.5,\n 'stream' => false\n ];\n\n error_log('DeepSeekAPI: Request data prepared: ' . json_encode($data));\n\n // Try up to maxRetries times with exponential backoff\n $lastError = null;\n for ($attempt = 1; $attempt <= $this->maxRetries; $attempt++) {\n try {\n error_log(\"DeepSeekAPI: Attempt $attempt of {$this->maxRetries}\");\n \n // Apply rate limiting delay if not first attempt\n if ($attempt > 1) {\n $delay = $this->rateLimitDelay * pow(2, $attempt - 1); // Exponential backoff\n error_log(\"DeepSeekAPI: Waiting {$delay} seconds before retry\");\n sleep($delay);\n }\n \n // Initialize cURL\n $ch = curl_init($this->apiEndpoint);\n if ($ch === false) {\n throw new \\Exception('Failed to initialize cURL');\n }\n\n // Set cURL options\n curl_setopt_array($ch, [\n CURLOPT_RETURNTRANSFER => true,\n CURLOPT_POST => true,\n CURLOPT_POSTFIELDS => json_encode($data),\n CURLOPT_HTTPHEADER => [\n 'Content-Type: application/json',\n 'Authorization: Bearer ' . $this->apiKey\n ],\n CURLOPT_TIMEOUT => $this->timeout,\n CURLOPT_SSL_VERIFYPEER => true\n ]);\n\n error_log('DeepSeekAPI: Sending request to ' . $this->apiEndpoint);\n\n // Execute request\n $response = curl_exec($ch);\n $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);\n $curlError = curl_error($ch);\n \n error_log('DeepSeekAPI: Response received - HTTP Code: ' . $httpCode);\n error_log('DeepSeekAPI: Raw response: ' . substr($response, 0, 1000));\n\n curl_close($ch);\n\n // Handle cURL errors\n if ($response === false) {\n throw new \\Exception('Failed to connect to AI service: ' . $curlError);\n }\n\n // Handle rate limiting\n if ($httpCode === 429) {\n $retryAfter = $this->getRetryAfterHeader($response) ?? $this->rateLimitDelay * pow(2, $attempt);\n error_log(\"DeepSeekAPI: Rate limited. Waiting {$retryAfter} seconds.\");\n sleep($retryAfter);\n continue;\n }\n\n // Handle other HTTP errors\n if ($httpCode !== 200) {\n $errorData = json_decode($response, true);\n $errorMessage = isset($errorData['error']['message']) ? $errorData['error']['message'] : 'Unknown error';\n throw new \\Exception('AI service error: ' . $errorMessage);\n }\n\n // Parse response\n $responseData = json_decode($response, true);\n if (json_last_error() !== JSON_ERROR_NONE) {\n throw new \\Exception('Invalid response from AI service: ' . json_last_error_msg());\n }\n\n // Extract message content\n if (!isset($responseData['choices'][0]['message']['content'])) {\n throw new \\Exception('Unexpected response format from AI service');\n }\n\n $content = $responseData['choices'][0]['message']['content'];\n error_log('DeepSeekAPI: Successfully received content: ' . substr($content, 0, 100) . '...');\n\n return [\n 'success' => true,\n 'answer' => $content,\n 'timestamp' => date('Y-m-d H:i:s'),\n 'usage' => $responseData['usage'] ?? null\n ];\n\n } catch (\\Exception $e) {\n $lastError = $e;\n error_log('DeepSeekAPI Error on attempt ' . $attempt . ': ' . $e->getMessage());\n \n // Don't retry on certain errors\n if ($this->isNonRetryableError($e)) {\n throw $e;\n }\n \n if ($attempt < $this->maxRetries) {\n continue;\n }\n }\n }\n\n // If we get here, all retries failed\n throw $lastError ?? new \\Exception('Failed to get response from AI service after ' . $this->maxRetries . ' attempts');\n }\n\n /**\n * Get the system prompt based on mode\n *\n * @param bool $trainingMode\n * @return string\n */\n private function getSystemPrompt($trainingMode)\n {\n if ($trainingMode) {\n return \"You are an AI teaching assistant trained to provide detailed, comprehensive educational guidance. \" .\n \"Focus on clear explanations, practical examples, and constructive feedback. \" .\n \"Use markdown formatting for better readability:\\n\" .\n \"- Use # for main headings\\n\" .\n \"- Use ## for subheadings\\n\" .\n \"- Use **bold** for emphasis\\n\" .\n \"- Use `code` for technical terms\\n\" .\n \"- Use numbered lists for steps\\n\" .\n \"- Use bullet points for key concepts\";\n }\n\n return \"You are a helpful AI teaching assistant. Provide clear, concise answers to educational questions. \" .\n \"Focus on being informative and supportive while maintaining a professional tone.\";\n }\n\n /**\n * Test the API connection\n *\n * @return bool\n * @throws \\Exception\n */\n public function testConnection()\n {\n try {\n // Check DNS resolution first\n $host = parse_url($this->apiEndpoint, PHP_URL_HOST);\n if (!$host) {\n throw new \\Exception('Invalid API endpoint URL');\n }\n\n $dnsCheck = dns_get_record($host, DNS_A);\n if (empty($dnsCheck)) {\n throw new \\Exception('Could not resolve API host');\n }\n\n error_log('DeepSeek API Test: DNS resolution successful for ' . $host);\n error_log('DeepSeek API Test: IP addresses - ' . implode(', ', array_column($dnsCheck, 'ip')));\n\n $response = $this->getResponse('Test connection');\n return true;\n } catch (\\Exception $e) {\n error_log('DeepSeek API Test Connection Failed: ' . $e->getMessage());\n throw new \\Exception('API connection test failed: ' . $e->getMessage());\n }\n }\n\n /**\n * Get retry-after value from response headers\n *\n * @param string $response The raw response\n * @return int|null The number of seconds to wait\n */\n private function getRetryAfterHeader($response)\n {\n $headers = [];\n $rawHeaders = substr($response, 0, strpos($response, \"\\r\\n\\r\\n\"));\n foreach (explode(\"\\r\\n\", $rawHeaders) as $line) {\n if (strpos($line, ':') !== false) {\n list($key, $value) = explode(':', $line, 2);\n $headers[strtolower(trim($key))] = trim($value);\n }\n }\n \n return isset($headers['retry-after']) ? (int)$headers['retry-after'] : null;\n }\n\n /**\n * Check if an error should not be retried\n *\n * @param \\Exception $error The error to check\n * @return bool\n */\n private function isNonRetryableError(\\Exception $error)\n {\n $nonRetryableMessages = [\n 'API key not found',\n 'Invalid API key',\n 'Permission denied',\n 'Invalid request format'\n ];\n \n foreach ($nonRetryableMessages as $message) {\n if (stripos($error->getMessage(), $message) !== false) {\n return true;\n }\n }\n \n return false;\n }\n}"}, "ea2bf72c5d38ab35b13101b6131cf74ba37f78a3": {"t": 1786068315.953645, "n": 12992, "h": "2c6e27267d38d384", "c": " getSettingByScope('ChatBot', 'deepseek_api_key');\n\n$response = $httpClient->post('https://api.deepseek.com/v1/chat/completions', [\n 'headers' => [\n 'Authorization' => 'Bearer ' . $apiKey,\n 'Content-Type' => 'application/json',\n ],\n 'json' => [\n 'model' => 'deepseek-chat',\n 'messages' => [['role' => 'user', 'content' => $userMessage]]\n]);\n//Ended\n\n\n\n public function __construct($apiKey = null, $connection = null, $container = null)\n {\n if ($apiKey === null) {\n // Try to get API key from Gibbon settings\n try {\n // Try to use container if available (preferred method)\n if (isset($container) && $container->has(SettingGateway::class)) {\n $settingGateway = $container->get(SettingGateway::class);\n $apiKey = $settingGateway->getSettingByScope('ChatBot', 'deepseek_api_key');\n \n if (empty($apiKey)) {\n throw new \\Exception('DeepSeek API key is empty in settings');\n }\n }\n // Fallback to using provided database connection\n else if ($connection instanceof Connection) {\n $sql = \"SELECT value FROM gibbonSetting WHERE scope='ChatBot' AND name='deepseek_api_key'\";\n $result = $connection->select($sql);\n \n if (empty($result)) {\n throw new \\Exception('DeepSeek API key not found in settings');\n }\n \n $apiKey = $result[0]['value'];\n if (empty($apiKey)) {\n throw new \\Exception('DeepSeek API key is empty');\n }\n }\n // Last resort: try to get database connection from globals\n else if (isset($GLOBALS['container']) && $GLOBALS['container']->has(SettingGateway::class)) {\n $settingGateway = $GLOBALS['container']->get(SettingGateway::class);\n $apiKey = $settingGateway->getSettingByScope('ChatBot', 'deepseek_api_key');\n \n if (empty($apiKey)) {\n throw new \\Exception('DeepSeek API key is empty in settings');\n }\n } \n else {\n throw new \\Exception('No valid connection or container available');\n }\n } catch (\\Exception $e) {\n error_log('DeepSeekAPI Database Error: ' . $e->getMessage());\n throw new \\Exception('Failed to retrieve API key from database: ' . $e->getMessage());\n }\n }\n \n $this->apiKey = $apiKey;\n \n // Try to get model settings if available\n if (isset($container) && $container->has(SettingGateway::class)) {\n $settingGateway = $container->get(SettingGateway::class);\n $modelName = $settingGateway->getSettingByScope('ChatBot', 'model_name');\n $maxTokens = $settingGateway->getSettingByScope('ChatBot', 'max_tokens');\n \n if (!empty($modelName)) $this->modelName = $modelName;\n if (!empty($maxTokens)) $this->maxTokens = intval($maxTokens);\n }\n }\n\n /**\n * Get a response from the DeepSeek API with rate limiting and exponential backoff\n *\n * @param string $message The user's message\n * @param bool $isTrainingMode Whether training mode is enabled\n * @return array The AI's response\n * @throws \\Exception\n */\n public function getResponse($message, $isTrainingMode = false)\n {\n error_log('DeepSeekAPI: Preparing request for message: ' . substr($message, 0, 100) . '...');\n \n // Validate input\n if (empty($message)) {\n error_log('DeepSeekAPI Error: Empty message provided');\n throw new \\Exception('Message cannot be empty');\n }\n\n // Define system prompt based on mode\n $systemPrompt = $this->getSystemPrompt($isTrainingMode);\n\n // Prepare request data\n $data = [\n 'model' => $this->modelName,\n 'messages' => [\n ['role' => 'system', 'content' => $systemPrompt],\n ['role' => 'user', 'content' => $message]\n ],\n 'max_tokens' => $this->maxTokens,\n 'temperature' => $isTrainingMode ? 0.7 : 0.5,\n 'stream' => false\n ];\n\n error_log('DeepSeekAPI: Request data prepared: ' . json_encode($data));\n\n // Try up to maxRetries times with exponential backoff\n $lastError = null;\n for ($attempt = 1; $attempt <= $this->maxRetries; $attempt++) {\n try {\n error_log(\"DeepSeekAPI: Attempt $attempt of {$this->maxRetries}\");\n \n // Apply rate limiting delay if not first attempt\n if ($attempt > 1) {\n $delay = $this->rateLimitDelay * pow(2, $attempt - 1); // Exponential backoff\n error_log(\"DeepSeekAPI: Waiting {$delay} seconds before retry\");\n sleep($delay);\n }\n \n // Initialize cURL\n $ch = curl_init($this->apiEndpoint);\n if ($ch === false) {\n throw new \\Exception('Failed to initialize cURL');\n }\n\n // Set cURL options\n curl_setopt_array($ch, [\n CURLOPT_RETURNTRANSFER => true,\n CURLOPT_POST => true,\n CURLOPT_POSTFIELDS => json_encode($data),\n CURLOPT_HTTPHEADER => [\n 'Content-Type: application/json',\n 'Authorization: Bearer ' . $this->apiKey\n ],\n CURLOPT_TIMEOUT => $this->timeout,\n CURLOPT_SSL_VERIFYPEER => true\n ]);\n\n error_log('DeepSeekAPI: Sending request to ' . $this->apiEndpoint);\n\n // Execute request\n $response = curl_exec($ch);\n $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);\n $curlError = curl_error($ch);\n \n error_log('DeepSeekAPI: Response received - HTTP Code: ' . $httpCode);\n error_log('DeepSeekAPI: Raw response: ' . substr($response, 0, 1000));\n\n curl_close($ch);\n\n // Handle cURL errors\n if ($response === false) {\n throw new \\Exception('Failed to connect to AI service: ' . $curlError);\n }\n\n // Handle rate limiting\n if ($httpCode === 429) {\n $retryAfter = $this->getRetryAfterHeader($response) ?? $this->rateLimitDelay * pow(2, $attempt);\n error_log(\"DeepSeekAPI: Rate limited. Waiting {$retryAfter} seconds.\");\n sleep($retryAfter);\n continue;\n }\n\n // Handle other HTTP errors\n if ($httpCode !== 200) {\n $errorData = json_decode($response, true);\n $errorMessage = isset($errorData['error']['message']) ? $errorData['error']['message'] : 'Unknown error';\n throw new \\Exception('AI service error: ' . $errorMessage);\n }\n\n // Parse response\n $responseData = json_decode($response, true);\n if (json_last_error() !== JSON_ERROR_NONE) {\n throw new \\Exception('Invalid response from AI service: ' . json_last_error_msg());\n }\n\n // Extract message content\n if (!isset($responseData['choices'][0]['message']['content'])) {\n throw new \\Exception('Unexpected response format from AI service');\n }\n\n $content = $responseData['choices'][0]['message']['content'];\n error_log('DeepSeekAPI: Successfully received content: ' . substr($content, 0, 100) . '...');\n\n return [\n 'success' => true,\n 'answer' => $content,\n 'timestamp' => date('Y-m-d H:i:s'),\n 'usage' => $responseData['usage'] ?? null\n ];\n\n } catch (\\Exception $e) {\n $lastError = $e;\n error_log('DeepSeekAPI Error on attempt ' . $attempt . ': ' . $e->getMessage());\n \n // Don't retry on certain errors\n if ($this->isNonRetryableError($e)) {\n throw $e;\n }\n \n if ($attempt < $this->maxRetries) {\n continue;\n }\n }\n }\n\n // If we get here, all retries failed\n throw $lastError ?? new \\Exception('Failed to get response from AI service after ' . $this->maxRetries . ' attempts');\n }\n\n /**\n * Get the system prompt based on mode\n *\n * @param bool $trainingMode\n * @return string\n */\n private function getSystemPrompt($trainingMode)\n {\n if ($trainingMode) {\n return \"You are an AI teaching assistant trained to provide detailed, comprehensive educational guidance. \" .\n \"Focus on clear explanations, practical examples, and constructive feedback. \" .\n \"Use markdown formatting for better readability:\\n\" .\n \"- Use # for main headings\\n\" .\n \"- Use ## for subheadings\\n\" .\n \"- Use **bold** for emphasis\\n\" .\n \"- Use `code` for technical terms\\n\" .\n \"- Use numbered lists for steps\\n\" .\n \"- Use bullet points for key concepts\";\n }\n\n return \"You are a helpful AI teaching assistant. Provide clear, concise answers to educational questions. \" .\n \"Focus on being informative and supportive while maintaining a professional tone.\";\n }\n\n /**\n * Test the API connection\n *\n * @return bool\n * @throws \\Exception\n */\n public function testConnection()\n {\n try {\n // Check DNS resolution first\n $host = parse_url($this->apiEndpoint, PHP_URL_HOST);\n if (!$host) {\n throw new \\Exception('Invalid API endpoint URL');\n }\n\n $dnsCheck = dns_get_record($host, DNS_A);\n if (empty($dnsCheck)) {\n throw new \\Exception('Could not resolve API host');\n }\n\n error_log('DeepSeek API Test: DNS resolution successful for ' . $host);\n error_log('DeepSeek API Test: IP addresses - ' . implode(', ', array_column($dnsCheck, 'ip')));\n\n $response = $this->getResponse('Test connection');\n return true;\n } catch (\\Exception $e) {\n error_log('DeepSeek API Test Connection Failed: ' . $e->getMessage());\n throw new \\Exception('API connection test failed: ' . $e->getMessage());\n }\n }\n\n /**\n * Get retry-after value from response headers\n *\n * @param string $response The raw response\n * @return int|null The number of seconds to wait\n */\n private function getRetryAfterHeader($response)\n {\n $headers = [];\n $rawHeaders = substr($response, 0, strpos($response, \"\\r\\n\\r\\n\"));\n foreach (explode(\"\\r\\n\", $rawHeaders) as $line) {\n if (strpos($line, ':') !== false) {\n list($key, $value) = explode(':', $line, 2);\n $headers[strtolower(trim($key))] = trim($value);\n }\n }\n \n return isset($headers['retry-after']) ? (int)$headers['retry-after'] : null;\n }\n\n /**\n * Check if an error should not be retried\n *\n * @param \\Exception $error The error to check\n * @return bool\n */\n private function isNonRetryableError(\\Exception $error)\n {\n $nonRetryableMessages = [\n 'API key not found',\n 'Invalid API key',\n 'Permission denied',\n 'Invalid request format'\n ];\n \n foreach ($nonRetryableMessages as $message) {\n if (stripos($error->getMessage(), $message) !== false) {\n return true;\n }\n }\n \n return false;\n }\n}"}}, "modules/ChatBot/src/Domain/Recommendations/RecommendationGenerator.php": {"5eacf702b1d14d58114e1990619ea4913b19ff59": {"t": 1785746168.4523878, "n": 5848, "h": "2e2c52b295b18cfa", "c": "connection = $connection;\n $this->studentGateway = $studentGateway;\n $this->assessmentGateway = $assessmentGateway;\n }\n\n /**\n * Generate recommendations for a student based on their assessment data\n *\n * @param string $gibbonPersonID\n * @return array\n */\n public function generateRecommendations($gibbonPersonID)\n {\n // Get student's assessment data\n $criteria = $this->assessmentGateway->newQueryCriteria()\n ->sortBy(['date', 'DESC']);\n\n $assessments = $this->assessmentGateway->queryAssessmentsByStudent($criteria, $gibbonPersonID);\n \n $recommendations = [];\n foreach ($assessments as $assessment) {\n $recommendation = $this->generateSingleRecommendation($assessment);\n if ($recommendation) {\n $recommendations[] = [\n 'assessment' => $assessment,\n 'recommendation' => $recommendation\n ];\n \n // Store the recommendation\n $this->storeRecommendation($gibbonPersonID, $assessment, $recommendation);\n }\n }\n \n return $recommendations;\n }\n\n /**\n * Generate a recommendation for a single assessment\n *\n * @param array $assessment\n * @return string|null\n */\n protected function generateSingleRecommendation($assessment)\n {\n // Get the API key\n $apiKey = $this->getSecureAPIKey();\n if (empty($apiKey)) {\n return null;\n }\n \n // Prepare the prompt for the AI\n $prompt = $this->preparePrompt($assessment);\n \n try {\n // Call the DeepSeek API\n $ch = curl_init();\n curl_setopt($ch, CURLOPT_URL, 'https://api.deepseek.com/v1/chat/completions');\n curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\n curl_setopt($ch, CURLOPT_POST, true);\n curl_setopt($ch, CURLOPT_HTTPHEADER, [\n 'Content-Type: application/json',\n 'Authorization: Bearer ' . $apiKey\n ]);\n \n $data = [\n 'model' => 'deepseek-v4-flash',\n 'messages' => [\n [\n 'role' => 'system',\n 'content' => 'You are an educational AI assistant providing specific, actionable recommendations for academic improvement based on assessment data and teacher feedback.'\n ],\n [\n 'role' => 'user',\n 'content' => $prompt\n ]\n ],\n 'max_tokens' => 500\n ];\n \n curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));\n \n $response = curl_exec($ch);\n $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);\n curl_close($ch);\n \n if ($httpCode === 200) {\n $result = json_decode($response, true);\n return $result['choices'][0]['message']['content'];\n }\n } catch (\\Exception $e) {\n return null;\n }\n \n return null;\n }\n\n /**\n * Prepare the AI prompt based on assessment data\n *\n * @param array $assessment\n * @return string\n */\n protected function preparePrompt($assessment)\n {\n $prompt = \"Based on the following assessment data:\\n\";\n $prompt .= \"Subject: {$assessment['subject']}\\n\";\n $prompt .= \"Grade: {$assessment['grade']}\\n\";\n $prompt .= \"Teacher Feedback: {$assessment['feedback']}\\n\\n\";\n $prompt .= \"Please provide specific, actionable recommendations for improvement in this subject. Consider:\\n\";\n $prompt .= \"1. Study strategies\\n\";\n $prompt .= \"2. Practice exercises\\n\";\n $prompt .= \"3. Additional resources\\n\";\n $prompt .= \"4. Areas needing focus based on the assessment\\n\";\n $prompt .= \"5. Time management suggestions\";\n \n return $prompt;\n }\n\n /**\n * Store a recommendation in the database\n *\n * @param string $gibbonPersonID\n * @param array $assessment\n * @param string $recommendation\n * @return bool\n */\n protected function storeRecommendation($gibbonPersonID, $assessment, $recommendation)\n {\n try {\n $data = [\n 'gibbonPersonID' => $gibbonPersonID,\n 'gibbonCourseClassID' => $assessment['gibbonCourseClassID'],\n 'assessmentID' => $assessment['assessmentID'],\n 'recommendation' => $recommendation\n ];\n \n $sql = \"INSERT INTO gibbonChatBotRecommendations \n (gibbonPersonID, gibbonCourseClassID, assessmentID, recommendation) \n VALUES \n (:gibbonPersonID, :gibbonCourseClassID, :assessmentID, :recommendation)\";\n \n $stmt = $this->connection->prepare($sql);\n return $stmt->execute($data);\n } catch (\\Exception $e) {\n return false;\n }\n }\n\n /**\n * Get the secure API key\n *\n * @return string|null\n */\n protected function getSecureAPIKey()\n {\n return getSecureAPIKey($this->connection);\n }\n} "}, "228065060bc341b101409b1b5a4415a502b48a3a": {"t": 1785756292.1091142, "n": 5848, "h": "2e2c52b295b18cfa", "c": "connection = $connection;\n $this->studentGateway = $studentGateway;\n $this->assessmentGateway = $assessmentGateway;\n }\n\n /**\n * Generate recommendations for a student based on their assessment data\n *\n * @param string $gibbonPersonID\n * @return array\n */\n public function generateRecommendations($gibbonPersonID)\n {\n // Get student's assessment data\n $criteria = $this->assessmentGateway->newQueryCriteria()\n ->sortBy(['date', 'DESC']);\n\n $assessments = $this->assessmentGateway->queryAssessmentsByStudent($criteria, $gibbonPersonID);\n \n $recommendations = [];\n foreach ($assessments as $assessment) {\n $recommendation = $this->generateSingleRecommendation($assessment);\n if ($recommendation) {\n $recommendations[] = [\n 'assessment' => $assessment,\n 'recommendation' => $recommendation\n ];\n \n // Store the recommendation\n $this->storeRecommendation($gibbonPersonID, $assessment, $recommendation);\n }\n }\n \n return $recommendations;\n }\n\n /**\n * Generate a recommendation for a single assessment\n *\n * @param array $assessment\n * @return string|null\n */\n protected function generateSingleRecommendation($assessment)\n {\n // Get the API key\n $apiKey = $this->getSecureAPIKey();\n if (empty($apiKey)) {\n return null;\n }\n \n // Prepare the prompt for the AI\n $prompt = $this->preparePrompt($assessment);\n \n try {\n // Call the DeepSeek API\n $ch = curl_init();\n curl_setopt($ch, CURLOPT_URL, 'https://api.deepseek.com/v1/chat/completions');\n curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\n curl_setopt($ch, CURLOPT_POST, true);\n curl_setopt($ch, CURLOPT_HTTPHEADER, [\n 'Content-Type: application/json',\n 'Authorization: Bearer ' . $apiKey\n ]);\n \n $data = [\n 'model' => 'deepseek-v4-flash',\n 'messages' => [\n [\n 'role' => 'system',\n 'content' => 'You are an educational AI assistant providing specific, actionable recommendations for academic improvement based on assessment data and teacher feedback.'\n ],\n [\n 'role' => 'user',\n 'content' => $prompt\n ]\n ],\n 'max_tokens' => 500\n ];\n \n curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));\n \n $response = curl_exec($ch);\n $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);\n curl_close($ch);\n \n if ($httpCode === 200) {\n $result = json_decode($response, true);\n return $result['choices'][0]['message']['content'];\n }\n } catch (\\Exception $e) {\n return null;\n }\n \n return null;\n }\n\n /**\n * Prepare the AI prompt based on assessment data\n *\n * @param array $assessment\n * @return string\n */\n protected function preparePrompt($assessment)\n {\n $prompt = \"Based on the following assessment data:\\n\";\n $prompt .= \"Subject: {$assessment['subject']}\\n\";\n $prompt .= \"Grade: {$assessment['grade']}\\n\";\n $prompt .= \"Teacher Feedback: {$assessment['feedback']}\\n\\n\";\n $prompt .= \"Please provide specific, actionable recommendations for improvement in this subject. Consider:\\n\";\n $prompt .= \"1. Study strategies\\n\";\n $prompt .= \"2. Practice exercises\\n\";\n $prompt .= \"3. Additional resources\\n\";\n $prompt .= \"4. Areas needing focus based on the assessment\\n\";\n $prompt .= \"5. Time management suggestions\";\n \n return $prompt;\n }\n\n /**\n * Store a recommendation in the database\n *\n * @param string $gibbonPersonID\n * @param array $assessment\n * @param string $recommendation\n * @return bool\n */\n protected function storeRecommendation($gibbonPersonID, $assessment, $recommendation)\n {\n try {\n $data = [\n 'gibbonPersonID' => $gibbonPersonID,\n 'gibbonCourseClassID' => $assessment['gibbonCourseClassID'],\n 'assessmentID' => $assessment['assessmentID'],\n 'recommendation' => $recommendation\n ];\n \n $sql = \"INSERT INTO gibbonChatBotRecommendations \n (gibbonPersonID, gibbonCourseClassID, assessmentID, recommendation) \n VALUES \n (:gibbonPersonID, :gibbonCourseClassID, :assessmentID, :recommendation)\";\n \n $stmt = $this->connection->prepare($sql);\n return $stmt->execute($data);\n } catch (\\Exception $e) {\n return false;\n }\n }\n\n /**\n * Get the secure API key\n *\n * @return string|null\n */\n protected function getSecureAPIKey()\n {\n return getSecureAPIKey($this->connection);\n }\n} "}, "030e63497fd5a0ea218107feedd91d649324421f": {"t": 1785756296.986118, "n": 5844, "h": "2f8a6179b72e8e95", "c": "connection = $connection;\n $this->studentGateway = $studentGateway;\n $this->assessmentGateway = $assessmentGateway;\n }\n\n /**\n * Generate recommendations for a student based on their assessment data\n *\n * @param string $gibbonPersonID\n * @return array\n */\n public function generateRecommendations($gibbonPersonID)\n {\n // Get student's assessment data\n $criteria = $this->assessmentGateway->newQueryCriteria()\n ->sortBy(['date', 'DESC']);\n\n $assessments = $this->assessmentGateway->queryAssessmentsByStudent($criteria, $gibbonPersonID);\n \n $recommendations = [];\n foreach ($assessments as $assessment) {\n $recommendation = $this->generateSingleRecommendation($assessment);\n if ($recommendation) {\n $recommendations[] = [\n 'assessment' => $assessment,\n 'recommendation' => $recommendation\n ];\n \n // Store the recommendation\n $this->storeRecommendation($gibbonPersonID, $assessment, $recommendation);\n }\n }\n \n return $recommendations;\n }\n\n /**\n * Generate a recommendation for a single assessment\n *\n * @param array $assessment\n * @return string|null\n */\n protected function generateSingleRecommendation($assessment)\n {\n // Get the API key\n $apiKey = $this->getSecureAPIKey();\n if (empty($apiKey)) {\n return null;\n }\n \n // Prepare the prompt for the AI\n $prompt = $this->preparePrompt($assessment);\n \n try {\n // Call the DeepSeek API\n $ch = curl_init();\n curl_setopt($ch, CURLOPT_URL, 'https://api.deepseek.com/v1/chat/completions');\n curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\n curl_setopt($ch, CURLOPT_POST, true);\n curl_setopt($ch, CURLOPT_HTTPHEADER, [\n 'Content-Type: application/json',\n 'Authorization: Bearer ' . $apiKey\n ]);\n \n $data = [\n 'model' => 'deepseek-chat',\n 'messages' => [\n [\n 'role' => 'system',\n 'content' => 'You are an educational AI assistant providing specific, actionable recommendations for academic improvement based on assessment data and teacher feedback.'\n ],\n [\n 'role' => 'user',\n 'content' => $prompt\n ]\n ],\n 'max_tokens' => 500\n ];\n \n curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));\n \n $response = curl_exec($ch);\n $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);\n curl_close($ch);\n \n if ($httpCode === 200) {\n $result = json_decode($response, true);\n return $result['choices'][0]['message']['content'];\n }\n } catch (\\Exception $e) {\n return null;\n }\n \n return null;\n }\n\n /**\n * Prepare the AI prompt based on assessment data\n *\n * @param array $assessment\n * @return string\n */\n protected function preparePrompt($assessment)\n {\n $prompt = \"Based on the following assessment data:\\n\";\n $prompt .= \"Subject: {$assessment['subject']}\\n\";\n $prompt .= \"Grade: {$assessment['grade']}\\n\";\n $prompt .= \"Teacher Feedback: {$assessment['feedback']}\\n\\n\";\n $prompt .= \"Please provide specific, actionable recommendations for improvement in this subject. Consider:\\n\";\n $prompt .= \"1. Study strategies\\n\";\n $prompt .= \"2. Practice exercises\\n\";\n $prompt .= \"3. Additional resources\\n\";\n $prompt .= \"4. Areas needing focus based on the assessment\\n\";\n $prompt .= \"5. Time management suggestions\";\n \n return $prompt;\n }\n\n /**\n * Store a recommendation in the database\n *\n * @param string $gibbonPersonID\n * @param array $assessment\n * @param string $recommendation\n * @return bool\n */\n protected function storeRecommendation($gibbonPersonID, $assessment, $recommendation)\n {\n try {\n $data = [\n 'gibbonPersonID' => $gibbonPersonID,\n 'gibbonCourseClassID' => $assessment['gibbonCourseClassID'],\n 'assessmentID' => $assessment['assessmentID'],\n 'recommendation' => $recommendation\n ];\n \n $sql = \"INSERT INTO gibbonChatBotRecommendations \n (gibbonPersonID, gibbonCourseClassID, assessmentID, recommendation) \n VALUES \n (:gibbonPersonID, :gibbonCourseClassID, :assessmentID, :recommendation)\";\n \n $stmt = $this->connection->prepare($sql);\n return $stmt->execute($data);\n } catch (\\Exception $e) {\n return false;\n }\n }\n\n /**\n * Get the secure API key\n *\n * @return string|null\n */\n protected function getSecureAPIKey()\n {\n return getSecureAPIKey($this->connection);\n }\n} "}, "ea2bf72c5d38ab35b13101b6131cf74ba37f78a3": {"t": 1786068382.811084, "n": 5848, "h": "2e2c52b295b18cfa", "c": "connection = $connection;\n $this->studentGateway = $studentGateway;\n $this->assessmentGateway = $assessmentGateway;\n }\n\n /**\n * Generate recommendations for a student based on their assessment data\n *\n * @param string $gibbonPersonID\n * @return array\n */\n public function generateRecommendations($gibbonPersonID)\n {\n // Get student's assessment data\n $criteria = $this->assessmentGateway->newQueryCriteria()\n ->sortBy(['date', 'DESC']);\n\n $assessments = $this->assessmentGateway->queryAssessmentsByStudent($criteria, $gibbonPersonID);\n \n $recommendations = [];\n foreach ($assessments as $assessment) {\n $recommendation = $this->generateSingleRecommendation($assessment);\n if ($recommendation) {\n $recommendations[] = [\n 'assessment' => $assessment,\n 'recommendation' => $recommendation\n ];\n \n // Store the recommendation\n $this->storeRecommendation($gibbonPersonID, $assessment, $recommendation);\n }\n }\n \n return $recommendations;\n }\n\n /**\n * Generate a recommendation for a single assessment\n *\n * @param array $assessment\n * @return string|null\n */\n protected function generateSingleRecommendation($assessment)\n {\n // Get the API key\n $apiKey = $this->getSecureAPIKey();\n if (empty($apiKey)) {\n return null;\n }\n \n // Prepare the prompt for the AI\n $prompt = $this->preparePrompt($assessment);\n \n try {\n // Call the DeepSeek API\n $ch = curl_init();\n curl_setopt($ch, CURLOPT_URL, 'https://api.deepseek.com/v1/chat/completions');\n curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\n curl_setopt($ch, CURLOPT_POST, true);\n curl_setopt($ch, CURLOPT_HTTPHEADER, [\n 'Content-Type: application/json',\n 'Authorization: Bearer ' . $apiKey\n ]);\n \n $data = [\n 'model' => 'deepseek-v4-flash',\n 'messages' => [\n [\n 'role' => 'system',\n 'content' => 'You are an educational AI assistant providing specific, actionable recommendations for academic improvement based on assessment data and teacher feedback.'\n ],\n [\n 'role' => 'user',\n 'content' => $prompt\n ]\n ],\n 'max_tokens' => 500\n ];\n \n curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));\n \n $response = curl_exec($ch);\n $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);\n curl_close($ch);\n \n if ($httpCode === 200) {\n $result = json_decode($response, true);\n return $result['choices'][0]['message']['content'];\n }\n } catch (\\Exception $e) {\n return null;\n }\n \n return null;\n }\n\n /**\n * Prepare the AI prompt based on assessment data\n *\n * @param array $assessment\n * @return string\n */\n protected function preparePrompt($assessment)\n {\n $prompt = \"Based on the following assessment data:\\n\";\n $prompt .= \"Subject: {$assessment['subject']}\\n\";\n $prompt .= \"Grade: {$assessment['grade']}\\n\";\n $prompt .= \"Teacher Feedback: {$assessment['feedback']}\\n\\n\";\n $prompt .= \"Please provide specific, actionable recommendations for improvement in this subject. Consider:\\n\";\n $prompt .= \"1. Study strategies\\n\";\n $prompt .= \"2. Practice exercises\\n\";\n $prompt .= \"3. Additional resources\\n\";\n $prompt .= \"4. Areas needing focus based on the assessment\\n\";\n $prompt .= \"5. Time management suggestions\";\n \n return $prompt;\n }\n\n /**\n * Store a recommendation in the database\n *\n * @param string $gibbonPersonID\n * @param array $assessment\n * @param string $recommendation\n * @return bool\n */\n protected function storeRecommendation($gibbonPersonID, $assessment, $recommendation)\n {\n try {\n $data = [\n 'gibbonPersonID' => $gibbonPersonID,\n 'gibbonCourseClassID' => $assessment['gibbonCourseClassID'],\n 'assessmentID' => $assessment['assessmentID'],\n 'recommendation' => $recommendation\n ];\n \n $sql = \"INSERT INTO gibbonChatBotRecommendations \n (gibbonPersonID, gibbonCourseClassID, assessmentID, recommendation) \n VALUES \n (:gibbonPersonID, :gibbonCourseClassID, :assessmentID, :recommendation)\";\n \n $stmt = $this->connection->prepare($sql);\n return $stmt->execute($data);\n } catch (\\Exception $e) {\n return false;\n }\n }\n\n /**\n * Get the secure API key\n *\n * @return string|null\n */\n protected function getSecureAPIKey()\n {\n return getSecureAPIKey($this->connection);\n }\n} "}}, "modules/aiTeacher/your_resource_generator_handler.php": {"5eacf702b1d14d58114e1990619ea4913b19ff59": {"t": 1786067468.3227258, "n": 2182, "h": "9408c739e637d4ab", "c": "// ...\n// require_once __DIR__ . '/../../gibbon.php'; // Or your Gibbon bootstrap\n// require_once __DIR__ . '/moduleFunctions.php';\n// require_once __DIR__ . '/src/DeepSeekAPI.php';\n\n// header('Content-Type: application/json'); // If you expect JSON back\n\n// $settings = getAITeacherSettings($pdo); // Assuming $pdo is initialized\n// $apiKey = $settings['deepseek_api_key'] ?? null;\n\n// if (!$apiKey) {\n// error_log(\"Resource Generator Error: API key not configured.\");\n// echo json_encode(['success' => false, 'error' => 'AI system error: API key not configured.']);\n// exit;\n// }\n\n// // Get data from the form (example for POST)\n// $subject = $_POST['subject'] ?? '';\n// $topic = $_POST['topic'] ?? '';\n// $assessmentType = $_POST['assessmentType'] ?? '';\n// $customPrompt = $_POST['customPrompt'] ?? '';\n\n// // Basic validation\n// if (empty($subject) || empty($topic) || empty($assessmentType)) {\n// error_log(\"Resource Generator Error: Missing required fields.\");\n// echo json_encode(['success' => false, 'error' => 'Missing required fields. Please fill in Subject, Topic, and Assessment Type.']);\n// exit;\n// }\n\n// // Construct the prompt for the AI\n// $prompt = \"Generate a {$assessmentType} for the subject '{$subject}' on the topic '{$topic}'.\";\n// if (!empty($customPrompt)) {\n// $prompt .= \" Follow these instructions: {$customPrompt}\";\n// }\n// // Add more details to the prompt as needed for better results.\n\n// try {\n// $api = new \\Gibbon\\Module\\aiTeacher\\DeepSeekAPI($apiKey);\n// $generatedContent = $api->generateResponse($prompt);\n\n// // Send the successful response back to the frontend\n// echo json_encode(['success' => true, 'assessment' => nl2br(htmlspecialchars($generatedContent))]);\n\n// } catch (\\Exception $e) {\n// // Log the detailed error to PHP error log\n// error_log(\"Resource Generator - AI API Call Failed: \" . $e->getMessage());\n// error_log(\"Prompt used: \" . $prompt); // Log the prompt for debugging\n\n// // Send a generic error to the client\n// echo json_encode(['success' => false, 'error' => 'AI system error: Failed to generate assessment. Please check system logs.']);\n// }\n// exit;"}, "ea2bf72c5d38ab35b13101b6131cf74ba37f78a3": {"t": 1786068316.061292, "n": 2182, "h": "9408c739e637d4ab", "c": "// ...\n// require_once __DIR__ . '/../../gibbon.php'; // Or your Gibbon bootstrap\n// require_once __DIR__ . '/moduleFunctions.php';\n// require_once __DIR__ . '/src/DeepSeekAPI.php';\n\n// header('Content-Type: application/json'); // If you expect JSON back\n\n// $settings = getAITeacherSettings($pdo); // Assuming $pdo is initialized\n// $apiKey = $settings['deepseek_api_key'] ?? null;\n\n// if (!$apiKey) {\n// error_log(\"Resource Generator Error: API key not configured.\");\n// echo json_encode(['success' => false, 'error' => 'AI system error: API key not configured.']);\n// exit;\n// }\n\n// // Get data from the form (example for POST)\n// $subject = $_POST['subject'] ?? '';\n// $topic = $_POST['topic'] ?? '';\n// $assessmentType = $_POST['assessmentType'] ?? '';\n// $customPrompt = $_POST['customPrompt'] ?? '';\n\n// // Basic validation\n// if (empty($subject) || empty($topic) || empty($assessmentType)) {\n// error_log(\"Resource Generator Error: Missing required fields.\");\n// echo json_encode(['success' => false, 'error' => 'Missing required fields. Please fill in Subject, Topic, and Assessment Type.']);\n// exit;\n// }\n\n// // Construct the prompt for the AI\n// $prompt = \"Generate a {$assessmentType} for the subject '{$subject}' on the topic '{$topic}'.\";\n// if (!empty($customPrompt)) {\n// $prompt .= \" Follow these instructions: {$customPrompt}\";\n// }\n// // Add more details to the prompt as needed for better results.\n\n// try {\n// $api = new \\Gibbon\\Module\\aiTeacher\\DeepSeekAPI($apiKey);\n// $generatedContent = $api->generateResponse($prompt);\n\n// // Send the successful response back to the frontend\n// echo json_encode(['success' => true, 'assessment' => nl2br(htmlspecialchars($generatedContent))]);\n\n// } catch (\\Exception $e) {\n// // Log the detailed error to PHP error log\n// error_log(\"Resource Generator - AI API Call Failed: \" . $e->getMessage());\n// error_log(\"Prompt used: \" . $prompt); // Log the prompt for debugging\n\n// // Send a generic error to the client\n// echo json_encode(['success' => false, 'error' => 'AI system error: Failed to generate assessment. Please check system logs.']);\n// }\n// exit;"}}, "modules/aiTeacher/manifest.php": {"ea2bf72c5d38ab35b13101b6131cf74ba37f78a3": {"t": 1786068316.2150326, "n": 17595, "h": "22ba78d2663f25a0", "c": ".\n*/\n\n// This file describes the module, including database tables\n\n// Basic variables\n$name = 'aiTeacher'; // The name of the module as it appears to users\n$description = 'AI-powered teaching assistant for CSEC curriculum support, assessment analysis, and teacher productivity tools.';\n$entryURL = \"index.php\";\n$type = \"Additional\";\n$category = 'Teaching & Learning';\n$version = '2.1.00';\n$author = 'Asley Smith';\n$url = 'https://tasanz.com'; \n\n\n// Module tables\n$moduleTables = array();\n\n$moduleTables[] = \"\nCREATE TABLE `gibbonChatBotFeedback` (\n `id` int(10) unsigned zerofill NOT NULL AUTO_INCREMENT,\n `user_message` text NOT NULL,\n `ai_response` text NOT NULL,\n `feedback_type` enum('like','dislike') NOT NULL,\n `feedback_text` text,\n `timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\";\n\n$moduleTables[] = \"\nCREATE TABLE `gibbonChatBotTraining` (\n `gibbonChatBotTrainingID` int(10) unsigned zerofill NOT NULL AUTO_INCREMENT,\n `question` text NOT NULL,\n `answer` text NOT NULL,\n `approved` tinyint(1) NOT NULL DEFAULT '0',\n `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n PRIMARY KEY (`gibbonChatBotTrainingID`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\";\n\n$moduleTables[] = \"\nCREATE TABLE `gibbonChatBotCourseMaterials` (\n `gibbonChatBotCourseMaterialsID` int(10) unsigned zerofill NOT NULL AUTO_INCREMENT,\n `title` varchar(255) NOT NULL,\n `type` varchar(50) NOT NULL,\n `description` text NULL,\n `filePath` varchar(255) NULL,\n `gibbonCourseID` int(8) unsigned zerofill NOT NULL,\n `gibbonSchoolYearID` int(3) unsigned zerofill NOT NULL,\n `dateAdded` date NOT NULL,\n `gibbonPersonIDCreator` int(10) unsigned zerofill NOT NULL,\n PRIMARY KEY (`gibbonChatBotCourseMaterialsID`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\";\n\n$moduleTables[] = \"\nCREATE TABLE `gibbonChatBotStudentProgress` (\n `gibbonChatBotStudentProgressID` int(10) unsigned zerofill NOT NULL AUTO_INCREMENT,\n `gibbonPersonID` int(10) unsigned zerofill NOT NULL,\n `gibbonCourseID` int(8) unsigned zerofill NOT NULL,\n `gibbonSchoolYearID` int(3) unsigned zerofill NOT NULL,\n `progress` decimal(5,2) NOT NULL DEFAULT '0.00',\n `lastActivity` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n PRIMARY KEY (`gibbonChatBotStudentProgressID`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\";\n\n$moduleTables[] = \"\nCREATE TABLE `gibbonChatBotStudentAnalytics` (\n `gibbonChatBotStudentAnalyticsID` int(10) unsigned zerofill NOT NULL AUTO_INCREMENT,\n `gibbonPersonID` int(10) unsigned zerofill NOT NULL,\n `gibbonCourseID` int(8) unsigned zerofill NOT NULL,\n `gibbonSchoolYearID` int(3) unsigned zerofill NOT NULL,\n `analyticsData` text NOT NULL,\n `dateGenerated` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n PRIMARY KEY (`gibbonChatBotStudentAnalyticsID`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\";\n\n$moduleTables[] = \"\nCREATE TABLE `gibbonChatBotInterventions` (\n `gibbonChatBotInterventionsID` int(10) unsigned zerofill NOT NULL AUTO_INCREMENT,\n `gibbonPersonID` int(10) unsigned zerofill NOT NULL,\n `gibbonCourseID` int(8) unsigned zerofill NOT NULL,\n `gibbonSchoolYearID` int(3) unsigned zerofill NOT NULL,\n `interventionType` varchar(50) NOT NULL,\n `description` text NOT NULL,\n `status` enum('Active','Completed','Cancelled') NOT NULL DEFAULT 'Active',\n `dateCreated` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n `dateModified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n PRIMARY KEY (`gibbonChatBotInterventionsID`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\";\n\n$moduleTables[] = \"\nCREATE TABLE IF NOT EXISTS `aiTeacherLogs` (\n `aiTeacherLogID` int(10) unsigned NOT NULL AUTO_INCREMENT,\n `gibbonPersonID` int(10) unsigned NOT NULL,\n `timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n `action` varchar(50) NOT NULL,\n `subject` varchar(100) NOT NULL,\n `details` text,\n `response` text,\n `feedback` text,\n PRIMARY KEY (`aiTeacherLogID`),\n KEY `gibbonPersonID` (`gibbonPersonID`),\n CONSTRAINT `aiTeacherLogs_ibfk_1` FOREIGN KEY (`gibbonPersonID`) REFERENCES `gibbonPerson` (`gibbonPersonID`) ON DELETE CASCADE\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\";\n\n$moduleTables[] = \"\nCREATE TABLE IF NOT EXISTS `aiTeacherUploads` (\n `aiTeacherUploadID` int(10) unsigned NOT NULL AUTO_INCREMENT,\n `gibbonPersonID` int(10) unsigned NOT NULL,\n `timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n `filename` varchar(255) NOT NULL,\n `filepath` varchar(255) NOT NULL,\n `filetype` varchar(50) NOT NULL,\n `filesize` int(10) unsigned NOT NULL,\n `subject` varchar(100) NOT NULL,\n `description` text,\n `status` enum('pending','processed','failed') NOT NULL DEFAULT 'pending',\n PRIMARY KEY (`aiTeacherUploadID`),\n KEY `gibbonPersonID` (`gibbonPersonID`),\n CONSTRAINT `aiTeacherUploads_ibfk_1` FOREIGN KEY (`gibbonPersonID`) REFERENCES `gibbonPerson` (`gibbonPersonID`) ON DELETE CASCADE\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\";\n\n// gibbonSettings: array of associative arrays (not SQL)\n$gibbonSettings = array(\n array(\n 'name' => 'aiTeacher_deepseek_api_key',\n 'value' => '',\n 'description' => 'DeepSeek API Key for AI integration'\n ),\n array(\n 'name' => 'aiTeacher_upload_path',\n 'value' => 'uploads/aiTeacher',\n 'description' => 'Path for storing uploaded resources'\n ),\n array(\n 'name' => 'aiTeacher_score_threshold',\n 'value' => '60',\n 'description' => 'Threshold for student performance alerts (percentage)'\n )\n);\n\n// Action rows: array of associative arrays (not SQL)\n$actionRows = array(\n array(\n 'name' => 'aiTeacher',\n 'precedence' => '0',\n 'category' => 'General',\n 'description' => 'Main dashboard for AI Teacher Assistance features',\n 'URLList' => 'index.php',\n 'entryURL' => 'index.php',\n 'entrySidebar' => 'Y',\n 'menuShow' => 'Y',\n 'defaultPermissionAdmin' => 'Y',\n 'defaultPermissionTeacher' => 'Y',\n 'defaultPermissionStudent' => 'N',\n 'defaultPermissionParent' => 'N',\n 'defaultPermissionSupport' => 'Y',\n 'categoryPermissionStaff' => 'Y',\n 'categoryPermissionStudent' => 'N',\n 'categoryPermissionParent' => 'N',\n 'categoryPermissionOther' => 'N'\n ),\n array(\n 'name' => 'Curriculum Support',\n 'precedence' => '1',\n 'category' => 'Features',\n 'description' => 'Generate lesson plans and schemes of work for CSEC subjects',\n 'URLList' => 'curriculum_support.php',\n 'entryURL' => 'curriculum_support.php',\n 'entrySidebar' => 'Y',\n 'menuShow' => 'Y',\n 'defaultPermissionAdmin' => 'Y',\n 'defaultPermissionTeacher' => 'Y',\n 'defaultPermissionStudent' => 'N',\n 'defaultPermissionParent' => 'N',\n 'defaultPermissionSupport' => 'Y',\n 'categoryPermissionStaff' => 'Y',\n 'categoryPermissionStudent' => 'N',\n 'categoryPermissionParent' => 'N',\n 'categoryPermissionOther' => 'N'\n ),\n array(\n 'name' => 'Assessment Analysis',\n 'precedence' => '2',\n 'category' => 'Features',\n 'description' => 'Analyze student performance and generate intervention strategies',\n 'URLList' => 'assessment_analysis.php',\n 'entryURL' => 'assessment_analysis.php',\n 'entrySidebar' => 'Y',\n 'menuShow' => 'Y',\n 'defaultPermissionAdmin' => 'Y',\n 'defaultPermissionTeacher' => 'Y',\n 'defaultPermissionStudent' => 'N',\n 'defaultPermissionParent' => 'N',\n 'defaultPermissionSupport' => 'Y',\n 'categoryPermissionStaff' => 'Y',\n 'categoryPermissionStudent' => 'N',\n 'categoryPermissionParent' => 'N',\n 'categoryPermissionOther' => 'N'\n ),\n array(\n 'name' => 'Resource Generator',\n 'precedence' => '3',\n 'category' => 'Features',\n 'description' => 'Generate quizzes, worksheets, and assessment materials',\n 'URLList' => 'resource_generator.php',\n 'entryURL' => 'resource_generator.php',\n 'entrySidebar' => 'Y',\n 'menuShow' => 'Y',\n 'defaultPermissionAdmin' => 'Y',\n 'defaultPermissionTeacher' => 'Y',\n 'defaultPermissionStudent' => 'N',\n 'defaultPermissionParent' => 'N',\n 'defaultPermissionSupport' => 'Y',\n 'categoryPermissionStaff' => 'Y',\n 'categoryPermissionStudent' => 'N',\n 'categoryPermissionParent' => 'N',\n 'categoryPermissionOther' => 'N'\n ),\n array(\n 'name' => 'Settings',\n 'precedence' => '4',\n 'category' => 'Configuration',\n 'description' => 'Configure AI Teacher Assistance settings',\n 'URLList' => 'settings.php',\n 'entryURL' => 'settings.php',\n 'entrySidebar' => 'Y',\n 'menuShow' => 'Y',\n 'defaultPermissionAdmin' => 'Y',\n 'defaultPermissionTeacher' => 'N',\n 'defaultPermissionStudent' => 'N',\n 'defaultPermissionParent' => 'N',\n 'defaultPermissionSupport' => 'N',\n 'categoryPermissionStaff' => 'Y',\n 'categoryPermissionStudent' => 'N',\n 'categoryPermissionParent' => 'N',\n 'categoryPermissionOther' => 'N'\n ),\n array(\n 'name' => 'AI Chatbot',\n 'precedence' => '5',\n 'category' => 'Features',\n 'description' => 'Chat with the AI Teacher Assistant and request content',\n 'URLList' => 'chatbot.php',\n 'entryURL' => 'chatbot.php',\n 'entrySidebar' => 'Y',\n 'menuShow' => 'Y',\n 'defaultPermissionAdmin' => 'Y',\n 'defaultPermissionTeacher' => 'Y',\n 'defaultPermissionStudent' => 'Y',\n 'defaultPermissionParent' => 'N',\n 'defaultPermissionSupport' => 'Y',\n 'categoryPermissionStaff' => 'Y',\n 'categoryPermissionStudent' => 'Y',\n 'categoryPermissionParent' => 'N',\n 'categoryPermissionOther' => 'N'\n ),\n // Module actions\n array(\n 'name' => __('Assessment Analysis Renamed'), // Temporarily changed\n 'precedence' => '6',\n 'category' => 'Features',\n 'description' => __('Analyze student assessments and get AI recommendations for failing students.'),\n 'URLList' => 'assessment_analysis.php',\n 'entryURL' => 'assessment_analysis.php',\n 'entrySidebar' => 'Y',\n 'menuShow' => 'Y',\n 'defaultPermissionAdmin' => 'Y',\n 'defaultPermissionTeacher' => 'Y',\n 'defaultPermissionStudent' => 'N',\n 'defaultPermissionParent' => 'N',\n 'defaultPermissionSupport' => 'Y',\n 'categoryPermissionStaff' => 'Y',\n 'categoryPermissionStudent' => 'N',\n 'categoryPermissionParent' => 'N',\n 'categoryPermissionOther' => 'N',\n 'displayPermissions' => array(\n 'Staff',\n 'Admin'\n )\n ),\n array(\n 'name' => __('My AI Feedback'), // New action for students\n 'precedence' => '7', // Added\n 'category' => 'Features', // Or a new category like 'Student Tools'\n 'description' => __('View AI-generated feedback for your assessments.'),\n 'URLList' => 'my_ai_feedback.php', // Added\n 'entryURL' => 'my_ai_feedback.php',\n 'entrySidebar' => 'Y', // Added\n 'menuShow' => 'Y', // Added\n 'defaultPermissionAdmin' => 'N', // Added (Admins might not need direct access here, or 'Y')\n 'defaultPermissionTeacher' => 'N', // Added\n 'defaultPermissionStudent' => 'Y', // Added\n 'defaultPermissionParent' => 'N', // Added\n 'defaultPermissionSupport' => 'N', // Added\n 'categoryPermissionStaff' => 'N', // Added\n 'categoryPermissionStudent' => 'Y', // Added\n 'categoryPermissionParent' => 'N', // Added\n 'categoryPermissionOther' => 'N', // Added\n 'displayPermissions' => array(\n 'Student'\n )\n ),\n array(\n 'name' => 'Get AI Recommendation', // This is likely an internal/AJAX script\n 'precedence' => '8', // Added\n 'category' => 'AJAX', // Or similar internal category\n 'description' => 'Handles AJAX requests for AI recommendations.',\n 'URLList' => 'get_ai_recommendation.php', // Added\n 'entryURL' => 'get_ai_recommendation.php',\n 'entrySidebar' => 'N', // Added (AJAX endpoints usually not in sidebar)\n 'menuShow' => 'N', // Added (AJAX endpoints usually not in menu)\n 'defaultPermissionAdmin' => 'Y', // Added\n 'defaultPermissionTeacher' => 'Y', // Added\n 'defaultPermissionStudent' => 'Y', // Added (If students can trigger this)\n 'defaultPermissionParent' => 'N', // Added\n 'defaultPermissionSupport' => 'Y', // Added\n 'categoryPermissionStaff' => 'Y', // Added\n 'categoryPermissionStudent' => 'Y', // Added\n 'categoryPermissionParent' => 'N', // Added\n 'categoryPermissionOther' => 'N', // Added\n 'displayPermissions' => array(\n 'Staff',\n 'Admin',\n 'Student'\n ),\n 'secure' => true // Mark as secure if it doesn't render a full page\n ),\n array(\n 'name' => 'AI Tutor Chat',\n 'precedence' => '9',\n 'category' => 'Features',\n 'description' => 'Chat with your personal AI tutor for homework help and study guidance',\n 'URLList' => 'student_ai_tutor.php,student_ai_tutor_ajax.php,student_chat_history.php,student_chat_view.php',\n 'entryURL' => 'student_ai_tutor.php',\n 'entrySidebar' => 'Y',\n 'menuShow' => 'Y',\n 'defaultPermissionAdmin' => 'Y',\n 'defaultPermissionTeacher' => 'Y',\n 'defaultPermissionStudent' => 'Y',\n 'defaultPermissionParent' => 'N',\n 'defaultPermissionSupport' => 'Y',\n 'categoryPermissionStaff' => 'Y',\n 'categoryPermissionStudent' => 'Y',\n 'categoryPermissionParent' => 'N',\n 'categoryPermissionOther' => 'N'\n ),\n\n array(\n 'name' => 'Student AI Tutor Usage',\n 'precedence' => '0',\n 'category' => 'Monitoring',\n 'description' => 'Monitor student AI tutor conversations and view prompts',\n 'URLList' => 'teacher_student_usage.php,teacher_conversation_view.php',\n 'entryURL' => 'teacher_student_usage.php',\n 'entrySidebar' => 'Y',\n 'menuShow' => 'Y',\n 'defaultPermissionAdmin' => 'Y',\n 'defaultPermissionTeacher' => 'Y',\n 'defaultPermissionStudent' => 'N',\n 'defaultPermissionParent' => 'N',\n 'defaultPermissionSupport' => 'N',\n 'categoryPermissionStaff' => 'Y',\n 'categoryPermissionStudent' => 'N',\n 'categoryPermissionParent' => 'N',\n 'categoryPermissionOther' => 'N'\n )\n);\n\n// Hooks: array of associative arrays\n$hooks = array(\n array(\n 'name' => 'AI Teacher Assistance',\n 'type' => 'hook',\n 'options' => array(\n 'source' => 'AI Teacher Assistance',\n 'hook' => 'moduleFunctions.php',\n 'priority' => '1'\n )\n )\n);\n\n// No accidental array assignments to $moduleTables below this point!\n"}}, "modules/aiTeacher/curriculum_support.php": {"ea2bf72c5d38ab35b13101b6131cf74ba37f78a3": {"t": 1786068318.234438, "n": 7668, "h": "77d0530be25f8bfd", "c": ".\n*/\n\n// Gibbon includes\nrequire_once __DIR__ . '/../../gibbon.php';\nrequire_once __DIR__ . '/../../functions.php';\n\n// Module includes\nrequire_once __DIR__ . '/moduleFunctions.php';\n\n$page->breadcrumbs->add(__('AI Teacher Assistance'), 'index.php');\n$page->breadcrumbs->add(__('Curriculum Support'));\n\n// Check if user has access\nif (isActionAccessible($guid, $connection2, '/modules/aiTeacher/curriculum_support.php') == false) {\n // Access denied\n $page->addError(__('You do not have access to this action.'));\n} else {\n // Get module settings\n $settings = getAITeacherSettings($pdo);\n \n // Check if API key is configured\n if (empty($settings['deepseek_api_key'])) {\n $page->addError(__('DeepSeek API key is not configured. Please contact your administrator.'));\n } else {\n // Add CSS for button visibility\n echo <<\n /* Style for form buttons to ensure visibility */\n .button, \n input[type=\"submit\"] { /* Target Gibbon's common button class and generic submit inputs */\n padding: 8px 15px;\n background-color: #007bff; /* A visible background color (e.g., blue) */\n color: white; /* Text color that contrasts with the background */\n border: 1px solid #007bff;\n border-radius: 4px;\n cursor: pointer;\n text-decoration: none; /* If it's an styled as a button */\n display: inline-block; /* Ensure proper rendering */\n font-size: 0.9em;\n }\n .button:hover,\n input[type=\"submit\"]:hover {\n background-color: #0056b3; /* Darker shade on hover */\n border-color: #0056b3;\n }\n /* Specific styling for the form layout if needed */\n .formLayout {\n margin-bottom: 20px;\n }\n .formLayout .right {\n text-align: right; /* Ensure button aligns to the right if container is full width */\n padding-top: 10px;\n }\n\nCSS;\n\n // Handle form submission\n if (isset($_POST['submit'])) {\n $subject = $_POST['subject'] ?? '';\n $topic = $_POST['topic'] ?? '';\n $gradeLevel = $_POST['grade_level'] ?? '';\n $objectives = $_POST['objectives'] ?? ''; // Added objectives\n \n if (empty($subject) || empty($topic) || empty($gradeLevel) || empty($objectives)) { // Added objectives check\n $page->addError(__('Please fill in all required fields, including objectives.'));\n } else {\n try {\n $lessonPlan = generateLessonPlan($pdo, $subject, $topic, $gradeLevel, $objectives); // Pass objectives\n \n // Log the action\n logAITeacherAction($pdo, $gibbon->session->get('gibbonPersonID'), 'Generate Lesson Plan', $subject, \n \"Topic: {$topic}, Grade Level: {$gradeLevel}, Objectives: {$objectives}\", $lessonPlan); // Added objectives to log\n \n echo '
';\n echo '

' . __('Generated Lesson Plan') . '

';\n echo '
' . nl2br(htmlspecialchars($lessonPlan ?? '')) . '
'; // Corrected line\n echo '
';\n } catch (Exception $e) {\n $page->addError(__('Failed to generate lesson plan. Please try again.'));\n }\n }\n }\n \n // Display form\n echo '
session->get('absoluteURL') . '/index.php?q=/modules/aiTeacher/curriculum_support.php\">';\n \n echo '';\n \n // Subject\n echo '';\n echo '';\n echo '';\n echo '';\n \n // Topic\n echo '';\n echo '';\n echo '';\n echo '';\n\n // Objectives\n echo '';\n echo '';\n echo '';\n echo '';\n \n // Grade Level\n echo '';\n echo '';\n echo '';\n echo '';\n \n echo '
';\n echo '' . __('Subject') . ' *
';\n echo __('Select the CSEC subject.') . '
';\n echo '
';\n echo '';\n echo '
';\n echo '' . __('Topic') . ' *
';\n echo __('Enter the specific topic for the lesson plan.') . '
';\n echo '
';\n echo '';\n echo '
';\n echo '' . __('Objectives') . ' *
';\n echo __('Enter the learning objectives for the lesson plan (e.g., \"Students will be able to identify input devices.\").') . '
';\n echo '
';\n echo '';\n echo '
';\n echo '' . __('Grade Level') . ' *
';\n echo __('Select the grade level.') . '
';\n echo '
';\n echo '';\n echo '
';\n \n echo '
'; // This div is used for alignment\n echo ''; // Added class=\"button\"\n echo '
';\n \n echo '
';\n }\n}"}}, "modules/aiTeacher/chatbot.php": {"ea2bf72c5d38ab35b13101b6131cf74ba37f78a3": {"t": 1786068319.7353308, "n": 6336, "h": "ad2546d932d7be1b", "c": "breadcrumbs->add(__('AI Teacher Assistance'), 'index.php');\n$page->breadcrumbs->add(__('AI Chatbot'));\n\nif (!isActionAccessible($guid, $connection2, '/modules/aiTeacher/chatbot.php')) {\n $page->addError(__('You do not have access to this action.'));\n} else {\n $settings = getAITeacherSettings($pdo);\n if (empty($settings['deepseek_api_key'])) {\n $page->addError(__('DeepSeek API key is not configured. Please contact your administrator.'));\n } else {\n try {\n if (!$pdo) throw new Exception('Database connection not initialized');\n\n $pdo->executeQuery([], \"CREATE TABLE IF NOT EXISTS `aiTeacherChats` (\n `aiTeacherChatID` int(10) unsigned NOT NULL AUTO_INCREMENT,\n `gibbonPersonID` int(10) unsigned NOT NULL,\n `title` varchar(255) NOT NULL,\n `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n PRIMARY KEY (`aiTeacherChatID`),\n KEY `gibbonPersonID` (`gibbonPersonID`),\n CONSTRAINT `aiTeacherChats_ibfk_1` FOREIGN KEY (`gibbonPersonID`) REFERENCES `gibbonPerson` (`gibbonPersonID`) ON DELETE CASCADE\n ) ENGINE=InnoDB DEFAULT CHARSET=utf8;\");\n\n $pdo->executeQuery([], \"CREATE TABLE IF NOT EXISTS `aiTeacherChatMessages` (\n `aiTeacherChatMessageID` int(10) unsigned NOT NULL AUTO_INCREMENT,\n `aiTeacherChatID` int(10) unsigned NOT NULL,\n `role` enum('user','assistant') NOT NULL,\n `content` text NOT NULL,\n `timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n PRIMARY KEY (`aiTeacherChatMessageID`),\n KEY `aiTeacherChatID` (`aiTeacherChatID`),\n CONSTRAINT `aiTeacherChatMessages_ibfk_1` FOREIGN KEY (`aiTeacherChatID`) REFERENCES `aiTeacherChats` (`aiTeacherChatID`) ON DELETE CASCADE\n ) ENGINE=InnoDB DEFAULT CHARSET=utf8;\");\n\n $gibbonPersonID = $gibbon->session->get('gibbonPersonID');\n $currentChatID = $_GET['chat_id'] ?? null;\n\n if (isset($_POST['new_chat'])) {\n $title = trim($_POST['chat_title'] ?? 'New Chat');\n $pdo->executeQuery([\n 'gibbonPersonID' => $gibbonPersonID,\n 'title' => $title\n ], \"INSERT INTO aiTeacherChats (gibbonPersonID, title) VALUES (:gibbonPersonID, :title)\");\n $currentChatID = $pdo->getConnection()->lastInsertId();\n }\n\n if (isset($_POST['delete_chat'], $_POST['chat_id'])) {\n $pdo->executeQuery([\n 'chatID' => $_POST['chat_id'],\n 'gibbonPersonID' => $gibbonPersonID\n ], \"DELETE FROM aiTeacherChats WHERE aiTeacherChatID = :chatID AND gibbonPersonID = :gibbonPersonID\");\n $currentChatID = null;\n }\n\n if (isset($_POST['send']) && !empty(trim($_POST['message']))) {\n $userMessage = trim($_POST['message']);\n\n if (!$currentChatID) {\n $title = substr($userMessage, 0, 50) . (strlen($userMessage) > 50 ? '...' : '');\n $pdo->executeQuery([\n 'gibbonPersonID' => $gibbonPersonID,\n 'title' => $title\n ], \"INSERT INTO aiTeacherChats (gibbonPersonID, title) VALUES (:gibbonPersonID, :title)\");\n $currentChatID = $pdo->getConnection()->lastInsertId();\n }\n\n $pdo->executeQuery([\n 'chatID' => $currentChatID,\n 'content' => $userMessage\n ], \"INSERT INTO aiTeacherChatMessages (aiTeacherChatID, role, content) VALUES (:chatID, 'user', :content)\");\n\n $aiResponse = null;\n try {\n $api = new DeepSeekAPI($settings['deepseek_api_key']);\n // $response = $api->send([ // Old problematic call\n // 'model' => 'deepseek-chat',\n // 'messages' => [['role' => 'user', 'content' => $userMessage]]\n // ]);\n // if (!$response['success']) {\n // throw new Exception($response['error']);\n // }\n // $aiResponse = $response['response']['choices'][0]['message']['content'] ?? null;\n $aiResponse = $api->generateResponse($userMessage); // Corrected: Use public method\n\n if ($aiResponse === null) {\n // generateResponse() logs detailed errors internally.\n // Throw a general error for the user.\n throw new Exception('Failed to get a response from the AI service. Please check the system logs.');\n }\n\n // $aiResponse is now the string content directly.\n if ($currentChatID) { // No need to check if $aiResponse is truthy if an empty response is valid.\n $pdo->executeQuery([\n 'chatID' => $currentChatID,\n 'content' => $aiResponse\n ], \"INSERT INTO aiTeacherChatMessages (aiTeacherChatID, role, content) VALUES (:chatID, 'assistant', :content)\");\n\n logAITeacherAction(\n $pdo,\n $gibbonPersonID,\n 'Chatbot Interaction',\n 'Chat Message',\n $userMessage,\n $aiResponse\n );\n }\n } catch (Exception $e) {\n $page->addError(__('AI system error: ') . $e->getMessage());\n }\n }\n\n include __DIR__ . '/chatInterface.php';\n } catch (Exception $e) {\n $page->addError(__('Database error: ') . $e->getMessage());\n }\n }\n}\n"}}}}