valide les double mais preirite et interval pas ok
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
Reference in New Issue
Block a user