From b875b799dbe471082ca382bddc6385bd8574cede Mon Sep 17 00:00:00 2001 From: "jan.zhanal" Date: Fri, 13 Mar 2026 15:45:53 +0100 Subject: [PATCH 1/5] ORIS entries: complete logic to push all entries to ORIS --- .gitignore | 2 +- _SQL/zmeny.sql.php | 1 + _SQL/zmeny_3.4.5.658.sql.php | 23 +++ adm_oris_sync.php | 43 +++++ connectors.php | 5 +- lib/OrisIntegrationService.php | 193 ++++++++++++++++++++ nav.inc.php | 1 + oris_sync.log | 148 +++++++++++++++ oris_sync_daemon.php | 321 +++++++++++++++++++++++++++++++++ race_edit.php | 5 + race_edit_exc.php | 19 +- race_new.php | 1 + race_new_exc.php | 3 +- race_regs_1_exc.php | 29 ++- race_regs_all_exc.php | 31 +++- 15 files changed, 799 insertions(+), 26 deletions(-) create mode 100644 _SQL/zmeny_3.4.5.658.sql.php create mode 100644 adm_oris_sync.php create mode 100644 lib/OrisIntegrationService.php create mode 100644 oris_sync.log create mode 100644 oris_sync_daemon.php diff --git a/.gitignore b/.gitignore index af3d87c..06f335c 100644 --- a/.gitignore +++ b/.gitignore @@ -4,5 +4,5 @@ _tables.php _globals.php _cfg.php /logs -.agents/* +.agents skills-lock.json diff --git a/_SQL/zmeny.sql.php b/_SQL/zmeny.sql.php index 8857aa3..a63b6e9 100644 --- a/_SQL/zmeny.sql.php +++ b/_SQL/zmeny.sql.php @@ -28,6 +28,7 @@ function AddZmenyFile($version) AddZmenyFile('3.4.5.651'); AddZmenyFile('3.4.5.652'); AddZmenyFile('3.4.5.655'); +AddZmenyFile('3.4.5.658'); //############################################################################# require_once ('connect.inc.php'); diff --git a/_SQL/zmeny_3.4.5.658.sql.php b/_SQL/zmeny_3.4.5.658.sql.php new file mode 100644 index 0000000..ec7084d --- /dev/null +++ b/_SQL/zmeny_3.4.5.658.sql.php @@ -0,0 +1,23 @@ + \ No newline at end of file diff --git a/adm_oris_sync.php b/adm_oris_sync.php new file mode 100644 index 0000000..5881268 --- /dev/null +++ b/adm_oris_sync.php @@ -0,0 +1,43 @@ + + + + + ORIS Sync Daemon Manual Trigger + + + +

Manual ORIS Sync Execution

+

Executing daemon...

+
+getMessage() . "\n"; +} +$output = ob_get_clean(); + +// Display standard output +echo htmlspecialchars($output); +?> +
+
+ Return to System + + \ No newline at end of file diff --git a/connectors.php b/connectors.php index c705992..3d2d167 100644 --- a/connectors.php +++ b/connectors.php @@ -27,6 +27,7 @@ class Race { public $oddil; public $modify_flag; public $kategorie; + public $oris_entry_start; // Constructor to initialize the object with key-value pairs public function __construct($data) { @@ -51,6 +52,7 @@ public function __construct($data) { $this->oddil = $data['oddil'] ?? null; $this->modify_flag = $data['modify_flag'] ?? null; $this->kategorie = $data['kategorie'] ?? null; + $this->oris_entry_start = $data['oris_entry_start'] ?? null; } } @@ -179,7 +181,8 @@ public function getRaceInfo($raceId) { 'vicedenni' => ($raceData['Stages']>1?1:0), 'oddil' => $oddily, 'modify_flag' => 0, - 'kategorie' => implode(';', $classNames ) + 'kategorie' => implode(';', $classNames ), + 'oris_entry_start' => !empty($raceData['EntryStart']) ? $raceData['EntryStart'] : null ]); } else { return null; // Return null if race not found or error diff --git a/lib/OrisIntegrationService.php b/lib/OrisIntegrationService.php new file mode 100644 index 0000000..6bced1f --- /dev/null +++ b/lib/OrisIntegrationService.php @@ -0,0 +1,193 @@ +clubKey = $clubKey; + } + + /** + * Executes the HTTP POST request to ORIS API + */ + private function executeRequest($method, $params) { + $params['method'] = $method; + $params['format'] = 'json'; + $params['clubkey'] = $this->clubKey; + + $postData = http_build_query($params); + + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $this->apiUrl); + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_POSTFIELDS, $postData); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + // Ensure SSL verification + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); + + $response = curl_exec($ch); + + if(curl_errno($ch)){ + $error = curl_error($ch); + curl_close($ch); + return ['status' => 'error', 'message' => 'cURL Error: ' . $error, 'request' => $postData]; + } + + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + curl_close($ch); + + $decoded = json_decode($response, true); + + if ($httpCode >= 200 && $httpCode < 300 && isset($decoded['Status']) && $decoded['Status'] === 'OK') { + return ['status' => 'success', 'data' => $decoded['Data'] ?? $decoded, 'request' => $postData]; + } else { + $apiErrorMsg = ''; + if (is_array($decoded) && isset($decoded['Status'])) { + $apiErrorMsg = " ORIS Status: " . $decoded['Status']; + // Some endpoints might return error details in Data or a specific Error field + if (isset($decoded['Data']) && is_string($decoded['Data'])) { + $apiErrorMsg .= " - " . $decoded['Data']; + } + } + return [ + 'status' => 'error', + 'message' => 'API Error or HTTP ' . $httpCode . $apiErrorMsg, + 'payload' => $response, + 'request' => $postData + ]; + } + } + + public function createEntry($clubuser, $classId, $si, $rentSi, $note = '') { + $params = [ + 'clubuser' => $clubuser, + 'class' => $classId + ]; + + if ($si) { + $params['si'] = $si; + } + if ($rentSi) { + $params['rent_si'] = 1; + } + if ($note) { + $params['note'] = $note; + } + + return $this->executeRequest('createEntry', $params); + } + + public function updateEntry($entryId, $clubuser, $classId, $si, $rentSi, $note = '') { + $params = [ + 'entryid' => $entryId, + 'clubuser' => $clubuser, + 'class' => $classId + ]; + + if ($si) { + $params['si'] = $si; + } + if ($rentSi) { + $params['rent_si'] = 1; + } + if ($note) { + $params['note'] = $note; + } + + return $this->executeRequest('updateEntry', $params); + } + + public function deleteEntry($entryId) { + $params = [ + 'entryid' => $entryId + ]; + return $this->executeRequest('deleteEntry', $params); + } + + public function getUser($rgnum) { + $params = [ + 'method' => 'getUser', + 'format' => 'json', + 'rgnum' => $rgnum + ]; + + $ch = curl_init(); + $url = $this->apiUrl . '?' . http_build_query($params); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); + + $response = curl_exec($ch); + curl_close($ch); + + return json_decode($response, true); + } + + public function getClubUsers($userId) { + $params = [ + 'method' => 'getClubUsers', + 'format' => 'json', + 'user' => $userId + ]; + + $ch = curl_init(); + $url = $this->apiUrl . '?' . http_build_query($params); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); + + $response = curl_exec($ch); + curl_close($ch); + + return json_decode($response, true); + } + + public function getEventEntries($eventId) { + $params = [ + 'method' => 'getEventEntries', + 'format' => 'json', + 'eventid' => $eventId + ]; + + $ch = curl_init(); + $url = $this->apiUrl . '?' . http_build_query($params); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); + + $response = curl_exec($ch); + curl_close($ch); + + return json_decode($response, true); + } + + public function getEvent($eventId) { + $params = [ + 'method' => 'getEvent', + 'format' => 'json', + 'id' => $eventId + ]; + + $ch = curl_init(); + $url = $this->apiUrl . '?' . http_build_query($params); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); + + $response = curl_exec($ch); + curl_close($ch); + + return json_decode($response, true); + } +} +?> \ No newline at end of file diff --git a/nav.inc.php b/nav.inc.php index 92ab224..872bff7 100644 --- a/nav.inc.php +++ b/nav.inc.php @@ -100,6 +100,7 @@ function DrawMenuGroupHeader($name) DrawMenuGroupHeader('Menu přihlašovatele'); DrawMenuItem('Přihlášky na závody',_REGISTRATOR_GROUP_ID_,1); DrawMenuItem('Editace závodů',_REGISTRATOR_GROUP_ID_,4); + DrawMenuItemStatic('Synchronizuj přihlášky s ORISem', 'adm_oris_sync.php'); } if(IsLoggedManager()) { diff --git a/oris_sync.log b/oris_sync.log new file mode 100644 index 0000000..a9beebf --- /dev/null +++ b/oris_sync.log @@ -0,0 +1,148 @@ +[2026-03-12 21:53:42] Processing create for entry ID 125 (user: 26, race: 48) +[2026-03-12 21:53:42] - Skipped: Race 48 is not imported from ORIS (no oris_comp_id). Marking LOCAL_ONLY. +[2026-03-12 21:56:25] Processing create for entry ID 138 (user: 39, race: 52) +[2026-03-12 21:56:25] - Sending create request to ORIS API. clubuser: 8721, class: H21, si: 0 +[2026-03-12 21:56:25] - Failed: Action create encountered an error: API Error or HTTP 200 +[2026-03-12 21:56:57] Processing create for entry ID 138 (user: 39, race: 52) +[2026-03-12 21:56:57] - Sending create request to ORIS API. clubuser: 8721, class: H21, si: 0 +[2026-03-12 21:56:57] - Failed: Action create encountered an error: API Error or HTTP 200 +[2026-03-12 21:59:24] Processing create for entry ID 138 (user: 39, race: 52) +[2026-03-12 21:59:24] - Sending create request to ORIS API. clubuser: 8721, class: H21, si: 0 +[2026-03-12 21:59:24] - Failed: Action create encountered an error: API Error or HTTP 200 ORIS Status: Reg.č. není platné +[2026-03-12 22:01:44] Processing create for entry ID 138 (user: 39, race: 52) +[2026-03-12 22:01:44] - Sending create request to ORIS API. clubuser: 8721, class: H21, si: 0 +[2026-03-12 22:01:44] - Failed: Action create encountered an error: API Error or HTTP 200 ORIS Status: Reg.č. není platné +[2026-03-12 22:01:58] Processing create for entry ID 138 (user: 39, race: 52) +[2026-03-12 22:01:58] - Sending create request to ORIS API. clubuser: 8721, class: H21, si: 0 +[2026-03-12 22:01:58] - Raw POST Data: clubuser=8721&class=H21&method=createEntry&format=json&clubkey=16704c0a8cc4bb025db487f49411e808 +[2026-03-12 22:01:58] - Failed: Action create encountered an error: API Error or HTTP 200 ORIS Status: Reg.č. není platné +[2026-03-12 22:02:59] Processing create for entry ID 138 (user: 39, race: 52) +[2026-03-12 22:02:59] - Sending create request to ORIS API. clubuser: ZBM8721, class: H21, si: 0 +[2026-03-12 22:02:59] - Raw POST Data: clubuser=ZBM8721&class=H21&method=createEntry&format=json&clubkey=16704c0a8cc4bb025db487f49411e808 +[2026-03-12 22:02:59] - Failed: Action create encountered an error: API Error or HTTP 200 ORIS Status: Reg.č. není platné +[2026-03-12 22:05:58] Processing create for entry ID 138 (user: 39, race: 52) +[2026-03-12 22:05:58] - Sending create request to ORIS API. clubuser: ZBM8721, class: H21, si: 0 +[2026-03-12 22:05:58] - Raw POST Data: clubuser=ZBM8721&class=H21&method=createEntry&format=json&clubkey=16704c0a8cc4bb025db487f49411e808 +[2026-03-12 22:05:58] - Failed: Action create encountered an error: API Error or HTTP 200 ORIS Status: Reg.č. není platné +[2026-03-12 22:06:30] Processing create for entry ID 138 (user: 39, race: 52) +[2026-03-12 22:06:30] - Sending create request to ORIS API. clubuser: 7000, class: H21, si: 0 +[2026-03-12 22:06:31] - Raw POST Data: clubuser=7000&class=H21&method=createEntry&format=json&clubkey=16704c0a8cc4bb025db487f49411e808 +[2026-03-12 22:06:31] - Failed: Action create encountered an error: API Error or HTTP 200 ORIS Status: Neplatná kategorie +[2026-03-12 22:11:24] Processing create for entry ID 138 (user: 39, race: 52) +[2026-03-12 22:11:25] - Warning: Could not resolve ORIS Class ID for category 'H21' +[2026-03-12 22:11:25] - Sending create request to ORIS API. clubuser: 7000, class: H21 (mapped from H21), si: 0 +[2026-03-12 22:11:25] - Raw POST Data: clubuser=7000&class=H21&method=createEntry&format=json&clubkey=16704c0a8cc4bb025db487f49411e808 +[2026-03-12 22:11:25] - Failed: Action create encountered an error: API Error or HTTP 200 ORIS Status: Neplatná kategorie +[2026-03-12 22:12:14] Processing create for entry ID 138 (user: 39, race: 52) +[2026-03-12 22:12:14] - Warning: Could not resolve ORIS Class ID for category 'H21' +[2026-03-12 22:12:14] - Sending create request to ORIS API. clubuser: 7000, class: H21 (mapped from H21), si: 0 +[2026-03-12 22:12:14] - Raw POST Data: clubuser=7000&class=H21&method=createEntry&format=json&clubkey=16704c0a8cc4bb025db487f49411e808 +[2026-03-12 22:12:14] - Failed: Action create encountered an error: API Error or HTTP 200 ORIS Status: Neplatná kategorie +[2026-03-12 22:15:33] Processing create for entry ID 138 (user: 39, race: 52) +[2026-03-12 22:15:34] - Sending create request to ORIS API. clubuser: 7000, class: 217130 (mapped from H21), si: 0 +[2026-03-12 22:15:34] - Raw POST Data: clubuser=7000&class=217130&method=createEntry&format=json&clubkey=16704c0a8cc4bb025db487f49411e808 +[2026-03-12 22:15:34] - Success: Action create completed. ORIS Entry ID: +[2026-03-12 22:24:40] Processing create for entry ID 138 (user: 39, race: 52) +[2026-03-12 22:24:41] - Sending create request to ORIS API. clubuser: 7323, class: 217130 (mapped from H21), si: 0 +[2026-03-12 22:24:41] - Raw POST Data: clubuser=7323&class=217130&method=createEntry&format=json&clubkey=16704c0a8cc4bb025db487f49411e808 +[2026-03-12 22:24:41] - Success: Action create completed. ORIS Entry ID: +[2026-03-12 22:42:43] Processing create for entry ID 138 (user: 39, race: 52) +[2026-03-12 22:42:43] - Sending create request to ORIS API. clubuser: 7323, class: 217130 (mapped from H21), si: 0 +[2026-03-12 22:42:43] - Raw POST Data: clubuser=7323&class=217130&method=createEntry&format=json&clubkey=16704c0a8cc4bb025db487f49411e808 +[2026-03-12 22:42:43] - Success: Action create completed. ORIS Entry ID: +[2026-03-13 13:41:09] Processing update for entry ID 139 (user: 39, race: 53) +[2026-03-13 13:41:10] - Sending update request to ORIS API. clubuser: 7323, class: 211624 (mapped from H21C), si: 0 +[2026-03-13 13:41:10] - Failed: Action update encountered an error: Cannot update: No ORIS Entry ID +[2026-03-13 13:45:25] Processing create for entry ID 139 (user: 39, race: 53) +[2026-03-13 13:45:25] - Sending create request to ORIS API. clubuser: 7323, class: 211624 (mapped from H21C), si: 0 +[2026-03-13 13:45:25] - Raw POST Data: clubuser=7323&class=211624&method=createEntry&format=json&clubkey=16704c0a8cc4bb025db487f49411e808 +[2026-03-13 13:45:25] - Success: Action create completed. ORIS Entry ID: +[2026-03-13 13:50:40] Processing delete for entry ID 139 (user: 39, race: 53) +[2026-03-13 13:50:41] - Sending delete request to ORIS API. clubuser: 7323, class: 211624 (mapped from H21C), si: 0 +[2026-03-13 13:50:41] - Success: Action delete completed. ORIS Entry ID: +[2026-03-13 13:53:30] Processing create for entry ID 140 (user: 11, race: 53) +[2026-03-13 13:53:30] - Sending create request to ORIS API. clubuser: 7315, class: 211626 (mapped from H35), si: 0 +[2026-03-13 13:53:31] - Raw POST Data: clubuser=7315&class=211626&method=createEntry&format=json&clubkey=16704c0a8cc4bb025db487f49411e808 +[2026-03-13 13:53:31] - Success: Action create completed. ORIS Entry ID: +[2026-03-13 13:53:31] Processing create for entry ID 141 (user: 39, race: 53) +[2026-03-13 13:53:31] - Sending create request to ORIS API. clubuser: 7323, class: 211626 (mapped from H35), si: 0 +[2026-03-13 13:53:31] - Raw POST Data: clubuser=7323&class=211626&method=createEntry&format=json&clubkey=16704c0a8cc4bb025db487f49411e808 +[2026-03-13 13:53:31] - Failed: Action create encountered an error: API Error or HTTP 200 ORIS Status: Již přihlášen +[2026-03-13 13:54:19] Processing delete for entry ID 140 (user: 11, race: 53) +[2026-03-13 13:54:19] - Sending delete request to ORIS API. clubuser: 7315, class: 211626 (mapped from H35), si: 0 +[2026-03-13 13:54:19] - Success: Action delete completed. ORIS Entry ID: +[2026-03-13 13:54:19] Processing delete for entry ID 141 (user: 39, race: 53) +[2026-03-13 13:54:20] - Sending delete request to ORIS API. clubuser: 7323, class: 211626 (mapped from H35), si: 0 +[2026-03-13 13:54:20] - Success: Action delete completed. ORIS Entry ID: +[2026-03-13 14:04:36] Processing update for entry ID 142 (user: 11, race: 53) +[2026-03-13 14:04:36] - Sending update request to ORIS API. clubuser: 7315, class: 211626 (mapped from H35), si: 0 +[2026-03-13 14:04:36] - Warning: Missing ORIS Entry ID for update. Falling back to create. +[2026-03-13 14:04:37] - Raw POST Data: clubuser=7315&class=211626&method=createEntry&format=json&clubkey=16704c0a8cc4bb025db487f49411e808 +[2026-03-13 14:04:37] - Failed: Action update encountered an error: API Error or HTTP 200 ORIS Status: Již přihlášen +[2026-03-13 14:04:37] Processing update for entry ID 143 (user: 39, race: 53) +[2026-03-13 14:04:37] - Sending update request to ORIS API. clubuser: 7323, class: 211626 (mapped from H35), si: 0 +[2026-03-13 14:04:37] - Warning: Missing ORIS Entry ID for update. Falling back to create. +[2026-03-13 14:04:37] - Raw POST Data: clubuser=7323&class=211626&method=createEntry&format=json&clubkey=16704c0a8cc4bb025db487f49411e808 +[2026-03-13 14:04:37] - Failed: Action update encountered an error: API Error or HTTP 200 ORIS Status: Již přihlášen +[2026-03-13 14:06:59] Processing create for entry ID 142 (user: 11, race: 53) +[2026-03-13 14:06:59] - Sending create request to ORIS API. clubuser: 7315, class: 211626 (mapped from H35), si: 0 +[2026-03-13 14:07:00] - Raw POST Data: clubuser=7315&class=211626&method=createEntry&format=json&clubkey=16704c0a8cc4bb025db487f49411e808 +[2026-03-13 14:07:00] - Failed: Action create encountered an error: API Error or HTTP 200 ORIS Status: Již přihlášen +[2026-03-13 14:07:00] Processing create for entry ID 143 (user: 39, race: 53) +[2026-03-13 14:07:00] - Sending create request to ORIS API. clubuser: 7323, class: 211626 (mapped from H35), si: 0 +[2026-03-13 14:07:00] - Raw POST Data: clubuser=7323&class=211626&method=createEntry&format=json&clubkey=16704c0a8cc4bb025db487f49411e808 +[2026-03-13 14:07:00] - Failed: Action create encountered an error: API Error or HTTP 200 ORIS Status: Již přihlášen +[2026-03-13 14:08:30] Processing update for entry ID 142 (user: 11, race: 53) +[2026-03-13 14:08:30] - Sending update request to ORIS API. clubuser: 7315, class: 211626 (mapped from H35), si: 0 +[2026-03-13 14:08:30] - Warning: Missing ORIS Entry ID for update. Attempting to fetch from ORIS. +[2026-03-13 14:08:31] - Found ORIS Entry ID 3170927 for clubuser 7315 +[2026-03-13 14:08:31] - Raw POST Data: entryid=3170927&clubuser=7315&class=211626&method=updateEntry&format=json&clubkey=16704c0a8cc4bb025db487f49411e808 +[2026-03-13 14:08:31] - Success: Action update completed. ORIS Entry ID: +[2026-03-13 14:08:31] Processing update for entry ID 143 (user: 39, race: 53) +[2026-03-13 14:08:31] - Sending update request to ORIS API. clubuser: 7323, class: 211626 (mapped from H35), si: 0 +[2026-03-13 14:08:31] - Warning: Missing ORIS Entry ID for update. Attempting to fetch from ORIS. +[2026-03-13 14:08:31] - Found ORIS Entry ID 3170917 for clubuser 7323 +[2026-03-13 14:08:32] - Raw POST Data: entryid=3170917&clubuser=7323&class=211626&method=updateEntry&format=json&clubkey=16704c0a8cc4bb025db487f49411e808 +[2026-03-13 14:08:32] - Success: Action update completed. ORIS Entry ID: +[2026-03-13 14:10:12] Processing update for entry ID 142 (user: 11, race: 53) +[2026-03-13 14:10:13] - Sending update request to ORIS API. clubuser: 7315, class: 211624 (mapped from H21C), si: 0 +[2026-03-13 14:10:13] - Warning: Missing ORIS Entry ID for update. Attempting to fetch from ORIS. +[2026-03-13 14:10:13] - Found ORIS Entry ID 3170927 for clubuser 7315 +[2026-03-13 14:10:13] - Raw POST Data: entryid=3170927&clubuser=7315&class=211624&method=updateEntry&format=json&clubkey=16704c0a8cc4bb025db487f49411e808 +[2026-03-13 14:10:13] - Success: Action update completed. ORIS Entry ID: 3170927 +[2026-03-13 14:10:13] Processing update for entry ID 143 (user: 39, race: 53) +[2026-03-13 14:10:14] - Warning: Could not resolve ORIS Class ID for category 'H21' +[2026-03-13 14:10:14] - Sending update request to ORIS API. clubuser: 7323, class: H21 (mapped from H21), si: 0 +[2026-03-13 14:10:14] - Warning: Missing ORIS Entry ID for update. Attempting to fetch from ORIS. +[2026-03-13 14:10:14] - Found ORIS Entry ID 3170917 for clubuser 7323 +[2026-03-13 14:10:14] - Raw POST Data: entryid=3170917&clubuser=7323&class=H21&method=updateEntry&format=json&clubkey=16704c0a8cc4bb025db487f49411e808 +[2026-03-13 14:10:14] - Success: Action update completed. ORIS Entry ID: 3170917 +[2026-03-13 14:39:31] Processing delete for entry ID 142 (user: 11, race: 53) +[2026-03-13 14:39:32] - Sending delete request to ORIS API. clubuser: 7315, class: 211624 (mapped from H21C), si: 0 +[2026-03-13 14:39:32] - Raw POST Data: entryid=3170927&method=deleteEntry&format=json&clubkey=16704c0a8cc4bb025db487f49411e808 +[2026-03-13 14:39:32] - Success: Action delete completed. ORIS Entry ID: 3170927 +[2026-03-13 14:39:32] Processing delete for entry ID 143 (user: 39, race: 53) +[2026-03-13 14:39:32] - Warning: Could not resolve ORIS Class ID for category 'H21' +[2026-03-13 14:39:32] - Sending delete request to ORIS API. clubuser: 7323, class: H21 (mapped from H21), si: 0 +[2026-03-13 14:39:33] - Raw POST Data: entryid=3170917&method=deleteEntry&format=json&clubkey=16704c0a8cc4bb025db487f49411e808 +[2026-03-13 14:39:33] - Success: Action delete completed. ORIS Entry ID: 3170917 +[2026-03-13 14:40:37] Processing create for entry ID 144 (user: 11, race: 53) +[2026-03-13 14:40:38] - Sending create request to ORIS API. clubuser: 7315, class: 211624 (mapped from H21C), si: 0 +[2026-03-13 14:40:39] - Raw POST Data: clubuser=7315&class=211624&method=createEntry&format=json&clubkey=16704c0a8cc4bb025db487f49411e808 +[2026-03-13 14:40:39] - Success: Action create completed. ORIS Entry ID: +[2026-03-13 14:40:39] Processing create for entry ID 145 (user: 39, race: 53) +[2026-03-13 14:40:40] - Sending create request to ORIS API. clubuser: 7323, class: 211626 (mapped from H35), si: 0 +[2026-03-13 14:40:40] - Raw POST Data: clubuser=7323&class=211626&method=createEntry&format=json&clubkey=16704c0a8cc4bb025db487f49411e808 +[2026-03-13 14:40:40] - Success: Action create completed. ORIS Entry ID: +[2026-03-13 14:44:01] Processing delete for entry ID 144 (user: 11, race: 53) +[2026-03-13 14:44:02] - Sending delete request to ORIS API. clubuser: 7315, class: 211624 (mapped from H21C), si: 0 +[2026-03-13 14:44:02] - Warning: Missing ORIS Entry ID for delete. Attempting to fetch from ORIS. +[2026-03-13 14:44:02] - Found ORIS Entry ID 3170940 for clubuser 7315 +[2026-03-13 14:44:02] - Raw POST Data: entryid=3170940&method=deleteEntry&format=json&clubkey=16704c0a8cc4bb025db487f49411e808 +[2026-03-13 14:44:02] - Success: Action delete completed. ORIS Entry ID: +[2026-03-13 14:44:02] Processing delete for entry ID 145 (user: 39, race: 53) +[2026-03-13 14:44:03] - Sending delete request to ORIS API. clubuser: 7323, class: 211626 (mapped from H35), si: 0 +[2026-03-13 14:44:03] - Warning: Missing ORIS Entry ID for delete. Attempting to fetch from ORIS. +[2026-03-13 14:44:03] - Found ORIS Entry ID 3170941 for clubuser 7323 +[2026-03-13 14:44:03] - Raw POST Data: entryid=3170941&method=deleteEntry&format=json&clubkey=16704c0a8cc4bb025db487f49411e808 +[2026-03-13 14:44:03] - Success: Action delete completed. ORIS Entry ID: diff --git a/oris_sync_daemon.php b/oris_sync_daemon.php new file mode 100644 index 0000000..0b39e9a --- /dev/null +++ b/oris_sync_daemon.php @@ -0,0 +1,321 @@ + $now) { + $diff = $entryStartTime - $now; + if ($diff <= 65) { // 65 seconds buffer for cron running every minute + logMessage("Race opening in $diff seconds (at $entryStartStr). Sleeping before processing..."); + sleep($diff); + } else { + // Race is not open yet and more than a minute away, skip for now + logMessage("Skipping entry ID {$row['id']} - Race opens at $entryStartStr (in $diff seconds)."); + continue; + } + } + } + + $action = ''; + if ($row['sync_status'] === 'PENDING_CREATE') { + $action = 'create'; + } elseif ($row['sync_status'] === 'PENDING_UPDATE') { + $action = 'update'; + } elseif ($row['sync_status'] === 'PENDING_DELETE') { + $action = 'delete'; + } elseif ($row['sync_status'] === 'FAILED_RETRY') { + // We will just let it be an 'update' if there's an ID, or 'create_or_update' + // if there's no ID so processEntry can check ORIS first. + $action = empty($row['oris_entry_id']) ? 'create' : 'update'; + // Wait, instead of setting it to 'create' which assumes it doesn't exist, + // if we set it to 'update', the new logic in processEntry will fetch from ORIS. + // If it's not found on ORIS, it falls back to 'create' anyway! + $action = 'update'; + } + + if (!empty($action)) { + processEntry($row, $action, $service); + } +} + +function logMessage($msg) { + $date = date('Y-m-d H:i:s'); + $line = "[$date] $msg\n"; + // Output to console when run via CLI + echo $line; + // Log to file in the same directory + file_put_contents(__DIR__ . '/oris_sync.log', $line, FILE_APPEND); +} + +function processEntry($row, $action, $service) { + global $g_oris_club_key, $g_shortcut; + + $id = $row['id']; + $userId = $row['id_user']; + $raceId = $row['id_zavod']; + + logMessage("Processing $action for entry ID $id (user: $userId, race: $raceId)"); + + // Get ORIS user ID + $userQuery = "SELECT * FROM `" . TBL_USER . "` WHERE `id` = " . (int)$userId; + $userRes = query_db($userQuery); + $userRow = mysqli_fetch_assoc($userRes); + + $clubuser = null; + $rgnum = null; + if ($userRow && isset($userRow['reg'])) { + // Reg number usually only contains the digits, so we prepend the club shortcut (e.g. ZBM) + $rgnum = $userRow['reg']; + // Ensure the club prefix is attached if it isn't already + if (!empty($rgnum) && !preg_match('/^[A-Z]{3}/', $rgnum)) { + $rgnum = $g_shortcut . str_pad($rgnum, 4, '0', STR_PAD_LEFT); + } + + // Use the Oris API to get the internal user ID required for createEntry + if (!empty($rgnum)) { + $userApiRes = $service->getUser($rgnum); + if (isset($userApiRes['Status']) && $userApiRes['Status'] === 'OK' && isset($userApiRes['Data']['ID'])) { + $globalUserId = $userApiRes['Data']['ID']; + + // Now get the club user ID + $clubUsersRes = $service->getClubUsers($globalUserId); + if (isset($clubUsersRes['Status']) && $clubUsersRes['Status'] === 'OK' && isset($clubUsersRes['Data'])) { + // It might be a single object or an array of objects + $clubUsers = is_array($clubUsersRes['Data']) ? $clubUsersRes['Data'] : []; + if (isset($clubUsers['ID'])) { + $clubUsers = [$clubUsers]; + } + + foreach ($clubUsers as $cu) { + if (isset($cu['ID'])) { + $clubuser = $cu['ID']; + break; + } + } + } + + if (empty($clubuser)) { + logMessage(" - Warning: Could not resolve ORIS Club User ID for user ID $globalUserId"); + } + } else { + logMessage(" - Warning: Could not resolve ORIS User ID for rgnum $rgnum"); + } + } + } + + // Get Race ORIS ID + $raceQuery = "SELECT * FROM `" . TBL_RACE . "` WHERE `id` = " . (int)$raceId; + $raceRes = query_db($raceQuery); + $raceRow = mysqli_fetch_assoc($raceRes); + + // Ensure the race was imported from ORIS + $comp = $raceRow['ext_id'] ?? null; + if (empty($comp)) { + logMessage(" - Skipped: Race $raceId is not imported from ORIS (no ext_id). Marking LOCAL_ONLY."); + $updateQuery = "UPDATE `" . TBL_ZAVXUS . "` SET `sync_status` = 'LOCAL_ONLY' WHERE `id` = " . (int)$id; + query_db($updateQuery); + return; + } + + // Ignore relays and sprint relays + // S is typically for štafety (relays) in local DB + if ($raceRow && isset($raceRow['typ0']) && $raceRow['typ0'] === 'S') { + logMessage(" - Skipped: Race $raceId is a relay (typ0 = S). Marking LOCAL_ONLY."); + $updateQuery = "UPDATE `" . TBL_ZAVXUS . "` SET `sync_status` = 'LOCAL_ONLY' WHERE `id` = " . (int)$id; + query_db($updateQuery); + return; + } + + // Fetch Classes from ORIS Event to map the string category (e.g. H21) to the ORIS class ID + $classId = null; + $katName = $row['kat']; + if (!empty($comp)) { + $eventRes = $service->getEvent($comp); + if (isset($eventRes['Status']) && $eventRes['Status'] === 'OK' && isset($eventRes['Data']['Classes'])) { + $classes = is_array($eventRes['Data']['Classes']) ? $eventRes['Data']['Classes'] : []; + + foreach ($classes as $cls) { + if (!is_array($cls)) continue; + $clsName = $cls['Name'] ?? ''; + // Some categories might have different spacing or case + if (trim($clsName) === trim($katName)) { + $classId = $cls['ID'] ?? null; + break; + } + } + } + } + + if (empty($classId)) { + logMessage(" - Warning: Could not resolve ORIS Class ID for category '$katName'"); + // Fallback to sending the string, which will likely be rejected by ORIS but preserves behavior + $classId = $katName; + } + + $si = $row['si_chip']; + $rentSi = $row['rent_si'] ?? 0; // assuming rent_si is added or mapped + $note = $row['pozn'] ?? ''; + + $response = []; + logMessage(" - Sending $action request to ORIS API. clubuser: $clubuser, class: $classId (mapped from $katName), si: $si"); + + if ($action === 'create') { + // Check if it already exists before creating to avoid "Již přihlášen" + $existingEntryId = null; + if (!empty($comp) && !empty($clubuser)) { + $entriesRes = $service->getEventEntries($comp); + if (isset($entriesRes['Status']) && $entriesRes['Status'] === 'OK' && isset($entriesRes['Data'])) { + $entries = is_array($entriesRes['Data']) ? $entriesRes['Data'] : []; + foreach ($entries as $entry) { + if (isset($entry['ClubUserID']) && $entry['ClubUserID'] == $clubuser) { + $existingEntryId = $entry['ID']; + logMessage(" - Found existing ORIS Entry ID $existingEntryId for clubuser $clubuser during create. Switching to update."); + break; + } + } + } + } + + if (!empty($existingEntryId)) { + $response = $service->updateEntry($existingEntryId, $clubuser, $classId, $si, $rentSi, $note); + // Also update the action string so log messages below make sense + $action = 'update'; + } else { + $response = $service->createEntry($clubuser, $classId, $si, $rentSi, $note); + } + } elseif ($action === 'update') { + $entryIdToUpdate = $row['oris_entry_id'] ?? null; + + if (empty($entryIdToUpdate) && !empty($comp) && !empty($clubuser)) { + logMessage(" - Warning: Missing ORIS Entry ID for update. Attempting to fetch from ORIS."); + $entriesRes = $service->getEventEntries($comp); + if (isset($entriesRes['Status']) && $entriesRes['Status'] === 'OK' && isset($entriesRes['Data'])) { + $entries = is_array($entriesRes['Data']) ? $entriesRes['Data'] : []; + foreach ($entries as $entry) { + if (isset($entry['ClubUserID']) && $entry['ClubUserID'] == $clubuser) { + $entryIdToUpdate = $entry['ID']; + logMessage(" - Found ORIS Entry ID $entryIdToUpdate for clubuser $clubuser"); + break; + } + } + } + } + + if (empty($entryIdToUpdate)) { + // It was never created on ORIS side successfully, so let's fall back to create instead of failing + logMessage(" - Warning: Missing ORIS Entry ID for update. Falling back to create."); + $response = $service->createEntry($clubuser, $classId, $si, $rentSi, $note); + } else { + $response = $service->updateEntry($entryIdToUpdate, $clubuser, $classId, $si, $rentSi, $note); + } + } elseif ($action === 'delete') { + $entryIdToDelete = $row['oris_entry_id'] ?? null; + + // If we don't have the entry ID locally, we must fetch it from ORIS first + if (empty($entryIdToDelete) && !empty($comp) && !empty($clubuser)) { + logMessage(" - Warning: Missing ORIS Entry ID for delete. Attempting to fetch from ORIS."); + $entriesRes = $service->getEventEntries($comp); + if (isset($entriesRes['Status']) && $entriesRes['Status'] === 'OK' && isset($entriesRes['Data'])) { + $entries = is_array($entriesRes['Data']) ? $entriesRes['Data'] : []; + foreach ($entries as $entry) { + if (isset($entry['ClubUserID']) && $entry['ClubUserID'] == $clubuser) { + $entryIdToDelete = $entry['ID']; + logMessage(" - Found ORIS Entry ID $entryIdToDelete for clubuser $clubuser"); + break; + } + } + } + } + + if (empty($entryIdToDelete)) { + // Already doesn't exist remotely or could not be found + logMessage(" - Warning: Could not find ORIS Entry ID to delete. Assuming already deleted."); + $response = ['status' => 'success', 'data' => []]; + } else { + $response = $service->deleteEntry($entryIdToDelete); + } + } + + // Log the exact raw payload sent to ORIS + if (isset($response['request'])) { + logMessage(" - Raw POST Data: " . $response['request']); + } + + if ($response['status'] === 'success') { + // 'create' usually returns ID in $response['data']['ID'] + // 'update' might not return the ID, so we use the one we just found/used + $entryId = $response['data']['ID'] ?? null; + if (empty($entryId)) { + if ($action === 'update' && !empty($entryIdToUpdate)) { + $entryId = $entryIdToUpdate; + } elseif ($action === 'update' && !empty($existingEntryId)) { // from the create fallback block + $entryId = $existingEntryId; + } else { + $entryId = $row['oris_entry_id']; + } + } + + logMessage(" - Success: Action $action completed. ORIS Entry ID: $entryId"); + + if ($action === 'delete') { + // Remove the row locally after successful delete + $updateQuery = "DELETE FROM `" . TBL_ZAVXUS . "` WHERE `id` = " . (int)$id; + query_db($updateQuery); + } else { + // Update the row + $updateQuery = "UPDATE `" . TBL_ZAVXUS . "` + SET `sync_status` = 'SYNCED', + `oris_entry_id` = " . ($entryId ? (int)$entryId : "NULL") . ", + `sync_timestamp` = NOW(), + `sync_error_payload` = NULL + WHERE `id` = " . (int)$id; + query_db($updateQuery); + } + } else { + $errMsg = $response['message'] ?? 'Unknown error'; + logMessage(" - Failed: Action $action encountered an error: $errMsg"); + + $errorPayload = correct_sql_string(json_encode($response)); + $updateQuery = "UPDATE `" . TBL_ZAVXUS . "` + SET `sync_status` = 'FAILED_RETRY', + `sync_error_payload` = '$errorPayload' + WHERE `id` = " . (int)$id; + query_db($updateQuery); + } +} +?> \ No newline at end of file diff --git a/race_edit.php b/race_edit.php index e5247cd..4ed4e4a 100644 --- a/race_edit.php +++ b/race_edit.php @@ -372,6 +372,11 @@ function toggleButtonState() { +oris_entry_start !== null) { + echo ''; +} +?> + 0) { + query_db("UPDATE ".TBL_RACE." SET prihlasenych = GREATEST(0, prihlasenych - 1) WHERE id = '$id'"); + } + } if ($result == FALSE) die ("Nepodařilo se změnit přihlášku člena."); - if ($result !== false && mysqli_affected_rows($db_conn) > 0) { - query_db("UPDATE ".TBL_RACE." SET prihlasenych = GREATEST(0, prihlasenych - 1) WHERE id = '$id'"); - } } else { // update @@ -97,7 +105,12 @@ $pozn2=correct_sql_string($pozn2); $termin=correct_sql_string($termin); - $result=query_db("UPDATE ".TBL_ZAVXUS." SET kat='$kateg', pozn='$pozn', pozn_in='$pozn2', termin='$termin', transport = '$transport', sedadel = ".$sedadel.", ubytovani = '$ubytovani' WHERE id_zavod = '$id' AND id_user = '$user_id'") + $sync_status_update = ""; + if (!empty($zaznam_z['ext_id']) && $zaznam['sync_status'] !== 'PENDING_CREATE') { + $sync_status_update = ", sync_status='PENDING_UPDATE'"; + } + + $result=query_db("UPDATE ".TBL_ZAVXUS." SET kat='$kateg', pozn='$pozn', pozn_in='$pozn2', termin='$termin', transport = '$transport', sedadel = ".$sedadel.", ubytovani = '$ubytovani'".$sync_status_update." WHERE id_zavod = '$id' AND id_user = '$user_id'") or die("Chyba při provádění dotazu do databáze."); if ($result == FALSE) die ("Nepodařilo se změnit přihlášku člena."); @@ -112,8 +125,10 @@ $pozn=correct_sql_string($pozn); $pozn2=correct_sql_string($pozn2); $termin=correct_sql_string($termin); + + $sync_status = !empty($zaznam_z['ext_id']) ? 'PENDING_CREATE' : 'LOCAL_ONLY'; - $result=query_db("INSERT INTO ".TBL_ZAVXUS." (id_user, id_zavod, kat, pozn, pozn_in,termin,transport,sedadel,ubytovani) VALUES ('$user_id','$id','$kateg', '$pozn', '$pozn2','$termin','$transport',".$sedadel.",'$ubytovani')") + $result=query_db("INSERT INTO ".TBL_ZAVXUS." (id_user, id_zavod, kat, pozn, pozn_in,termin,transport,sedadel,ubytovani,sync_status) VALUES ('$user_id','$id','$kateg', '$pozn', '$pozn2','$termin','$transport',".$sedadel.",'$ubytovani','$sync_status')") or die("Chyba při provádění dotazu do databáze."); if ($result == FALSE) die ("Nepodařilo se změnit přihlášku člena."); diff --git a/race_regs_all_exc.php b/race_regs_all_exc.php index e289db6..f7deb04 100644 --- a/race_regs_all_exc.php +++ b/race_regs_all_exc.php @@ -22,7 +22,7 @@ $sub_query = (IsLoggedRegistrator() || IsLoggedManager()) ? '' : ' AND '.TBL_USER.'.chief_id = '.$usr->user_id.' OR '.TBL_USER.'.id = '.$usr->user_id; -$query = 'SELECT '.TBL_USER.'.id, kat, termin FROM '.TBL_USER.' LEFT JOIN '.TBL_ZAVXUS.' ON '.TBL_USER.'.id = '.TBL_ZAVXUS.'.id_user AND '.TBL_ZAVXUS.'.id_zavod='.$id.' WHERE '.TBL_USER.'.hidden = 0'.$sub_query; +$query = 'SELECT '.TBL_USER.'.id, kat, termin, sync_status FROM '.TBL_USER.' LEFT JOIN '.TBL_ZAVXUS.' ON '.TBL_USER.'.id = '.TBL_ZAVXUS.'.id_user AND '.TBL_ZAVXUS.'.id_zavod='.$id.' WHERE '.TBL_USER.'.hidden = 0'.$sub_query; @$vysledek=query_db($query); @@ -75,13 +75,21 @@ if ($kat == "") { // del // echo "DEL"; - $result=query_db("DELETE FROM ".TBL_ZAVXUS." WHERE id_zavod = '$id' AND id_user = '$user'") - or die("Chyba při provádění dotazu do databáze."); + $has_ext_id = !empty($zaznam_z['ext_id']); + $is_pending_create = ($zaznamZ['sync_status'] === 'PENDING_CREATE'); + + if ($has_ext_id && !$is_pending_create) { + $result=query_db("UPDATE ".TBL_ZAVXUS." SET sync_status='PENDING_DELETE' WHERE id_zavod = '$id' AND id_user = '$user'") + or die("Chyba při provádění dotazu do databáze."); + } else { + $result=query_db("DELETE FROM ".TBL_ZAVXUS." WHERE id_zavod = '$id' AND id_user = '$user'") + or die("Chyba při provádění dotazu do databáze."); + if ($result !== false && mysqli_affected_rows($db_conn) > 0) { + query_db("UPDATE ".TBL_RACE." SET prihlasenych = GREATEST(0, prihlasenych - 1) WHERE id = '$id'"); + } + } if ($result == FALSE) die ("Nepodařilo se změnit přihlášku člena."); - if ($result !== false && mysqli_affected_rows($db_conn) > 0) { - query_db("UPDATE ".TBL_RACE." SET prihlasenych = GREATEST(0, prihlasenych - 1) WHERE id = '$id'"); - } } else { // update @@ -91,7 +99,12 @@ $poz2=correct_sql_string($poz2); $cterm=correct_sql_string($cterm); - $result=query_db("UPDATE ".TBL_ZAVXUS." SET kat='$kat', pozn='$poz', pozn_in='$poz2', termin='$cterm', transport=$trans, sedadel=$sedl, ubytovani=$ubyt WHERE id_zavod = '$id' AND id_user = '$user'") + $sync_status_update = ""; + if (!empty($zaznam_z['ext_id']) && $zaznamZ['sync_status'] !== 'PENDING_CREATE') { + $sync_status_update = ", sync_status='PENDING_UPDATE'"; + } + + $result=query_db("UPDATE ".TBL_ZAVXUS." SET kat='$kat', pozn='$poz', pozn_in='$poz2', termin='$cterm', transport=$trans, sedadel=$sedl, ubytovani=$ubyt".$sync_status_update." WHERE id_zavod = '$id' AND id_user = '$user'") or die("Chyba při provádění dotazu do databáze."); if ($result == FALSE) die ("Nepodařilo se změnit přihlášku člena."); @@ -107,7 +120,9 @@ $poz2=correct_sql_string($poz2); $cterm=correct_sql_string($cterm); - $result=query_db("INSERT INTO ".TBL_ZAVXUS." (id_user, id_zavod, kat, pozn, pozn_in, termin, transport, sedadel,ubytovani) VALUES ('$user','$id','$kat','$poz','$poz2','$cterm',$trans,$sedl,$ubyt)") + $sync_status = !empty($zaznam_z['ext_id']) ? 'PENDING_CREATE' : 'LOCAL_ONLY'; + + $result=query_db("INSERT INTO ".TBL_ZAVXUS." (id_user, id_zavod, kat, pozn, pozn_in, termin, transport, sedadel,ubytovani, sync_status) VALUES ('$user','$id','$kat','$poz','$poz2','$cterm',$trans,$sedl,$ubyt,'$sync_status')") or die("Chyba při provádění dotazu do databáze."); if ($result == FALSE) die ("Nepodařilo se změnit přihlášku člena."); From 92fc994b21a6e6b526d0f91146c7f4a6a8443b55 Mon Sep 17 00:00:00 2001 From: "jan.zhanal" Date: Fri, 13 Mar 2026 15:49:24 +0100 Subject: [PATCH 2/5] ORIS entries: complete logic to push all entries to ORIS --- oris_sync_daemon.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/oris_sync_daemon.php b/oris_sync_daemon.php index 0b39e9a..aad999a 100644 --- a/oris_sync_daemon.php +++ b/oris_sync_daemon.php @@ -19,7 +19,7 @@ global $g_oris_club_key; if (empty($g_oris_club_key)) { - die("Error: ORIS club key is not configured."); + die("Error: ORIS club key neni nastaven."); } $service = new OrisIntegrationService($g_oris_club_key); @@ -41,11 +41,11 @@ if ($entryStartTime > $now) { $diff = $entryStartTime - $now; if ($diff <= 65) { // 65 seconds buffer for cron running every minute - logMessage("Race opening in $diff seconds (at $entryStartStr). Sleeping before processing..."); + logMessage("Prihlasky se oteviraji za $diff seconds (at $entryStartStr). Jdu sapt pred pokracovanim..."); sleep($diff); } else { // Race is not open yet and more than a minute away, skip for now - logMessage("Skipping entry ID {$row['id']} - Race opens at $entryStartStr (in $diff seconds)."); + logMessage("Preskakuju prihlasku ID {$row['id']} - Zavod otevira prihlasky v $entryStartStr (za $diff s)."); continue; } } From 165328281163e8c66cbc77453cb11a67486ea6f5 Mon Sep 17 00:00:00 2001 From: "jan.zhanal" Date: Fri, 13 Mar 2026 23:18:21 +0100 Subject: [PATCH 3/5] ORIS entries: fine tuning, refactor 2 connector files --- ads_oris.inc.php | 11 +-- connectors.php | 23 +++-- lib/OrisIntegrationService.php | 45 +++++++++- oris_sync.log | 148 --------------------------------- oris_sync_daemon.php | 3 +- 5 files changed, 63 insertions(+), 167 deletions(-) delete mode 100644 oris_sync.log diff --git a/ads_oris.inc.php b/ads_oris.inc.php index 3fcd481..78db57b 100644 --- a/ads_oris.inc.php +++ b/ads_oris.inc.php @@ -46,17 +46,18 @@ function startsWith( $haystack, $needle ) { return substr( $haystack, 0, $length ) === $needle; } -$json = file_get_contents('https://oris.orientacnisporty.cz/API/?format=json&method=getRegistration&sport=1&year='.$ORIS_year); -$obj = json_decode($json); +require_once './lib/OrisIntegrationService.php'; +$service = new OrisIntegrationService(null); +$obj = $service->getRegistration(1, $ORIS_year); $arr_oris = array(); -foreach ($obj->Data as $key=>$value) +foreach ($obj['Data'] as $key=>$value) { $user = new User(); - $user->create($value->UserID, $value->FirstName, $value->LastName, $value->RegNo, $value->SI, $value->ClubID); + $user->create($value['UserID'], $value['FirstName'], $value['LastName'], $value['RegNo'], $value['SI'], $value['ClubID']); - $reg = $value->RegNo; + $reg = $value['RegNo']; if (startsWith($reg, $g_shortcut)) { $arr_oris["user"] [$reg]= $user; diff --git a/connectors.php b/connectors.php index 3d2d167..f3fc817 100644 --- a/connectors.php +++ b/connectors.php @@ -62,12 +62,16 @@ public function getRaceURL(string $id): string; public function getRaceInfo(string $id); } +require_once __DIR__ . '/lib/OrisIntegrationService.php'; + class OrisCZConnector implements ConnectorInterface { - private $sourceUrl = 'https://oris.orientacnisporty.cz/'; + private $sourceUrl = 'https://oris.ceskyorientak.cz/'; private $apiUrl; + private $service; public function __construct() { $this->apiUrl = $this->sourceUrl . 'API/'; + $this->service = new OrisIntegrationService(null); } @@ -119,10 +123,9 @@ private function getClubs(&$raceData) { // Method to get race date based on race ID public function getRaceDate($raceId) { - $url = $this->apiUrl . '?format=json&method=getEvent&id=' . $raceId; - $response = $this->makeRequest($url); + $response = $this->service->getEvent($raceId); - if ($response && $response['Status'] == "OK") { + if ($response && isset($response['Status']) && $response['Status'] == "OK") { $raceData = $response['Data']; return String2DateDMY(formatDate($raceData['Date'])); @@ -134,11 +137,9 @@ public function getRaceDate($raceId) { // Method to get detailed race information based on race ID public function getRaceInfo($raceId) { - $url = $this->apiUrl . '?format=json&method=getEvent&id=' . $raceId; - - $response = $this->makeRequest($url); + $response = $this->service->getEvent($raceId); - if ($response && $response['Status'] == "OK") { + if ($response && isset($response['Status']) && $response['Status'] == "OK") { $raceData = $response['Data']; $classNames = []; @@ -217,11 +218,9 @@ private function makeRequestCurl($url) { } function getRacesList($fromDate, $toDate) { - $url = $this->apiUrl.'?format=json&method=getEventList&all=1&datefrom='.$fromDate.'&dateto='.$toDate; -// echo($url.'
'); - $response = $this->makeRequest($url); + $response = $this->service->getEventList($fromDate, $toDate, 1); - if ($response && $response['Status'] == "OK") { + if ($response && isset($response['Status']) && $response['Status'] == "OK") { $racesData = $response['Data']; $rows = array(); foreach($racesData as $oneRace) { diff --git a/lib/OrisIntegrationService.php b/lib/OrisIntegrationService.php index 6bced1f..cf7c6c5 100644 --- a/lib/OrisIntegrationService.php +++ b/lib/OrisIntegrationService.php @@ -189,5 +189,48 @@ public function getEvent($eventId) { return json_decode($response, true); } + + public function getEventList($fromDate, $toDate, $all = 1) { + $params = [ + 'method' => 'getEventList', + 'format' => 'json', + 'all' => $all, + 'datefrom' => $fromDate, + 'dateto' => $toDate + ]; + + $ch = curl_init(); + $url = $this->apiUrl . '?' . http_build_query($params); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); + + $response = curl_exec($ch); + curl_close($ch); + + return json_decode($response, true); + } + + public function getRegistration($sport, $year) { + $params = [ + 'method' => 'getRegistration', + 'format' => 'json', + 'sport' => $sport, + 'year' => $year + ]; + + $ch = curl_init(); + $url = $this->apiUrl . '?' . http_build_query($params); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); + + $response = curl_exec($ch); + curl_close($ch); + + return json_decode($response, true); + } } -?> \ No newline at end of file +?> diff --git a/oris_sync.log b/oris_sync.log deleted file mode 100644 index a9beebf..0000000 --- a/oris_sync.log +++ /dev/null @@ -1,148 +0,0 @@ -[2026-03-12 21:53:42] Processing create for entry ID 125 (user: 26, race: 48) -[2026-03-12 21:53:42] - Skipped: Race 48 is not imported from ORIS (no oris_comp_id). Marking LOCAL_ONLY. -[2026-03-12 21:56:25] Processing create for entry ID 138 (user: 39, race: 52) -[2026-03-12 21:56:25] - Sending create request to ORIS API. clubuser: 8721, class: H21, si: 0 -[2026-03-12 21:56:25] - Failed: Action create encountered an error: API Error or HTTP 200 -[2026-03-12 21:56:57] Processing create for entry ID 138 (user: 39, race: 52) -[2026-03-12 21:56:57] - Sending create request to ORIS API. clubuser: 8721, class: H21, si: 0 -[2026-03-12 21:56:57] - Failed: Action create encountered an error: API Error or HTTP 200 -[2026-03-12 21:59:24] Processing create for entry ID 138 (user: 39, race: 52) -[2026-03-12 21:59:24] - Sending create request to ORIS API. clubuser: 8721, class: H21, si: 0 -[2026-03-12 21:59:24] - Failed: Action create encountered an error: API Error or HTTP 200 ORIS Status: Reg.č. není platné -[2026-03-12 22:01:44] Processing create for entry ID 138 (user: 39, race: 52) -[2026-03-12 22:01:44] - Sending create request to ORIS API. clubuser: 8721, class: H21, si: 0 -[2026-03-12 22:01:44] - Failed: Action create encountered an error: API Error or HTTP 200 ORIS Status: Reg.č. není platné -[2026-03-12 22:01:58] Processing create for entry ID 138 (user: 39, race: 52) -[2026-03-12 22:01:58] - Sending create request to ORIS API. clubuser: 8721, class: H21, si: 0 -[2026-03-12 22:01:58] - Raw POST Data: clubuser=8721&class=H21&method=createEntry&format=json&clubkey=16704c0a8cc4bb025db487f49411e808 -[2026-03-12 22:01:58] - Failed: Action create encountered an error: API Error or HTTP 200 ORIS Status: Reg.č. není platné -[2026-03-12 22:02:59] Processing create for entry ID 138 (user: 39, race: 52) -[2026-03-12 22:02:59] - Sending create request to ORIS API. clubuser: ZBM8721, class: H21, si: 0 -[2026-03-12 22:02:59] - Raw POST Data: clubuser=ZBM8721&class=H21&method=createEntry&format=json&clubkey=16704c0a8cc4bb025db487f49411e808 -[2026-03-12 22:02:59] - Failed: Action create encountered an error: API Error or HTTP 200 ORIS Status: Reg.č. není platné -[2026-03-12 22:05:58] Processing create for entry ID 138 (user: 39, race: 52) -[2026-03-12 22:05:58] - Sending create request to ORIS API. clubuser: ZBM8721, class: H21, si: 0 -[2026-03-12 22:05:58] - Raw POST Data: clubuser=ZBM8721&class=H21&method=createEntry&format=json&clubkey=16704c0a8cc4bb025db487f49411e808 -[2026-03-12 22:05:58] - Failed: Action create encountered an error: API Error or HTTP 200 ORIS Status: Reg.č. není platné -[2026-03-12 22:06:30] Processing create for entry ID 138 (user: 39, race: 52) -[2026-03-12 22:06:30] - Sending create request to ORIS API. clubuser: 7000, class: H21, si: 0 -[2026-03-12 22:06:31] - Raw POST Data: clubuser=7000&class=H21&method=createEntry&format=json&clubkey=16704c0a8cc4bb025db487f49411e808 -[2026-03-12 22:06:31] - Failed: Action create encountered an error: API Error or HTTP 200 ORIS Status: Neplatná kategorie -[2026-03-12 22:11:24] Processing create for entry ID 138 (user: 39, race: 52) -[2026-03-12 22:11:25] - Warning: Could not resolve ORIS Class ID for category 'H21' -[2026-03-12 22:11:25] - Sending create request to ORIS API. clubuser: 7000, class: H21 (mapped from H21), si: 0 -[2026-03-12 22:11:25] - Raw POST Data: clubuser=7000&class=H21&method=createEntry&format=json&clubkey=16704c0a8cc4bb025db487f49411e808 -[2026-03-12 22:11:25] - Failed: Action create encountered an error: API Error or HTTP 200 ORIS Status: Neplatná kategorie -[2026-03-12 22:12:14] Processing create for entry ID 138 (user: 39, race: 52) -[2026-03-12 22:12:14] - Warning: Could not resolve ORIS Class ID for category 'H21' -[2026-03-12 22:12:14] - Sending create request to ORIS API. clubuser: 7000, class: H21 (mapped from H21), si: 0 -[2026-03-12 22:12:14] - Raw POST Data: clubuser=7000&class=H21&method=createEntry&format=json&clubkey=16704c0a8cc4bb025db487f49411e808 -[2026-03-12 22:12:14] - Failed: Action create encountered an error: API Error or HTTP 200 ORIS Status: Neplatná kategorie -[2026-03-12 22:15:33] Processing create for entry ID 138 (user: 39, race: 52) -[2026-03-12 22:15:34] - Sending create request to ORIS API. clubuser: 7000, class: 217130 (mapped from H21), si: 0 -[2026-03-12 22:15:34] - Raw POST Data: clubuser=7000&class=217130&method=createEntry&format=json&clubkey=16704c0a8cc4bb025db487f49411e808 -[2026-03-12 22:15:34] - Success: Action create completed. ORIS Entry ID: -[2026-03-12 22:24:40] Processing create for entry ID 138 (user: 39, race: 52) -[2026-03-12 22:24:41] - Sending create request to ORIS API. clubuser: 7323, class: 217130 (mapped from H21), si: 0 -[2026-03-12 22:24:41] - Raw POST Data: clubuser=7323&class=217130&method=createEntry&format=json&clubkey=16704c0a8cc4bb025db487f49411e808 -[2026-03-12 22:24:41] - Success: Action create completed. ORIS Entry ID: -[2026-03-12 22:42:43] Processing create for entry ID 138 (user: 39, race: 52) -[2026-03-12 22:42:43] - Sending create request to ORIS API. clubuser: 7323, class: 217130 (mapped from H21), si: 0 -[2026-03-12 22:42:43] - Raw POST Data: clubuser=7323&class=217130&method=createEntry&format=json&clubkey=16704c0a8cc4bb025db487f49411e808 -[2026-03-12 22:42:43] - Success: Action create completed. ORIS Entry ID: -[2026-03-13 13:41:09] Processing update for entry ID 139 (user: 39, race: 53) -[2026-03-13 13:41:10] - Sending update request to ORIS API. clubuser: 7323, class: 211624 (mapped from H21C), si: 0 -[2026-03-13 13:41:10] - Failed: Action update encountered an error: Cannot update: No ORIS Entry ID -[2026-03-13 13:45:25] Processing create for entry ID 139 (user: 39, race: 53) -[2026-03-13 13:45:25] - Sending create request to ORIS API. clubuser: 7323, class: 211624 (mapped from H21C), si: 0 -[2026-03-13 13:45:25] - Raw POST Data: clubuser=7323&class=211624&method=createEntry&format=json&clubkey=16704c0a8cc4bb025db487f49411e808 -[2026-03-13 13:45:25] - Success: Action create completed. ORIS Entry ID: -[2026-03-13 13:50:40] Processing delete for entry ID 139 (user: 39, race: 53) -[2026-03-13 13:50:41] - Sending delete request to ORIS API. clubuser: 7323, class: 211624 (mapped from H21C), si: 0 -[2026-03-13 13:50:41] - Success: Action delete completed. ORIS Entry ID: -[2026-03-13 13:53:30] Processing create for entry ID 140 (user: 11, race: 53) -[2026-03-13 13:53:30] - Sending create request to ORIS API. clubuser: 7315, class: 211626 (mapped from H35), si: 0 -[2026-03-13 13:53:31] - Raw POST Data: clubuser=7315&class=211626&method=createEntry&format=json&clubkey=16704c0a8cc4bb025db487f49411e808 -[2026-03-13 13:53:31] - Success: Action create completed. ORIS Entry ID: -[2026-03-13 13:53:31] Processing create for entry ID 141 (user: 39, race: 53) -[2026-03-13 13:53:31] - Sending create request to ORIS API. clubuser: 7323, class: 211626 (mapped from H35), si: 0 -[2026-03-13 13:53:31] - Raw POST Data: clubuser=7323&class=211626&method=createEntry&format=json&clubkey=16704c0a8cc4bb025db487f49411e808 -[2026-03-13 13:53:31] - Failed: Action create encountered an error: API Error or HTTP 200 ORIS Status: Již přihlášen -[2026-03-13 13:54:19] Processing delete for entry ID 140 (user: 11, race: 53) -[2026-03-13 13:54:19] - Sending delete request to ORIS API. clubuser: 7315, class: 211626 (mapped from H35), si: 0 -[2026-03-13 13:54:19] - Success: Action delete completed. ORIS Entry ID: -[2026-03-13 13:54:19] Processing delete for entry ID 141 (user: 39, race: 53) -[2026-03-13 13:54:20] - Sending delete request to ORIS API. clubuser: 7323, class: 211626 (mapped from H35), si: 0 -[2026-03-13 13:54:20] - Success: Action delete completed. ORIS Entry ID: -[2026-03-13 14:04:36] Processing update for entry ID 142 (user: 11, race: 53) -[2026-03-13 14:04:36] - Sending update request to ORIS API. clubuser: 7315, class: 211626 (mapped from H35), si: 0 -[2026-03-13 14:04:36] - Warning: Missing ORIS Entry ID for update. Falling back to create. -[2026-03-13 14:04:37] - Raw POST Data: clubuser=7315&class=211626&method=createEntry&format=json&clubkey=16704c0a8cc4bb025db487f49411e808 -[2026-03-13 14:04:37] - Failed: Action update encountered an error: API Error or HTTP 200 ORIS Status: Již přihlášen -[2026-03-13 14:04:37] Processing update for entry ID 143 (user: 39, race: 53) -[2026-03-13 14:04:37] - Sending update request to ORIS API. clubuser: 7323, class: 211626 (mapped from H35), si: 0 -[2026-03-13 14:04:37] - Warning: Missing ORIS Entry ID for update. Falling back to create. -[2026-03-13 14:04:37] - Raw POST Data: clubuser=7323&class=211626&method=createEntry&format=json&clubkey=16704c0a8cc4bb025db487f49411e808 -[2026-03-13 14:04:37] - Failed: Action update encountered an error: API Error or HTTP 200 ORIS Status: Již přihlášen -[2026-03-13 14:06:59] Processing create for entry ID 142 (user: 11, race: 53) -[2026-03-13 14:06:59] - Sending create request to ORIS API. clubuser: 7315, class: 211626 (mapped from H35), si: 0 -[2026-03-13 14:07:00] - Raw POST Data: clubuser=7315&class=211626&method=createEntry&format=json&clubkey=16704c0a8cc4bb025db487f49411e808 -[2026-03-13 14:07:00] - Failed: Action create encountered an error: API Error or HTTP 200 ORIS Status: Již přihlášen -[2026-03-13 14:07:00] Processing create for entry ID 143 (user: 39, race: 53) -[2026-03-13 14:07:00] - Sending create request to ORIS API. clubuser: 7323, class: 211626 (mapped from H35), si: 0 -[2026-03-13 14:07:00] - Raw POST Data: clubuser=7323&class=211626&method=createEntry&format=json&clubkey=16704c0a8cc4bb025db487f49411e808 -[2026-03-13 14:07:00] - Failed: Action create encountered an error: API Error or HTTP 200 ORIS Status: Již přihlášen -[2026-03-13 14:08:30] Processing update for entry ID 142 (user: 11, race: 53) -[2026-03-13 14:08:30] - Sending update request to ORIS API. clubuser: 7315, class: 211626 (mapped from H35), si: 0 -[2026-03-13 14:08:30] - Warning: Missing ORIS Entry ID for update. Attempting to fetch from ORIS. -[2026-03-13 14:08:31] - Found ORIS Entry ID 3170927 for clubuser 7315 -[2026-03-13 14:08:31] - Raw POST Data: entryid=3170927&clubuser=7315&class=211626&method=updateEntry&format=json&clubkey=16704c0a8cc4bb025db487f49411e808 -[2026-03-13 14:08:31] - Success: Action update completed. ORIS Entry ID: -[2026-03-13 14:08:31] Processing update for entry ID 143 (user: 39, race: 53) -[2026-03-13 14:08:31] - Sending update request to ORIS API. clubuser: 7323, class: 211626 (mapped from H35), si: 0 -[2026-03-13 14:08:31] - Warning: Missing ORIS Entry ID for update. Attempting to fetch from ORIS. -[2026-03-13 14:08:31] - Found ORIS Entry ID 3170917 for clubuser 7323 -[2026-03-13 14:08:32] - Raw POST Data: entryid=3170917&clubuser=7323&class=211626&method=updateEntry&format=json&clubkey=16704c0a8cc4bb025db487f49411e808 -[2026-03-13 14:08:32] - Success: Action update completed. ORIS Entry ID: -[2026-03-13 14:10:12] Processing update for entry ID 142 (user: 11, race: 53) -[2026-03-13 14:10:13] - Sending update request to ORIS API. clubuser: 7315, class: 211624 (mapped from H21C), si: 0 -[2026-03-13 14:10:13] - Warning: Missing ORIS Entry ID for update. Attempting to fetch from ORIS. -[2026-03-13 14:10:13] - Found ORIS Entry ID 3170927 for clubuser 7315 -[2026-03-13 14:10:13] - Raw POST Data: entryid=3170927&clubuser=7315&class=211624&method=updateEntry&format=json&clubkey=16704c0a8cc4bb025db487f49411e808 -[2026-03-13 14:10:13] - Success: Action update completed. ORIS Entry ID: 3170927 -[2026-03-13 14:10:13] Processing update for entry ID 143 (user: 39, race: 53) -[2026-03-13 14:10:14] - Warning: Could not resolve ORIS Class ID for category 'H21' -[2026-03-13 14:10:14] - Sending update request to ORIS API. clubuser: 7323, class: H21 (mapped from H21), si: 0 -[2026-03-13 14:10:14] - Warning: Missing ORIS Entry ID for update. Attempting to fetch from ORIS. -[2026-03-13 14:10:14] - Found ORIS Entry ID 3170917 for clubuser 7323 -[2026-03-13 14:10:14] - Raw POST Data: entryid=3170917&clubuser=7323&class=H21&method=updateEntry&format=json&clubkey=16704c0a8cc4bb025db487f49411e808 -[2026-03-13 14:10:14] - Success: Action update completed. ORIS Entry ID: 3170917 -[2026-03-13 14:39:31] Processing delete for entry ID 142 (user: 11, race: 53) -[2026-03-13 14:39:32] - Sending delete request to ORIS API. clubuser: 7315, class: 211624 (mapped from H21C), si: 0 -[2026-03-13 14:39:32] - Raw POST Data: entryid=3170927&method=deleteEntry&format=json&clubkey=16704c0a8cc4bb025db487f49411e808 -[2026-03-13 14:39:32] - Success: Action delete completed. ORIS Entry ID: 3170927 -[2026-03-13 14:39:32] Processing delete for entry ID 143 (user: 39, race: 53) -[2026-03-13 14:39:32] - Warning: Could not resolve ORIS Class ID for category 'H21' -[2026-03-13 14:39:32] - Sending delete request to ORIS API. clubuser: 7323, class: H21 (mapped from H21), si: 0 -[2026-03-13 14:39:33] - Raw POST Data: entryid=3170917&method=deleteEntry&format=json&clubkey=16704c0a8cc4bb025db487f49411e808 -[2026-03-13 14:39:33] - Success: Action delete completed. ORIS Entry ID: 3170917 -[2026-03-13 14:40:37] Processing create for entry ID 144 (user: 11, race: 53) -[2026-03-13 14:40:38] - Sending create request to ORIS API. clubuser: 7315, class: 211624 (mapped from H21C), si: 0 -[2026-03-13 14:40:39] - Raw POST Data: clubuser=7315&class=211624&method=createEntry&format=json&clubkey=16704c0a8cc4bb025db487f49411e808 -[2026-03-13 14:40:39] - Success: Action create completed. ORIS Entry ID: -[2026-03-13 14:40:39] Processing create for entry ID 145 (user: 39, race: 53) -[2026-03-13 14:40:40] - Sending create request to ORIS API. clubuser: 7323, class: 211626 (mapped from H35), si: 0 -[2026-03-13 14:40:40] - Raw POST Data: clubuser=7323&class=211626&method=createEntry&format=json&clubkey=16704c0a8cc4bb025db487f49411e808 -[2026-03-13 14:40:40] - Success: Action create completed. ORIS Entry ID: -[2026-03-13 14:44:01] Processing delete for entry ID 144 (user: 11, race: 53) -[2026-03-13 14:44:02] - Sending delete request to ORIS API. clubuser: 7315, class: 211624 (mapped from H21C), si: 0 -[2026-03-13 14:44:02] - Warning: Missing ORIS Entry ID for delete. Attempting to fetch from ORIS. -[2026-03-13 14:44:02] - Found ORIS Entry ID 3170940 for clubuser 7315 -[2026-03-13 14:44:02] - Raw POST Data: entryid=3170940&method=deleteEntry&format=json&clubkey=16704c0a8cc4bb025db487f49411e808 -[2026-03-13 14:44:02] - Success: Action delete completed. ORIS Entry ID: -[2026-03-13 14:44:02] Processing delete for entry ID 145 (user: 39, race: 53) -[2026-03-13 14:44:03] - Sending delete request to ORIS API. clubuser: 7323, class: 211626 (mapped from H35), si: 0 -[2026-03-13 14:44:03] - Warning: Missing ORIS Entry ID for delete. Attempting to fetch from ORIS. -[2026-03-13 14:44:03] - Found ORIS Entry ID 3170941 for clubuser 7323 -[2026-03-13 14:44:03] - Raw POST Data: entryid=3170941&method=deleteEntry&format=json&clubkey=16704c0a8cc4bb025db487f49411e808 -[2026-03-13 14:44:03] - Success: Action delete completed. ORIS Entry ID: diff --git a/oris_sync_daemon.php b/oris_sync_daemon.php index aad999a..5e751db 100644 --- a/oris_sync_daemon.php +++ b/oris_sync_daemon.php @@ -29,6 +29,7 @@ FROM `" . TBL_ZAVXUS . "` zx LEFT JOIN `" . TBL_RACE . "` r ON zx.id_zavod = r.id WHERE zx.sync_status IN ('PENDING_CREATE', 'PENDING_UPDATE', 'PENDING_DELETE', 'FAILED_RETRY') + AND (r.datum >= DATE_SUB(CURDATE(), INTERVAL 1 DAY) OR r.datum IS NULL) ORDER BY ISNULL(r.oris_entry_start), r.oris_entry_start ASC "; $res = query_db($query); @@ -79,7 +80,7 @@ function logMessage($msg) { // Output to console when run via CLI echo $line; // Log to file in the same directory - file_put_contents(__DIR__ . '/oris_sync.log', $line, FILE_APPEND); + file_put_contents(__DIR__ . '/logs/oris_sync.log', $line, FILE_APPEND); } function processEntry($row, $action, $service) { From 965ebcec0dcb8a9edeea761a2982b2e996d5fdb0 Mon Sep 17 00:00:00 2001 From: "jan.zhanal" Date: Tue, 17 Mar 2026 21:50:40 +0100 Subject: [PATCH 4/5] refactor connectors --- connectors.php | 140 ++++------------------ lib/OrisDTOs.php | 89 ++++++++++++++ lib/OrisExceptions.php | 26 ++++ lib/OrisIntegrationService.php | 211 +++++++-------------------------- 4 files changed, 181 insertions(+), 285 deletions(-) create mode 100644 lib/OrisDTOs.php create mode 100644 lib/OrisExceptions.php diff --git a/connectors.php b/connectors.php index f3fc817..f91ab7f 100644 --- a/connectors.php +++ b/connectors.php @@ -4,57 +4,8 @@ { require_once('./cfg/_cfg.php'); } -// Define a class to represent the race data -class Race { - public $ext_id; - public $datum; - public $datum2; - public $nazev; - public $misto; - public $typ; - public $zebricek2; - public $ranking; - public $odkaz; - public $prihlasky; - public $prihlasky1; - public $prihlasky2; - public $prihlasky3; - public $prihlasky4; - public $prihlasky5; - public $etap; - public $poznamka; - public $vicedenni; - public $oddil; - public $modify_flag; - public $kategorie; - public $oris_entry_start; - // Constructor to initialize the object with key-value pairs - public function __construct($data) { - $this->ext_id = $data['ext_id'] ?? null; - $this->datum = $data['datum'] ?? null; - $this->datum2 = $data['datum2'] ?? null; - $this->nazev = $data['nazev'] ?? null; - $this->misto = $data['misto'] ?? null; - $this->typ = $data['typ'] ?? null; - $this->zebricek2 = $data['zebricek2'] ?? null; - $this->ranking = $data['ranking'] ?? null; - $this->odkaz = $data['odkaz'] ?? null; - $this->prihlasky = $data['prihlasky'] ?? null; - $this->prihlasky1 = $data['prihlasky1'] ?? null; - $this->prihlasky2 = $data['prihlasky2'] ?? null; - $this->prihlasky3 = $data['prihlasky3'] ?? null; - $this->prihlasky4 = $data['prihlasky4'] ?? null; - $this->prihlasky5 = $data['prihlasky5'] ?? null; - $this->etap = $data['etap'] ?? null; - $this->poznamka = $data['poznamka'] ?? null; - $this->vicedenni = $data['vicedenni'] ?? null; - $this->oddil = $data['oddil'] ?? null; - $this->modify_flag = $data['modify_flag'] ?? null; - $this->kategorie = $data['kategorie'] ?? null; - $this->oris_entry_start = $data['oris_entry_start'] ?? null; - } -} +require_once __DIR__ . '/lib/OrisIntegrationService.php'; interface ConnectorInterface { public function getSystemName(): string; @@ -62,25 +13,18 @@ public function getRaceURL(string $id): string; public function getRaceInfo(string $id); } -require_once __DIR__ . '/lib/OrisIntegrationService.php'; - class OrisCZConnector implements ConnectorInterface { private $sourceUrl = 'https://oris.ceskyorientak.cz/'; - private $apiUrl; private $service; public function __construct() { - $this->apiUrl = $this->sourceUrl . 'API/'; $this->service = new OrisIntegrationService(null); } - - // Method to get the system name public function getSystemName(): string { return "Oris"; } - // Race URL public function getRaceURL($raceId) : string { return $this->sourceUrl . 'Zavod?id=' . $raceId; } @@ -99,7 +43,6 @@ private function mapLevelToZebricek2($levelId) { } private function mapSport($sportId) { - //sport ID from ORIS : 1=OB, 2=LOB, 3=MTBO, 4=TRAIL $map = [ 1 => 1, // OB 2 => 4, // LOB @@ -110,7 +53,6 @@ private function mapSport($sportId) { } private function getClubs(&$raceData) { - $oddily = []; if (isset($raceData['Org1']['Abbr'])) { $oddily[] = $raceData['Org1']['Abbr']; @@ -121,26 +63,21 @@ private function getClubs(&$raceData) { return implode('+', $oddily); } - // Method to get race date based on race ID public function getRaceDate($raceId) { - $response = $this->service->getEvent($raceId); - - if ($response && isset($response['Status']) && $response['Status'] == "OK") { - $raceData = $response['Data']; - - return String2DateDMY(formatDate($raceData['Date'])); - } else { - return ''; // Return empty string if race not found or error + try { + $raceData = $this->service->getEvent($raceId); + if (isset($raceData['Date'])) { + return String2DateDMY(formatDate($raceData['Date'])); + } + } catch (OrisException $e) { + // fallback } - + return ''; } - // Method to get detailed race information based on race ID public function getRaceInfo($raceId) { - $response = $this->service->getEvent($raceId); - - if ($response && isset($response['Status']) && $response['Status'] == "OK") { - $raceData = $response['Data']; + try { + $raceData = $this->service->getEvent($raceId); $classNames = []; if (isset($raceData['Classes'])) { @@ -152,20 +89,16 @@ public function getRaceInfo($raceId) { } sort($classNames); - $oddily = $this->getClubs($raceData); - // Get last Stage date if multistage event $date2 = ($raceData['Stages'] > 1) ? $this->getRaceDate($raceData['Stage'.$raceData['Stages']]) : 0; - // Use associative array to pass data to constructor - return new Race([ + + return new RaceDTO([ 'ext_id' => $raceData['ID'], 'datum' => String2DateDMY(formatDate($raceData['Date'])), 'datum2' => $date2, 'nazev' => $raceData['Name'], 'misto' => $raceData['Place'], -// 'category' => $raceData['Category'], - //typ0 => Typ akce 'typ0' => 'Z', 'typ' => $this->mapSport($raceData['Sport']['ID']), 'zebricek2' => $this->mapLevelToZebricek2($raceData['Level']['ID']), @@ -174,54 +107,21 @@ public function getRaceInfo($raceId) { 'prihlasky' => strtotime($raceData['EntryDate1']), 'prihlasky1' => strtotime($raceData['EntryDate2']), 'prihlasky2' => strtotime($raceData['EntryDate3']), -// 'prihlasky3' => '', -// 'prihlasky4' => '', -// 'prihlasky5' => '', 'etap' => $raceData['Stages'], -// 'poznamka' => $poznamka, 'vicedenni' => ($raceData['Stages']>1?1:0), 'oddil' => $oddily, 'modify_flag' => 0, 'kategorie' => implode(';', $classNames ), 'oris_entry_start' => !empty($raceData['EntryStart']) ? $raceData['EntryStart'] : null - ]); - } else { - return null; // Return null if race not found or error - } - } - - // Helper method to make HTTP requests - private function makeRequest($url) { - $response = file_get_contents($url); - - // Decode JSON response - return json_decode($response, true); - } - - private function makeRequestCurl($url) { - $ch = curl_init($url); - - // Set curl options - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - $response = curl_exec($ch); - - // Check for errors - if (curl_errno($ch)) { - echo 'Curl error: ' . curl_error($ch); + ]); + } catch (OrisException $e) { return null; } - - curl_close($ch); - - // Decode JSON response - return json_decode($response, true); } function getRacesList($fromDate, $toDate) { - $response = $this->service->getEventList($fromDate, $toDate, 1); - - if ($response && isset($response['Status']) && $response['Status'] == "OK") { - $racesData = $response['Data']; + try { + $racesData = $this->service->getEventList($fromDate, $toDate, 1); $rows = array(); foreach($racesData as $oneRace) { $oddily = $this->getClubs($oneRace); @@ -234,8 +134,8 @@ function getRacesList($fromDate, $toDate) { $rows[] = $row; } return $rows; - } else { - return null; // Return null if race not found or error + } catch (OrisException $e) { + return null; } } } @@ -248,6 +148,6 @@ public static function create(): ?ConnectorInterface { return new $g_external_is_connector(); } - return null; // Return null explicitly if no valid connector is found + return null; } } diff --git a/lib/OrisDTOs.php b/lib/OrisDTOs.php new file mode 100644 index 0000000..5e6004e --- /dev/null +++ b/lib/OrisDTOs.php @@ -0,0 +1,89 @@ +ext_id = $data['ext_id'] ?? null; + $this->datum = $data['datum'] ?? null; + $this->datum2 = $data['datum2'] ?? null; + $this->nazev = $data['nazev'] ?? null; + $this->misto = $data['misto'] ?? null; + $this->typ = $data['typ'] ?? null; + $this->zebricek2 = $data['zebricek2'] ?? null; + $this->ranking = $data['ranking'] ?? null; + $this->odkaz = $data['odkaz'] ?? null; + $this->prihlasky = $data['prihlasky'] ?? null; + $this->prihlasky1 = $data['prihlasky1'] ?? null; + $this->prihlasky2 = $data['prihlasky2'] ?? null; + $this->prihlasky3 = $data['prihlasky3'] ?? null; + $this->prihlasky4 = $data['prihlasky4'] ?? null; + $this->prihlasky5 = $data['prihlasky5'] ?? null; + $this->etap = $data['etap'] ?? null; + $this->poznamka = $data['poznamka'] ?? null; + $this->vicedenni = $data['vicedenni'] ?? null; + $this->oddil = $data['oddil'] ?? null; + $this->modify_flag = $data['modify_flag'] ?? null; + $this->kategorie = $data['kategorie'] ?? null; + $this->oris_entry_start = $data['oris_entry_start'] ?? null; + $this->typ0 = $data['typ0'] ?? null; + } +} + +class OrisEntryRequestDTO { + public ?string $clubuser; + public ?string $classId; + public ?string $si; + public int $rentSi; + public ?string $note; + public ?string $entryId; + + public function __construct( + ?string $clubuser, + ?string $classId, + ?string $si, + bool $rentSi = false, + ?string $note = null, + ?string $entryId = null + ) { + $this->clubuser = $clubuser; + $this->classId = $classId; + $this->si = $si; + $this->rentSi = $rentSi ? 1 : 0; + $this->note = $note; + $this->entryId = $entryId; + } + + public function toArray(): array { + $data = []; + if ($this->clubuser !== null) $data['clubuser'] = $this->clubuser; + if ($this->classId !== null) $data['class'] = $this->classId; + if ($this->si !== null && $this->si !== '') $data['si'] = $this->si; + if ($this->rentSi) $data['rent_si'] = 1; + if ($this->note !== null && $this->note !== '') $data['note'] = $this->note; + if ($this->entryId !== null) $data['entryid'] = $this->entryId; + return $data; + } +} diff --git a/lib/OrisExceptions.php b/lib/OrisExceptions.php new file mode 100644 index 0000000..4cda17f --- /dev/null +++ b/lib/OrisExceptions.php @@ -0,0 +1,26 @@ +apiStatus = $apiStatus; + $this->apiData = $apiData; + } + + public function getApiStatus() { + return $this->apiStatus; + } + + public function getApiData() { + return $this->apiData; + } +} + +class OrisValidationException extends OrisException {} diff --git a/lib/OrisIntegrationService.php b/lib/OrisIntegrationService.php index cf7c6c5..65bf619 100644 --- a/lib/OrisIntegrationService.php +++ b/lib/OrisIntegrationService.php @@ -1,33 +1,44 @@ clubKey = $clubKey; } /** - * Executes the HTTP POST request to ORIS API + * Internal generic HTTP request method. */ - private function executeRequest($method, $params) { + private function makeRequest($method, $params = [], $isPost = false) { $params['method'] = $method; $params['format'] = 'json'; - $params['clubkey'] = $this->clubKey; + if ($this->clubKey) { + $params['clubkey'] = $this->clubKey; + } + + $ch = curl_init(); - $postData = http_build_query($params); + if ($isPost) { + $postData = http_build_query($params); + curl_setopt($ch, CURLOPT_URL, $this->apiUrl); + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_POSTFIELDS, $postData); + } else { + $url = $this->apiUrl . '?' . http_build_query($params); + curl_setopt($ch, CURLOPT_URL, $url); + } - $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, $this->apiUrl); - curl_setopt($ch, CURLOPT_POST, 1); - curl_setopt($ch, CURLOPT_POSTFIELDS, $postData); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - // Ensure SSL verification curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); @@ -36,7 +47,7 @@ private function executeRequest($method, $params) { if(curl_errno($ch)){ $error = curl_error($ch); curl_close($ch); - return ['status' => 'error', 'message' => 'cURL Error: ' . $error, 'request' => $postData]; + throw new OrisNetworkException('cURL Error: ' . $error); } $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); @@ -45,192 +56,62 @@ private function executeRequest($method, $params) { $decoded = json_decode($response, true); if ($httpCode >= 200 && $httpCode < 300 && isset($decoded['Status']) && $decoded['Status'] === 'OK') { - return ['status' => 'success', 'data' => $decoded['Data'] ?? $decoded, 'request' => $postData]; + return $decoded['Data'] ?? $decoded; } else { - $apiErrorMsg = ''; - if (is_array($decoded) && isset($decoded['Status'])) { - $apiErrorMsg = " ORIS Status: " . $decoded['Status']; - // Some endpoints might return error details in Data or a specific Error field - if (isset($decoded['Data']) && is_string($decoded['Data'])) { - $apiErrorMsg .= " - " . $decoded['Data']; - } + $apiStatus = $decoded['Status'] ?? 'Unknown'; + $apiData = $decoded['Data'] ?? null; + $msg = "API Error or HTTP {$httpCode}. Status: {$apiStatus}"; + if (is_string($apiData)) { + $msg .= " - " . $apiData; } - return [ - 'status' => 'error', - 'message' => 'API Error or HTTP ' . $httpCode . $apiErrorMsg, - 'payload' => $response, - 'request' => $postData - ]; + throw new OrisApiException($msg, $apiStatus, $apiData); } } - public function createEntry($clubuser, $classId, $si, $rentSi, $note = '') { - $params = [ - 'clubuser' => $clubuser, - 'class' => $classId - ]; - - if ($si) { - $params['si'] = $si; - } - if ($rentSi) { - $params['rent_si'] = 1; - } - if ($note) { - $params['note'] = $note; - } + // --- Write/Mutating Operations (Phase C) --- - return $this->executeRequest('createEntry', $params); + public function createEntry(OrisEntryRequestDTO $dto) { + return $this->makeRequest('createEntry', $dto->toArray(), true); } - public function updateEntry($entryId, $clubuser, $classId, $si, $rentSi, $note = '') { - $params = [ - 'entryid' => $entryId, - 'clubuser' => $clubuser, - 'class' => $classId - ]; - - if ($si) { - $params['si'] = $si; - } - if ($rentSi) { - $params['rent_si'] = 1; - } - if ($note) { - $params['note'] = $note; - } - - return $this->executeRequest('updateEntry', $params); + public function updateEntry(OrisEntryRequestDTO $dto) { + return $this->makeRequest('updateEntry', $dto->toArray(), true); } public function deleteEntry($entryId) { - $params = [ - 'entryid' => $entryId - ]; - return $this->executeRequest('deleteEntry', $params); + return $this->makeRequest('deleteEntry', ['entryid' => $entryId], true); } + // --- Read-Only and Protected Read Endpoints (Phase A & B) --- + public function getUser($rgnum) { - $params = [ - 'method' => 'getUser', - 'format' => 'json', - 'rgnum' => $rgnum - ]; - - $ch = curl_init(); - $url = $this->apiUrl . '?' . http_build_query($params); - curl_setopt($ch, CURLOPT_URL, $url); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); - curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); - - $response = curl_exec($ch); - curl_close($ch); - - return json_decode($response, true); + return $this->makeRequest('getUser', ['rgnum' => $rgnum]); } public function getClubUsers($userId) { - $params = [ - 'method' => 'getClubUsers', - 'format' => 'json', - 'user' => $userId - ]; - - $ch = curl_init(); - $url = $this->apiUrl . '?' . http_build_query($params); - curl_setopt($ch, CURLOPT_URL, $url); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); - curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); - - $response = curl_exec($ch); - curl_close($ch); - - return json_decode($response, true); + return $this->makeRequest('getClubUsers', ['user' => $userId]); } public function getEventEntries($eventId) { - $params = [ - 'method' => 'getEventEntries', - 'format' => 'json', - 'eventid' => $eventId - ]; - - $ch = curl_init(); - $url = $this->apiUrl . '?' . http_build_query($params); - curl_setopt($ch, CURLOPT_URL, $url); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); - curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); - - $response = curl_exec($ch); - curl_close($ch); - - return json_decode($response, true); + return $this->makeRequest('getEventEntries', ['eventid' => $eventId]); } public function getEvent($eventId) { - $params = [ - 'method' => 'getEvent', - 'format' => 'json', - 'id' => $eventId - ]; - - $ch = curl_init(); - $url = $this->apiUrl . '?' . http_build_query($params); - curl_setopt($ch, CURLOPT_URL, $url); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); - curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); - - $response = curl_exec($ch); - curl_close($ch); - - return json_decode($response, true); + return $this->makeRequest('getEvent', ['id' => $eventId]); } public function getEventList($fromDate, $toDate, $all = 1) { - $params = [ - 'method' => 'getEventList', - 'format' => 'json', + return $this->makeRequest('getEventList', [ 'all' => $all, 'datefrom' => $fromDate, 'dateto' => $toDate - ]; - - $ch = curl_init(); - $url = $this->apiUrl . '?' . http_build_query($params); - curl_setopt($ch, CURLOPT_URL, $url); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); - curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); - - $response = curl_exec($ch); - curl_close($ch); - - return json_decode($response, true); + ]); } public function getRegistration($sport, $year) { - $params = [ - 'method' => 'getRegistration', - 'format' => 'json', + return $this->makeRequest('getRegistration', [ 'sport' => $sport, 'year' => $year - ]; - - $ch = curl_init(); - $url = $this->apiUrl . '?' . http_build_query($params); - curl_setopt($ch, CURLOPT_URL, $url); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); - curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); - - $response = curl_exec($ch); - curl_close($ch); - - return json_decode($response, true); + ]); } } -?> From 49517b6037573fd1892b06346ead4ad50644bb0e Mon Sep 17 00:00:00 2001 From: "jan.zhanal" Date: Mon, 23 Mar 2026 00:41:25 +0100 Subject: [PATCH 5/5] ORIS: immediate entry, fix PR comments, --- _SQL/zmeny_3.4.5.658.sql.php | 5 +- adm_oris_sync.php | 1 + ads_oris.inc.php | 20 +- ads_oris_si_sync.php | 4 +- lib/OrisDTOs.php | 6 +- lib/OrisIntegrationService.php | 3 + lib/oris_sync.inc.php | 370 +++++++++++++++++++++++++++++++++ nav.inc.php | 1 + oris_sync_daemon.php | 274 +----------------------- race_reg_chip_exc.php | 46 +++- race_regs_1_exc.php | 77 ++++++- race_regs_all_exc.php | 105 ++++++++-- rg_oris_sync_log.php | 33 +++ us_race_regoff_exc.php | 44 +++- us_race_regon_exc.php | 84 +++++++- 15 files changed, 764 insertions(+), 309 deletions(-) create mode 100644 lib/oris_sync.inc.php create mode 100644 rg_oris_sync_log.php diff --git a/_SQL/zmeny_3.4.5.658.sql.php b/_SQL/zmeny_3.4.5.658.sql.php index ec7084d..3dbf46a 100644 --- a/_SQL/zmeny_3.4.5.658.sql.php +++ b/_SQL/zmeny_3.4.5.658.sql.php @@ -12,9 +12,10 @@ //############################################################################ $sql[0] = "ALTER TABLE `".TBL_ZAVXUS."` ADD COLUMN `oris_entry_id` INT UNSIGNED NULL, - ADD COLUMN `sync_status` VARCHAR(32) NOT NULL DEFAULT 'LOCAL_ONLY', + ADD COLUMN `sync_status` ENUM('LOCAL_ONLY', 'SYNCED', 'PENDING_CREATE', 'PENDING_UPDATE', 'PENDING_DELETE', 'FAILED_CREATE', 'FAILED_UPDATE', 'FAILED_DELETE') NOT NULL DEFAULT 'LOCAL_ONLY', ADD COLUMN `sync_timestamp` DATETIME NULL, - ADD COLUMN `sync_error_payload` TEXT NULL"; + ADD COLUMN `sync_error_payload` TEXT NULL, + ADD INDEX `sync_status` (`sync_status`)"; $sql[1] = "ALTER TABLE `".TBL_RACE."` ADD COLUMN `oris_entry_start` DATETIME NULL"; diff --git a/adm_oris_sync.php b/adm_oris_sync.php index 5881268..7cbf612 100644 --- a/adm_oris_sync.php +++ b/adm_oris_sync.php @@ -26,6 +26,7 @@ $value) -{ - $user = new User(); - $user->create($value['UserID'], $value['FirstName'], $value['LastName'], $value['RegNo'], $value['SI'], $value['ClubID']); - - $reg = $value['RegNo']; - if (startsWith($reg, $g_shortcut)) +if (is_array($obj) || is_object($obj)) { + foreach ($obj as $key=>$value) { - $arr_oris["user"] [$reg]= $user; - $arr_oris["members"][$reg] = 0; + $user = new User(); + $user->create($value['UserID'], $value['FirstName'], $value['LastName'], $value['RegNo'], $value['SI'], $value['ClubID']); + + $reg = $value['RegNo']; + if (startsWith($reg, $g_shortcut)) + { + $arr_oris["user"] [$reg]= $user; + $arr_oris["members"][$reg] = 0; + } } } //konec nahrani dat z orisu diff --git a/ads_oris_si_sync.php b/ads_oris_si_sync.php index c8e94aa..37f0b9b 100644 --- a/ads_oris_si_sync.php +++ b/ads_oris_si_sync.php @@ -29,7 +29,6 @@ 'format' => 'json', 'method' => 'editPerson', 'userid' => $oris_id, - 'si' => $zaznam['si_chip'], 'clubkey' => $g_oris_club_key, 'firstname' => $zaznam['jmeno'], 'lastname' => $zaznam['prijmeni'], @@ -39,6 +38,9 @@ 'zip' => $zaznam['psc'], 'country' => (!empty($zaznam['narodnost']) ? $zaznam['narodnost'] : 'CZ') ); + if (!empty($zaznam['si_chip']) && $zaznam['si_chip'] != 0 && $zaznam['si_chip'] !== '0') { + $params['si'] = $zaznam['si_chip']; + } $url = "https://oris.ceskyorientak.cz/API/?" . http_build_query($params); diff --git a/lib/OrisDTOs.php b/lib/OrisDTOs.php index 5e6004e..d57f018 100644 --- a/lib/OrisDTOs.php +++ b/lib/OrisDTOs.php @@ -80,8 +80,10 @@ public function toArray(): array { $data = []; if ($this->clubuser !== null) $data['clubuser'] = $this->clubuser; if ($this->classId !== null) $data['class'] = $this->classId; - if ($this->si !== null && $this->si !== '') $data['si'] = $this->si; - if ($this->rentSi) $data['rent_si'] = 1; + if ($this->si !== null && $this->si !== '' && $this->si !== '0' && $this->si !== 0) { + $data['si'] = $this->si; + } + $data['rent_si'] = $this->rentSi ? 1 : 0; if ($this->note !== null && $this->note !== '') $data['note'] = $this->note; if ($this->entryId !== null) $data['entryid'] = $this->entryId; return $data; diff --git a/lib/OrisIntegrationService.php b/lib/OrisIntegrationService.php index 65bf619..9e379c3 100644 --- a/lib/OrisIntegrationService.php +++ b/lib/OrisIntegrationService.php @@ -61,6 +61,9 @@ private function makeRequest($method, $params = [], $isPost = false) { $apiStatus = $decoded['Status'] ?? 'Unknown'; $apiData = $decoded['Data'] ?? null; $msg = "API Error or HTTP {$httpCode}. Status: {$apiStatus}"; + if ($isPost) { + $msg .= "\nPOST Data sent: " . print_r($params, true); + } if (is_string($apiData)) { $msg .= " - " . $apiData; } diff --git a/lib/oris_sync.inc.php b/lib/oris_sync.inc.php new file mode 100644 index 0000000..44a64d5 --- /dev/null +++ b/lib/oris_sync.inc.php @@ -0,0 +1,370 @@ +getUser($rgnum); + if (is_array($userApiRes) && isset($userApiRes['ID'])) { + $globalUserId = $userApiRes['ID']; + + // Now get the club user ID + try { + $clubUsersRes = $service->getClubUsers($globalUserId); + if (is_array($clubUsersRes)) { + $clubUsers = $clubUsersRes; + // Check if it's associative array representing a single user directly + if (isset($clubUsers['ID'])) { + $clubUsers = [$clubUsers]; + } + + // We must find the correct clubuser entry for the current club + // First, try to fetch current club key from config + global $g_external_is_club_id; + $clubId = $g_external_is_club_id ?? null; + + foreach ($clubUsers as $cu) { + if (isset($cu['ID'])) { + // If club ID is known, prefer that match + if ($clubId && isset($cu['ClubID']) && $cu['ClubID'] == $clubId) { + $clubuser = $cu['ID']; + break; + } + // Otherwise, just take the first one or valid one + if (empty($clubuser)) { + $clubuser = $cu['ID']; + } + } + } + } + } catch (Exception $e) { + $errMsg = $e->getMessage(); + logMessage(" - Warning: getClubUsers failed for user ID $globalUserId: $errMsg"); + if ($e instanceof OrisNetworkException || ($e instanceof OrisApiException && strpos($errMsg, 'API Error or HTTP 5') !== false)) { + logMessage(" - Network error getting club users. Skipping to keep PENDING."); + return 'queued'; + } + } + + if (empty($clubuser)) { + logMessage(" - Warning: Could not resolve ORIS Club User ID for user ID $globalUserId"); + } + } else { + logMessage(" - Warning: Could not resolve ORIS User ID for rgnum $rgnum"); + } + } catch (Exception $e) { + $errMsg = $e->getMessage(); + logMessage(" - Warning: getUser failed for rgnum $rgnum: $errMsg"); + if ($e instanceof OrisNetworkException || ($e instanceof OrisApiException && strpos($errMsg, 'API Error or HTTP 5') !== false)) { + logMessage(" - Network error getting user. Skipping to keep PENDING."); + return 'queued'; + } + } + } + } + } + + // Get Race ORIS ID + $raceQuery = "SELECT * FROM `" . TBL_RACE . "` WHERE `id` = " . (int)$raceId; + $raceRes = query_db($raceQuery); + $raceRow = mysqli_fetch_assoc($raceRes); + + // Ensure the race was imported from ORIS + $comp = $raceRow['ext_id'] ?? null; + if (empty($comp)) { + logMessage(" - Skipped: Race $raceId is not imported from ORIS (no ext_id). Marking LOCAL_ONLY."); + $updateQuery = "UPDATE `" . TBL_ZAVXUS . "` SET `sync_status` = 'LOCAL_ONLY' WHERE `id` = " . (int)$id; + query_db($updateQuery); + return; + } + + // Ignore relays and sprint relays + // S is typically for štafety (relays) in local DB + if ($raceRow && isset($raceRow['typ0']) && $raceRow['typ0'] === 'S') { + logMessage(" - Skipped: Race $raceId is a relay (typ0 = S). Marking LOCAL_ONLY."); + $updateQuery = "UPDATE `" . TBL_ZAVXUS . "` SET `sync_status` = 'LOCAL_ONLY' WHERE `id` = " . (int)$id; + query_db($updateQuery); + return; + } + + // Fetch Classes from ORIS Event to map the string category (e.g. H21) to the ORIS class ID + $classId = null; + $katName = $row['kat']; + if (!empty($comp)) { + try { + $eventRes = $service->getEvent($comp); + if (is_array($eventRes) && isset($eventRes['Classes'])) { + $classes = is_array($eventRes['Classes']) ? $eventRes['Classes'] : []; + + foreach ($classes as $cls) { + if (!is_array($cls)) continue; + $clsName = $cls['Name'] ?? ''; + // Some categories might have different spacing or case + if (trim($clsName) === trim($katName)) { + $classId = $cls['ID'] ?? null; + break; + } + } + } + } catch (Exception $e) { + $errMsg = $e->getMessage(); + logMessage(" - Warning: getEvent failed for event $comp: $errMsg"); + if ($e instanceof OrisNetworkException || ($e instanceof OrisApiException && strpos($errMsg, 'API Error or HTTP 5') !== false)) { + logMessage(" - Network error getting event. Skipping to keep PENDING."); + return 'queued'; + } + } + } + + if (empty($classId)) { + logMessage(" - Warning: Could not resolve ORIS Class ID for category '$katName'"); + $errorPayload = correct_sql_string(json_encode(['status' => 'error', 'message' => "Nelze spárovat kategorii '$katName' s ORISem."])); + $failedStatus = 'FAILED_' . strtoupper($action); + $updateQuery = "UPDATE `" . TBL_ZAVXUS . "` + SET `sync_status` = '$failedStatus', + `sync_error_payload` = '$errorPayload' + WHERE `id` = " . (int)$id; + query_db($updateQuery); + return false; + } + + $si = $row['si_chip']; + if (empty($si) && !empty($userRow['si_chip'])) { + $si = $userRow['si_chip']; + } + $rentSi = $row['rent_si'] ?? 0; // assuming rent_si is added or mapped + $note = $row['pozn'] ?? ''; + + $response = []; + logMessage(" - Sending $action request to ORIS API. clubuser: $clubuser, class: $classId (mapped from $katName), si: $si"); + + if (empty($clubuser)) { + logMessage(" - Failed: Cannot perform action '$action' because ORIS Club User ID (clubuser) could not be resolved."); + $errorPayload = correct_sql_string(json_encode(['status' => 'error', 'message' => 'Chybí ORIS ID uživatele v klubu (clubuser).'])); + $failedStatus = 'FAILED_' . strtoupper($action); + $updateQuery = "UPDATE `" . TBL_ZAVXUS . "` + SET `sync_status` = '$failedStatus', + `sync_error_payload` = '$errorPayload' + WHERE `id` = " . (int)$id; + query_db($updateQuery); + return false; + } + + try { + if ($action === 'create') { + // Check if it already exists before creating to avoid "Již přihlášen" + $existingEntryId = null; + if (!empty($comp) && !empty($clubuser)) { + try { + $entriesRes = $service->getEventEntries($comp); + if (is_array($entriesRes)) { + $entries = $entriesRes; + foreach ($entries as $entry) { + if (isset($entry['ClubUserID']) && $entry['ClubUserID'] == $clubuser) { + $existingEntryId = $entry['ID']; + logMessage(" - Found existing ORIS Entry ID $existingEntryId for clubuser $clubuser during create. Switching to update."); + break; + } + } + } + } catch (Exception $e) { + logMessage(" - Warning: getEventEntries failed: " . $e->getMessage()); + } + } + + if (!empty($existingEntryId)) { + $updateDto = new OrisEntryRequestDTO($clubuser, $classId, $si, (bool)$rentSi, $note, $existingEntryId); + $response = $service->updateEntry($updateDto); + // Also update the action string so log messages below make sense + $action = 'update'; + } else { + $createDto = new OrisEntryRequestDTO($clubuser, $classId, $si, (bool)$rentSi, $note); + $response = $service->createEntry($createDto); + } + } elseif ($action === 'update') { + $entryIdToUpdate = $row['oris_entry_id'] ?? null; + + if (empty($entryIdToUpdate) && !empty($comp) && !empty($clubuser)) { + logMessage(" - Warning: Missing ORIS Entry ID for update. Attempting to fetch from ORIS."); + try { + $entriesRes = $service->getEventEntries($comp); + if (is_array($entriesRes)) { + $entries = $entriesRes; + foreach ($entries as $entry) { + if (isset($entry['ClubUserID']) && $entry['ClubUserID'] == $clubuser) { + $entryIdToUpdate = $entry['ID']; + logMessage(" - Found ORIS Entry ID $entryIdToUpdate for clubuser $clubuser"); + break; + } + } + } + } catch (Exception $e) { + logMessage(" - Warning: getEventEntries failed: " . $e->getMessage()); + } + } + + if (empty($entryIdToUpdate)) { + // It was never created on ORIS side successfully, so let's fall back to create instead of failing + logMessage(" - Warning: Missing ORIS Entry ID for update. Falling back to create."); + $createDto = new OrisEntryRequestDTO($clubuser, $classId, $si, (bool)$rentSi, $note); + $response = $service->createEntry($createDto); + } else { + $updateDto = new OrisEntryRequestDTO($clubuser, $classId, $si, (bool)$rentSi, $note, $entryIdToUpdate); + $response = $service->updateEntry($updateDto); + } + } elseif ($action === 'delete') { + $entryIdToDelete = $row['oris_entry_id'] ?? null; + + // If we don't have the entry ID locally, we must fetch it from ORIS first + if (empty($entryIdToDelete) && !empty($comp) && !empty($clubuser)) { + logMessage(" - Warning: Missing ORIS Entry ID for delete. Attempting to fetch from ORIS."); + try { + $entriesRes = $service->getEventEntries($comp); + if (is_array($entriesRes)) { + $entries = $entriesRes; + foreach ($entries as $entry) { + if (isset($entry['ClubUserID']) && $entry['ClubUserID'] == $clubuser) { + $entryIdToDelete = $entry['ID']; + logMessage(" - Found ORIS Entry ID $entryIdToDelete for clubuser $clubuser"); + break; + } + } + } + } catch (Exception $e) { + logMessage(" - Warning: getEventEntries failed: " . $e->getMessage()); + } + } + + if (empty($entryIdToDelete)) { + // Already doesn't exist remotely or could not be found + logMessage(" - Warning: Could not find ORIS Entry ID to delete. Assuming already deleted."); + $response = ['ID' => null]; + } else { + $response = $service->deleteEntry($entryIdToDelete); + } + } + + // Log the exact raw payload sent to ORIS if available (custom debug info) + if (isset($response['request'])) { + logMessage(" - Raw POST Data: " . $response['request']); + } + + // Action succeeded since no exception was thrown + // 'create' usually returns ID in $response['ID'] + // 'update' might not return the ID, so we use the one we just found/used + $entryId = $response['ID'] ?? null; + if (empty($entryId)) { + if ($action === 'update' && !empty($entryIdToUpdate)) { + $entryId = $entryIdToUpdate; + } elseif ($action === 'update' && !empty($existingEntryId)) { // from the create fallback block + $entryId = $existingEntryId; + } else { + $entryId = $row['oris_entry_id']; + } + } + + logMessage(" - Success: Action $action completed. ORIS Entry ID: " . ($entryId ?: "N/A")); + + if ($action === 'delete') { + // Remove the row locally after successful delete + $updateQuery = "DELETE FROM `" . TBL_ZAVXUS . "` WHERE `id` = " . (int)$id; + query_db($updateQuery); + } else { + // Update the row + $updateQuery = "UPDATE `" . TBL_ZAVXUS . "` + SET `sync_status` = 'SYNCED', + `oris_entry_id` = " . ($entryId ? (int)$entryId : "NULL") . ", + `sync_timestamp` = NOW(), + `sync_error_payload` = NULL + WHERE `id` = " . (int)$id; + query_db($updateQuery); + } + return true; + + } catch (Exception $e) { + $errMsg = $e->getMessage(); + logMessage(" - Failed: Action $action encountered an error: $errMsg"); + + if ($e instanceof OrisNetworkException || ($e instanceof OrisApiException && strpos($errMsg, 'API Error or HTTP 5') !== false)) { + // It's a temporary network error or 5xx server error, keep it as PENDING and retry later + logMessage(" - Will keep entry in PENDING status for retry later because it is a network error."); + return 'queued'; + } + + $errorData = [ + 'status' => 'error', + 'message' => $errMsg, + 'api_status' => ($e instanceof OrisApiException) ? $e->getApiStatus() : 'Exception' + ]; + $errorPayload = correct_sql_string(json_encode($errorData)); + $failedStatus = 'FAILED_' . strtoupper($action); + $updateQuery = "UPDATE `" . TBL_ZAVXUS . "` + SET `sync_status` = '$failedStatus', + `sync_error_payload` = '$errorPayload' + WHERE `id` = " . (int)$id; + query_db($updateQuery); + return false; + } +} + +function getOrisSyncError($id) { + $q = query_db("SELECT sync_error_payload FROM `" . TBL_ZAVXUS . "` WHERE `id` = " . (int)$id); + if ($q && $r = mysqli_fetch_assoc($q)) { + if (!empty($r['sync_error_payload'])) { + $err = json_decode($r['sync_error_payload'], true); + return $err['message'] ?? 'Neznámá chyba'; + } + } + return 'Neznámá chyba'; +} +?> diff --git a/nav.inc.php b/nav.inc.php index 872bff7..4cde7a4 100644 --- a/nav.inc.php +++ b/nav.inc.php @@ -101,6 +101,7 @@ function DrawMenuGroupHeader($name) DrawMenuItem('Přihlášky na závody',_REGISTRATOR_GROUP_ID_,1); DrawMenuItem('Editace závodů',_REGISTRATOR_GROUP_ID_,4); DrawMenuItemStatic('Synchronizuj přihlášky s ORISem', 'adm_oris_sync.php'); + DrawMenuItemStatic('ORIS Sync Log', 'rg_oris_sync_log.php'); } if(IsLoggedManager()) { diff --git a/oris_sync_daemon.php b/oris_sync_daemon.php index 5e751db..1552854 100644 --- a/oris_sync_daemon.php +++ b/oris_sync_daemon.php @@ -8,6 +8,7 @@ require_once("common.inc.php"); require_once("lib/OrisIntegrationService.php"); require_once("oris_user.class.php"); +require_once("lib/oris_sync.inc.php"); // Establish database connection if not already connected if (!isset($GLOBALS['db_conn']) || !$GLOBALS['db_conn']) { @@ -24,14 +25,15 @@ $service = new OrisIntegrationService($g_oris_club_key); -$query = " - SELECT zx.*, r.oris_entry_start + $query = " + SELECT zx.*, r.oris_entry_start, u.si_chip as default_si_chip FROM `" . TBL_ZAVXUS . "` zx - LEFT JOIN `" . TBL_RACE . "` r ON zx.id_zavod = r.id - WHERE zx.sync_status IN ('PENDING_CREATE', 'PENDING_UPDATE', 'PENDING_DELETE', 'FAILED_RETRY') + LEFT JOIN `" . TBL_RACE . "` r ON zx.id_zavod = r.id + LEFT JOIN `" . TBL_USER . "` u ON zx.id_user = u.id + WHERE zx.sync_status IN ('PENDING_CREATE', 'PENDING_UPDATE', 'PENDING_DELETE', 'FAILED_CREATE', 'FAILED_UPDATE', 'FAILED_DELETE') AND (r.datum >= DATE_SUB(CURDATE(), INTERVAL 1 DAY) OR r.datum IS NULL) ORDER BY ISNULL(r.oris_entry_start), r.oris_entry_start ASC -"; + "; $res = query_db($query); while ($row = mysqli_fetch_assoc($res)) { @@ -53,270 +55,16 @@ } $action = ''; - if ($row['sync_status'] === 'PENDING_CREATE') { + if ($row['sync_status'] === 'PENDING_CREATE' || $row['sync_status'] === 'FAILED_CREATE') { $action = 'create'; - } elseif ($row['sync_status'] === 'PENDING_UPDATE') { + } elseif ($row['sync_status'] === 'PENDING_UPDATE' || $row['sync_status'] === 'FAILED_UPDATE') { $action = 'update'; - } elseif ($row['sync_status'] === 'PENDING_DELETE') { + } elseif ($row['sync_status'] === 'PENDING_DELETE' || $row['sync_status'] === 'FAILED_DELETE') { $action = 'delete'; - } elseif ($row['sync_status'] === 'FAILED_RETRY') { - // We will just let it be an 'update' if there's an ID, or 'create_or_update' - // if there's no ID so processEntry can check ORIS first. - $action = empty($row['oris_entry_id']) ? 'create' : 'update'; - // Wait, instead of setting it to 'create' which assumes it doesn't exist, - // if we set it to 'update', the new logic in processEntry will fetch from ORIS. - // If it's not found on ORIS, it falls back to 'create' anyway! - $action = 'update'; } if (!empty($action)) { processEntry($row, $action, $service); } } - -function logMessage($msg) { - $date = date('Y-m-d H:i:s'); - $line = "[$date] $msg\n"; - // Output to console when run via CLI - echo $line; - // Log to file in the same directory - file_put_contents(__DIR__ . '/logs/oris_sync.log', $line, FILE_APPEND); -} - -function processEntry($row, $action, $service) { - global $g_oris_club_key, $g_shortcut; - - $id = $row['id']; - $userId = $row['id_user']; - $raceId = $row['id_zavod']; - - logMessage("Processing $action for entry ID $id (user: $userId, race: $raceId)"); - - // Get ORIS user ID - $userQuery = "SELECT * FROM `" . TBL_USER . "` WHERE `id` = " . (int)$userId; - $userRes = query_db($userQuery); - $userRow = mysqli_fetch_assoc($userRes); - - $clubuser = null; - $rgnum = null; - if ($userRow && isset($userRow['reg'])) { - // Reg number usually only contains the digits, so we prepend the club shortcut (e.g. ZBM) - $rgnum = $userRow['reg']; - // Ensure the club prefix is attached if it isn't already - if (!empty($rgnum) && !preg_match('/^[A-Z]{3}/', $rgnum)) { - $rgnum = $g_shortcut . str_pad($rgnum, 4, '0', STR_PAD_LEFT); - } - - // Use the Oris API to get the internal user ID required for createEntry - if (!empty($rgnum)) { - $userApiRes = $service->getUser($rgnum); - if (isset($userApiRes['Status']) && $userApiRes['Status'] === 'OK' && isset($userApiRes['Data']['ID'])) { - $globalUserId = $userApiRes['Data']['ID']; - - // Now get the club user ID - $clubUsersRes = $service->getClubUsers($globalUserId); - if (isset($clubUsersRes['Status']) && $clubUsersRes['Status'] === 'OK' && isset($clubUsersRes['Data'])) { - // It might be a single object or an array of objects - $clubUsers = is_array($clubUsersRes['Data']) ? $clubUsersRes['Data'] : []; - if (isset($clubUsers['ID'])) { - $clubUsers = [$clubUsers]; - } - - foreach ($clubUsers as $cu) { - if (isset($cu['ID'])) { - $clubuser = $cu['ID']; - break; - } - } - } - - if (empty($clubuser)) { - logMessage(" - Warning: Could not resolve ORIS Club User ID for user ID $globalUserId"); - } - } else { - logMessage(" - Warning: Could not resolve ORIS User ID for rgnum $rgnum"); - } - } - } - - // Get Race ORIS ID - $raceQuery = "SELECT * FROM `" . TBL_RACE . "` WHERE `id` = " . (int)$raceId; - $raceRes = query_db($raceQuery); - $raceRow = mysqli_fetch_assoc($raceRes); - - // Ensure the race was imported from ORIS - $comp = $raceRow['ext_id'] ?? null; - if (empty($comp)) { - logMessage(" - Skipped: Race $raceId is not imported from ORIS (no ext_id). Marking LOCAL_ONLY."); - $updateQuery = "UPDATE `" . TBL_ZAVXUS . "` SET `sync_status` = 'LOCAL_ONLY' WHERE `id` = " . (int)$id; - query_db($updateQuery); - return; - } - - // Ignore relays and sprint relays - // S is typically for štafety (relays) in local DB - if ($raceRow && isset($raceRow['typ0']) && $raceRow['typ0'] === 'S') { - logMessage(" - Skipped: Race $raceId is a relay (typ0 = S). Marking LOCAL_ONLY."); - $updateQuery = "UPDATE `" . TBL_ZAVXUS . "` SET `sync_status` = 'LOCAL_ONLY' WHERE `id` = " . (int)$id; - query_db($updateQuery); - return; - } - - // Fetch Classes from ORIS Event to map the string category (e.g. H21) to the ORIS class ID - $classId = null; - $katName = $row['kat']; - if (!empty($comp)) { - $eventRes = $service->getEvent($comp); - if (isset($eventRes['Status']) && $eventRes['Status'] === 'OK' && isset($eventRes['Data']['Classes'])) { - $classes = is_array($eventRes['Data']['Classes']) ? $eventRes['Data']['Classes'] : []; - - foreach ($classes as $cls) { - if (!is_array($cls)) continue; - $clsName = $cls['Name'] ?? ''; - // Some categories might have different spacing or case - if (trim($clsName) === trim($katName)) { - $classId = $cls['ID'] ?? null; - break; - } - } - } - } - - if (empty($classId)) { - logMessage(" - Warning: Could not resolve ORIS Class ID for category '$katName'"); - // Fallback to sending the string, which will likely be rejected by ORIS but preserves behavior - $classId = $katName; - } - - $si = $row['si_chip']; - $rentSi = $row['rent_si'] ?? 0; // assuming rent_si is added or mapped - $note = $row['pozn'] ?? ''; - - $response = []; - logMessage(" - Sending $action request to ORIS API. clubuser: $clubuser, class: $classId (mapped from $katName), si: $si"); - - if ($action === 'create') { - // Check if it already exists before creating to avoid "Již přihlášen" - $existingEntryId = null; - if (!empty($comp) && !empty($clubuser)) { - $entriesRes = $service->getEventEntries($comp); - if (isset($entriesRes['Status']) && $entriesRes['Status'] === 'OK' && isset($entriesRes['Data'])) { - $entries = is_array($entriesRes['Data']) ? $entriesRes['Data'] : []; - foreach ($entries as $entry) { - if (isset($entry['ClubUserID']) && $entry['ClubUserID'] == $clubuser) { - $existingEntryId = $entry['ID']; - logMessage(" - Found existing ORIS Entry ID $existingEntryId for clubuser $clubuser during create. Switching to update."); - break; - } - } - } - } - - if (!empty($existingEntryId)) { - $response = $service->updateEntry($existingEntryId, $clubuser, $classId, $si, $rentSi, $note); - // Also update the action string so log messages below make sense - $action = 'update'; - } else { - $response = $service->createEntry($clubuser, $classId, $si, $rentSi, $note); - } - } elseif ($action === 'update') { - $entryIdToUpdate = $row['oris_entry_id'] ?? null; - - if (empty($entryIdToUpdate) && !empty($comp) && !empty($clubuser)) { - logMessage(" - Warning: Missing ORIS Entry ID for update. Attempting to fetch from ORIS."); - $entriesRes = $service->getEventEntries($comp); - if (isset($entriesRes['Status']) && $entriesRes['Status'] === 'OK' && isset($entriesRes['Data'])) { - $entries = is_array($entriesRes['Data']) ? $entriesRes['Data'] : []; - foreach ($entries as $entry) { - if (isset($entry['ClubUserID']) && $entry['ClubUserID'] == $clubuser) { - $entryIdToUpdate = $entry['ID']; - logMessage(" - Found ORIS Entry ID $entryIdToUpdate for clubuser $clubuser"); - break; - } - } - } - } - - if (empty($entryIdToUpdate)) { - // It was never created on ORIS side successfully, so let's fall back to create instead of failing - logMessage(" - Warning: Missing ORIS Entry ID for update. Falling back to create."); - $response = $service->createEntry($clubuser, $classId, $si, $rentSi, $note); - } else { - $response = $service->updateEntry($entryIdToUpdate, $clubuser, $classId, $si, $rentSi, $note); - } - } elseif ($action === 'delete') { - $entryIdToDelete = $row['oris_entry_id'] ?? null; - - // If we don't have the entry ID locally, we must fetch it from ORIS first - if (empty($entryIdToDelete) && !empty($comp) && !empty($clubuser)) { - logMessage(" - Warning: Missing ORIS Entry ID for delete. Attempting to fetch from ORIS."); - $entriesRes = $service->getEventEntries($comp); - if (isset($entriesRes['Status']) && $entriesRes['Status'] === 'OK' && isset($entriesRes['Data'])) { - $entries = is_array($entriesRes['Data']) ? $entriesRes['Data'] : []; - foreach ($entries as $entry) { - if (isset($entry['ClubUserID']) && $entry['ClubUserID'] == $clubuser) { - $entryIdToDelete = $entry['ID']; - logMessage(" - Found ORIS Entry ID $entryIdToDelete for clubuser $clubuser"); - break; - } - } - } - } - - if (empty($entryIdToDelete)) { - // Already doesn't exist remotely or could not be found - logMessage(" - Warning: Could not find ORIS Entry ID to delete. Assuming already deleted."); - $response = ['status' => 'success', 'data' => []]; - } else { - $response = $service->deleteEntry($entryIdToDelete); - } - } - - // Log the exact raw payload sent to ORIS - if (isset($response['request'])) { - logMessage(" - Raw POST Data: " . $response['request']); - } - - if ($response['status'] === 'success') { - // 'create' usually returns ID in $response['data']['ID'] - // 'update' might not return the ID, so we use the one we just found/used - $entryId = $response['data']['ID'] ?? null; - if (empty($entryId)) { - if ($action === 'update' && !empty($entryIdToUpdate)) { - $entryId = $entryIdToUpdate; - } elseif ($action === 'update' && !empty($existingEntryId)) { // from the create fallback block - $entryId = $existingEntryId; - } else { - $entryId = $row['oris_entry_id']; - } - } - - logMessage(" - Success: Action $action completed. ORIS Entry ID: $entryId"); - - if ($action === 'delete') { - // Remove the row locally after successful delete - $updateQuery = "DELETE FROM `" . TBL_ZAVXUS . "` WHERE `id` = " . (int)$id; - query_db($updateQuery); - } else { - // Update the row - $updateQuery = "UPDATE `" . TBL_ZAVXUS . "` - SET `sync_status` = 'SYNCED', - `oris_entry_id` = " . ($entryId ? (int)$entryId : "NULL") . ", - `sync_timestamp` = NOW(), - `sync_error_payload` = NULL - WHERE `id` = " . (int)$id; - query_db($updateQuery); - } - } else { - $errMsg = $response['message'] ?? 'Unknown error'; - logMessage(" - Failed: Action $action encountered an error: $errMsg"); - - $errorPayload = correct_sql_string(json_encode($response)); - $updateQuery = "UPDATE `" . TBL_ZAVXUS . "` - SET `sync_status` = 'FAILED_RETRY', - `sync_error_payload` = '$errorPayload' - WHERE `id` = " . (int)$id; - query_db($updateQuery); - } -} -?> \ No newline at end of file +?> diff --git a/race_reg_chip_exc.php b/race_reg_chip_exc.php index 5f4fc3b..eb8ad0d 100644 --- a/race_reg_chip_exc.php +++ b/race_reg_chip_exc.php @@ -4,6 +4,9 @@ require_once ("./connect.inc.php"); require_once ("./sess.inc.php"); +require_once ("./lib/oris_sync.inc.php"); +require_once ("./common.inc.php"); +require_once ("./common_race.inc.php"); if (!IsLoggedRegistrator()) { @@ -15,7 +18,12 @@ db_Connect(); -$query = 'SELECT z.id_user, u.si_chip FROM '.TBL_ZAVXUS.' as z, '.TBL_USER.' as u WHERE z.id_user = u.id AND z.id_zavod='.$id_zav.' AND u.hidden = 0'; +@$vysledek_z=query_db("SELECT ext_id FROM ".TBL_RACE." WHERE id=$id_zav"); +$zaznam_z = mysqli_fetch_array($vysledek_z); +$has_ext_id = !empty($zaznam_z['ext_id']); +$sync_queue = []; + +$query = 'SELECT z.id as z_id, z.id_user, z.sync_status, z.si_chip as z_chip, u.si_chip FROM '.TBL_ZAVXUS.' as z, '.TBL_USER.' as u WHERE z.id_user = u.id AND z.id_zavod='.$id_zav.' AND u.hidden = 0'; @$vysledek=query_db($query); @@ -27,12 +35,39 @@ if (IsSet($chip[$user])) { $si_chip = (int)$chip[$user]; - if ($si_chip != $zaznam['si_chip']) + $old_chip = (int)($zaznam['z_chip'] ? $zaznam['z_chip'] : $zaznam['si_chip']); + if ($si_chip != $old_chip) { - $result=query_db('UPDATE '.TBL_ZAVXUS.' SET `si_chip`= '.$si_chip.' WHERE `id_zavod` = '.$id_zav.' AND `id_user` = '.$user) + $sync_status_update = ""; + if ($has_ext_id && $zaznam['sync_status'] !== 'PENDING_CREATE') { + $sync_status_update = ", `sync_status`='PENDING_UPDATE'"; + } + $result=query_db('UPDATE '.TBL_ZAVXUS.' SET `si_chip`= '.$si_chip.$sync_status_update.' WHERE `id_zavod` = '.$id_zav.' AND `id_user` = '.$user) or die("Chyba při provádění dotazu do databáze."); if ($result == FALSE) die ("Nepodařilo se změnit přihlášku člena."); + + if ($has_ext_id) { + $action = ($zaznam['sync_status'] === 'PENDING_CREATE') ? 'create' : 'update'; + $sync_queue[] = ['id' => $zaznam['z_id'], 'action' => $action]; + } + } + } + } +} + +$sync_errors = []; +if ($has_ext_id && count($sync_queue) > 0) { + global $g_oris_club_key; + if (!empty($g_oris_club_key)) { + $service = new OrisIntegrationService($g_oris_club_key); + foreach ($sync_queue as $sq) { + $rowQuery = query_db("SELECT * FROM `" . TBL_ZAVXUS . "` WHERE `id` = " . (int)$sq['id']); + if ($rowQuery && $syncRow = mysqli_fetch_assoc($rowQuery)) { + $syncRes = processEntry($syncRow, $sq['action'], $service); + if ($syncRes !== true && $syncRes !== 'queued') { + $sync_errors[] = "Záznam " . $sq['id'] . ": " . getOrisSyncError($sq['id']); + } } } } @@ -40,6 +75,11 @@ ?> + +
+ Chyby při synchronizaci s ORIS:

