Implement auto-sync functionality in ChronoTrack API, including new methods for managing auto-sync events and handling expiration. Update push panel messages for clarity on cron job execution and enhance user feedback. This change supports improved synchronization management and lays the foundation for future enhancements.

This commit is contained in:
2026-07-14 12:15:34 -04:00
parent 0db8da9810
commit eac42923fd
4 changed files with 244 additions and 6 deletions

View File

@ -298,11 +298,11 @@ function fxChronotrackApiAdminRenderPushPanel($intEveId, $blnClosed = false, $ar
echo '</div>';
echo '<p class="small text-muted mb-0 mt-2" id="ct_api_auto_hint">';
if ($blnAutoOn && $strUntilRaw !== '') {
echo 'Config enregistrée — exécution serveur (cron) à brancher. Arrêt prévu : '
echo 'Config enregistrée — cron <code>auto_chronotrack_sync.php</code> (toutes les minutes). Arrêt : '
. fxChronotrackApiAdminEsc($strUntilRaw) . '.';
} else {
echo 'Quand cest coché, le serveur (cron) poussera le différentiel à cet intervalle jusquà la date de fin. '
. 'Pas encore actif côté serveur.';
echo 'Cochez, choisissez fréquence + date/heure de fin, puis Enregistrer. '
. 'Le cron serveur doit appeler <code>auto_chronotrack_sync.php</code> chaque minute.';
}
echo '</p>';
echo '<div id="ct_api_auto_msg" class="small mt-1"></div>';
@ -1174,10 +1174,10 @@ function fxChronotrackApiAdminRenderPageScript($intEveId, $arrConfig, $blnClosed
$msg.addClass('text-success').text(res.message || 'Enregistré');
if (res.auto_sync) {
if (res.auto_sync.enabled && res.auto_sync.until) {
$hint.text('Config enregistrée — exécution serveur (cron) à brancher. Arrêt prévu : '
$hint.text('Config OK — cron toutes les minutes lit cette config (auto_chronotrack_sync.php). Arrêt : '
+ res.auto_sync.until + '.');
} else {
$hint.text('Sync auto désactivée. Le cron (phase B) ne traitera pas cet événement.');
$hint.text('Sync auto désactivée.');
}
}
}, 'json').fail(function () {

View File

@ -197,7 +197,7 @@ function fxChronotrackApiConfigSaveAutoSync($intEveId, $blnEnabled, $intInterval
'state' => 'ok',
'message' => $blnEnabled
? ('Sync auto enregistrée (toutes les ' . $intIntervalMin . ' min jusquau '
. trim((string)($arrFresh['auto_sync_until'] ?? '')) . '). Le cron démarrera en phase B.')
. trim((string)($arrFresh['auto_sync_until'] ?? '')) . '). Cron : auto_chronotrack_sync.php')
: 'Sync auto désactivée.',
'auto_sync' => array(
'enabled' => intval($arrFresh['auto_sync_enabled'] ?? 0) === 1,
@ -208,6 +208,60 @@ function fxChronotrackApiConfigSaveAutoSync($intEveId, $blnEnabled, $intInterval
);
}
/**
* MSIN-4328 — events avec sync auto ON (pas fermés).
*/
function fxChronotrackApiConfigListAutoSyncEnabled() {
global $objDatabase;
$sql = "SELECT c.*"
. " FROM api_chronotrack_config c"
. " WHERE IFNULL(c.auto_sync_enabled, 0) = 1"
. " AND c.sync_status <> '" . $objDatabase->fxEscape(MSIN_API_CHRONOTRACK_SYNC_FERME) . "'"
. " ORDER BY c.eve_id ASC";
$arrRows = $objDatabase->fxGetResults($sql);
$tabOut = array();
if (!is_array($arrRows)) {
return $tabOut;
}
for ($i = 1; $i <= count($arrRows); $i++) {
$tabOut[] = $arrRows[$i];
}
return $tabOut;
}
/**
* MSIN-4328 — horodatage dernier passage cron auto.
*/
function fxChronotrackApiConfigTouchAutoRun($intEveId) {
global $objDatabase;
$intEveId = intval($intEveId);
if ($intEveId <= 0) {
return false;
}
$sql = "UPDATE api_chronotrack_config SET last_auto_run_at = '" . fxGetDateTime() . "'"
. ", updated_at = '" . fxGetDateTime() . "'"
. " WHERE eve_id = " . $intEveId;
return (bool)$objDatabase->fxQuery($sql);
}
/**
* MSIN-4328 — coupe la sync auto (échéance atteinte).
*/
function fxChronotrackApiConfigDisableAutoSync($intEveId) {
global $objDatabase;
$intEveId = intval($intEveId);
if ($intEveId <= 0) {
return false;
}
$sql = "UPDATE api_chronotrack_config SET auto_sync_enabled = 0"
. ", updated_at = '" . fxGetDateTime() . "'"
. " WHERE eve_id = " . $intEveId;
return (bool)$objDatabase->fxQuery($sql);
}
function fxChronotrackApiRaceMapGetForEvent($intEveId) {
global $objDatabase;

View File

@ -1661,3 +1661,143 @@ function fxChronotrackApiSyncListCtOrphans($intEveId) {
'orphans' => $tabOrphans,
);
}
/**
* MSIN-4328 — push différentiel complet pour un event (cron auto).
*/
function fxChronotrackApiSyncRunAutoPushForEvent($intEveId) {
$intEveId = intval($intEveId);
$arrPrep = fxChronotrackApiSyncPushPrepare($intEveId);
if (($arrPrep['state'] ?? '') !== 'ok') {
// Rien à pousser = succès « idle »
$strMsg = (string)($arrPrep['message'] ?? 'prepare failed');
if (stripos($strMsg, 'Aucun changement') !== false || intval($arrPrep['pending_push'] ?? -1) === 0) {
fxChronotrackApiSyncLog($intEveId, 0, 'auto_push', 'ok', 'idle — ' . $strMsg);
return array('state' => 'ok', 'message' => $strMsg, 'pushed_ok' => 0, 'idle' => true);
}
fxChronotrackApiSyncLog($intEveId, 0, 'auto_push', 'error', $strMsg);
return $arrPrep;
}
$intOffset = 0;
$intLimit = MSIN_API_CHRONOTRACK_SYNC_CHUNK_SIZE;
$intOkSum = 0;
$intErrSum = 0;
$intLoops = 0;
$intMaxLoops = 500;
while ($intLoops < $intMaxLoops) {
$intLoops++;
$arrChunk = fxChronotrackApiSyncPushEvent($intEveId, array(
'offset' => $intOffset,
'limit' => $intLimit,
));
$intOkSum += intval($arrChunk['pushed_ok'] ?? 0);
$intErrSum += intval($arrChunk['pushed_error'] ?? 0);
if (!empty($arrChunk['done'])) {
break;
}
if (($arrChunk['state'] ?? '') === 'error' && intval($arrChunk['pushed_ok'] ?? 0) === 0) {
break;
}
$intOffset = intval($arrChunk['next_offset'] ?? ($intOffset + $intLimit));
}
$strState = ($intErrSum === 0) ? 'ok' : (($intOkSum > 0) ? 'partial' : 'error');
$strMsg = 'auto push ok=' . $intOkSum . ' err=' . $intErrSum
. ' total_job=' . intval($arrPrep['total'] ?? 0);
fxChronotrackApiSyncLog($intEveId, 0, 'auto_push', ($strState === 'ok') ? 'ok' : 'error', $strMsg);
return array(
'state' => $strState,
'message' => $strMsg,
'pushed_ok' => $intOkSum,
'pushed_error' => $intErrSum,
'idle' => false,
);
}
/**
* MSIN-4328 — passage cron : events auto ON, fréquence / fin respectées.
*/
function fxChronotrackApiSyncRunAutoCron() {
if (!fxChronotrackApiSettingsConfigured()) {
return array(
'state' => 'error',
'message' => fxChronotrackApiSettingsErrorMessage(),
'ran' => 0,
'skipped' => 0,
);
}
$tabConfigs = fxChronotrackApiConfigListAutoSyncEnabled();
$intRan = 0;
$intSkipped = 0;
$intExpired = 0;
$tabDetails = array();
$intNow = time();
foreach ($tabConfigs as $arrCfg) {
$intEveId = intval($arrCfg['eve_id'] ?? 0);
if ($intEveId <= 0) {
continue;
}
$strUntil = trim((string)($arrCfg['auto_sync_until'] ?? ''));
if ($strUntil !== '' && $strUntil !== '0000-00-00 00:00:00') {
$intUntil = strtotime($strUntil);
if ($intUntil !== false && $intNow > $intUntil) {
fxChronotrackApiConfigDisableAutoSync($intEveId);
fxChronotrackApiSyncLog(
$intEveId,
0,
'auto_sync',
'ok',
'Échéance atteinte (' . $strUntil . ') — sync auto désactivée'
);
$intExpired++;
$tabDetails[] = array('eve_id' => $intEveId, 'action' => 'expired');
continue;
}
} else {
// Pas de fin = on ne tourne pas (UI exige une fin à lenregistrement)
$intSkipped++;
$tabDetails[] = array('eve_id' => $intEveId, 'action' => 'skip_no_until');
continue;
}
$intInterval = intval($arrCfg['auto_sync_interval_min'] ?? 15);
if (!in_array($intInterval, fxChronotrackApiConfigAutoSyncIntervals(), true)) {
$intInterval = 15;
}
$strLast = trim((string)($arrCfg['last_auto_run_at'] ?? ''));
if ($strLast !== '' && $strLast !== '0000-00-00 00:00:00') {
$intLast = strtotime($strLast);
if ($intLast !== false && ($intNow - $intLast) < ($intInterval * 60)) {
$intSkipped++;
$tabDetails[] = array('eve_id' => $intEveId, 'action' => 'skip_interval');
continue;
}
}
$arrRes = fxChronotrackApiSyncRunAutoPushForEvent($intEveId);
fxChronotrackApiConfigTouchAutoRun($intEveId);
$intRan++;
$tabDetails[] = array(
'eve_id' => $intEveId,
'action' => 'run',
'result' => $arrRes['state'] ?? 'error',
'message' => $arrRes['message'] ?? '',
'ok' => intval($arrRes['pushed_ok'] ?? 0),
);
}
return array(
'state' => 'ok',
'message' => 'cron auto — ran=' . $intRan . ' skipped=' . $intSkipped . ' expired=' . $intExpired,
'ran' => $intRan,
'skipped' => $intSkipped,
'expired' => $intExpired,
'details' => $tabDetails,
);
}