2 hours ago * and still has 'clicked' status, it gets marked as 'timeout'. */ error_reporting(E_ALL); ini_set('display_errors', 0); ini_set('log_errors', 1); // Load client session for database connection require_once __DIR__ . '/../clients/client-session.php'; // Get the path parts $request_uri = $_SERVER['REQUEST_URI']; $path = parse_url($request_uri, PHP_URL_PATH); $parts = explode('/', trim($path, '/')); // Expected: ['r', project_id, status] if (count($parts) < 3 || $parts[0] !== 'r') { header('HTTP/1.0 404 Not Found'); echo "Invalid redirect URL"; exit; } $project_id = $parts[1]; $status = strtolower($parts[2]); // Get unique_id from query string (if provided by client) $unique_id = $_GET['uid'] ?? $_GET['id'] ?? $_GET['user'] ?? null; // Valid status values (no quality_term - use latescreenout instead) $valid_statuses = [ 'complete', 'partial', 'earlyscreenout', 'latescreenout', 'quotafull', 'timeout' ]; if (!in_array($status, $valid_statuses)) { $status = 'partial'; // Default to partial if unknown } try { $pdo = getClientDBConnection(); // Get respondent IP $respondentIp = $_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['HTTP_CF_CONNECTING_IP'] ?? $_SERVER['REMOTE_ADDR'] ?? null; if ($respondentIp && strpos($respondentIp, ',') !== false) { $respondentIp = trim(explode(',', $respondentIp)[0]); } // If we have unique_id, update specific URL if ($unique_id) { // First get the current record to calculate LOI $stmt = $pdo->prepare(" SELECT id, clicked_at, project_id, respondent_ip FROM survey_urls WHERE project_id = ? AND unique_identifier = ? LIMIT 1 "); $stmt->execute([$project_id, $unique_id]); $urlRecord = $stmt->fetch(PDO::FETCH_ASSOC); if ($urlRecord) { // Calculate actual LOI in seconds $actualLoiSeconds = null; if ($urlRecord['clicked_at']) { $clickedTime = strtotime($urlRecord['clicked_at']); $actualLoiSeconds = time() - $clickedTime; if ($actualLoiSeconds < 0) $actualLoiSeconds = null; } // Use IP from click if not captured now (fallback) $ip = $respondentIp ?: $urlRecord['respondent_ip']; // Determine quality flag for completes $qualityFlag = 'valid'; $qualityNotes = null; if ($status === 'complete' && $actualLoiSeconds !== null) { // Get project ELOI and speedster threshold for speedster check $stmt2 = $pdo->prepare("SELECT eloi, speedster_threshold_pct FROM projects WHERE project_id = ?"); $stmt2->execute([$project_id]); $proj = $stmt2->fetch(PDO::FETCH_ASSOC); if ($proj && $proj['eloi'] > 0) { $eloiSeconds = intval($proj['eloi']) * 60; // ELOI is in minutes $thresholdPct = floatval($proj['speedster_threshold_pct'] ?? 33.33); $speedThreshold = $eloiSeconds * ($thresholdPct / 100); // Configurable benchmark if ($actualLoiSeconds < $speedThreshold) { $qualityFlag = 'speedster'; $qualityNotes = sprintf( 'aLOI %ds < threshold %ds (ELOI %dmin x %.1f%%)', $actualLoiSeconds, round($speedThreshold), $proj['eloi'], $thresholdPct ); } } // Check IP duplication (only for completes with valid IP) if ($qualityFlag === 'valid' && $ip) { $stmt2 = $pdo->prepare(" SELECT COUNT(*) as cnt FROM survey_urls WHERE project_id = ? AND respondent_ip = ? AND status = 'complete' AND id != ? "); $stmt2->execute([$project_id, $ip, $urlRecord['id']]); $ipCheck = $stmt2->fetch(PDO::FETCH_ASSOC); if ($ipCheck && intval($ipCheck['cnt']) > 0) { $qualityFlag = 'ip_duplicate'; $qualityNotes = sprintf('IP %s already has %d complete(s) in this project', $ip, $ipCheck['cnt']); } } } // Update the URL record $stmt = $pdo->prepare(" UPDATE survey_urls SET status = ?, completed_at = NOW(), actual_loi_seconds = ?, respondent_ip = COALESCE(respondent_ip, ?), quality_flag = ?, quality_notes = ? WHERE project_id = ? AND unique_identifier = ? AND status NOT IN ('complete') "); $stmt->execute([ $status, $actualLoiSeconds, $ip, $qualityFlag, $qualityNotes, $project_id, $unique_id ]); $updated = $stmt->rowCount(); if ($updated > 0) { error_log("Survey status updated: Project=$project_id, UniqueID=$unique_id, Status=$status, LOI={$actualLoiSeconds}s, QFlag=$qualityFlag"); } } } else { // No unique_id provided - try to find most recent clicked URL for this project $stmt = $pdo->prepare(" SELECT id, clicked_at FROM survey_urls WHERE project_id = ? AND status = 'clicked' ORDER BY clicked_at DESC LIMIT 1 "); $stmt->execute([$project_id]); $urlRecord = $stmt->fetch(PDO::FETCH_ASSOC); $actualLoiSeconds = null; if ($urlRecord && $urlRecord['clicked_at']) { $actualLoiSeconds = time() - strtotime($urlRecord['clicked_at']); if ($actualLoiSeconds < 0) $actualLoiSeconds = null; } $stmt = $pdo->prepare(" UPDATE survey_urls SET status = ?, completed_at = NOW(), actual_loi_seconds = ?, respondent_ip = COALESCE(respondent_ip, ?) WHERE project_id = ? AND status = 'clicked' ORDER BY clicked_at DESC LIMIT 1 "); $stmt->execute([$status, $actualLoiSeconds, $respondentIp, $project_id]); error_log("Survey status updated (no UID): Project=$project_id, Status=$status, LOI={$actualLoiSeconds}s"); } // Also mark any URLs that were clicked > 2 hours ago and still 'clicked' as timeout $stmt = $pdo->prepare(" UPDATE survey_urls SET status = 'timeout', completed_at = NOW(), actual_loi_seconds = TIMESTAMPDIFF(SECOND, clicked_at, NOW()) WHERE project_id = ? AND status = 'clicked' AND clicked_at < DATE_SUB(NOW(), INTERVAL 2 HOUR) "); $stmt->execute([$project_id]); } catch (Exception $e) { error_log("Redirect status error: " . $e->getMessage()); // Continue to show the landing page even if DB update fails } // Set variables for the landing pages $current_project_id = $project_id; $current_status = $status; $current_user_id = $unique_id ?? ''; // Map status to landing page file $page_map = [ 'complete' => 'complete.php', 'partial' => 'partial.php', 'earlyscreenout' => 'earlyscreenout.php', 'latescreenout' => 'latescreenout.php', 'quotafull' => 'quotafull.php', 'timeout' => 'timeout.php' ]; $page_file = __DIR__ . '/pages/' . ($page_map[$status] ?? 'partial.php'); if (file_exists($page_file)) { include $page_file; } else { // Fallback if page file doesn't exist echo '
Your survey response has been recorded.
Project: ' . htmlspecialchars($project_id) . '
'; } ?>