"""Document service: retrieval and version management.""" from __future__ import annotations import uuid from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.orm import selectinload from app.models.document import Document, DocumentItem, ItemVersion async def get_document_with_items(slug: str, db: AsyncSession) -> Document | None: """Load a document with all its items and their versions, eagerly. Parameters ---------- slug: Unique slug of the document. db: Async database session. Returns ------- Document | None The document with items and versions loaded, or None if not found. """ result = await db.execute( select(Document) .options( selectinload(Document.items).selectinload(DocumentItem.versions) ) .where(Document.slug == slug) ) return result.scalar_one_or_none() async def apply_version( item_id: uuid.UUID, version_id: uuid.UUID, db: AsyncSession, ) -> DocumentItem: """Apply an accepted version to a document item. This replaces the item's current_text with the version's proposed_text and marks the version as 'accepted'. Parameters ---------- item_id: UUID of the DocumentItem to update. version_id: UUID of the ItemVersion to apply. db: Async database session. Returns ------- DocumentItem The updated document item. Raises ------ ValueError If the item or version is not found, or the version does not belong to the item. """ # Load item item_result = await db.execute( select(DocumentItem).where(DocumentItem.id == item_id) ) item = item_result.scalar_one_or_none() if item is None: raise ValueError(f"Element de document introuvable : {item_id}") # Load version version_result = await db.execute( select(ItemVersion).where(ItemVersion.id == version_id) ) version = version_result.scalar_one_or_none() if version is None: raise ValueError(f"Version introuvable : {version_id}") if version.item_id != item.id: raise ValueError( f"La version {version_id} n'appartient pas a l'element {item_id}" ) # Apply the version item.current_text = version.proposed_text version.status = "accepted" # Mark all other pending/voting versions for this item as rejected other_versions_result = await db.execute( select(ItemVersion).where( ItemVersion.item_id == item_id, ItemVersion.id != version_id, ItemVersion.status.in_(["proposed", "voting"]), ) ) for other_version in other_versions_result.scalars(): other_version.status = "rejected" await db.commit() await db.refresh(item) return item