v5.0 Introduce centralized homepage & portrait photo management
Quality Gate / gate (push) Failing after 11s

This commit is contained in:
Jim Lancaster
2026-08-23 09:11:36 -05:00
parent efe7785392
commit 86b8e83ff4
26 changed files with 835 additions and 343 deletions
+20
View File
@@ -15,6 +15,7 @@ from transcription.db.models import Job
from transcription.db.models import Person
from transcription.db.models import Source
from transcription.db.models import Tag
from transcription.db.models import Photo
from transcription.services.documents import DocumentDeleteBlockedError
from transcription.services.documents import DocumentError
from transcription.services.documents import DocumentService
@@ -275,6 +276,25 @@ async def test_delete_person_succeeds_when_unlinked(default_session_factory):
await service.read_person_detail(person.id)
@pytest.mark.asyncio
async def test_delete_person_blocks_when_photos_exist(default_session_factory):
service = PeopleService(session_factory=default_session_factory)
person = await service.create_person(Person(full_name="Photo Protected Person"))
async with service._session_scope() as session:
session.add(
Photo(
person_id=person.id,
path="photos/sample.png",
is_primary=True,
)
)
await session.commit()
with pytest.raises(PeopleError, match="Photos"):
await service.delete_person(person)
@pytest.mark.asyncio
async def test_create_document_uses_existing_document_type_registry(default_session_factory):
service = DocumentService(session_factory=default_session_factory)
+53
View File
@@ -0,0 +1,53 @@
from __future__ import annotations
import pytest
from transcription.config import Settings
from transcription.db.models import Person
from transcription.services.people import PeopleService
from transcription.services.photos import PhotosService
PNG_BYTES = bytes.fromhex(
"89504e470d0a1a0a0000000d49484452000000010000000108060000001f15c4890000000a49444154789c6360000002000100"
"05fe02fea7b1b8000000004945"
) + b"NDAE\xae\x42\x60\x82"
@pytest.mark.asyncio
async def test_create_photo_persists_media_and_primary_state(default_session_factory, tmp_path):
settings = Settings(openrouter_api_key="test-key", upload_dir=tmp_path / "uploads")
people = PeopleService(session_factory=default_session_factory)
photos = PhotosService(session_factory=default_session_factory, settings=settings)
person = await people.create_person(Person(full_name="Photo Person"))
first = await photos.create_photo(person_id=person.id, filename="one.png", file_bytes=PNG_BYTES)
second = await photos.create_photo(person_id=person.id, filename="two.png", file_bytes=PNG_BYTES)
listed = await photos.list_photos(person_id=person.id)
assert first.path.startswith("photos/")
assert (settings.upload_dir / first.path).exists()
assert first.is_primary is True
assert second.is_primary is False
assert listed[0].is_primary is True
@pytest.mark.asyncio
async def test_set_primary_and_delete_promotes_next_photo(default_session_factory, tmp_path):
settings = Settings(openrouter_api_key="test-key", upload_dir=tmp_path / "uploads")
people = PeopleService(session_factory=default_session_factory)
photos = PhotosService(session_factory=default_session_factory, settings=settings)
person = await people.create_person(Person(full_name="Primary Person"))
first = await photos.create_photo(person_id=person.id, filename="one.png", file_bytes=PNG_BYTES)
second = await photos.create_photo(person_id=person.id, filename="two.png", file_bytes=PNG_BYTES)
await photos.set_primary(photo_id=second.id)
switched = await photos.list_photos(person_id=person.id)
assert switched[0].id == second.id
assert switched[0].is_primary is True
await photos.delete_photo(photo_id=second.id)
remaining = await photos.list_photos(person_id=person.id)
assert len(remaining) == 1
assert remaining[0].id == first.id
assert remaining[0].is_primary is True
+7 -7
View File
@@ -11,7 +11,7 @@ from transcription.db.models import Job
from transcription.db.models import JobSource
from transcription.db.models import Source
from transcription.errors import ErrorCategory
from transcription.services.people import store_person_portrait
from transcription.services.photos import PhotosService
from transcription.services.sources import source_mime_type
from transcription.services.store import SourceStorageError
from transcription.services.store import StoredSourceFile
@@ -121,18 +121,18 @@ async def test_create_document_job_stores_source_under_document_id_directory(asy
@pytest.mark.asyncio
async def test_store_person_portrait_stores_file_under_person_id_directory(tmp_path):
async def test_store_person_photo_stores_file_under_shared_photos_directory(default_session_factory, tmp_path):
settings = Settings(openrouter_api_key="test-key", upload_dir=tmp_path)
person_id = uuid4()
service = PhotosService(session_factory=default_session_factory, settings=settings)
stored_path = await store_person_portrait(
person_id=person_id,
created = await service.create_photo(
person_id=None,
filename="portrait.png",
file_bytes=b"portrait-bytes",
settings=settings,
)
assert stored_path.parent == (tmp_path / "persons" / str(person_id))
stored_path = tmp_path / created.path
assert stored_path.parent == (tmp_path / "photos")
assert stored_path.exists()