from __future__ import annotations from datetime import datetime, UTC from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from app.models.entities import Blob, FileEntry, FileVersion class FileRepository: def __init__(self, session: AsyncSession) -> None: self.session = session async def get_by_id(self, file_id: str) -> FileEntry | None: return await self.session.get(FileEntry, file_id) async def get_by_directory_and_name(self, directory_id: str, name: str) -> FileEntry | None: result = await self.session.execute( select(FileEntry).where( FileEntry.directory_id == directory_id, FileEntry.name == name, FileEntry.is_deleted.is_(False), FileEntry.deleted_at.is_(None), ) ) return result.scalar_one_or_none() async def list_by_directory(self, directory_id: str) -> list[FileEntry]: result = await self.session.execute( select(FileEntry) .where( FileEntry.directory_id == directory_id, FileEntry.is_deleted.is_(False), FileEntry.deleted_at.is_(None), ) .order_by(FileEntry.name.asc()) ) return list(result.scalars().all()) async def list_deleted(self) -> list[FileEntry]: result = await self.session.execute( select(FileEntry) .where(FileEntry.is_deleted.is_(True), FileEntry.deleted_at.is_not(None)) .order_by(FileEntry.deleted_at.desc(), FileEntry.name.asc()) ) return list(result.scalars().all()) async def list_active(self) -> list[FileEntry]: result = await self.session.execute( select(FileEntry) .where( FileEntry.is_deleted.is_(False), FileEntry.deleted_at.is_(None), ) .order_by(FileEntry.created_at.asc()) ) return list(result.scalars().all()) async def create_file_graph( self, file_entry: FileEntry, file_version: FileVersion, blob: Blob, ) -> FileEntry: self.session.add(file_entry) self.session.add(file_version) await self.session.flush() self.session.add(blob) await self.session.flush() await self.session.refresh(file_entry) return file_entry async def get_primary_blob_for_file(self, file_id: str) -> Blob | None: result = await self.session.execute( select(Blob) .join(FileVersion, Blob.file_version_id == FileVersion.id) .join(FileEntry, FileVersion.id == FileEntry.current_version_id) .where(FileEntry.id == file_id, Blob.blob_index == 0) ) return result.scalar_one_or_none() async def get_blob_by_id(self, blob_id: str) -> Blob | None: return await self.session.get(Blob, blob_id) async def soft_delete(self, file_entry: FileEntry) -> FileEntry: file_entry.is_deleted = True file_entry.deleted_at = datetime.now(UTC) await self.session.flush() await self.session.refresh(file_entry) return file_entry async def restore(self, file_entry: FileEntry) -> FileEntry: file_entry.is_deleted = False file_entry.deleted_at = None await self.session.flush() await self.session.refresh(file_entry) return file_entry async def save(self, file_entry: FileEntry) -> FileEntry: await self.session.flush() await self.session.refresh(file_entry) return file_entry