+ ', array_map('htmlspecialchars', $sync_errors)); ?> +

+ Zpět na přehled +
+ diff --git a/rg_oris_sync_log.php b/rg_oris_sync_log.php new file mode 100644 index 0000000..56e9189 --- /dev/null +++ b/rg_oris_sync_log.php @@ -0,0 +1,33 @@ +'; +if (file_exists($logFile)) { + $content = file_get_contents($logFile); + if ($content !== false) { + echo nl2br(htmlspecialchars($content)); + } else { + echo "Error reading log file."; + } +} else { + echo "Log file does not exist yet."; +} +echo ''; + +echo ''; + +HTML_Footer(); +?> \ No newline at end of file diff --git a/us_race_regoff_exc.php b/us_race_regoff_exc.php index 4b72cd9..a0554f4 100644 --- a/us_race_regoff_exc.php +++ b/us_race_regoff_exc.php @@ -4,6 +4,7 @@ require_once ("./connect.inc.php"); require_once ("./sess.inc.php"); +require_once ("./lib/oris_sync.inc.php"); if (!IsLogged()) { @@ -25,18 +26,53 @@ if (!$entry_lock) { - @$vysledek=query_db("DELETE FROM ".TBL_ZAVXUS." WHERE id_zavod = '$id_zav' AND id_user = '$id_us'"); - if ($vysledek !== false && mysqli_affected_rows($db_conn) > 0) { - query_db("UPDATE ".TBL_RACE." SET prihlasenych = GREATEST(0, prihlasenych - 1) WHERE id = '$id_zav'"); + $vysledek_z=query_db("SELECT ext_id FROM ".TBL_RACE." WHERE id='$id_zav'"); + $zaznam_z = mysqli_fetch_array($vysledek_z); + $has_ext_id = !empty($zaznam_z['ext_id']); + + $vysledek_zx=query_db("SELECT id, sync_status FROM ".TBL_ZAVXUS." WHERE id_zavod='$id_zav' AND id_user='$id_us'"); + $zaznam_zx = mysqli_fetch_array($vysledek_zx); + + $sync_error_msg = null; + if ($zaznam_zx) { + $zx_id = $zaznam_zx['id']; + $sync_status = $zaznam_zx['sync_status']; + + if ($has_ext_id && $sync_status !== 'PENDING_CREATE') { + $vysledek = query_db("UPDATE ".TBL_ZAVXUS." SET sync_status='PENDING_DELETE' WHERE id = '$zx_id'"); + if ($vysledek !== false) { + global $g_oris_club_key; + if (!empty($g_oris_club_key)) { + $service = new OrisIntegrationService($g_oris_club_key); + $rowQuery = query_db("SELECT * FROM `" . TBL_ZAVXUS . "` WHERE `id` = '$zx_id'"); + if ($rowQuery && $syncRow = mysqli_fetch_assoc($rowQuery)) { + $syncRes = processEntry($syncRow, 'delete', $service); + if ($syncRes === true || $syncRes === 'queued') { + query_db("UPDATE ".TBL_RACE." SET prihlasenych = GREATEST(0, prihlasenych - 1) WHERE id = '$id_zav'"); + } else { + $sync_error_msg = getOrisSyncError($zx_id); + } + } + } + } + } else { + @$vysledek=query_db("DELETE FROM ".TBL_ZAVXUS." WHERE id = '$zx_id'"); + if ($vysledek !== false && mysqli_affected_rows($db_conn) > 0) { + query_db("UPDATE ".TBL_RACE." SET prihlasenych = GREATEST(0, prihlasenych - 1) WHERE id = '$id_zav'"); + } + } } } ?> \ No newline at end of file + diff --git a/us_race_regon_exc.php b/us_race_regon_exc.php index 795bf99..0fa2fdc 100644 --- a/us_race_regon_exc.php +++ b/us_race_regon_exc.php @@ -6,6 +6,7 @@ require_once ("./sess.inc.php"); require_once ("./common.inc.php"); require_once ("./common_race.inc.php"); +require_once ("./lib/oris_sync.inc.php"); if (!IsLogged()) { @@ -34,7 +35,7 @@ $pozn=correct_sql_string($pozn); $pozn2=correct_sql_string($pozn2); - @$vysledek_z=query_db("SELECT datum, vicedenni, prihlasky, prihlasky1, prihlasky2, prihlasky3, prihlasky4, prihlasky5, transport FROM ".TBL_RACE." WHERE id=$id_zav"); + @$vysledek_z=query_db("SELECT datum, vicedenni, prihlasky, prihlasky1, prihlasky2, prihlasky3, prihlasky4, prihlasky5, transport, ext_id FROM ".TBL_RACE." WHERE id=$id_zav"); $zaznam_z = mysqli_fetch_array($vysledek_z); $termin = raceterms::GetCurr4RegTerm($zaznam_z); @@ -59,32 +60,99 @@ $ubytovani = !isset($ubytovani)? 'null': 1; $novy = !isset($novy)? 0: (int)$novy; + $has_ext_id = !empty($zaznam_z['ext_id']); + $inserted_or_updated_id = 0; + $sync_action = ''; + $is_new_insert = false; + $previous_state = null; + if ($novy) { $vysledek=query_db("SELECT * FROM ".TBL_ZAVXUS." WHERE id_zavod='$id_zav' and id_user='$id_us'"); if ($vysledek != FALSE && ($zaznam = mysqli_fetch_array($vysledek)) != FALSE ) { // latest new == update - query_db("UPDATE ".TBL_ZAVXUS." SET kat='$kat', pozn='$pozn', pozn_in='$pozn2', termin='$termin', transport=$transport, sedadel=$sedadel, ubytovani=$ubytovani WHERE id='".$zaznam['id']."'"); + $previous_state = $zaznam; + $sync_status_update = ($has_ext_id && $zaznam['sync_status'] !== 'PENDING_CREATE') ? ", sync_status='PENDING_UPDATE'" : ""; + $sync_action = ($has_ext_id && $zaznam['sync_status'] === 'PENDING_CREATE') ? 'create' : 'update'; + query_db("UPDATE ".TBL_ZAVXUS." SET kat='$kat', pozn='$pozn', pozn_in='$pozn2', termin='$termin', transport=$transport, sedadel=$sedadel, ubytovani=$ubytovani".$sync_status_update." WHERE id='".$zaznam['id']."'"); + $inserted_or_updated_id = $zaznam['id']; } else { // really new - $vysledek = query_db("INSERT INTO ".TBL_ZAVXUS." (id_user, id_zavod, kat, pozn, pozn_in, termin, transport, sedadel, ubytovani) VALUES ('$id_us','$id_zav','$kat','$pozn','$pozn2','$termin',$transport, $sedadel, $ubytovani)"); + $is_new_insert = true; + $sync_status = $has_ext_id ? 'PENDING_CREATE' : 'LOCAL_ONLY'; + $sync_action = $has_ext_id ? 'create' : ''; + $vysledek = query_db("INSERT INTO ".TBL_ZAVXUS." (id_user, id_zavod, kat, pozn, pozn_in, termin, transport, sedadel, ubytovani, sync_status) VALUES ('$id_us','$id_zav','$kat','$pozn','$pozn2','$termin',$transport, $sedadel, $ubytovani, '$sync_status')"); if ($vysledek !== false && mysqli_affected_rows($db_conn) > 0) { + $inserted_or_updated_id = mysqli_insert_id($db_conn); query_db("UPDATE ".TBL_RACE." SET prihlasenych = prihlasenych + 1 WHERE id = '$id_zav'"); } } } else { // update - query_db("UPDATE ".TBL_ZAVXUS." SET kat='$kat', pozn='$pozn', pozn_in='$pozn2', transport=$transport, sedadel=$sedadel, ubytovani=$ubytovani WHERE id='".$id_z."'"); + $vysledek=query_db("SELECT * FROM ".TBL_ZAVXUS." WHERE id='".$id_z."'"); + if ($vysledek != FALSE && ($zaznam = mysqli_fetch_array($vysledek)) != FALSE ) + { + $previous_state = $zaznam; + $sync_status_update = ($has_ext_id && $zaznam['sync_status'] !== 'PENDING_CREATE') ? ", sync_status='PENDING_UPDATE'" : ""; + $sync_action = ($has_ext_id && $zaznam['sync_status'] === 'PENDING_CREATE') ? 'create' : 'update'; + query_db("UPDATE ".TBL_ZAVXUS." SET kat='$kat', pozn='$pozn', pozn_in='$pozn2', transport=$transport, sedadel=$sedadel, ubytovani=$ubytovani".$sync_status_update." WHERE id='".$id_z."'"); + $inserted_or_updated_id = $id_z; + } + } + + $sync_error_msg = null; + if ($has_ext_id && $inserted_or_updated_id > 0 && $sync_action !== '') { + global $g_oris_club_key; + if (!empty($g_oris_club_key)) { + $service = new OrisIntegrationService($g_oris_club_key); + $rowQuery = query_db("SELECT * FROM `" . TBL_ZAVXUS . "` WHERE `id` = " . (int)$inserted_or_updated_id); + if ($rowQuery && $syncRow = mysqli_fetch_assoc($rowQuery)) { + $syncRes = processEntry($syncRow, $sync_action, $service); + if ($syncRes !== true && $syncRes !== 'queued') { + $sync_error_msg = getOrisSyncError($inserted_or_updated_id); + // Rollback changes + if ($is_new_insert) { + query_db("DELETE FROM ".TBL_ZAVXUS." WHERE id = '$inserted_or_updated_id'"); + query_db("UPDATE ".TBL_RACE." SET prihlasenych = prihlasenych - 1 WHERE id = '$id_zav'"); + } else if ($previous_state) { + $prev_kat = correct_sql_string($previous_state['kat']); + $prev_pozn = correct_sql_string($previous_state['pozn']); + $prev_pozn_in = correct_sql_string($previous_state['pozn_in']); + $prev_termin = (int)$previous_state['termin']; + $prev_transport = (int)$previous_state['transport']; + $prev_sedadel = (!isset($previous_state['sedadel']) || $previous_state['sedadel'] === null) ? 'null' : (int)$previous_state['sedadel']; + $prev_ubytovani = (!isset($previous_state['ubytovani']) || $previous_state['ubytovani'] === null) ? 'null' : (int)$previous_state['ubytovani']; + $prev_sync_status = correct_sql_string($previous_state['sync_status']); + + query_db("UPDATE ".TBL_ZAVXUS." SET kat='$prev_kat', pozn='$prev_pozn', pozn_in='$prev_pozn_in', termin='$prev_termin', transport=$prev_transport, sedadel=$prev_sedadel, ubytovani=$prev_ubytovani, sync_status='$prev_sync_status' WHERE id='$inserted_or_updated_id'"); + } + } + } + } } } } } ?> + +
+ Chyba při synchronizaci s ORIS:

+ +

+ Zpět na přehled +
+ + \ No newline at end of file +