80 lines
2.6 KiB
Python
80 lines
2.6 KiB
Python
"""Push de-duplication and push-facing recommendation reads."""
|
|
|
|
from datetime import datetime, timedelta
|
|
|
|
from app.db.recommendation_state import classify_recommendation_result, derive_execution_fields
|
|
from app.db.schema import get_conn
|
|
|
|
|
|
PUSH_COOLDOWN_HOURS = 12
|
|
|
|
|
|
def should_push(symbol: str, push_type: str, action_status: str = "") -> bool:
|
|
"""Return whether a symbol/type can be pushed under the status-aware cooldown."""
|
|
conn = get_conn()
|
|
cutoff = (datetime.now() - timedelta(hours=PUSH_COOLDOWN_HOURS)).isoformat()
|
|
try:
|
|
if action_status:
|
|
row = conn.execute(
|
|
"SELECT action_status FROM push_log WHERE symbol=%s AND push_type=%s AND pushed_at > %s ORDER BY id DESC LIMIT 1",
|
|
(symbol, push_type, cutoff),
|
|
).fetchone()
|
|
if row is None:
|
|
return True
|
|
return row[0] != action_status
|
|
|
|
row = conn.execute(
|
|
"SELECT id FROM push_log WHERE symbol=%s AND push_type=%s AND pushed_at > %s ORDER BY id DESC LIMIT 1",
|
|
(symbol, push_type, cutoff),
|
|
).fetchone()
|
|
return row is None
|
|
finally:
|
|
conn.close()
|
|
|
|
|
|
def log_push(symbol: str, push_type: str, action_status: str = "", rec_id: int = 0):
|
|
"""Record one push event and keep the recommendation source traceable."""
|
|
conn = get_conn()
|
|
try:
|
|
conn.execute(
|
|
"INSERT INTO push_log (symbol, push_type, action_status, rec_id, pushed_at) VALUES (%s,%s,%s,%s,%s)",
|
|
(symbol, push_type, action_status, int(rec_id or 0), datetime.now().isoformat()),
|
|
)
|
|
conn.commit()
|
|
finally:
|
|
conn.close()
|
|
|
|
|
|
def get_recommendation_for_push(rec_id: int):
|
|
"""Read one recommendation with the same derived display state used by the web/API layer."""
|
|
try:
|
|
rec_id = int(rec_id or 0)
|
|
except Exception:
|
|
rec_id = 0
|
|
if rec_id <= 0:
|
|
return None
|
|
|
|
conn = get_conn()
|
|
try:
|
|
row = conn.execute(
|
|
"""
|
|
SELECT r.*,
|
|
lpc.price AS latest_cache_price,
|
|
lpc.updated_at AS latest_cache_updated_at
|
|
FROM recommendation r
|
|
LEFT JOIN latest_price_cache lpc ON lpc.symbol = r.symbol
|
|
WHERE r.id=%s
|
|
""",
|
|
(rec_id,),
|
|
).fetchone()
|
|
finally:
|
|
conn.close()
|
|
if not row:
|
|
return None
|
|
|
|
item = dict(row)
|
|
rec_result, rec_result_label = classify_recommendation_result(item)
|
|
item["recommendation_result"] = rec_result
|
|
item["recommendation_result_label"] = rec_result_label
|
|
return derive_execution_fields(item)
|