valide les double mais preirite et interval pas ok

This commit is contained in:
2025-12-10 18:04:10 -05:00
parent 9db677e313
commit 5873ab4900
6 changed files with 44 additions and 99 deletions

View File

@ -1,3 +1,5 @@
from engine.logger import log_info, log_debug
import mysql.connector
def insert_detail_record(
cur,
cnx,
@ -81,5 +83,16 @@ def insert_detail_record(
tempstour_str
)
cur.execute(sql, data)
cnx.commit()
try:
cur.execute(sql, data)
cnx.commit()
except mysql.connector.IntegrityError as e:
# Duplicate key = lecture déjà traitée → on ignore et on continue
if e.errno == 1062:
log_info(f"[DUPLICATE IGNORED] lecture déjà enregistrée ({e.msg})")
return # on sort proprement, SANS échec
# Sinon, vraie erreur → on relance
raise

View File

@ -1,3 +1,4 @@
# process_course.py
from engine.engine_components.tp_config import load_tp_config_for_con, resolve_tp_for_lecture
from engine.engine_components.course_config import load_course_config
from engine.engine_components.engine_state import get_last_processed_read_id, update_last_processed_read_id
@ -13,11 +14,6 @@ from engine.engine_components.regles import resolve_priority
LOCAL_TZ = ZoneInfo("America/Toronto")
# -------------------------------------------------
# Mémoire locale pour la dernière lecture valide
# -------------------------------------------------
_last_valid_read = {} # (tp_id, dossard) -> datetime locale
# -------------------------------------------------
# Conversion d'un timestamp Decimal en datetime UTC
@ -45,9 +41,6 @@ def _normalize_course_time(dt):
# -------------------------------------------------
def process_course(cur, cnx, con_id: int):
# -------------------------------------------------
# Lecture de la configuration TP + configuration course
# -------------------------------------------------
tp_config = load_tp_config_for_con(cur, con_id)
course_cfg = load_course_config(cur, con_id)
@ -63,29 +56,22 @@ def process_course(cur, cnx, con_id: int):
# -------------------------------------------------
# Pause
# -------------------------------------------------
# Lire le statut
cur.execute(f"SELECT process_status FROM `{engine_table}` LIMIT 1")
row = cur.fetchone()
status = (row["process_status"] or "").upper()
# Si une pause est demandée
if status == "PAUSE_REQUEST":
# Le moteur se met officiellement en PAUSE
cur.execute(f"UPDATE `{engine_table}` SET process_status = 'PAUSE'")
cnx.commit()
log_info(f"[PAUSE] con_id={con_id} — pause appliquée avant traitement.")
return
# Si déjà en pause → sortir immédiatement
if status == "PAUSE":
log_info(f"[PAUSE] con_id={con_id} — course déjà en pause.")
return
# -------------------------------------------------
# Lire dernier ID traité (mécanisme incremental)
# Lire dernier ID traité
# -------------------------------------------------
last_processed = get_last_processed_read_id(cur, engine_table)
if last_processed is None:
@ -109,8 +95,6 @@ def process_course(cur, cnx, con_id: int):
# -------------------------------------------------
for lec in lectures:
rcourse_id = int(lec["rcourse_id"])
chr_loc = lec["chrinfo_location"]
timestamp = Decimal(lec["chrinfo_time"])
@ -138,7 +122,7 @@ def process_course(cur, cnx, con_id: int):
reason_ref_id = None
# -------------------------------------------------
# Vérifier que le BIB existe dans la table classement
# Vérifier BIB dans classement
# -------------------------------------------------
tag = lec["chrinfo_tag"]
coureur = get_classement_by_tag(cur, classement_table, tag)
@ -149,7 +133,7 @@ def process_course(cur, cnx, con_id: int):
dossard = coureur["cla_info_dossard"]
# -------------------------------------------------
# Résoudre le timing point selon la config
# Résoudre timing point
# -------------------------------------------------
tp_info = resolve_tp_for_lecture(tp_config, con_id, chr_loc)
if not tp_info:
@ -162,42 +146,14 @@ def process_course(cur, cnx, con_id: int):
det_id = tp_info["det_id"]
detail_table = f"resultv2_{con_id}_detail"
# -------------------------------------------------
# Règle de priorité (peut rejeter ou forcer tour)
# Règle de priorité
# -------------------------------------------------
status, reason_code, reason_ref_id, forced_tour = resolve_priority(
cur,
cnx,
detail_table,
tp_id,
dossard,
timestamp,
prio,
det_id,
tp_config
cur, cnx, detail_table, tp_id, dossard, timestamp, prio, det_id, tp_config
)
# -------------------------------------------------
# Gestion mémoire locale selon la règle de priorité
# -------------------------------------------------
# Si la lecture courante est rejetée par priorité :
if status == 0 and reason_code in ("REPLACED_BY_HIGHER_PRIORITY", "LOWER_PRIORITY"):
# On doit retirer l'ancienne lecture valide de la mémoire
if (tp_id, dossard) in _last_valid_read:
del _last_valid_read[(tp_id, dossard)]
# Si la lecture courante remplace une lecture existante :
elif reason_code == "REPLACED_BY_HIGHER_PRIORITY":
# La nouvelle lecture devient la base correcte pour les intervalles
_last_valid_read[(tp_id, dossard)] = dt_local
# -------------------------------------------------
# Si la priorité a déjà rejeté la lecture, on n'applique pas les autres règles
# -------------------------------------------------
if status == 0:
insert_detail_record(
cur, cnx, con_id,
@ -214,14 +170,8 @@ def process_course(cur, cnx, con_id: int):
)
continue
# -------------------------------------------------
# Règle d'intervalle minimum entre lectures
# Règle d'intervalle
# -------------------------------------------------
interval_cfg = (
tp_config[tp_id]["config"]
@ -236,16 +186,15 @@ def process_course(cur, cnx, con_id: int):
except:
interval_sec = 0
last_dt = _last_valid_read.get((tp_id, dossard))
last_dt = get_last_valid_lecture_time(cur, detail_table, tp_id, dossard)
if last_dt and interval_sec > 0:
delta = (dt_local - last_dt).total_seconds()
delta = (dt_utc - last_dt).total_seconds()
if delta < interval_sec:
status = 0
reason_code = "SKIP_INTERVAL"
reason_code = f"SKIP_INTERVAL {delta:.1f}s < {interval_sec}s"
reason_ref_id = det_id
log_debug(f"[SKIP INTERVAL] id={rcourse_id} {delta:.1f}s < {interval_sec}s")
insert_detail_record(
cur, cnx, con_id,
@ -262,26 +211,13 @@ def process_course(cur, cnx, con_id: int):
)
continue
# -------------------------------------------------
# Calcul du tour + temps de tour
# Calcul tour (placeholder)
# -------------------------------------------------
tour, prev_ts = compute_tour(cur, detail_table, tp_id, dossard)
tour = 1
tempstour = Decimal("0")
tempstour_str = "0"
if forced_tour is not None:
tour = forced_tour
if prev_ts is None:
tempstour = Decimal("0")
else:
tempstour = timestamp - prev_ts
tempstour_str = format_temps_decimal(tempstour)
# -------------------------------------------------
# Écriture dans la table detail
# -------------------------------------------------
insert_detail_record(
cur, cnx, con_id,
dossard, lec,
@ -296,26 +232,23 @@ def process_course(cur, cnx, con_id: int):
tempstour_str=tempstour_str
)
# -------------------------------------------------
# Mise à jour mémoire locale dernière lecture valide
# -------------------------------------------------
if status == 1:
_last_valid_read[(tp_id, dossard)] = dt_local
log_info(f"[OK] id={rcourse_id} tag={tag} dossard={dossard} tp={tp_name} prio={prio}")
# -------------------------------------------------
# Mise à jour du pointeur de traitement
# Mise à jour du pointeur
# -------------------------------------------------
update_last_processed_read_id(cur, cnx, engine_table, max_id)
# -------------------------------------------------
# Recherche du dernier tour pour un coureur à un TP
# Dernière lecture valide
# -------------------------------------------------
def compute_tour(cur, detail_table, tp_id, dossard):
def get_last_valid_lecture_time(cur, detail_table, tp_id, dossard):
cur.execute(f"""
SELECT tour, timestamp_read
SELECT timestamp_read
FROM `{detail_table}`
WHERE tp_id = %s
AND cla_info_dossard = %s
@ -326,13 +259,8 @@ def compute_tour(cur, detail_table, tp_id, dossard):
row = cur.fetchone()
if not row or row["tour"] is None:
return 1, None
if not row:
return None
last_tour = row["tour"]
prev_ts = Decimal(str(row["timestamp_read"]))
return last_tour + 1, prev_ts
ts_decimal = Decimal(str(row["timestamp_read"]))
return ts_to_utc(ts_decimal)

View File

@ -72,6 +72,9 @@ def resolve_priority(
if new_priority > old_priority:
return 0, "LOWER_PRIORITY", det_id, None
# Nouvelle lecture plus prioritaire → remplacer
if new_priority < old_priority:
@ -89,5 +92,6 @@ def resolve_priority(
forced_tour = old_tour
return 1, None, None, forced_tour
# Même priorité → rejet
return 0, "EQUAL_PRIORITY", det_id, None
return 0, "EQUAL_PRIORITY", det_id, None