Spaces:
Sleeping
Sleeping
| """νλ μ΄ μΈμ μμν β SQLite (κ³ν: docs/plans/persistence.md). | |
| - κΆμ: CLAUDE.md "νμ¬ μ μ μ μμ¬ μμΉ κΆμ: DB play_session.current_state_id"μ MVP ꡬν. | |
| payload(JSON)μ StoryEngine.snapshot() μ 체λ₯Ό λ΄λλ€ (story μμΉ + μ¨μ μν) | |
| - μ½ν μΈ λ²μ λΆμΌμΉ μΈμ μ loadμμ None (무ν¨ν β μ μΈμ μμ μ λ) | |
| - ν¬μΌ(μ±ν ) μ€κ° μνλ μ μ₯νμ§ μλλ€ (μ μ κ²°μ β 볡μ μ ν¬μΌ νκΈ°) | |
| """ | |
| from __future__ import annotations | |
| import json | |
| import sqlite3 | |
| from contextlib import closing | |
| from datetime import datetime, timedelta, timezone | |
| from pathlib import Path | |
| _SCHEMA = """ | |
| CREATE TABLE IF NOT EXISTS play_session ( | |
| sid TEXT PRIMARY KEY, | |
| payload TEXT NOT NULL, | |
| content_version TEXT NOT NULL, | |
| updated_at TEXT NOT NULL | |
| ) | |
| """ | |
| class PlaySessionRepository: | |
| def __init__(self, db_path: str | Path): | |
| self.db_path = Path(db_path) | |
| self.db_path.parent.mkdir(parents=True, exist_ok=True) | |
| with closing(self._conn()) as conn, conn: | |
| conn.execute(_SCHEMA) | |
| def _conn(self) -> sqlite3.Connection: | |
| conn = sqlite3.connect(self.db_path) | |
| conn.execute("PRAGMA journal_mode=WAL") | |
| return conn | |
| def save(self, sid: str, payload: dict, content_version: str) -> None: | |
| with closing(self._conn()) as conn, conn: | |
| conn.execute( | |
| "INSERT INTO play_session (sid, payload, content_version, updated_at) " | |
| "VALUES (?, ?, ?, ?) " | |
| "ON CONFLICT(sid) DO UPDATE SET payload=excluded.payload, " | |
| "content_version=excluded.content_version, updated_at=excluded.updated_at", | |
| (sid, json.dumps(payload, ensure_ascii=False), content_version, | |
| datetime.now(timezone.utc).isoformat())) | |
| def load(self, sid: str, content_version: str) -> dict | None: | |
| """μ€λ μ· λ‘λ. λ―Έμ‘΄μ¬ λλ μ½ν μΈ λ²μ λΆμΌμΉλ©΄ None.""" | |
| with closing(self._conn()) as conn: | |
| row = conn.execute( | |
| "SELECT payload, content_version FROM play_session WHERE sid=?", | |
| (sid,)).fetchone() | |
| if row is None or row[1] != content_version: | |
| return None | |
| return json.loads(row[0]) | |
| def purge(self, days: int = 30) -> int: | |
| """NμΌ λ―Έκ°±μ μΈμ μμ . μμ 건μ λ°ν.""" | |
| cutoff = (datetime.now(timezone.utc) - timedelta(days=days)).isoformat() | |
| with closing(self._conn()) as conn, conn: | |
| cur = conn.execute( | |
| "DELETE FROM play_session WHERE updated_at < ?", (cutoff,)) | |
| return cur.rowcount | |