getMessage()); echo json_encode([ 'success' => false, 'message' => 'Failed to load required files', 'error' => $e->getMessage() ]); exit; } class PanelOptimizationHandler { private $db; private $auth; private $batchSize = 100; private $analysisState = [ 'is_running' => false, 'is_paused' => false, 'processed_count' => 0, 'total_count' => 0, 'current_id' => null, 'last_processed_time' => null, 'status' => '' ]; private $gptConfig = [ "model" => "gpt-4", "temperature" => 0.7, "max_tokens" => 150 ]; public function __construct() { try { $this->db = Database::getInstance(); $this->auth = new Auth(); // Create required tables if they don't exist $this->ensureTablesExist(); $this->loadAnalysisState(); } catch (Exception $e) { logError("Constructor error: " . $e->getMessage()); throw $e; } } private function ensureTablesExist() { $this->db->query(" CREATE TABLE IF NOT EXISTS panel_analysis_state ( id INT NOT NULL AUTO_INCREMENT, is_running BOOLEAN DEFAULT FALSE, is_paused BOOLEAN DEFAULT FALSE, processed_count INT DEFAULT 0, total_count INT DEFAULT 0, current_id INT DEFAULT NULL, status VARCHAR(255) DEFAULT '', start_time TIMESTAMP NULL, last_processed TIMESTAMP NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id) ) "); $this->db->query(" CREATE TABLE IF NOT EXISTS panel_directives ( id INT NOT NULL AUTO_INCREMENT, description TEXT NOT NULL, record_count INT DEFAULT 0, status ENUM('pending', 'approved', 'rejected') DEFAULT 'pending', source ENUM('system', 'manual') DEFAULT 'system', created_by INT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), INDEX idx_status (status), INDEX idx_source (source) ) "); $this->db->query(" CREATE TABLE IF NOT EXISTS directive_records ( directive_id INT NOT NULL, panelist_id VARCHAR(10) NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (directive_id, panelist_id), FOREIGN KEY (directive_id) REFERENCES panel_directives(id) ON DELETE CASCADE ) "); } private function loadAnalysisState() { $query = $this->db->query(" SELECT * FROM panel_analysis_state ORDER BY id DESC LIMIT 1 "); if ($query && $query->num_rows > 0) { $state = $query->fetch_assoc(); $this->analysisState = [ 'is_running' => (bool)$state['is_running'], 'is_paused' => (bool)$state['is_paused'], 'processed_count' => (int)$state['processed_count'], 'total_count' => (int)$state['total_count'], 'current_id' => $state['current_id'], 'last_processed_time' => $state['last_processed'], 'status' => $state['status'] ]; } } private function saveAnalysisState() { $this->analysisState['last_processed_time'] = date('Y-m-d H:i:s'); $sql = "INSERT INTO panel_analysis_state (is_running, is_paused, processed_count, total_count, current_id, status, last_processed) VALUES ( " . ($this->analysisState['is_running'] ? 1 : 0) . ", " . ($this->analysisState['is_paused'] ? 1 : 0) . ", " . $this->analysisState['processed_count'] . ", " . $this->analysisState['total_count'] . ", " . ($this->analysisState['current_id'] ? $this->analysisState['current_id'] : 'NULL') . ", '" . $this->db->escape($this->analysisState['status']) . "', '" . $this->analysisState['last_processed_time'] . "' )"; $this->db->query($sql); } public function startAnalysis() { try { if ($this->analysisState['is_running'] && !$this->analysisState['is_paused']) { return [ 'success' => false, 'message' => 'Analysis is already running' ]; } // Get total unprocessed records $countQuery = $this->db->query(" SELECT COUNT(*) as count FROM panel_data p WHERE NOT EXISTS ( SELECT 1 FROM directive_records dr WHERE dr.panelist_id = p.panelist_id ) "); if (!$countQuery) { throw new Exception("Failed to count records"); } $totalRecords = $countQuery->fetch_assoc()['count']; if ($totalRecords === 0) { return [ 'success' => false, 'message' => 'No records to analyze' ]; } if (!$this->analysisState['is_paused']) { // Fresh start $this->analysisState = [ 'is_running' => true, 'is_paused' => false, 'processed_count' => 0, 'total_count' => $totalRecords, 'current_id' => null, 'status' => 'Starting analysis...' ]; } else { // Resume from pause $this->analysisState['is_paused'] = false; $this->analysisState['status'] = 'Resuming analysis...'; } $this->saveAnalysisState(); return [ 'success' => true, 'total' => $totalRecords, 'processed' => $this->analysisState['processed_count'], 'status' => $this->analysisState['status'] ]; } catch (Exception $e) { logError("Start analysis error: " . $e->getMessage()); return [ 'success' => false, 'message' => $e->getMessage() ]; } } public function stopAnalysis() { try { if (!$this->analysisState['is_running']) { return [ 'success' => false, 'message' => 'No analysis running' ]; } $this->analysisState['is_running'] = false; $this->analysisState['is_paused'] = false; $this->analysisState['status'] = 'Analysis stopped'; $this->saveAnalysisState(); return [ 'success' => true, 'message' => 'Analysis stopped successfully' ]; } catch (Exception $e) { logError("Stop analysis error: " . $e->getMessage()); return [ 'success' => false, 'message' => $e->getMessage() ]; } } public function pauseAnalysis() { try { if (!$this->analysisState['is_running']) { return [ 'success' => false, 'message' => 'No analysis running' ]; } $this->analysisState['is_paused'] = true; $this->analysisState['status'] = 'Analysis paused'; $this->saveAnalysisState(); return [ 'success' => true, 'message' => 'Analysis paused successfully' ]; } catch (Exception $e) { logError("Pause analysis error: " . $e->getMessage()); return [ 'success' => false, 'message' => $e->getMessage() ]; } } public function processBatch() { try { if (!$this->analysisState['is_running'] || $this->analysisState['is_paused']) { return [ 'success' => false, 'message' => 'Analysis is not running' ]; } $query = $this->db->query(" SELECT p.*, GROUP_CONCAT(DISTINCT CONCAT(a.name, ': ', JSON_UNQUOTE( JSON_EXTRACT(p.attribute_values, CONCAT('$.', a.id)) ) ) SEPARATOR '\n') as formatted_attributes FROM panel_data p JOIN attributes a ON JSON_EXTRACT(p.attribute_values, CONCAT('$.', a.id)) IS NOT NULL WHERE NOT EXISTS ( SELECT 1 FROM directive_records dr WHERE dr.panelist_id = p.panelist_id ) " . ($this->analysisState['current_id'] ? "AND p.id > " . $this->analysisState['current_id'] : "") . " GROUP BY p.id ORDER BY p.id LIMIT " . $this->batchSize ); if ($query->num_rows === 0) { $this->analysisState['is_running'] = false; $this->analysisState['status'] = 'Analysis complete'; $this->saveAnalysisState(); return [ 'success' => true, 'complete' => true, 'progress' => 100 ]; } $processedInBatch = 0; while ($record = $query->fetch_assoc()) { $this->analysisState['current_id'] = $record['id']; // Process the record $issues = $this->analyzeRecord($record['formatted_attributes']); if ($issues) { foreach ($issues as $issue) { $this->createDirective($issue, $record['panelist_id']); } } $processedInBatch++; $this->analysisState['processed_count']++; } $this->analysisState['status'] = 'Analyzing records...'; $this->saveAnalysisState(); $progress = ($this->analysisState['processed_count'] / $this->analysisState['total_count']) * 100; return [ 'success' => true, 'complete' => false, 'progress' => round($progress, 2), 'processed' => $this->analysisState['processed_count'], 'total' => $this->analysisState['total_count'], 'status' => $this->analysisState['status'] ]; } catch (Exception $e) { logError("Process batch error: " . $e->getMessage()); return [ 'success' => false, 'message' => $e->getMessage() ]; } } private function analyzeRecord($attributes) { try { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, 'https://api.openai.com/v1/chat/completions'); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Content-Type: application/json', 'Authorization: Bearer ' . OPENAI_API_KEY ]); $data = array_merge($this->gptConfig, [ 'messages' => [ [ 'role' => 'system', 'content' => "Analyze this panelist's demographic attributes for logical inconsistencies. Identify only clear contradictions or improbable combinations. Return an array of brief, specific inconsistency descriptions." ], [ 'role' => 'user', 'content' => $attributes ] ], 'response_format' => ['type' => 'json_object'] ]); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data)); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($httpCode !== 200) { throw new Exception("GPT API error: HTTP $httpCode"); } $result = json_decode($response, true); $analysis = json_decode($result['choices'][0]['message']['content'], true); return isset($analysis['issues']) ? $analysis['issues'] : []; } catch (Exception $e) { logError("GPT analysis error: " . $e->getMessage()); return null; } } private function createDirective($description, $panelistId) { $this->db->query("START TRANSACTION"); try { $checkSql = "SELECT id FROM panel_directives WHERE description = '" . $this->db->escape($description) . "' AND status = 'pending'"; $existing = $this->db->query($checkSql); if ($existing && $existing->num_rows > 0) { $directiveId = $existing->fetch_assoc()['id']; } else { $sql = "INSERT INTO panel_directives (description, source, created_by) VALUES ( '" . $this->db->escape($description) . "', 'system', " . $_SESSION['user_id'] . " )"; if (!$this->db->query($sql)) { throw new Exception("Failed to create directive"); } $directiveId = $this->db->getLastInsertId(); } // Add record to directive $sql = "INSERT INTO directive_records (directive_id, panelist_id) VALUES ($directiveId, '" . $this->db->escape($panelistId) . "')"; if (!$this->db->query($sql)) { throw new Exception("Failed to add record to directive"); } // Update record count $this->db->query(" UPDATE panel_directives SET record_count = ( SELECT COUNT(*) FROM directive_records WHERE directive_id = $directiveId ) WHERE id = $directiveId "); $this->db->query("COMMIT"); return true; } catch (Exception $e) { $this->db->query("ROLLBACK"); logError("Create directive error: " . $e->getMessage()); return false; } } } // Request Handler try { header('Content-Type: application/json'); $auth = new Auth(); if (!$auth->isLoggedIn()) { throw new Exception('Unauthorized'); } $handler = new PanelOptimizationHandler(); $action = $_POST['action'] ?? ''; $response = ['success' => false, 'message' => 'Invalid action']; switch ($action) { case 'test': $response = [ 'success' => true, 'message' => 'Optimization handler is working' ]; break; case 'start_analysis': $response = $handler->startAnalysis(); break; case 'process_batch': $response = $handler->processBatch(); break; case 'pause_analysis': $response = $handler->pauseAnalysis(); break; case 'stop_analysis': $response = $handler->stopAnalysis(); break; case 'get_analysis_status': $status = $handler->getAnalysisState(); if ($status) { $response = [ 'success' => true, 'is_running' => $status['is_running'], 'is_paused' => $status['is_paused'], 'processed' => $status['processed_count'], 'total' => $status['total_count'], 'progress' => $status['total_count'] > 0 ? round(($status['processed_count'] / $status['total_count']) * 100, 2) : 0, 'status' => $status['status'] ]; } break; case 'create_directive': if (!isset($_POST['description'])) { throw new Exception('Description is required'); } $response = $handler->createManualDirective( $_POST['description'], $_SESSION['user_id'] ); break; case 'get_directives': $page = intval($_POST['page'] ?? 1); $response = $handler->getDirectives($page); break; case 'approve_directive': if (!isset($_POST['directive_id'])) { throw new Exception('Directive ID is required'); } $response = $handler->approveDirective( intval($_POST['directive_id']) ); break; case 'reject_directive': if (!isset($_POST['directive_id'])) { throw new Exception('Directive ID is required'); } $response = $handler->rejectDirective( intval($_POST['directive_id']) ); break; case 'delete_directive': if (!isset($_POST['directive_id'])) { throw new Exception('Directive ID is required'); } $response = $handler->deleteDirective( intval($_POST['directive_id']) ); break; default: $response = [ 'success' => false, 'message' => 'Unknown action: ' . $action ]; break; } echo json_encode($response); } catch (Exception $e) { logError($e->getMessage()); http_response_code(500); echo json_encode([ 'success' => false, 'message' => 'Server error occurred', 'error' => $e->getMessage() ]); } ?>