Files
ms1readerv4/ci4app/app/Models/EventsModel.php
2026-07-17 11:48:07 -04:00

539 lines
18 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace App\Models;
use CodeIgniter\Model;
use Config\Database;
class EventsModel extends Model
{
protected $table = 'reader_config'; // table bidon
protected $primaryKey = 'con_id';
protected $allowedFields = [
'name',
'location',
'date',
];
// ---------------------------------------------------------
// Lire un événement selon l'ID
// ---------------------------------------------------------
public function getEvent(int $id): ?array
{
return $this->asArray()->find($id);
}
public function getLinkedData(int $id): array
{
// 1) Lire la config dans la DB par défaut
$config = $this->asArray()->find($id);
if (!$config) {
return [];
}
// 2) Connexion à la base MS1Timing
$db = \Config\Database::connect('ms1timing');
// 3) Lire Course
$course = $db->table('courses')
->where('cou_id', $config['cou_id'])
->get()
->getRowArray();
// 4) Lire Event
$event = $db->table('evenements')
->where('eve_id', $config['eve_id'])
->get()
->getRowArray();
return [
'config' => $config,
'course' => $course,
'event' => $event,
];
}
public function getLinkedDatatimingpoint(int $id): array
{
$db = \Config\Database::connect();
// 1) Lire les timing points pour cette course
$timingpoints = $db->table('reader_config_timingpoint')
->where('con_id', $id)
->orderBy('tp_trie', 'ASC')
->get()
->getResultArray();
// Si aucun TP, on retourne vide
if (empty($timingpoints)) {
return ['timingpoint' => []];
}
// 2) Préparer liste des tp_id
$tpIds = array_column($timingpoints, 'tp_id');
// 3) Construire nom de la table dynamic
$detailTable = "resultv2_{$id}_detail";
// 4) UNE SEULE requête SQL pour compter par TP (optimisation majeure)
$counts = $db->table($detailTable)
->select('tp_id, COUNT(*) as total')
->whereIn('tp_id', $tpIds)
->groupBy('tp_id')
->get()
->getResultArray();
// 5) Convertir le résultat en tableau associatif: [tp_id => count]
$countMap = [];
foreach ($counts as $row) {
$countMap[$row['tp_id']] = (int) $row['total'];
}
// 6) Injecter le count dans chaque timing point
foreach ($timingpoints as &$tp) {
$tpId = $tp['tp_id'];
$tp['lecture_count'] = $countMap[$tpId] ?? 0;
}
return [
'timingpoint' => $timingpoints
];
}
public function getProcessStatus($con_id)
{
$table = "resultv2_{$con_id}_lecture";
$row = $this->db->query("SELECT process_status FROM `$table` LIMIT 1")->getRow();
return $row ? $row->process_status : null;
}
public function getStatsByEventName(string $eventName): array
{
$db = \Config\Database::connect(); // DB par défaut
$results = $db->table('reader_lectures')
->select('chrinfo_location, COUNT(*) AS total, MIN(chrinfo_time) AS start_time, MAX(chrinfo_time) AS end_time')
->where('chrinfo_event_name', $eventName)
->where('chrinfo_type_transaction !=', 'NEW LOCATION')
->groupBy('chrinfo_location')
->get()
->getResultArray();
// Transformer en tableau avec clé 1,2,3...
$final = [];
$i = 1;
foreach ($results as $row) {
$final[$i] = [
'location' => $row['chrinfo_location'],
'count' => (int)$row['total'],
'start_time' => $row['start_time'],
'end_time' => $row['end_time'],
'icon' => 'lucide--eye',
'bg_color' => '#F7B6A8'
];
$i++;
}
return $final;
}
public function getEventList(): array
{
// 1) Lire tous les événements
$events = $this->asArray()
->findAll();
$final = [];
// 2) Ajouter les données liées pour chaque ligne
foreach ($events as $event) {
$linked = $this->getLinkedData($event['con_id']);
$final[] = [
'config' => $linked['config'],
'course' => $linked['course'],
'event' => $linked['event'],
];
}
// 3) Trier par eve_date_debut DESC (du plus récent au plus ancien)
usort($final, function($a, $b) {
return strcmp($b['event']['eve_date_debut'], $a['event']['eve_date_debut']);
});
return $final;
}
/**
* Duplique les tables de résultats sur la connexion ms1timing_slave
* à partir des tables *_template vers *_{$courseId}.
*
* @param int $courseId ex: 2152
* @return array liste des statuts par table
*/
public function duplicateResultTables(int $courseId): array
{
// Connexion à la base ms1timing_slave (déjà définie dans Database.php)
$db = \Config\Database::connect();
// Suffixes des tables
$suffixes = [
'classement',
'detail',
'log',
'pause',
'lecture',
'lecture_statut'
];
$results = [];
foreach ($suffixes as $suffix) {
// Nouveau format demandé
$templateTable = "resultv2_template_{$suffix}";
$newTable = "resultv2_{$courseId}_{$suffix}";
// Vérifier que la table template existe
if (! $db->tableExists($templateTable)) {
$results[] = [
'table' => $newTable,
'status' => 'template_missing',
'message'=> "La table template {$templateTable} nexiste pas.",
];
continue;
}
// Si la nouvelle table existe déjà, on ne la touche pas
if ($db->tableExists($newTable)) {
$results[] = [
'table' => $newTable,
'status' => 'exists',
'message'=> "La table {$newTable} existe déjà. Aucune action.",
];
continue;
}
// Création + remplissage dans une transaction
$db->transStart();
// CREATE TABLE nouveau LIKE template
$db->query(
"CREATE TABLE `{$newTable}` LIKE `{$templateTable}`"
);
// Cas particulier pour la table lecture : on met directement les bonnes valeurs initiales
if ($suffix === 'lecture') {
$db->query("
INSERT INTO `{$newTable}` (
id,
last_processed_read_id,
config_hash,
needs_rebuild,
process_status,
backup_flag,
last_rebuild_ts,
last_backup_ts,
moteur_version,
created_at,
updated_at
) VALUES (
1,
0,
'',
0,
'IDLE',
0,
NULL,
NULL,
'1.0',
NOW(),
NOW()
)
");
} else {
// INSERT INTO new SELECT * FROM template (comportement existant)
$db->query(
"INSERT INTO `{$newTable}` SELECT * FROM `{$templateTable}`"
);
}
$db->transComplete();
if ($db->transStatus() === false) {
$results[] = [
'table' => $newTable,
'status' => 'error',
'message'=> "Erreur lors de la duplication de {$newTable}.",
];
} else {
$results[] = [
'table' => $newTable,
'status' => 'created',
'message'=> "Table {$newTable} créée et remplie à partir de {$templateTable}.",
];
}
}
return $results;
}
/**
* Récupère les timingpoints + toutes les configurations associées
* à partir de la table reader_config_detail.
*
* @param int $id ID du contrôleur (con_id)
* @return array Tableau final des timingpoints enrichis
*/
public function getLinkedDatatimingpointWithConfig(int $id)
{
//----------------------------------------------------------------------
// 1. Récupération des timingpoints existants (déjà fonctionnel chez toi)
//----------------------------------------------------------------------
$data = $this->getLinkedDatatimingpoint($id);
// Sécurité : si aucun timingpoint, on retourne tout de suite
if (!isset($data['timingpoint']) || empty($data['timingpoint'])) {
return $data;
}
//----------------------------------------------------------------------
// 2. Boucle sur chaque timingpoint (un tp_id à la fois)
//----------------------------------------------------------------------
foreach ($data['timingpoint'] as $index => $tp) {
$tp_id = $tp['tp_id'];
//------------------------------------------------------------------
// 3. Aller chercher toutes les lignes reader_config_detail
// où det_ref1_id = tp_id
//------------------------------------------------------------------
$builder = $this->db->table('reader_config_detail');
$builder->where('det_ref1_id', $tp_id);
$builder->orderBy('det_trie', 'ASC');
$rows = $builder->get()->getResultArray();
//------------------------------------------------------------------
// 4. Aucune config → on continue au suivant
//------------------------------------------------------------------
if (empty($rows)) {
continue;
}
//------------------------------------------------------------------
// 5. Préparation des conteneurs de regroupement
//------------------------------------------------------------------
$configSets = [];
//------------------------------------------------------------------
// 6. Boucle à travers chaque config trouvée
//------------------------------------------------------------------
foreach ($rows as $row) {
//--------------------------------------------------------------
// 6.1 Extraction du groupe (ex: configs-tp, configs-tp-interval)
// → conversion en identifiant propre pour le tableau final
//--------------------------------------------------------------
$groupName = !empty($row['det_groupe'])
? str_replace('-', '_', $row['det_groupe'])
: 'configs_generic'; // fallback, jamais vide
//--------------------------------------------------------------
// 6.2 La clef interne (ex: clef_controleur)
//--------------------------------------------------------------
$clef = $row['det_clef'];
if (empty($clef)) {
// Si jamais c'est vide, on ignore cette ligne
continue;
}
//--------------------------------------------------------------
// 6.3 Préparer la structure si elle n'existe pas encore
//--------------------------------------------------------------
if (!isset($configSets[$groupName])) {
$configSets[$groupName] = [];
}
if (!isset($configSets[$groupName][$clef])) {
$configSets[$groupName][$clef] = [];
}
//--------------------------------------------------------------
// 6.4 Ajouter la ligne de configuration
//--------------------------------------------------------------
$configSets[$groupName][$clef][] = [
'value1' => $row['det_value1'],
'value2' => $row['det_value2'],
'value3' => $row['det_value3'],
];
}
//------------------------------------------------------------------
// 7. Injection dans le timingpoint final
//------------------------------------------------------------------
$data['timingpoint'][$index] = array_merge(
$data['timingpoint'][$index],
$configSets
);
}
//----------------------------------------------------------------------
// 8. Retour final
//----------------------------------------------------------------------
return $data;
}
public function getcourseConfig(int $id)
{
//------------------------------------------------------------------
$builder = $this->db->table('reader_config_detail');
$builder->where('det_ref1_id', $id);
$builder->where('det_type', 2);
$builder->orderBy('det_trie', 'ASC');
$rows = $builder->get()->getResultArray();
//------------------------------------------------------------------
// 5. Préparation des conteneurs de regroupement
//------------------------------------------------------------------
$configSets = [];
//------------------------------------------------------------------
// 6. Boucle à travers chaque config trouvée
//------------------------------------------------------------------
foreach ($rows as $row) {
//--------------------------------------------------------------
// 6.1 Extraction du groupe (ex: configs-tp, configs-tp-interval)
// → conversion en identifiant propre pour le tableau final
//--------------------------------------------------------------
$groupName = !empty($row['det_groupe'])
? str_replace('-', '_', $row['det_groupe'])
: 'configs_generic'; // fallback, jamais vide
//--------------------------------------------------------------
// 6.2 La clef interne (ex: clef_controleur)
//--------------------------------------------------------------
$clef = $row['det_clef'];
if (empty($clef)) {
// Si jamais c'est vide, on ignore cette ligne
continue;
}
//--------------------------------------------------------------
// 6.3 Préparer la structure si elle n'existe pas encore
//--------------------------------------------------------------
if (!isset($configSets[$groupName])) {
$configSets[$groupName] = [];
}
if (!isset($configSets[$groupName][$clef])) {
$configSets[$groupName][$clef] = [];
}
//--------------------------------------------------------------
// 6.4 Ajouter la ligne de configuration
//--------------------------------------------------------------
$output=$row['det_value1'];
if($row['det_type_output']!='')
$output=format_output($row['det_value1'], $row['det_type_output']);
$configSets[$groupName][$clef][] = [
'value1' => $row['det_value1'],
'value2' => $row['det_value2'],
'value3' => $row['det_value3'],
'output' => $output,
];
}
//--------------------------------------------------------------
//----------------------------------------------------------------------
// 8. Retour final
//----------------------------------------------------------------------
return $configSets;
}
public function resetCourse($con_id)
{
$lectureTable = "resultv2_{$con_id}_lecture";
$detailTable = "resultv2_{$con_id}_detail";
// -------------------------------------------------
// 1) Demander la pause (PAUSE_REQUEST)
// -------------------------------------------------
$this->db->query("UPDATE `$lectureTable` SET process_status = 'PAUSE_REQUEST'");
// -------------------------------------------------
// 2) Attendre que Python applique PAUSE
// -------------------------------------------------
$timeoutSeconds = 20 * 60; // 20 minutes
$waitInterval = 500000; // 0.5 seconde
$attempts = ($timeoutSeconds * 1000000) / $waitInterval;
$isPaused = false;
for ($i = 0; $i < $attempts; $i++) {
$row = $this->db->query("SELECT process_status FROM `$lectureTable` LIMIT 1")->getRow();
if ($row && strtoupper($row->process_status) === 'PAUSE') {
$isPaused = true;
break;
}
// Attendre 0.5 sec avant de rechecker
usleep($waitInterval);
}
if (!$isPaused) {
return "ERREUR : Impossible d'obtenir PAUSE (timeout). Base NON modifiée.";
}
// -------------------------------------------------
// 3) TRUNCATE de la table detail (sécurisé)
// -------------------------------------------------
$this->db->query("TRUNCATE TABLE `$detailTable`");
// -------------------------------------------------
// 4) Remettre last_processed_read_id à 0
// -------------------------------------------------
$this->db->query("
UPDATE `$lectureTable`
SET last_processed_read_id = 0
");
// -------------------------------------------------
// 5) Remettre la course en IDLE pour que Python puisse repartir
// -------------------------------------------------
$this->db->query("UPDATE `$lectureTable` SET process_status = 'IDLE'");
return "OK : Course réinitialisée.";
}
}