MSIN-4328 — Increase AJAX timeout for preview synchronization in the ChronoTrack admin interface to improve user experience during long operations. Enhance error messaging for better clarity on network issues and session handling. Update participant classification function to allow optional payload building. Increment version code.

This commit is contained in:
2026-07-22 21:57:21 -04:00
parent 1306770f2e
commit c154196090
3 changed files with 50 additions and 42 deletions

View File

@ -1453,7 +1453,7 @@ function fxChronotrackApiAdminRenderPageScript($intEveId, $arrConfig, $blnClosed
url: strAjaxUrl,
type: 'POST',
dataType: 'json',
timeout: 60000,
timeout: 120000,
data: { a: 'sync_preview', eve_id: intEveId }
}).done(function (res) {
if (rejectIfSessionLost(res)) {
@ -1465,14 +1465,21 @@ function fxChronotrackApiAdminRenderPageScript($intEveId, $arrConfig, $blnClosed
if (strStatus === 'abort') {
return;
}
var strMsg = (strStatus === 'timeout')
? 'Délai dépassé — réessayez Actualiser.'
: 'Erreur réseau (preview).';
var strMsg;
if (strStatus === 'timeout') {
strMsg = 'Délai dépassé (preview) — réessayez Actualiser.';
} else if (strStatus === 'parsererror') {
strMsg = 'Réponse invalide (preview) — souvent un envoi encore en cours ou erreur PHP.';
} else if (jqXHR && jqXHR.status) {
strMsg = 'Erreur preview HTTP ' + jqXHR.status + ' — réessayez Actualiser.';
} else {
strMsg = 'Erreur réseau (preview). Si un envoi tourne, attendez la fin puis Actualiser.';
}
if ($status.length) {
$status.removeClass('ct-api-push-status--ok ct-api-push-status--todo ct-api-push-status--loading')
.html(
'<p class="ct-api-push-status__title text-danger mb-1">' + escHtml(strMsg) + '</p>'
+ '<p class="ct-api-push-status__hint mb-0 text-muted">Réessayez Actualiser.</p>'
+ '<p class="ct-api-push-status__hint mb-0 text-muted">Les derniers compteurs affichés sont conservés.</p>'
+ '<p id="ct_api_status_refresh_cd" class="small text-muted mb-0 mt-1"></p>'
);
}
@ -1836,6 +1843,9 @@ function fxChronotrackApiAdminRenderPageScript($intEveId, $arrConfig, $blnClosed
var blnFatal = false;
blnPushRunning = true;
if (xhrPushPreview && xhrPushPreview.readyState !== 4) {
xhrPushPreview.abort();
}
$('#ct_api_btn_push, #ct_api_btn_push_full, #ct_api_btn_push_refresh, #ct_api_btn_ct_count, #ct_api_btn_logs').prop('disabled', true);
function renderProgress(intDone, intTot, strExtra) {
@ -1879,7 +1889,13 @@ function fxChronotrackApiAdminRenderPageScript($intEveId, $arrConfig, $blnClosed
}
function pushChunk() {
renderProgress(intOffset, intTotal, 'lot en cours…');
var intLot = Math.floor(intOffset / Math.max(1, intChunk)) + 1;
var intLots = Math.max(1, Math.ceil(intTotal / Math.max(1, intChunk)));
renderProgress(
intOffset,
intTotal,
'lot ' + intLot + '/' + intLots + ' (POST ' + Math.min(intChunk, intTotal - intOffset) + ')…'
);
$.ajax({
url: strAjaxUrl,
type: 'POST',
@ -1967,7 +1983,7 @@ function fxChronotrackApiAdminRenderPageScript($intEveId, $arrConfig, $blnClosed
});
}
renderProgress(0, intTotal, 'préparation…');
renderProgress(0, intTotal > 0 ? intTotal : 1, 'préparation MS1 (liste + job)…');
$.ajax({
url: strAjaxUrl,
type: 'POST',

View File

@ -403,7 +403,7 @@ function fxChronotrackApiSyncGroupBlockedByType(array $tabBlocked) {
/**
* Classe les participants MS1 : transférables vs exclus (dossard, sexe, doublons MS1, mapping).
*/
function fxChronotrackApiSyncClassifyParticipants(array $tabParticipants, array $arrRaceMap, $intCtEventId = 0) {
function fxChronotrackApiSyncClassifyParticipants(array $tabParticipants, array $arrRaceMap, $intCtEventId = 0, $blnBuildPayloads = true) {
$tabCandidates = array();
$tabBlocked = array();
$tabBibCounts = array();
@ -560,9 +560,12 @@ function fxChronotrackApiSyncClassifyParticipants(array $tabParticipants, array
continue;
}
$arrPayload = fxChronotrackApiSyncBuildEntryPayload($arrRow, $arrRaceMap, $intCtEventId);
if ($arrPayload === null) {
continue;
$arrPayload = null;
if ($blnBuildPayloads) {
$arrPayload = fxChronotrackApiSyncBuildEntryPayload($arrRow, $arrRaceMap, $intCtEventId);
if ($arrPayload === null) {
continue;
}
}
$tabTransferable[] = array(
@ -630,9 +633,12 @@ function fxChronotrackApiSyncClassifyParticipants(array $tabParticipants, array
continue;
}
$arrPayload = fxChronotrackApiSyncBuildEntryPayload($arrRep, $arrRaceMap, $intCtEventId);
if ($arrPayload === null) {
continue;
$arrPayload = null;
if ($blnBuildPayloads) {
$arrPayload = fxChronotrackApiSyncBuildEntryPayload($arrRep, $arrRaceMap, $intCtEventId);
if ($arrPayload === null) {
continue;
}
}
$tabTransferable[] = array(
@ -2018,7 +2024,8 @@ function fxChronotrackApiSyncBuildPreview($intEveId) {
$intCtEventId = intval($arrConfig['ct_event_id']);
$arrRaceMap = fxChronotrackApiRaceMapGetForEvent($intEveId);
$tabParticipants = fxChronotrackApiSyncLoadMs1Participants($intEveId);
$arrClass = fxChronotrackApiSyncClassifyParticipants($tabParticipants, $arrRaceMap, $intCtEventId);
// MSIN-4328 — preview rapide : pas de build payloads / entry_notes (seulement compteurs + exclus)
$arrClass = fxChronotrackApiSyncClassifyParticipants($tabParticipants, $arrRaceMap, $intCtEventId, false);
$intMs1Total = count($tabParticipants);
$intTransferable = count($arrClass['transferable']);
@ -2036,7 +2043,7 @@ function fxChronotrackApiSyncBuildPreview($intEveId) {
'transferable' => $intTransferable,
'blocked_count' => $arrClass['blocked_count'],
'blocked_by_type' => $arrClass['blocked_by_type'],
'blocked' => $arrClass['blocked'],
// Pas de liste plate « blocked » (doublon lourd) — lUI utilise blocked_by_type
'eligible' => $intTransferable,
'skipped_no_race' => $arrClass['skipped_no_race'],
'skipped_no_external' => $arrClass['skipped_no_external'],
@ -2048,7 +2055,6 @@ function fxChronotrackApiSyncBuildPreview($intEveId) {
'anomaly_no_sex' => $arrClass['blocked_no_sex'],
'anomaly_duplicate_bib' => $arrClass['blocked_duplicate_bib'],
'duplicate_bib_numbers' => $arrClass['duplicate_bib_numbers'],
'anomalies' => $arrClass['blocked'],
'ct_total' => null,
'ct_total_loaded' => false,
'already_in_ct' => null,
@ -2290,11 +2296,10 @@ function fxChronotrackApiSyncPushPrepare($intEveId, $arrOptions = array()) {
$tabPending = fxChronotrackApiSyncFilterChangedSincePush($arrClass['transferable'], $strLastPushAt);
}
// Plus de scan CT à la préparation : POST upsert (crée ou maj) sans entry_id.
// Garder entry_id en meta/job pour retry dossard ; lenvoi lot utilise POST upsert (pas PUT unitaire).
$tabBatch = array();
$tabMeta = array();
foreach ($tabPending as $arrItem) {
// Garder entry_id si déjà lié CT → push chunk fera PUT (maj dossard fiable).
$arrPayload = $arrItem['payload'];
$strCtEntryId = preg_replace('/[^0-9]/', '', (string)($arrItem['row']['ct_entry_id'] ?? ''));
if ($strCtEntryId !== '') {
@ -2443,34 +2448,17 @@ function fxChronotrackApiSyncPushEvent($intEveId, $arrOptions = array()) {
$tabErrors = array();
$tabBibRetryChunk = array();
// MSIN-4328 — entries liées (ct_entry_id) : PUT unitaire (+ clear bib si swap).
// Sans entry_id : POST lot (upsert). Ne plus stripper entry_id avant adaptive.
// MSIN-4328 — toujours POST en lot (upsert par external_id).
// Avant : PUT unitaire si ct_entry_id → ~100 appels/lot = 12 min figé à 0/N.
// entry_id reste sur le payload pour la 2e passe dossard ; retiré seulement à lappel POST.
$tabPostEntries = array();
$tabPostMeta = array();
foreach ($tabBatch as $intI => $arrPayload) {
if (!is_array($arrPayload)) {
continue;
}
$strEntryId = trim((string)($arrPayload['entry_id'] ?? ''));
if ($strEntryId !== '') {
$arrOne = fxChronotrackApiSyncPushOneEntryWithBibConflictHandling(
$intCtEventId,
$arrPayload,
$tabMeta[$intI] ?? array('par_id' => 0, 'external_id' => ''),
$intEveId
);
$intOk += intval($arrOne['ok'] ?? 0);
$intErr += intval($arrOne['err'] ?? 0);
$intPutOk += intval($arrOne['ok'] ?? 0);
if (!empty($arrOne['bib_retry']) && is_array($arrOne['bib_retry'])) {
foreach ($arrOne['bib_retry'] as $arrR) {
$tabBibRetryChunk[] = $arrR;
}
}
} else {
$tabPostEntries[] = $arrPayload;
$tabPostMeta[] = $tabMeta[$intI] ?? array('par_id' => 0, 'external_id' => '');
}
$tabPostEntries[] = $arrPayload;
$tabPostMeta[] = $tabMeta[$intI] ?? array('par_id' => 0, 'external_id' => '');
}
if (count($tabPostEntries) > 0) {

View File

@ -6,6 +6,10 @@ require_once dirname(__FILE__) . '/php/inc_functions.php';
require_once dirname(__FILE__) . '/../php/chronotrack_api/inc_bootstrap.php';
$intUsaId = fxSuperadmAjaxAuthOrJsonExit();
// MSIN-4328 — libérer le verrou session : sinon un push long bloque Actualiser / preview (Erreur réseau).
if (session_status() === PHP_SESSION_ACTIVE) {
session_write_close();
}
$strAction = trim((string)($_REQUEST['a'] ?? ''));
$intEveId = intval($_REQUEST['eve_id'] ?? 0);
@ -91,7 +95,7 @@ switch ($strAction) {
$tabRetour = array('state' => 'error', 'message' => 'eve_id manquant');
break;
}
set_time_limit(60);
set_time_limit(120);
$tabRetour = fxChronotrackApiSyncBuildPreview($intEveId);
break;