implentation tour pas fini

This commit is contained in:
2025-12-12 09:52:22 -05:00
parent 5873ab4900
commit f85cd66795
7 changed files with 268 additions and 111 deletions

View File

@ -87,7 +87,7 @@ def insert_detail_record(
try:
cur.execute(sql, data)
cnx.commit()
return cur.lastrowid
except mysql.connector.IntegrityError as e:
# Duplicate key = lecture déjà traitée → on ignore et on continue
if e.errno == 1062:

View File

@ -11,19 +11,15 @@ from zoneinfo import ZoneInfo
from decimal import Decimal
from engine.utils import format_temps_decimal
from engine.engine_components.regles import resolve_priority
from engine.engine_components.regles import check_interval_rule
from engine.engine_components.regles import get_last_valid_lecture_time
from engine.engine_components.regles import ts_to_utc
from engine.engine_components.regles import update_detail_status_zero
LOCAL_TZ = ZoneInfo("America/Toronto")
# -------------------------------------------------
# Conversion d'un timestamp Decimal en datetime UTC
# -------------------------------------------------
def ts_to_utc(ts):
ts = Decimal(str(ts))
seconds = int(ts)
micro = int((ts - Decimal(seconds)) * Decimal(1_000_000))
return datetime.fromtimestamp(seconds, tz=timezone.utc).replace(microsecond=micro)
# -------------------------------------------------
# Normalisation du temps de course (start/end)
@ -128,9 +124,12 @@ def process_course(cur, cnx, con_id: int):
coureur = get_classement_by_tag(cur, classement_table, tag)
if not coureur:
continue
dossard = coureur["cla_info_dossard"]
dossard = "???"
status = 0
reason_code = "UNKNOWN_BIB"
reason_ref_id = None
else:
dossard = coureur["cla_info_dossard"]
# -------------------------------------------------
# Résoudre timing point
@ -148,77 +147,9 @@ def process_course(cur, cnx, con_id: int):
detail_table = f"resultv2_{con_id}_detail"
# -------------------------------------------------
# Règle de priorité
# 🔥 INSÉRER IMMÉDIATEMENT DANS DETAILS 🔥
# -------------------------------------------------
status, reason_code, reason_ref_id, forced_tour = resolve_priority(
cur, cnx, detail_table, tp_id, dossard, timestamp, prio, det_id, tp_config
)
if status == 0:
insert_detail_record(
cur, cnx, con_id,
dossard, lec,
tp_id, tp_name,
chr_loc, prio, controller,
dt_utc,
status=status,
reason_code=reason_code,
reason_ref_id=reason_ref_id,
tour=None,
tempstour=None,
tempstour_str=None
)
continue
# -------------------------------------------------
# Règle d'intervalle
# -------------------------------------------------
interval_cfg = (
tp_config[tp_id]["config"]
.get("config_tp_interval", {})
.get("clef_temp_minimum_read_entre", [])
)
interval_sec = 0
if interval_cfg:
try:
interval_sec = int(interval_cfg[0]["value1"])
except:
interval_sec = 0
last_dt = get_last_valid_lecture_time(cur, detail_table, tp_id, dossard)
if last_dt and interval_sec > 0:
delta = (dt_utc - last_dt).total_seconds()
if delta < interval_sec:
status = 0
reason_code = f"SKIP_INTERVAL {delta:.1f}s < {interval_sec}s"
reason_ref_id = det_id
insert_detail_record(
cur, cnx, con_id,
dossard, lec,
tp_id, tp_name,
chr_loc, prio, controller,
dt_utc,
status=status,
reason_code=reason_code,
reason_ref_id=reason_ref_id,
tour=None,
tempstour=None,
tempstour_str=None
)
continue
# -------------------------------------------------
# Calcul tour (placeholder)
# -------------------------------------------------
tour = 1
tempstour = Decimal("0")
tempstour_str = "0"
insert_detail_record(
detail_id=insert_detail_record(
cur, cnx, con_id,
dossard, lec,
tp_id, tp_name,
@ -227,14 +158,122 @@ def process_course(cur, cnx, con_id: int):
status=status,
reason_code=reason_code,
reason_ref_id=reason_ref_id,
tour=tour,
tempstour=tempstour,
tempstour_str=tempstour_str
tour=None,
tempstour=None,
tempstour_str=None
)
if status == 1:
log_info(f"[OK] id={rcourse_id} tag={tag} dossard={dossard} tp={tp_name} prio={prio}")
# 👉 Après ceci, TU CONTINUES TA LOGIQUE (priorité, intervalle, tours)
# mais la donnée brute est déjà sauvée.
# on traite pas les dossard ???
if dossard != "???":
# -------------------------------------------------
# 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
#)
# -------------------------------------------------
# Règle d'intervalle
# -------------------------------------------------
interval_cfg = (
tp_config[tp_id]["config"]
.get("config_tp_interval", {})
.get("clef_temp_minimum_read_entre", [])
)
interval_sec = 0
if interval_cfg:
try:
interval_sec = int(interval_cfg[0]["value1"])
except:
interval_sec = 0
# Appel de la règle dintervalle
status_interval, rc_interval, rr_interval = check_interval_rule(
cur,
detail_table,
tp_id,
dossard,
timestamp,
interval_sec,
det_id
)
# Si la règle dintervalle rejette
if status_interval == 0:
update_detail_status_zero(
cur,
cnx,
detail_table,
detail_id, # <-- l'ID que tu as obtenu juste après insert_detail_record
rc_interval, # reason_code retourné par la règle
rr_interval # reason_ref_id retourné par la règle
)
# Et on arrête le traitement de cette lecture
continue
# -------------------------------------------------
# Calcul du tour + temps de tour
# -------------------------------------------------
# Appel de la règle tour
status_tour = check_tour_rule(
cur,
detail_table,
tp_id,
dossard,
timestamp,
det_id
)
# Si la règle dtour rejette
if status_interval == 0:
update_detail_status_zero(
cur,
cnx,
detail_table,
detail_id, # <-- l'ID que tu as obtenu juste après insert_detail_record
rc_interval, # reason_code retourné par la règle
rr_interval # reason_ref_id retourné par la règle
)
# Et on arrête le traitement de cette lecture
continue
#tour, prev_ts = compute_tour(cur, detail_table, tp_id, dossard)
#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)
if status == 1:
log_info(f"[OK] id={rcourse_id} tag={tag} dossard={dossard} tp={tp_name} prio={prio}")
else:
log_info(f"[dossard ???] id={rcourse_id} ")
# -------------------------------------------------
# Mise à jour du pointeur
# -------------------------------------------------
@ -242,25 +281,4 @@ def process_course(cur, cnx, con_id: int):
# -------------------------------------------------
# Dernière lecture valide
# -------------------------------------------------
def get_last_valid_lecture_time(cur, detail_table, tp_id, dossard):
cur.execute(f"""
SELECT timestamp_read
FROM `{detail_table}`
WHERE tp_id = %s
AND cla_info_dossard = %s
AND status = 1
ORDER BY detail_id DESC
LIMIT 1
""", (tp_id, dossard))
row = cur.fetchone()
if not row:
return None
ts_decimal = Decimal(str(row["timestamp_read"]))
return ts_to_utc(ts_decimal)

View File

@ -1,5 +1,7 @@
# regles.py
from engine.logger import log_info, log_debug
from decimal import Decimal
from datetime import datetime, timezone
# -------------------------------------------------
# Règle de priorité : remplace, rejette, ou force tour
# -------------------------------------------------
@ -94,4 +96,126 @@ def resolve_priority(
# Même priorité → rejet
return 0, "EQUAL_PRIORITY", det_id, None
return 0, "EQUAL_PRIORITY", det_id, None
# -------------------------------------------------
# Regle interval
# ------------------------------------------------
def check_interval_rule(cur, detail_table, tp_id, dossard, timestamp, interval_sec, det_id):
"""
Retourne :
status (1 ou 0)
reason_code
reason_ref_id
"""
log_debug(f"[INTERVAL] tp_id={tp_id} dossard={dossard} interval_sec={interval_sec}")
last_timestamp = get_last_valid_lecture_time(cur, detail_table, tp_id, dossard, timestamp)
log_debug(f"[INTERVAL] last_valid={last_timestamp} current={timestamp}")
if last_timestamp and interval_sec > 0:
delta = (timestamp - last_timestamp)
log_debug(f"[INTERVAL] delta={delta:.3f}s")
if delta < interval_sec:
reason = f"SKIP_INTERVAL {delta:.1f}s < {interval_sec}s"
log_debug(
f"[INTERVAL] REJECT dossard={dossard} tp_id={tp_id} "
f"reason='{reason}' det_id={det_id}"
)
return (0, reason, det_id)
log_debug(f"[INTERVAL] OK dossard={dossard} tp_id={tp_id}")
return (1, None, None)
# -------------------------------------------------
# Dernière lecture valide (timestamp_read < T)
# -------------------------------------------------
def get_last_valid_lecture_time(cur, detail_table, tp_id, dossard, current_ts):
log_debug(f"[get_last_valid_lecture_time] tp={tp_id} dossard={dossard} current={current_ts}")
cur.execute(f"""
SELECT timestamp_read
FROM `{detail_table}`
WHERE tp_id = %s
AND cla_info_dossard = %s
AND status = 1
AND timestamp_read < %s
ORDER BY timestamp_read DESC
LIMIT 1
""", (tp_id, dossard, current_ts))
log_debug(
f"SELECT timestamp_read FROM `{detail_table}` "
f"WHERE tp_id = {tp_id} AND cla_info_dossard = '{dossard}' "
f"AND status = 1 AND timestamp_read < {current_ts} "
f"ORDER BY timestamp_read DESC LIMIT 1;"
)
row = cur.fetchone()
log_debug(f"[get_last_valid_lecture_time] result={row}")
if not row:
return None
return Decimal(str(row["timestamp_read"]))
# -------------------------------------------------
# Conversion d'un timestamp Decimal en datetime UTC
# -------------------------------------------------
def ts_to_utc(ts):
ts = Decimal(str(ts))
seconds = int(ts)
micro = int((ts - Decimal(seconds)) * Decimal(1_000_000))
return datetime.fromtimestamp(seconds, tz=timezone.utc).replace(microsecond=micro)
# -------------------------------------------------
# Recherche du dernier tour pour un coureur à un TP
# -------------------------------------------------
#def compute_tour(cur, detail_table, tp_id, dossard):
def check_tour_rule(
cur,
detail_table,
tp_id,
dossard,
timestamp,
det_id
):
cur.execute(f"""
SELECT tour, timestamp_read
FROM `{detail_table}`
WHERE tp_id = %s
AND cla_info_dossard = %s
AND status = 1
ORDER BY detail_id DESC
LIMIT 1
""", (tp_id, dossard))
row = cur.fetchone()
if not row or row["tour"] is None:
return 1, None
last_tour = row["tour"]
prev_ts = Decimal(str(row["timestamp_read"]))
return last_tour + 1, prev_ts
def update_detail_status_zero(cur, cnx, detail_table, detail_id, reason_code, reason_ref_id):
"""
Met à jour un enregistrement Detail existant pour le passer en status=0,
avec reason_code et reason_ref_id.
"""
cur.execute(f"""
UPDATE `{detail_table}`
SET status = 0,
reason_code = %s,
reason_ref_id = %s
WHERE detail_id = %s
""", (reason_code, reason_ref_id, detail_id))
cnx.commit()