"""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)