implentation tour pas fini
This commit is contained in:
15
a faire
Normal file
15
a faire
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
Notes pour correction du moteur Python
|
||||||
|
|
||||||
|
Les priorités ne fonctionnent pas.
|
||||||
|
La logique actuelle doit être revue.
|
||||||
|
|
||||||
|
La validation des intervalles est mal faite.
|
||||||
|
Elle doit être basée directement sur l’heure pour obtenir un rapprochement plus uniforme.
|
||||||
|
|
||||||
|
Les tours ne fonctionnent pas.
|
||||||
|
Le calcul du tour et du temps de tour doit être revu complètement.
|
||||||
|
|
||||||
|
Concept général à corriger :
|
||||||
|
Lorsqu’une entrée est jugée « bonne », on doit l’écrire immédiatement dans la base de données,
|
||||||
|
avant d’appliquer n’importe quelle autre validation.
|
||||||
|
Parce que ces lectures doivent être conservées quoi qu’il arrive.
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -87,7 +87,7 @@ def insert_detail_record(
|
|||||||
try:
|
try:
|
||||||
cur.execute(sql, data)
|
cur.execute(sql, data)
|
||||||
cnx.commit()
|
cnx.commit()
|
||||||
|
return cur.lastrowid
|
||||||
except mysql.connector.IntegrityError as e:
|
except mysql.connector.IntegrityError as e:
|
||||||
# Duplicate key = lecture déjà traitée → on ignore et on continue
|
# Duplicate key = lecture déjà traitée → on ignore et on continue
|
||||||
if e.errno == 1062:
|
if e.errno == 1062:
|
||||||
|
|||||||
@ -11,18 +11,14 @@ from zoneinfo import ZoneInfo
|
|||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from engine.utils import format_temps_decimal
|
from engine.utils import format_temps_decimal
|
||||||
from engine.engine_components.regles import resolve_priority
|
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")
|
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)
|
|
||||||
|
|
||||||
|
|
||||||
# -------------------------------------------------
|
# -------------------------------------------------
|
||||||
@ -128,9 +124,12 @@ def process_course(cur, cnx, con_id: int):
|
|||||||
coureur = get_classement_by_tag(cur, classement_table, tag)
|
coureur = get_classement_by_tag(cur, classement_table, tag)
|
||||||
|
|
||||||
if not coureur:
|
if not coureur:
|
||||||
continue
|
dossard = "???"
|
||||||
|
status = 0
|
||||||
dossard = coureur["cla_info_dossard"]
|
reason_code = "UNKNOWN_BIB"
|
||||||
|
reason_ref_id = None
|
||||||
|
else:
|
||||||
|
dossard = coureur["cla_info_dossard"]
|
||||||
|
|
||||||
# -------------------------------------------------
|
# -------------------------------------------------
|
||||||
# Résoudre timing point
|
# Résoudre timing point
|
||||||
@ -148,77 +147,9 @@ def process_course(cur, cnx, con_id: int):
|
|||||||
detail_table = f"resultv2_{con_id}_detail"
|
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(
|
detail_id=insert_detail_record(
|
||||||
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(
|
|
||||||
cur, cnx, con_id,
|
cur, cnx, con_id,
|
||||||
dossard, lec,
|
dossard, lec,
|
||||||
tp_id, tp_name,
|
tp_id, tp_name,
|
||||||
@ -227,13 +158,121 @@ def process_course(cur, cnx, con_id: int):
|
|||||||
status=status,
|
status=status,
|
||||||
reason_code=reason_code,
|
reason_code=reason_code,
|
||||||
reason_ref_id=reason_ref_id,
|
reason_ref_id=reason_ref_id,
|
||||||
tour=tour,
|
tour=None,
|
||||||
tempstour=tempstour,
|
tempstour=None,
|
||||||
tempstour_str=tempstour_str
|
tempstour_str=None
|
||||||
)
|
)
|
||||||
|
|
||||||
if status == 1:
|
# 👉 Après ceci, TU CONTINUES TA LOGIQUE (priorité, intervalle, tours)
|
||||||
log_info(f"[OK] id={rcourse_id} tag={tag} dossard={dossard} tp={tp_name} prio={prio}")
|
# 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 d’intervalle
|
||||||
|
status_interval, rc_interval, rr_interval = check_interval_rule(
|
||||||
|
cur,
|
||||||
|
detail_table,
|
||||||
|
tp_id,
|
||||||
|
dossard,
|
||||||
|
timestamp,
|
||||||
|
interval_sec,
|
||||||
|
det_id
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
# Si la règle d’intervalle 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 d’tour 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
|
# 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)
|
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
# regles.py
|
# regles.py
|
||||||
from engine.logger import log_info, log_debug
|
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
|
# Règle de priorité : remplace, rejette, ou force tour
|
||||||
# -------------------------------------------------
|
# -------------------------------------------------
|
||||||
@ -95,3 +97,125 @@ def resolve_priority(
|
|||||||
|
|
||||||
# Même priorité → rejet
|
# 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()
|
||||||
Reference in New Issue
Block a user