Initial commit
This commit is contained in:
11
database/db.py
Normal file
11
database/db.py
Normal file
@@ -0,0 +1,11 @@
|
||||
import sqlite3
|
||||
|
||||
from config import PROJECT_ROOT
|
||||
|
||||
DB_PATH = PROJECT_ROOT / "database" / "data_base.db"
|
||||
|
||||
|
||||
def get_connection():
|
||||
conn = sqlite3.connect(DB_PATH)
|
||||
conn.execute("PRAGMA foreign_keys = ON")
|
||||
return conn
|
||||
293
database/init_db.py
Normal file
293
database/init_db.py
Normal file
@@ -0,0 +1,293 @@
|
||||
from database.db import get_connection
|
||||
|
||||
|
||||
def _table_sql(cur, table_name: str) -> str:
|
||||
cur.execute(
|
||||
"""
|
||||
SELECT sql
|
||||
FROM sqlite_master
|
||||
WHERE type = 'table' AND name = ?
|
||||
""",
|
||||
(table_name,),
|
||||
)
|
||||
row = cur.fetchone()
|
||||
return row[0] if row and row[0] else ""
|
||||
|
||||
|
||||
def _rebuild_table(cur, table_name: str, create_sql: str, copy_sql: str) -> None:
|
||||
temp_name = f"{table_name}_new"
|
||||
cur.execute(f"DROP TABLE IF EXISTS {temp_name}")
|
||||
cur.execute(create_sql.format(table=temp_name))
|
||||
cur.execute(copy_sql.format(table=temp_name))
|
||||
cur.execute(f"DROP TABLE {table_name}")
|
||||
cur.execute(f"ALTER TABLE {temp_name} RENAME TO {table_name}")
|
||||
|
||||
|
||||
def init_db():
|
||||
with get_connection() as conn:
|
||||
cur = conn.cursor()
|
||||
|
||||
# ─── PINPAD ERRORS ───────────────────────
|
||||
cur.execute("""
|
||||
CREATE TABLE IF NOT EXISTS pinpad_errors (
|
||||
code INTEGER PRIMARY KEY,
|
||||
reason TEXT NOT NULL,
|
||||
action TEXT NOT NULL
|
||||
);
|
||||
""")
|
||||
|
||||
# ─── TERMINAL INSTRUCTIONS ───────────────
|
||||
cur.execute("""
|
||||
CREATE TABLE IF NOT EXISTS terminal_instructions (
|
||||
id INTEGER PRIMARY KEY,
|
||||
title TEXT NOT NULL
|
||||
);
|
||||
""")
|
||||
|
||||
cur.execute("""
|
||||
CREATE TABLE IF NOT EXISTS terminal_instruction_keys (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
instruction_id INTEGER NOT NULL,
|
||||
key TEXT NOT NULL,
|
||||
key_type TEXT CHECK(key_type IN ('code','text')) NOT NULL,
|
||||
FOREIGN KEY (instruction_id)
|
||||
REFERENCES terminal_instructions(id)
|
||||
ON DELETE CASCADE
|
||||
);
|
||||
""")
|
||||
|
||||
cur.execute("""
|
||||
CREATE TABLE IF NOT EXISTS terminal_instruction_steps (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
instruction_id INTEGER NOT NULL,
|
||||
step_order INTEGER NOT NULL,
|
||||
type TEXT CHECK(type IN ('text','image','pause','goto')) NOT NULL,
|
||||
content TEXT NOT NULL,
|
||||
FOREIGN KEY (instruction_id)
|
||||
REFERENCES terminal_instructions(id)
|
||||
ON DELETE CASCADE
|
||||
);
|
||||
""")
|
||||
|
||||
# Tech problems
|
||||
cur.execute("""
|
||||
CREATE TABLE IF NOT EXISTS tech_problems (
|
||||
id TEXT PRIMARY KEY,
|
||||
task_type TEXT CHECK(task_type IN ('ADMIN','TECH')) NOT NULL,
|
||||
keywords TEXT NOT NULL
|
||||
);
|
||||
""")
|
||||
|
||||
cur.execute("""
|
||||
CREATE TABLE IF NOT EXISTS tech_problem_solutions (
|
||||
problem_id TEXT PRIMARY KEY,
|
||||
problem_name TEXT NOT NULL,
|
||||
task_type TEXT CHECK(task_type IN ('ADMIN','TECH')) NOT NULL,
|
||||
can_fix_self TEXT CHECK(can_fix_self IN ('YES','NO')) NOT NULL,
|
||||
need_result_feedback TEXT CHECK(need_result_feedback IN ('YES','NO')) NOT NULL,
|
||||
solution_steps TEXT NOT NULL,
|
||||
tools_needed TEXT NOT NULL,
|
||||
when_stop_and_report TEXT NOT NULL,
|
||||
FOREIGN KEY (problem_id)
|
||||
REFERENCES tech_problems(id)
|
||||
ON DELETE CASCADE
|
||||
);
|
||||
""")
|
||||
|
||||
cur.execute("""
|
||||
CREATE TABLE IF NOT EXISTS tech_problem_progress (
|
||||
user_id INTEGER PRIMARY KEY,
|
||||
problem_id TEXT NOT NULL,
|
||||
task_type TEXT CHECK(task_type IN ('ADMIN','TECH')) NOT NULL,
|
||||
need_result_feedback TEXT CHECK(need_result_feedback IN ('YES','NO')) NOT NULL,
|
||||
FOREIGN KEY (problem_id)
|
||||
REFERENCES tech_problems(id)
|
||||
ON DELETE CASCADE
|
||||
);
|
||||
""")
|
||||
|
||||
# Stores per-user pause progress for terminal instructions
|
||||
cur.execute("""
|
||||
CREATE TABLE IF NOT EXISTS instruction_progress (
|
||||
user_id INTEGER PRIMARY KEY,
|
||||
instruction_id INTEGER NOT NULL,
|
||||
next_step INTEGER NOT NULL,
|
||||
pause_at_end INTEGER NOT NULL DEFAULT 0,
|
||||
FOREIGN KEY (instruction_id)
|
||||
REFERENCES terminal_instructions(id)
|
||||
ON DELETE CASCADE
|
||||
);
|
||||
""")
|
||||
|
||||
# Chat logs
|
||||
cur.execute("""
|
||||
CREATE TABLE IF NOT EXISTS message_log (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id INTEGER NOT NULL,
|
||||
created_at TEXT NOT NULL,
|
||||
message TEXT NOT NULL
|
||||
);
|
||||
""")
|
||||
|
||||
cur.execute("""
|
||||
CREATE TABLE IF NOT EXISTS event_log (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id INTEGER NOT NULL,
|
||||
created_at TEXT NOT NULL,
|
||||
event_type TEXT NOT NULL,
|
||||
event_value TEXT NOT NULL
|
||||
);
|
||||
""")
|
||||
|
||||
# Migrate terminal_instruction_steps if it doesn't allow 'pause' or 'goto'
|
||||
terminal_steps_sql = _table_sql(cur, "terminal_instruction_steps")
|
||||
if terminal_steps_sql and ("pause" not in terminal_steps_sql or "goto" not in terminal_steps_sql):
|
||||
cur.execute("""
|
||||
CREATE TABLE terminal_instruction_steps_new (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
instruction_id INTEGER NOT NULL,
|
||||
step_order INTEGER NOT NULL,
|
||||
type TEXT CHECK(type IN ('text','image','pause','goto')) NOT NULL,
|
||||
content TEXT NOT NULL,
|
||||
FOREIGN KEY (instruction_id)
|
||||
REFERENCES terminal_instructions(id)
|
||||
ON DELETE CASCADE
|
||||
);
|
||||
""")
|
||||
cur.execute("""
|
||||
INSERT INTO terminal_instruction_steps_new
|
||||
(id, instruction_id, step_order, type, content)
|
||||
SELECT id, instruction_id, step_order, type, content
|
||||
FROM terminal_instruction_steps
|
||||
""")
|
||||
cur.execute("DROP TABLE terminal_instruction_steps")
|
||||
cur.execute("ALTER TABLE terminal_instruction_steps_new RENAME TO terminal_instruction_steps")
|
||||
|
||||
if "FOREIGN KEY" not in _table_sql(cur, "terminal_instruction_keys").upper():
|
||||
_rebuild_table(
|
||||
cur,
|
||||
"terminal_instruction_keys",
|
||||
"""
|
||||
CREATE TABLE {table} (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
instruction_id INTEGER NOT NULL,
|
||||
key TEXT NOT NULL,
|
||||
key_type TEXT CHECK(key_type IN ('code','text')) NOT NULL,
|
||||
FOREIGN KEY (instruction_id)
|
||||
REFERENCES terminal_instructions(id)
|
||||
ON DELETE CASCADE
|
||||
);
|
||||
""",
|
||||
"""
|
||||
INSERT INTO {table} (id, instruction_id, key, key_type)
|
||||
SELECT k.id, k.instruction_id, k.key, k.key_type
|
||||
FROM terminal_instruction_keys k
|
||||
JOIN terminal_instructions i ON i.id = k.instruction_id
|
||||
""",
|
||||
)
|
||||
|
||||
if "FOREIGN KEY" not in _table_sql(cur, "terminal_instruction_steps").upper():
|
||||
_rebuild_table(
|
||||
cur,
|
||||
"terminal_instruction_steps",
|
||||
"""
|
||||
CREATE TABLE {table} (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
instruction_id INTEGER NOT NULL,
|
||||
step_order INTEGER NOT NULL,
|
||||
type TEXT CHECK(type IN ('text','image','pause','goto')) NOT NULL,
|
||||
content TEXT NOT NULL,
|
||||
FOREIGN KEY (instruction_id)
|
||||
REFERENCES terminal_instructions(id)
|
||||
ON DELETE CASCADE
|
||||
);
|
||||
""",
|
||||
"""
|
||||
INSERT INTO {table} (id, instruction_id, step_order, type, content)
|
||||
SELECT s.id, s.instruction_id, s.step_order, s.type, s.content
|
||||
FROM terminal_instruction_steps s
|
||||
JOIN terminal_instructions i ON i.id = s.instruction_id
|
||||
""",
|
||||
)
|
||||
|
||||
if "FOREIGN KEY" not in _table_sql(cur, "tech_problem_solutions").upper():
|
||||
_rebuild_table(
|
||||
cur,
|
||||
"tech_problem_solutions",
|
||||
"""
|
||||
CREATE TABLE {table} (
|
||||
problem_id TEXT PRIMARY KEY,
|
||||
problem_name TEXT NOT NULL,
|
||||
task_type TEXT CHECK(task_type IN ('ADMIN','TECH')) NOT NULL,
|
||||
can_fix_self TEXT CHECK(can_fix_self IN ('YES','NO')) NOT NULL,
|
||||
need_result_feedback TEXT CHECK(need_result_feedback IN ('YES','NO')) NOT NULL,
|
||||
solution_steps TEXT NOT NULL,
|
||||
tools_needed TEXT NOT NULL,
|
||||
when_stop_and_report TEXT NOT NULL,
|
||||
FOREIGN KEY (problem_id)
|
||||
REFERENCES tech_problems(id)
|
||||
ON DELETE CASCADE
|
||||
);
|
||||
""",
|
||||
"""
|
||||
INSERT INTO {table}
|
||||
(problem_id, problem_name, task_type, can_fix_self,
|
||||
need_result_feedback, solution_steps, tools_needed,
|
||||
when_stop_and_report)
|
||||
SELECT s.problem_id, s.problem_name, s.task_type, s.can_fix_self,
|
||||
s.need_result_feedback, s.solution_steps, s.tools_needed,
|
||||
s.when_stop_and_report
|
||||
FROM tech_problem_solutions s
|
||||
JOIN tech_problems p ON p.id = s.problem_id
|
||||
""",
|
||||
)
|
||||
|
||||
if "FOREIGN KEY" not in _table_sql(cur, "tech_problem_progress").upper():
|
||||
_rebuild_table(
|
||||
cur,
|
||||
"tech_problem_progress",
|
||||
"""
|
||||
CREATE TABLE {table} (
|
||||
user_id INTEGER PRIMARY KEY,
|
||||
problem_id TEXT NOT NULL,
|
||||
task_type TEXT CHECK(task_type IN ('ADMIN','TECH')) NOT NULL,
|
||||
need_result_feedback TEXT CHECK(need_result_feedback IN ('YES','NO')) NOT NULL,
|
||||
FOREIGN KEY (problem_id)
|
||||
REFERENCES tech_problems(id)
|
||||
ON DELETE CASCADE
|
||||
);
|
||||
""",
|
||||
"""
|
||||
INSERT INTO {table} (user_id, problem_id, task_type, need_result_feedback)
|
||||
SELECT p.user_id, p.problem_id, p.task_type, p.need_result_feedback
|
||||
FROM tech_problem_progress p
|
||||
JOIN tech_problems t ON t.id = p.problem_id
|
||||
""",
|
||||
)
|
||||
|
||||
if "FOREIGN KEY" not in _table_sql(cur, "instruction_progress").upper():
|
||||
_rebuild_table(
|
||||
cur,
|
||||
"instruction_progress",
|
||||
"""
|
||||
CREATE TABLE {table} (
|
||||
user_id INTEGER PRIMARY KEY,
|
||||
instruction_id INTEGER NOT NULL,
|
||||
next_step INTEGER NOT NULL,
|
||||
pause_at_end INTEGER NOT NULL DEFAULT 0,
|
||||
FOREIGN KEY (instruction_id)
|
||||
REFERENCES terminal_instructions(id)
|
||||
ON DELETE CASCADE
|
||||
);
|
||||
""",
|
||||
"""
|
||||
INSERT INTO {table} (user_id, instruction_id, next_step, pause_at_end)
|
||||
SELECT p.user_id, p.instruction_id, p.next_step, p.pause_at_end
|
||||
FROM instruction_progress p
|
||||
JOIN terminal_instructions i ON i.id = p.instruction_id
|
||||
""",
|
||||
)
|
||||
|
||||
conn.commit()
|
||||
|
||||
print("✅ Database initialized")
|
||||
14
database/pinpad.py
Normal file
14
database/pinpad.py
Normal file
@@ -0,0 +1,14 @@
|
||||
from database.db import get_connection
|
||||
|
||||
|
||||
def get_pinpad_error(code: str):
|
||||
"""
|
||||
Возвращает (reason, action) или None
|
||||
"""
|
||||
with get_connection() as conn:
|
||||
cur = conn.cursor()
|
||||
cur.execute(
|
||||
"SELECT reason, action FROM pinpad_errors WHERE code = ?",
|
||||
(code,)
|
||||
)
|
||||
return cur.fetchone()
|
||||
311
database/repository.py
Normal file
311
database/repository.py
Normal file
@@ -0,0 +1,311 @@
|
||||
from database.db import get_connection
|
||||
import re
|
||||
|
||||
|
||||
_STOPWORDS = {
|
||||
"и", "в", "во", "на", "с", "со", "к", "ко", "от", "до", "по",
|
||||
"за", "из", "у", "о", "об", "про", "для", "при", "без", "не",
|
||||
"нет", "ли", "же", "а", "но", "или", "то", "это", "все", "всё",
|
||||
}
|
||||
|
||||
|
||||
def _normalize_query(query: str):
|
||||
text = re.sub(r"[^\w\s]+", " ", query.lower(), flags=re.UNICODE)
|
||||
parts = [p for p in text.split() if p and p not in _STOPWORDS]
|
||||
# Keep tokens of length >= 3 to reduce noise
|
||||
return [p for p in parts if len(p) >= 3]
|
||||
|
||||
|
||||
def _normalize_title(text: str) -> str:
|
||||
text = (text or "").strip().lower()
|
||||
text = re.sub(r"\s+", " ", text)
|
||||
return text
|
||||
|
||||
|
||||
def _levenshtein(a: str, b: str) -> int:
|
||||
if a == b:
|
||||
return 0
|
||||
if not a:
|
||||
return len(b)
|
||||
if not b:
|
||||
return len(a)
|
||||
|
||||
if len(a) < len(b):
|
||||
a, b = b, a
|
||||
|
||||
prev = list(range(len(b) + 1))
|
||||
for i, ca in enumerate(a, start=1):
|
||||
cur = [i]
|
||||
for j, cb in enumerate(b, start=1):
|
||||
ins = cur[j - 1] + 1
|
||||
delete = prev[j] + 1
|
||||
sub = prev[j - 1] + (0 if ca == cb else 1)
|
||||
cur.append(min(ins, delete, sub))
|
||||
prev = cur
|
||||
return prev[-1]
|
||||
|
||||
|
||||
def _fuzzy_match(token: str, word: str) -> bool:
|
||||
if token in word:
|
||||
return True
|
||||
dist = _levenshtein(token, word)
|
||||
if len(token) <= 5:
|
||||
return dist <= 1
|
||||
if len(token) <= 8:
|
||||
return dist <= 2
|
||||
return dist <= 3
|
||||
|
||||
def find_instructions(query: str):
|
||||
query = query.lower()
|
||||
tokens = _normalize_query(query)
|
||||
|
||||
with get_connection() as conn:
|
||||
cur = conn.cursor()
|
||||
|
||||
# 1️⃣ точное совпадение по коду
|
||||
cur.execute("""
|
||||
SELECT DISTINCT i.id, i.title
|
||||
FROM terminal_instruction_keys k
|
||||
JOIN terminal_instructions i ON i.id = k.instruction_id
|
||||
WHERE k.key_type = 'code' AND k.key = ?
|
||||
""", (query,))
|
||||
rows = cur.fetchall()
|
||||
if rows:
|
||||
return rows
|
||||
|
||||
# 2️⃣ частичное совпадение по тексту
|
||||
if tokens:
|
||||
like_parts = " OR ".join(["k.key LIKE ?"] * len(tokens))
|
||||
params = [f"%{t}%" for t in tokens]
|
||||
cur.execute(
|
||||
f"""
|
||||
SELECT i.id, i.title, k.key
|
||||
FROM terminal_instruction_keys k
|
||||
JOIN terminal_instructions i ON i.id = k.instruction_id
|
||||
WHERE k.key_type = 'text' AND ({like_parts})
|
||||
""",
|
||||
params,
|
||||
)
|
||||
rows = cur.fetchall()
|
||||
if rows:
|
||||
scores = {}
|
||||
titles = {}
|
||||
for iid, title, key in rows:
|
||||
key_l = (key or "").lower()
|
||||
title_l = (title or "").lower()
|
||||
matched = set()
|
||||
for t in tokens:
|
||||
if t in key_l or t in title_l:
|
||||
matched.add(t)
|
||||
if matched:
|
||||
scores[iid] = max(scores.get(iid, 0), len(matched))
|
||||
titles[iid] = title
|
||||
if scores:
|
||||
ordered = sorted(scores.items(), key=lambda x: (-x[1], x[0]))
|
||||
return [(iid, titles[iid]) for iid, _ in ordered]
|
||||
|
||||
cur.execute("""
|
||||
SELECT DISTINCT i.id, i.title
|
||||
FROM terminal_instruction_keys k
|
||||
JOIN terminal_instructions i ON i.id = k.instruction_id
|
||||
WHERE k.key_type = 'text' AND k.key LIKE ?
|
||||
""", (f"%{query}%",))
|
||||
|
||||
rows = cur.fetchall()
|
||||
if rows:
|
||||
return rows
|
||||
|
||||
# 3️⃣ fuzzy поиск по ключевым словам (опечатки)
|
||||
if tokens:
|
||||
cur.execute("""
|
||||
SELECT i.id, i.title, k.key
|
||||
FROM terminal_instruction_keys k
|
||||
JOIN terminal_instructions i ON i.id = k.instruction_id
|
||||
WHERE k.key_type = 'text'
|
||||
""")
|
||||
all_rows = cur.fetchall()
|
||||
scores = {}
|
||||
titles = {}
|
||||
for iid, title, key in all_rows:
|
||||
key_words = [w for w in re.split(r"\s+", (key or "").lower()) if w]
|
||||
title_words = [w for w in re.split(r"\s+", (title or "").lower()) if w]
|
||||
matched = set()
|
||||
for t in tokens:
|
||||
if any(_fuzzy_match(t, w) for w in key_words) or any(_fuzzy_match(t, w) for w in title_words):
|
||||
matched.add(t)
|
||||
if matched:
|
||||
scores[iid] = max(scores.get(iid, 0), len(matched))
|
||||
titles[iid] = title
|
||||
if scores:
|
||||
ordered = sorted(scores.items(), key=lambda x: (-x[1], x[0]))
|
||||
return [(iid, titles[iid]) for iid, _ in ordered]
|
||||
|
||||
return []
|
||||
|
||||
def get_terminal_steps(instruction_id: int):
|
||||
with get_connection() as conn:
|
||||
cur = conn.cursor()
|
||||
cur.execute("""
|
||||
SELECT type, content
|
||||
FROM terminal_instruction_steps
|
||||
WHERE instruction_id = ?
|
||||
ORDER BY step_order
|
||||
""", (instruction_id,))
|
||||
return cur.fetchall()
|
||||
|
||||
|
||||
def get_instruction_id_by_title(title: str):
|
||||
normalized = _normalize_title(title)
|
||||
if not normalized:
|
||||
return None
|
||||
|
||||
with get_connection() as conn:
|
||||
cur = conn.cursor()
|
||||
cur.execute("""
|
||||
SELECT id, title
|
||||
FROM terminal_instructions
|
||||
""")
|
||||
for iid, db_title in cur.fetchall():
|
||||
if _normalize_title(db_title) == normalized:
|
||||
return iid
|
||||
return None
|
||||
|
||||
|
||||
def find_tech_problems(query: str):
|
||||
tokens = _normalize_query(query)
|
||||
if not tokens:
|
||||
return []
|
||||
|
||||
with get_connection() as conn:
|
||||
cur = conn.cursor()
|
||||
cur.execute("""
|
||||
SELECT id, task_type, keywords
|
||||
FROM tech_problems
|
||||
""")
|
||||
rows = cur.fetchall()
|
||||
|
||||
scores = {}
|
||||
types = {}
|
||||
for pid, task_type, keywords in rows:
|
||||
key_words = [w for w in re.split(r"[,\s]+", (keywords or "").lower()) if w]
|
||||
matched = set()
|
||||
for t in tokens:
|
||||
if any(_fuzzy_match(t, w) for w in key_words):
|
||||
matched.add(t)
|
||||
if matched:
|
||||
scores[pid] = max(scores.get(pid, 0), len(matched))
|
||||
types[pid] = task_type
|
||||
|
||||
if not scores:
|
||||
return []
|
||||
|
||||
ordered = sorted(scores.items(), key=lambda x: (-x[1], x[0]))
|
||||
return [(pid, types[pid]) for pid, _ in ordered]
|
||||
|
||||
|
||||
def get_tech_solution(problem_id: str):
|
||||
with get_connection() as conn:
|
||||
cur = conn.cursor()
|
||||
cur.execute("""
|
||||
SELECT problem_id, problem_name, task_type, can_fix_self,
|
||||
need_result_feedback, solution_steps, tools_needed,
|
||||
when_stop_and_report
|
||||
FROM tech_problem_solutions
|
||||
WHERE problem_id = ?
|
||||
""", (problem_id,))
|
||||
return cur.fetchone()
|
||||
|
||||
|
||||
def get_tech_problem_by_name(problem_name: str):
|
||||
normalized = _normalize_title(problem_name)
|
||||
if not normalized:
|
||||
return None
|
||||
|
||||
with get_connection() as conn:
|
||||
cur = conn.cursor()
|
||||
cur.execute("""
|
||||
SELECT problem_id, problem_name, task_type
|
||||
FROM tech_problem_solutions
|
||||
""")
|
||||
for pid, name, task_type in cur.fetchall():
|
||||
if _normalize_title(name) == normalized:
|
||||
return pid, name, task_type
|
||||
return None
|
||||
|
||||
|
||||
def find_tech_solutions_by_name_contains(fragment: str):
|
||||
frag = _normalize_title(fragment)
|
||||
if not frag:
|
||||
return []
|
||||
with get_connection() as conn:
|
||||
cur = conn.cursor()
|
||||
cur.execute(
|
||||
"""
|
||||
SELECT problem_id, problem_name, task_type
|
||||
FROM tech_problem_solutions
|
||||
WHERE lower(problem_name) LIKE ?
|
||||
""",
|
||||
(f"%{frag}%",),
|
||||
)
|
||||
return cur.fetchall()
|
||||
|
||||
|
||||
def set_tech_problem_progress(user_id: int, problem_id: str, task_type: str, need_result_feedback: str):
|
||||
with get_connection() as conn:
|
||||
cur = conn.cursor()
|
||||
cur.execute("""
|
||||
INSERT INTO tech_problem_progress (user_id, problem_id, task_type, need_result_feedback)
|
||||
VALUES (?, ?, ?, ?)
|
||||
ON CONFLICT(user_id) DO UPDATE SET
|
||||
problem_id = excluded.problem_id,
|
||||
task_type = excluded.task_type,
|
||||
need_result_feedback = excluded.need_result_feedback
|
||||
""", (user_id, problem_id, task_type, need_result_feedback))
|
||||
conn.commit()
|
||||
|
||||
|
||||
def get_tech_problem_progress(user_id: int):
|
||||
with get_connection() as conn:
|
||||
cur = conn.cursor()
|
||||
cur.execute("""
|
||||
SELECT problem_id, task_type, need_result_feedback
|
||||
FROM tech_problem_progress
|
||||
WHERE user_id = ?
|
||||
""", (user_id,))
|
||||
return cur.fetchone()
|
||||
|
||||
|
||||
def clear_tech_problem_progress(user_id: int):
|
||||
with get_connection() as conn:
|
||||
cur = conn.cursor()
|
||||
cur.execute("DELETE FROM tech_problem_progress WHERE user_id = ?", (user_id,))
|
||||
conn.commit()
|
||||
|
||||
def set_instruction_progress(user_id: int, instruction_id: int, next_step: int, pause_at_end: bool):
|
||||
with get_connection() as conn:
|
||||
cur = conn.cursor()
|
||||
cur.execute("""
|
||||
INSERT INTO instruction_progress (user_id, instruction_id, next_step, pause_at_end)
|
||||
VALUES (?, ?, ?, ?)
|
||||
ON CONFLICT(user_id) DO UPDATE SET
|
||||
instruction_id = excluded.instruction_id,
|
||||
next_step = excluded.next_step,
|
||||
pause_at_end = excluded.pause_at_end
|
||||
""", (user_id, instruction_id, next_step, 1 if pause_at_end else 0))
|
||||
conn.commit()
|
||||
|
||||
def get_instruction_progress(user_id: int):
|
||||
with get_connection() as conn:
|
||||
cur = conn.cursor()
|
||||
cur.execute("""
|
||||
SELECT instruction_id, next_step, pause_at_end
|
||||
FROM instruction_progress
|
||||
WHERE user_id = ?
|
||||
""", (user_id,))
|
||||
return cur.fetchone()
|
||||
|
||||
def clear_instruction_progress(user_id: int):
|
||||
with get_connection() as conn:
|
||||
cur = conn.cursor()
|
||||
cur.execute("DELETE FROM instruction_progress WHERE user_id = ?", (user_id,))
|
||||
conn.commit()
|
||||
Reference in New Issue
Block a user