170 lines
5.6 KiB
Python
170 lines
5.6 KiB
Python
"""
|
|
CRUD для таблицы scenario_definitions.
|
|
|
|
Функции:
|
|
list_definitions(client_id, stand) — список активных
|
|
get_definition(def_id, cid, stand) — один
|
|
create_definition(cid, stand, name, steps, updated_by) → id
|
|
update_definition(def_id, cid, stand, name, steps, version, updated_by) → ok|conflict
|
|
delete_definition(def_id, cid, stand) — мягкое (is_active=false)
|
|
lock_check(cid, stand) — есть ли RUNNING запуск → 409 если да
|
|
"""
|
|
|
|
import json
|
|
from db.pool import get_conn, put_conn
|
|
|
|
|
|
def list_definitions(client_id, stand):
|
|
conn = get_conn()
|
|
if not conn:
|
|
return []
|
|
try:
|
|
cur = conn.cursor()
|
|
cur.execute("""
|
|
SELECT id, name, steps, version, is_active, created_at, updated_at
|
|
FROM scenario_definitions
|
|
WHERE is_active = TRUE
|
|
AND ((client_id = %s AND stand = %s) OR (client_id = '' AND stand = ''))
|
|
ORDER BY name
|
|
""", (client_id, stand))
|
|
rows = cur.fetchall()
|
|
cols = [d[0] for d in cur.description]
|
|
result = []
|
|
for r in rows:
|
|
d = dict(zip(cols, r))
|
|
d["created_at"] = d["created_at"].isoformat() if d["created_at"] else None
|
|
d["updated_at"] = d["updated_at"].isoformat() if d["updated_at"] else None
|
|
result.append(d)
|
|
return result
|
|
except Exception as e:
|
|
print(f"[DEFS] list error: {e}", flush=True)
|
|
return []
|
|
finally:
|
|
put_conn(conn)
|
|
|
|
|
|
def get_definition(def_id, client_id, stand):
|
|
conn = get_conn()
|
|
if not conn:
|
|
return None
|
|
try:
|
|
cur = conn.cursor()
|
|
cur.execute("""
|
|
SELECT id, name, steps, version, is_active, created_at, updated_at, updated_by
|
|
FROM scenario_definitions
|
|
WHERE id = %s AND ((client_id = %s AND stand = %s) OR (client_id = '' AND stand = ''))
|
|
""", (def_id, client_id, stand))
|
|
row = cur.fetchone()
|
|
if not row:
|
|
return None
|
|
cols = [d[0] for d in cur.description]
|
|
d = dict(zip(cols, row))
|
|
d["created_at"] = d["created_at"].isoformat() if d["created_at"] else None
|
|
d["updated_at"] = d["updated_at"].isoformat() if d["updated_at"] else None
|
|
return d
|
|
except Exception as e:
|
|
print(f"[DEFS] get error: {e}", flush=True)
|
|
return None
|
|
finally:
|
|
put_conn(conn)
|
|
|
|
|
|
def create_definition(client_id, stand, name, steps, updated_by=""):
|
|
conn = get_conn()
|
|
if not conn:
|
|
return None
|
|
try:
|
|
cur = conn.cursor()
|
|
cur.execute("""
|
|
INSERT INTO scenario_definitions (client_id, stand, name, steps, updated_by)
|
|
VALUES (%s, %s, %s, %s, %s)
|
|
ON CONFLICT (client_id, stand, LOWER(name))
|
|
DO UPDATE SET steps = EXCLUDED.steps, version = scenario_definitions.version + 1,
|
|
is_active = TRUE, updated_at = NOW(), updated_by = EXCLUDED.updated_by
|
|
RETURNING id, version
|
|
""", (client_id, stand, name, json.dumps(steps), updated_by))
|
|
row = cur.fetchone()
|
|
conn.commit()
|
|
cur.close()
|
|
return {"id": row[0], "version": row[1]}
|
|
except Exception as e:
|
|
print(f"[DEFS] create error: {e}", flush=True)
|
|
conn.rollback()
|
|
return None
|
|
finally:
|
|
put_conn(conn)
|
|
|
|
|
|
def update_definition(def_id, client_id, stand, name, steps, version, updated_by=""):
|
|
"""Возвращает dict {ok, version} или {conflict: True}."""
|
|
conn = get_conn()
|
|
if not conn:
|
|
return {"error": "no db"}
|
|
try:
|
|
cur = conn.cursor()
|
|
cur.execute("""
|
|
UPDATE scenario_definitions
|
|
SET name = %s, steps = %s, version = version + 1,
|
|
updated_at = NOW(), updated_by = %s
|
|
WHERE id = %s AND client_id = %s AND stand = %s AND version = %s
|
|
RETURNING version
|
|
""", (name, json.dumps(steps), updated_by, def_id, client_id, stand, version))
|
|
row = cur.fetchone()
|
|
conn.commit()
|
|
cur.close()
|
|
if row:
|
|
return {"ok": True, "version": row[0]}
|
|
return {"conflict": True}
|
|
except Exception as e:
|
|
print(f"[DEFS] update error: {e}", flush=True)
|
|
conn.rollback()
|
|
return {"error": str(e)}
|
|
finally:
|
|
put_conn(conn)
|
|
|
|
|
|
def delete_definition(def_id, client_id, stand):
|
|
"""Мягкое удаление."""
|
|
conn = get_conn()
|
|
if not conn:
|
|
return False
|
|
try:
|
|
cur = conn.cursor()
|
|
cur.execute("""
|
|
UPDATE scenario_definitions
|
|
SET is_active = FALSE, updated_at = NOW()
|
|
WHERE id = %s AND client_id = %s AND stand = %s
|
|
""", (def_id, client_id, stand))
|
|
ok = cur.rowcount > 0
|
|
conn.commit()
|
|
cur.close()
|
|
return ok
|
|
except Exception as e:
|
|
print(f"[DEFS] delete error: {e}", flush=True)
|
|
conn.rollback()
|
|
return False
|
|
finally:
|
|
put_conn(conn)
|
|
|
|
|
|
def lock_check(client_id, stand):
|
|
"""Проверить нет ли активного RUNNING запуска. True = можно запускать."""
|
|
conn = get_conn()
|
|
if not conn:
|
|
return True # без БД — разрешаем
|
|
try:
|
|
cur = conn.cursor()
|
|
cur.execute("""
|
|
SELECT id FROM scenario_runs
|
|
WHERE client_id = %s AND stand = %s AND status = 'RUNNING'
|
|
LIMIT 1
|
|
""", (client_id, stand))
|
|
row = cur.fetchone()
|
|
cur.close()
|
|
return row is None
|
|
except Exception as e:
|
|
print(f"[DEFS] lock_check error: {e}", flush=True)
|
|
return True
|
|
finally:
|
|
put_conn(conn)
|