"""Shared CRUD skeleton for id-keyed entity repositories.""" from typing import Generic, Optional, TypeVar from sqlmodel import Session, select from agenteval.storage.db import get_session M = TypeVar("M") # domain model DB = TypeVar("DB") # persisted table row class BaseRepository(Generic[M, DB]): """Shared CRUD skeleton for id-keyed entity repositories. Subclasses declare the table (``_table``) and the ``list_all`` ordering column name (``_order_by``, newest-first), and implement the ``_to_db`` / ``_from_db`` converter pair. The converters are instance methods so a subclass whose ``_from_db`` needs cross-table reads (e.g. Scenario's model bindings) can reach ``self.session``. Entities with bespoke create/update (binding validation, versioning) override just those methods. """ _table: type _order_by: str def __init__(self, session: Optional[Session] = None): self.session = session or get_session() def _to_db(self, obj: M) -> DB: raise NotImplementedError def _from_db(self, db: DB) -> M: raise NotImplementedError def list_all(self) -> list[M]: column = getattr(self._table, self._order_by) statement = select(self._table).order_by(column.desc()) return [self._from_db(r) for r in self.session.exec(statement).all()] def get(self, entity_id: str) -> Optional[M]: db = self.session.get(self._table, entity_id) return self._from_db(db) if db else None def create(self, obj: M) -> M: db = self._to_db(obj) self.session.add(db) self.session.commit() self.session.refresh(db) return self._from_db(db) def delete(self, entity_id: str) -> bool: db = self.session.get(self._table, entity_id) if not db: return False self.session.delete(db) self.session.commit() return True