67 lines
2.9 KiB
Python
67 lines
2.9 KiB
Python
"""SQLAlchemy storage for position-roundtable role configuration."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from datetime import datetime
|
|
from typing import Any
|
|
|
|
from sqlalchemy import delete, select
|
|
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker
|
|
|
|
from deerflow.persistence.position_roles.base import PositionRoleStore
|
|
from deerflow.persistence.position_roles.model import PositionRoleRow
|
|
|
|
|
|
def _to_dict(row: PositionRoleRow) -> dict[str, Any]:
|
|
data = row.to_dict()
|
|
if isinstance(data.get("updated_at"), datetime):
|
|
data["updated_at"] = data["updated_at"].isoformat()
|
|
return data
|
|
|
|
|
|
def _row_from(data: dict[str, Any], updated_by: str | None = None) -> PositionRoleRow:
|
|
return PositionRoleRow(
|
|
id=str(data["id"]),
|
|
name=str(data.get("name") or ""),
|
|
description=str(data.get("description") or ""),
|
|
role_type=str(data.get("role_type") or "standard"),
|
|
enabled=bool(data.get("enabled", True)),
|
|
is_builtin=bool(data.get("is_builtin", False)),
|
|
sort_order=int(data.get("sort_order", 0)),
|
|
updated_by=updated_by,
|
|
)
|
|
|
|
|
|
class PositionRoleRepository(PositionRoleStore):
|
|
def __init__(self, session_factory: async_sessionmaker[AsyncSession]) -> None:
|
|
self._sf = session_factory
|
|
|
|
async def list_roles(self) -> list[dict[str, Any]]:
|
|
statement = select(PositionRoleRow).order_by(PositionRoleRow.sort_order.asc(), PositionRoleRow.id.asc())
|
|
async with self._sf() as session:
|
|
rows = (await session.execute(statement)).scalars()
|
|
return [_to_dict(row) for row in rows]
|
|
|
|
async def replace_all(self, rows: list[dict[str, Any]], *, updated_by: str | None = None) -> list[dict[str, Any]]:
|
|
# Keep the final occurrence of duplicate ids so malformed UI payloads
|
|
# cannot violate the primary-key constraint.
|
|
by_id = {str(row["id"]): row for row in rows if row.get("id")}
|
|
async with self._sf() as session:
|
|
await session.execute(delete(PositionRoleRow))
|
|
created = [_row_from(row, updated_by) for row in by_id.values()]
|
|
session.add_all(created)
|
|
await session.commit()
|
|
for row in created:
|
|
await session.refresh(row)
|
|
return [_to_dict(row) for row in sorted(created, key=lambda item: (item.sort_order, item.id))]
|
|
|
|
async def ensure_seed(self, rows: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
|
"""Idempotently insert new built-ins without changing operator edits."""
|
|
async with self._sf() as session:
|
|
existing_ids = set((await session.execute(select(PositionRoleRow.id))).scalars())
|
|
inserts = [_row_from(row) for row in rows if row.get("id") and str(row["id"]) not in existing_ids]
|
|
if inserts:
|
|
session.add_all(inserts)
|
|
await session.commit()
|
|
return await self.list_roles()
|