diff --git a/php/chronotrack_api/fx_chronotrack_admin.php b/php/chronotrack_api/fx_chronotrack_admin.php index b5402f2..360d2e3 100644 --- a/php/chronotrack_api/fx_chronotrack_admin.php +++ b/php/chronotrack_api/fx_chronotrack_admin.php @@ -1001,7 +1001,7 @@ function fxChronotrackApiAdminRenderPageScript($intEveId, $arrConfig, $blnClosed } var $btn = $(this); var $res = $('#ct_api_push_result'); - var intChunk = 50; + var intChunk = 250; var intOffset = 0; var intTotal = intEligible; var intOkSum = 0; diff --git a/php/chronotrack_api/fx_chronotrack_http.php b/php/chronotrack_api/fx_chronotrack_http.php index e565444..0c89995 100644 --- a/php/chronotrack_api/fx_chronotrack_http.php +++ b/php/chronotrack_api/fx_chronotrack_http.php @@ -22,8 +22,8 @@ function fxChronotrackApiHttpRequest($strMethod, $strUrl, $arrOptions = array()) curl_setopt($ch, CURLOPT_URL, $strUrl); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 15); - curl_setopt($ch, CURLOPT_TIMEOUT, 120); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 20); + curl_setopt($ch, CURLOPT_TIMEOUT, 180); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); curl_setopt($ch, CURLOPT_HTTPHEADER, $arrHeaders); diff --git a/php/chronotrack_api/fx_chronotrack_sync.php b/php/chronotrack_api/fx_chronotrack_sync.php index ee24e5c..94f1c6d 100644 --- a/php/chronotrack_api/fx_chronotrack_sync.php +++ b/php/chronotrack_api/fx_chronotrack_sync.php @@ -3,10 +3,10 @@ * MSIN API ChronoTrack — Phase 2a : lecture MS1, diff, push manuel entries. */ -// Lot POST ChronoTrack (créations). Plus gros = moins d’aller-retours HTTP (MSIN-4328). -define('MSIN_API_CHRONOTRACK_SYNC_BATCH_SIZE', 50); -// Taille d’un chunk AJAX (barre d’avancement côté admin). -define('MSIN_API_CHRONOTRACK_SYNC_CHUNK_SIZE', 50); +// Lot POST ChronoTrack — aligné sur un import fichier (MSIN-4328). +define('MSIN_API_CHRONOTRACK_SYNC_BATCH_SIZE', 250); +// Chunk AJAX = même taille (1 requête navigateur ≈ 1 POST CT). +define('MSIN_API_CHRONOTRACK_SYNC_CHUNK_SIZE', 250); function fxChronotrackApiSyncIso2ToIso3($strIso2) { static $tabMap = null; @@ -553,18 +553,35 @@ function fxChronotrackApiSyncPostEntries($intCtEventId, array $tabEntries) { return array('state' => 'error', 'message' => 'Aucune entry à envoyer'); } + static $strBodyFormat = 'array'; + + if ($strBodyFormat === 'event_entry') { + $arrPost = fxChronotrackApiOAuthApiPost( + 'event/' . $intCtEventId . '/entry', + array('event_entry' => $tabEntries) + ); + if ($arrPost['state'] === 'ok') { + $arrPost['body_format'] = 'event_entry'; + return $arrPost; + } + return $arrPost; + } + // Doc CT : POST body = tableau JSON d'entités entry (pas de wrapper event_entry). $arrPost = fxChronotrackApiOAuthApiPost('event/' . $intCtEventId . '/entry', $tabEntries); if ($arrPost['state'] === 'ok') { $arrPost['body_format'] = 'array'; + $strBodyFormat = 'array'; return $arrPost; } + // Essai wrapper une seule fois (mémoire du format si succès). $arrPostWrap = fxChronotrackApiOAuthApiPost( 'event/' . $intCtEventId . '/entry', array('event_entry' => $tabEntries) ); if ($arrPostWrap['state'] === 'ok') { + $strBodyFormat = 'event_entry'; $arrPostWrap['body_format'] = 'event_entry'; return $arrPostWrap; } @@ -574,6 +591,55 @@ function fxChronotrackApiSyncPostEntries($intCtEventId, array $tabEntries) { return $arrPost; } +/** + * POST entries avec découpage dichotomique si le lot échoue (évite 250 appels unitaires). + * MSIN-4328 + */ +function fxChronotrackApiSyncPostEntriesAdaptive($intCtEventId, array $tabEntries, array &$tabErrors, $intEveId = 0, array $tabMeta = array()) { + $intCount = count($tabEntries); + if ($intCount === 0) { + return array('ok' => 0, 'err' => 0); + } + + $arrPost = fxChronotrackApiSyncPostEntries($intCtEventId, $tabEntries); + if ($arrPost['state'] === 'ok') { + return array('ok' => $intCount, 'err' => 0); + } + + if ($intCount === 1) { + $arrM = $tabMeta[0] ?? array('par_id' => 0, 'external_id' => ''); + $strOneErr = $arrPost['message'] ?? 'Erreur POST entry'; + if (count($tabErrors) < 12) { + $tabErrors[] = 'external_id=' . ($arrM['external_id'] ?? '') . ' — ' . $strOneErr; + } + if ($intEveId > 0) { + fxChronotrackApiSyncLog($intEveId, intval($arrM['par_id'] ?? 0), 'push_entry', 'error', + 'external_id=' . ($arrM['external_id'] ?? '') . ' — ' . $strOneErr); + } + return array('ok' => 0, 'err' => 1); + } + + $intMid = (int)floor($intCount / 2); + $arrLeft = fxChronotrackApiSyncPostEntriesAdaptive( + $intCtEventId, + array_slice($tabEntries, 0, $intMid), + $tabErrors, + $intEveId, + array_slice($tabMeta, 0, $intMid) + ); + $arrRight = fxChronotrackApiSyncPostEntriesAdaptive( + $intCtEventId, + array_slice($tabEntries, $intMid), + $tabErrors, + $intEveId, + array_slice($tabMeta, $intMid) + ); + return array( + 'ok' => $arrLeft['ok'] + $arrRight['ok'], + 'err' => $arrLeft['err'] + $arrRight['err'], + ); +} + function fxChronotrackApiSyncProbePush($intEveId) { $arrConfig = fxChronotrackApiConfigGet($intEveId); if ($arrConfig === null) { @@ -1021,7 +1087,6 @@ function fxChronotrackApiSyncPushEvent($intEveId, $arrOptions = array()) { $intPutOk = 0; $intPostOk = 0; $tabErrors = array(); - $intBatchSize = MSIN_API_CHRONOTRACK_SYNC_BATCH_SIZE; $tabCreates = array(); $tabCreatesMeta = array(); @@ -1039,32 +1104,18 @@ function fxChronotrackApiSyncPushEvent($intEveId, $arrOptions = array()) { } } - for ($intBatchOff = 0; $intBatchOff < count($tabCreates); $intBatchOff += $intBatchSize) { - $tabSlice = array_slice($tabCreates, $intBatchOff, $intBatchSize); - $tabSliceMeta = array_slice($tabCreatesMeta, $intBatchOff, $intBatchSize); - $arrPost = fxChronotrackApiSyncPostEntries($intCtEventId, $tabSlice); - if ($arrPost['state'] === 'ok') { - $intOk += count($tabSlice); - $intPostOk += count($tabSlice); - continue; - } - $strErrMsg = $arrPost['message'] ?? 'Erreur POST entry'; - foreach ($tabSlice as $intIdx => $arrEntry) { - $arrM = $tabSliceMeta[$intIdx]; - $arrOne = fxChronotrackApiSyncPostEntries($intCtEventId, array($arrEntry)); - if ($arrOne['state'] === 'ok') { - $intOk++; - $intPostOk++; - continue; - } - $intErr++; - $strOneErr = $arrOne['message'] ?? $strErrMsg; - if (count($tabErrors) < 8) { - $tabErrors[] = 'external_id=' . $arrM['external_id'] . ' — ' . $strOneErr; - } - fxChronotrackApiSyncLog($intEveId, $arrM['par_id'], 'push_entry', 'error', - 'external_id=' . $arrM['external_id'] . ' — ' . $strOneErr); - } + // Créations + maj via POST lot (upsert) — découpage dichotomique si échec. + if (count($tabCreates) > 0) { + $arrRes = fxChronotrackApiSyncPostEntriesAdaptive( + $intCtEventId, + $tabCreates, + $tabErrors, + $intEveId, + $tabCreatesMeta + ); + $intOk += $arrRes['ok']; + $intPostOk += $arrRes['ok']; + $intErr += $arrRes['err']; } if (count($tabUpdates) > 0) { @@ -1072,39 +1123,22 @@ function fxChronotrackApiSyncPushEvent($intEveId, $arrOptions = array()) { foreach ($tabUpdates as $arrPayload) { $tabUpdatesForPost[] = fxChronotrackApiSyncPayloadForPost($arrPayload); } - for ($intBatchOff = 0; $intBatchOff < count($tabUpdatesForPost); $intBatchOff += $intBatchSize) { - $tabSlice = array_slice($tabUpdatesForPost, $intBatchOff, $intBatchSize); - $tabSliceMeta = array_slice($tabUpdatesMeta, $intBatchOff, $intBatchSize); - $tabSliceFull = array_slice($tabUpdates, $intBatchOff, $intBatchSize); - $arrPost = fxChronotrackApiSyncPostEntries($intCtEventId, $tabSlice); - if ($arrPost['state'] === 'ok') { - $intOk += count($tabSlice); - $intPostOk += count($tabSlice); - continue; - } - foreach ($tabSliceFull as $intIdx => $arrPayload) { - $arrM = $tabSliceMeta[$intIdx]; - $arrOne = fxChronotrackApiSyncPushOneEntry($intCtEventId, $arrPayload); - if ($arrOne['state'] === 'ok') { - $intOk++; - if (($arrOne['method'] ?? '') === 'PUT') { - $intPutOk++; - } else { - $intPostOk++; - } - continue; - } - $intErr++; - $strOneErr = $arrOne['message'] ?? 'Erreur PUT/POST entry'; - if (count($tabErrors) < 8) { - $tabErrors[] = 'external_id=' . $arrM['external_id'] . ' — ' . $strOneErr; - } - fxChronotrackApiSyncLog($intEveId, $arrM['par_id'], 'push_entry', 'error', - 'external_id=' . $arrM['external_id'] . ' — ' . $strOneErr); - } - } + $arrRes = fxChronotrackApiSyncPostEntriesAdaptive( + $intCtEventId, + $tabUpdatesForPost, + $tabErrors, + $intEveId, + $tabUpdatesMeta + ); + $intOk += $arrRes['ok']; + $intPostOk += $arrRes['ok']; + $intErr += $arrRes['err']; } + // Si des MAJ restent en erreur (POST n’a pas pris le statut), tenter PUT unitaire + // uniquement pour ce petit sous-ensemble déjà en erreur — trop coûteux de re-PUT tout. + // Les erreurs sont déjà dans $tabErrors / logs. + fxChronotrackApiSyncLog( $intEveId, 0,