MSIN-4328 — Introduce a new button for full resync in the ChronoTrack admin panel, allowing all eligible participants to be resent regardless of last push date. Update JavaScript to handle the new button's functionality and enhance logging for successful entries. Adjust push preparation logic to accommodate full resync options. Increment version code.
This commit is contained in:
@ -327,6 +327,9 @@ function fxChronotrackApiAdminRenderPushPanel($intEveId, $blnClosed = false, $ar
|
||||
echo '<i class="fa fa-refresh" aria-hidden="true"></i> Actualiser</button> ';
|
||||
echo '<button type="button" class="btn btn-primary btn-lg" id="ct_api_btn_push" disabled>';
|
||||
echo '<i class="fa fa-cloud-upload" aria-hidden="true"></i> Envoyer vers ChronoTrack</button> ';
|
||||
echo '<button type="button" class="btn btn-outline-warning" id="ct_api_btn_push_full" disabled'
|
||||
. ' title="Renvoie tous les éligibles, sans tenir compte de la date du dernier envoi">';
|
||||
echo '<i class="fa fa-refresh" aria-hidden="true"></i> Tout renvoyer</button> ';
|
||||
echo '<button type="button" class="btn btn-outline-dark btn-sm" id="ct_api_btn_logs">';
|
||||
echo '<i class="fa fa-list" aria-hidden="true"></i> Voir les logs</button> ';
|
||||
echo '<button type="button" class="btn btn-outline-secondary btn-sm" id="ct_api_btn_auto_diag">'
|
||||
@ -334,7 +337,9 @@ function fxChronotrackApiAdminRenderPushPanel($intEveId, $blnClosed = false, $ar
|
||||
echo '<button type="button" class="btn btn-outline-secondary btn-sm" id="ct_api_btn_cron_ping" title="Appelle auto_chronotrack_sync.php sur ce même domaine">'
|
||||
. 'Lancer le script cron</button>';
|
||||
echo '</div>';
|
||||
echo '<p class="small text-muted mb-2">Envoi manuel : nouveaux / modifiés depuis le dernier push. '
|
||||
echo '<p class="small text-muted mb-2"><strong>Envoyer</strong> = seulement les nouveaux / modifiés depuis le dernier push. '
|
||||
. '<strong>Tout renvoyer</strong> = tous les éligibles, dates ignorées. '
|
||||
. 'Chaque envoi est journalisé (Voir les logs). '
|
||||
. 'Cet écran se rafraîchit doucement chaque minute (statut MS1 seulement).</p>';
|
||||
echo '<div id="ct_api_push_result" class="small mb-3"></div>';
|
||||
echo '<div id="ct_api_logs_wrap" class="mb-3 d-none"></div>';
|
||||
@ -1010,6 +1015,7 @@ function fxChronotrackApiAdminRenderPageScript($intEveId, $arrConfig, $blnClosed
|
||||
var $sum = $('#ct_api_push_summary');
|
||||
var $status = $('#ct_api_push_status');
|
||||
var $btn = $('#ct_api_btn_push');
|
||||
var $btnFull = $('#ct_api_btn_push_full');
|
||||
objPushPreview = res;
|
||||
if (!$sum.length && !$status.length) {
|
||||
return;
|
||||
@ -1024,6 +1030,7 @@ function fxChronotrackApiAdminRenderPageScript($intEveId, $arrConfig, $blnClosed
|
||||
}
|
||||
$('#ct_api_push_blocked_wrap').addClass('d-none').empty();
|
||||
$btn.prop('disabled', true);
|
||||
$btnFull.prop('disabled', true);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1087,7 +1094,8 @@ function fxChronotrackApiAdminRenderPageScript($intEveId, $arrConfig, $blnClosed
|
||||
|
||||
$sum.html(html);
|
||||
renderBlockedPanel(res);
|
||||
$btn.prop('disabled', intPending <= 0);
|
||||
$btn.prop('disabled', intPending <= 0 || blnClosed || blnPushRunning);
|
||||
$btnFull.prop('disabled', intEligible <= 0 || blnClosed || blnPushRunning);
|
||||
|
||||
// MSIN-4328 — auto-réparation au load : last_push déjà posé mais 100 % des
|
||||
// éligibles « à pousser » (= faux différentiel, ex. par_maj bumpé par backfill entry_id).
|
||||
@ -1341,12 +1349,13 @@ function fxChronotrackApiAdminRenderPageScript($intEveId, $arrConfig, $blnClosed
|
||||
}
|
||||
var tabLogs = res.logs || [];
|
||||
var html = '<div class="card border-secondary"><div class="card-header py-2 d-flex justify-content-between align-items-center">';
|
||||
html += '<strong class="small">Logs sync (50 derniers)</strong>';
|
||||
html += '<strong class="small">Logs sync (200 derniers)</strong>';
|
||||
html += '<button type="button" class="btn btn-link btn-sm p-0" id="ct_api_btn_logs_close">Fermer</button>';
|
||||
html += '</div><div class="card-body py-2">';
|
||||
if (!tabLogs.length) {
|
||||
html += '<p class="small text-muted mb-0">Aucun log pour cet événement.</p>';
|
||||
} else {
|
||||
html += '<p class="small text-muted mb-2">Chaque participant envoyé = ligne <code>push_entry</code> (ok ou error) avec external_id / dossard / nom.</p>';
|
||||
html += '<div class="ct-api-logs-scroll border rounded">';
|
||||
html += '<table class="table table-sm table-striped mb-0" style="font-size:0.78rem;">';
|
||||
html += '<thead class="thead-light"><tr><th>Quand</th><th>Action</th><th>Statut</th><th>par_id</th><th>Message</th></tr></thead><tbody>';
|
||||
@ -1369,7 +1378,7 @@ function fxChronotrackApiAdminRenderPageScript($intEveId, $arrConfig, $blnClosed
|
||||
function loadSyncLogs() {
|
||||
var $wrap = $('#ct_api_logs_wrap');
|
||||
$wrap.removeClass('d-none').html(waitHtml('Chargement des logs…'));
|
||||
$.post(strAjaxUrl, { a: 'sync_logs', eve_id: intEveId, limit: 50 }, function (res) {
|
||||
$.post(strAjaxUrl, { a: 'sync_logs', eve_id: intEveId, limit: 200 }, function (res) {
|
||||
if (rejectIfSessionLost(res)) {
|
||||
return;
|
||||
}
|
||||
@ -1492,17 +1501,23 @@ function fxChronotrackApiAdminRenderPageScript($intEveId, $arrConfig, $blnClosed
|
||||
});
|
||||
});
|
||||
|
||||
$('#ct_api_btn_push').on('click', function () {
|
||||
// MSIN-4328 — envoi manuel (différentiel) ou resync complète (force_full)
|
||||
function runManualPush(blnForceFull) {
|
||||
var intPending = 0;
|
||||
if (objPushPreview) {
|
||||
if (typeof objPushPreview.pending_push !== 'undefined') {
|
||||
if (blnForceFull) {
|
||||
intPending = objPushPreview.transferable || 0;
|
||||
} else if (typeof objPushPreview.pending_push !== 'undefined') {
|
||||
intPending = objPushPreview.pending_push;
|
||||
} else {
|
||||
intPending = objPushPreview.to_push || objPushPreview.transferable || 0;
|
||||
}
|
||||
}
|
||||
// MSIN-4328 — push immédiat, sans confirm
|
||||
var $btn = $(this);
|
||||
if (blnForceFull) {
|
||||
if (!window.confirm('Tout renvoyer vers ChronoTrack ?\n\nTous les participants éligibles seront renvoyés, sans tenir compte des dates du dernier envoi.')) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
var $res = $('#ct_api_push_result');
|
||||
var intChunk = 100;
|
||||
var intOffset = 0;
|
||||
@ -1514,13 +1529,14 @@ function fxChronotrackApiAdminRenderPageScript($intEveId, $arrConfig, $blnClosed
|
||||
var tabErrAll = [];
|
||||
var blnFatal = false;
|
||||
|
||||
$btn.prop('disabled', true);
|
||||
blnPushRunning = true;
|
||||
$('#ct_api_btn_push_refresh, #ct_api_btn_ct_count, #ct_api_btn_logs').prop('disabled', true);
|
||||
$('#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) {
|
||||
var intPct = (intTot > 0) ? Math.min(100, Math.round((intDone * 100) / intTot)) : 0;
|
||||
var strHtml = '<div class="mb-1"><strong>Envoi</strong> — '
|
||||
var strHtml = '<div class="mb-1"><strong>'
|
||||
+ (blnForceFull ? 'Resync complète' : 'Envoi')
|
||||
+ '</strong> — '
|
||||
+ intDone + ' / ' + intTot
|
||||
+ (strExtra ? (' · ' + escHtml(strExtra)) : '')
|
||||
+ '</div>';
|
||||
@ -1540,10 +1556,12 @@ function fxChronotrackApiAdminRenderPageScript($intEveId, $arrConfig, $blnClosed
|
||||
function finishPush() {
|
||||
blnPushRunning = false;
|
||||
$('#ct_api_btn_push_refresh, #ct_api_btn_ct_count, #ct_api_btn_logs').prop('disabled', false);
|
||||
$btn.prop('disabled', false);
|
||||
var strFinal = (intErrSum === 0)
|
||||
? (intOkSum + ' participant(s) synchronisé(s) (POST ' + intPostSum + ' · PUT ' + intPutSum + ').')
|
||||
: (intOkSum + ' OK, ' + intErrSum + ' erreur(s).');
|
||||
if (blnForceFull && intErrSum === 0) {
|
||||
strFinal = 'Resync complète — ' + strFinal;
|
||||
}
|
||||
var strState = blnFatal ? 'error' : ((intErrSum === 0) ? 'ok' : 'partial');
|
||||
renderPushResult({
|
||||
state: strState,
|
||||
@ -1551,6 +1569,7 @@ function fxChronotrackApiAdminRenderPageScript($intEveId, $arrConfig, $blnClosed
|
||||
errors: tabErrAll.slice(0, 20)
|
||||
});
|
||||
loadPushPreview(true);
|
||||
loadSyncLogs();
|
||||
}
|
||||
|
||||
function pushChunk() {
|
||||
@ -1577,8 +1596,6 @@ function fxChronotrackApiAdminRenderPageScript($intEveId, $arrConfig, $blnClosed
|
||||
finishPush();
|
||||
return;
|
||||
}
|
||||
// MSIN-4328 — ne pas abandonner le job si un lot a des erreurs :
|
||||
// continuer jusqu’à done (premier envoi = tous les lots).
|
||||
if (res.state !== 'ok' && res.state !== 'partial' && res.state !== 'error') {
|
||||
blnFatal = true;
|
||||
if (res.message) {
|
||||
@ -1591,7 +1608,7 @@ function fxChronotrackApiAdminRenderPageScript($intEveId, $arrConfig, $blnClosed
|
||||
renderPushResult(res);
|
||||
$('#ct_api_btn_push_refresh, #ct_api_btn_ct_count, #ct_api_btn_logs').prop('disabled', false);
|
||||
blnPushRunning = false;
|
||||
$btn.prop('disabled', false);
|
||||
loadPushPreview(true);
|
||||
return;
|
||||
}
|
||||
if (res.total) {
|
||||
@ -1645,7 +1662,11 @@ function fxChronotrackApiAdminRenderPageScript($intEveId, $arrConfig, $blnClosed
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
timeout: 180000,
|
||||
data: { a: 'sync_push_prepare', eve_id: intEveId }
|
||||
data: {
|
||||
a: 'sync_push_prepare',
|
||||
eve_id: intEveId,
|
||||
force_full: blnForceFull ? 1 : 0
|
||||
}
|
||||
}).done(function (prep) {
|
||||
if (rejectIfSessionLost(prep)) {
|
||||
return;
|
||||
@ -1653,15 +1674,15 @@ function fxChronotrackApiAdminRenderPageScript($intEveId, $arrConfig, $blnClosed
|
||||
if (!prep || prep.state !== 'ok') {
|
||||
renderPushResult(prep || { state: 'error', message: 'Préparation échouée' });
|
||||
$('#ct_api_btn_push_refresh, #ct_api_btn_ct_count, #ct_api_btn_logs').prop('disabled', false);
|
||||
blnPushRunning = false;
|
||||
$btn.prop('disabled', false);
|
||||
blnPushRunning = false;
|
||||
loadPushPreview(true);
|
||||
return;
|
||||
}
|
||||
intTotal = parseInt(prep.total, 10) || intTotal;
|
||||
if (prep.chunk_size) {
|
||||
intChunk = parseInt(prep.chunk_size, 10) || intChunk;
|
||||
}
|
||||
renderProgress(0, intTotal, 'démarrage');
|
||||
renderProgress(0, intTotal, blnForceFull ? 'resync complète' : 'démarrage');
|
||||
pushChunk();
|
||||
}).fail(function (jqXHR, strStatus) {
|
||||
var strMsg = 'Erreur préparation push (' + strStatus
|
||||
@ -1673,8 +1694,15 @@ function fxChronotrackApiAdminRenderPageScript($intEveId, $arrConfig, $blnClosed
|
||||
renderPushResult({ state: 'error', message: strMsg });
|
||||
$('#ct_api_btn_push_refresh, #ct_api_btn_ct_count, #ct_api_btn_logs').prop('disabled', false);
|
||||
blnPushRunning = false;
|
||||
$btn.prop('disabled', false);
|
||||
loadPushPreview(true);
|
||||
});
|
||||
}
|
||||
|
||||
$('#ct_api_btn_push').on('click', function () {
|
||||
runManualPush(false);
|
||||
});
|
||||
$('#ct_api_btn_push_full').on('click', function () {
|
||||
runManualPush(true);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -995,6 +995,22 @@ function fxChronotrackApiSyncPostEntriesAdaptive($intCtEventId, array $tabEntrie
|
||||
// MSIN-4328 — récupérer entry_id CT pour lien admin
|
||||
if ($intEveId > 0 && count($tabMeta) > 0) {
|
||||
fxChronotrackApiSyncApplyEntryIdsFromApiJson($arrPost['json'] ?? null, $tabMeta);
|
||||
// MSIN-4328 — log précis de chaque envoi réussi (contrôle / audit)
|
||||
foreach ($tabMeta as $arrM) {
|
||||
if (!is_array($arrM)) {
|
||||
continue;
|
||||
}
|
||||
$strMsg = 'external_id=' . ($arrM['external_id'] ?? '')
|
||||
. ' bib=' . ($arrM['bib'] ?? '')
|
||||
. ' ' . trim((string)($arrM['name'] ?? ''));
|
||||
fxChronotrackApiSyncLog(
|
||||
$intEveId,
|
||||
intval($arrM['par_id'] ?? 0),
|
||||
'push_entry',
|
||||
'ok',
|
||||
trim($strMsg)
|
||||
);
|
||||
}
|
||||
}
|
||||
return array('ok' => $intCount, 'err' => 0);
|
||||
}
|
||||
@ -1689,9 +1705,12 @@ function fxChronotrackApiSyncPushJobClear($intEveId) {
|
||||
|
||||
/**
|
||||
* Prépare un job de push (liste éligibles + entry_id CT). MSIN-4328.
|
||||
*
|
||||
* @param array $arrOptions force_full=true → tous les éligibles (ignore last_push_at)
|
||||
*/
|
||||
function fxChronotrackApiSyncPushPrepare($intEveId) {
|
||||
function fxChronotrackApiSyncPushPrepare($intEveId, $arrOptions = array()) {
|
||||
$intEveId = intval($intEveId);
|
||||
$blnForceFull = !empty($arrOptions['force_full']);
|
||||
$arrConfig = fxChronotrackApiConfigGet($intEveId);
|
||||
if ($arrConfig === null) {
|
||||
return array('state' => 'error', 'message' => 'Événement non lié à ChronoTrack');
|
||||
@ -1708,18 +1727,29 @@ function fxChronotrackApiSyncPushPrepare($intEveId) {
|
||||
// MSIN-4328 — ne plus journaliser push_skip ici (spam à chaque prepare/auto).
|
||||
// Les exclus restent dans l’UI « Détails & exclus » (preview), jamais dans le lot poussé.
|
||||
|
||||
// MSIN-4328 — ne pousser que les éligibles modifiés depuis last_push_at
|
||||
// MSIN-4328 — différentiel par défaut ; force_full = tout renvoyer
|
||||
$strLastPushAt = trim((string)($arrConfig['last_push_at'] ?? ''));
|
||||
$tabPending = fxChronotrackApiSyncFilterChangedSincePush($arrClass['transferable'], $strLastPushAt);
|
||||
if ($blnForceFull) {
|
||||
$tabPending = $arrClass['transferable'];
|
||||
} else {
|
||||
$tabPending = fxChronotrackApiSyncFilterChangedSincePush($arrClass['transferable'], $strLastPushAt);
|
||||
}
|
||||
|
||||
// Plus de scan CT à la préparation : POST upsert (crée ou maj) sans entry_id.
|
||||
$tabBatch = array();
|
||||
$tabMeta = array();
|
||||
foreach ($tabPending as $arrItem) {
|
||||
$tabBatch[] = fxChronotrackApiSyncPayloadForPost($arrItem['payload']);
|
||||
$arrPayload = fxChronotrackApiSyncPayloadForPost($arrItem['payload']);
|
||||
$tabBatch[] = $arrPayload;
|
||||
$tabMeta[] = array(
|
||||
'par_id' => intval($arrItem['row']['par_id'] ?? 0),
|
||||
'external_id' => $arrItem['payload']['external_id'],
|
||||
'external_id' => $arrPayload['external_id'] ?? '',
|
||||
'bib' => isset($arrPayload['bib']) ? (string)$arrPayload['bib'] : '',
|
||||
'name' => trim(
|
||||
trim((string)($arrPayload['first_name'] ?? ''))
|
||||
. ' '
|
||||
. trim((string)($arrPayload['last_name'] ?? ''))
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@ -1738,15 +1768,17 @@ function fxChronotrackApiSyncPushPrepare($intEveId) {
|
||||
'transferable' => count($arrClass['transferable']),
|
||||
'pending_push' => 0,
|
||||
'total' => 0,
|
||||
'force_full' => $blnForceFull ? 1 : 0,
|
||||
);
|
||||
}
|
||||
|
||||
$strMode = $blnForceFull ? 'FULL resync' : 'différentiel';
|
||||
fxChronotrackApiSyncLog(
|
||||
$intEveId,
|
||||
0,
|
||||
'push_prepare',
|
||||
'ok',
|
||||
'job prêt — pending=' . count($tabBatch)
|
||||
$strMode . ' — job prêt — pending=' . count($tabBatch)
|
||||
. ' éligibles=' . count($arrClass['transferable'])
|
||||
. ' exclus=' . intval($arrClass['blocked_count'])
|
||||
);
|
||||
@ -1756,6 +1788,7 @@ function fxChronotrackApiSyncPushPrepare($intEveId) {
|
||||
'batch' => $tabBatch,
|
||||
'meta' => $tabMeta,
|
||||
'blocked_count' => $arrClass['blocked_count'],
|
||||
'force_full' => $blnForceFull ? 1 : 0,
|
||||
'ok_sum' => 0,
|
||||
'err_sum' => 0,
|
||||
'created_at' => time(),
|
||||
@ -1769,13 +1802,15 @@ function fxChronotrackApiSyncPushPrepare($intEveId) {
|
||||
|
||||
return array(
|
||||
'state' => 'ok',
|
||||
'message' => count($tabBatch) . ' prêts à envoyer',
|
||||
'message' => count($tabBatch) . ' prêts à envoyer'
|
||||
. ($blnForceFull ? ' (resync complète)' : ''),
|
||||
'total' => count($tabBatch),
|
||||
'pending_push' => count($tabBatch),
|
||||
'transferable' => count($arrClass['transferable']),
|
||||
'blocked_count' => $arrClass['blocked_count'],
|
||||
'chunk_size' => MSIN_API_CHRONOTRACK_SYNC_CHUNK_SIZE,
|
||||
'ct_event_id' => $intCtEventId,
|
||||
'force_full' => $blnForceFull ? 1 : 0,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -186,7 +186,10 @@ switch ($strAction) {
|
||||
break;
|
||||
}
|
||||
set_time_limit(180);
|
||||
$tabRetour = fxChronotrackApiSyncPushPrepare($intEveId);
|
||||
$blnForceFull = (intval($_REQUEST['force_full'] ?? 0) === 1);
|
||||
$tabRetour = fxChronotrackApiSyncPushPrepare($intEveId, array(
|
||||
'force_full' => $blnForceFull,
|
||||
));
|
||||
break;
|
||||
|
||||
case 'sync_push':
|
||||
|
||||
Reference in New Issue
Block a user