feat(attachments): provenance payload attachments + download route
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -9,7 +9,14 @@ mutates and never imports tag/ML modules.
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from ..models import Artist, ImageProvenance, ImageRecord, Post, Source
|
||||
from ..models import (
|
||||
Artist,
|
||||
ImageProvenance,
|
||||
ImageRecord,
|
||||
Post,
|
||||
PostAttachment,
|
||||
Source,
|
||||
)
|
||||
from ..utils.html_sanitize import sanitize_post_html
|
||||
|
||||
|
||||
@@ -33,10 +40,32 @@ def _artist_dict(a: Artist) -> dict:
|
||||
return {"id": a.id, "name": a.name, "slug": a.slug}
|
||||
|
||||
|
||||
def _attachment_dict(a: PostAttachment) -> dict:
|
||||
return {
|
||||
"id": a.id,
|
||||
"original_filename": a.original_filename,
|
||||
"size_bytes": a.size_bytes,
|
||||
"ext": a.ext,
|
||||
"download_url": f"/api/attachments/{a.id}/download",
|
||||
}
|
||||
|
||||
|
||||
class ProvenanceService:
|
||||
def __init__(self, session: AsyncSession):
|
||||
self.session = session
|
||||
|
||||
async def _attachments_for_posts(self, post_ids: list[int]) -> list[dict]:
|
||||
if not post_ids:
|
||||
return []
|
||||
rows = (
|
||||
await self.session.execute(
|
||||
select(PostAttachment)
|
||||
.where(PostAttachment.post_id.in_(post_ids))
|
||||
.order_by(PostAttachment.id.asc())
|
||||
)
|
||||
).scalars().all()
|
||||
return [_attachment_dict(a) for a in rows]
|
||||
|
||||
async def for_image(self, image_id: int) -> dict | None:
|
||||
rec = await self.session.get(ImageRecord, image_id)
|
||||
if rec is None:
|
||||
@@ -51,6 +80,8 @@ class ProvenanceService:
|
||||
ImageProvenance.id.asc())
|
||||
)
|
||||
rows = (await self.session.execute(stmt)).all()
|
||||
post_ids = [ip.post_id for ip, _p, _s, _a in rows]
|
||||
attachments = await self._attachments_for_posts(post_ids)
|
||||
return {
|
||||
"image_id": image_id,
|
||||
"provenance": [
|
||||
@@ -64,6 +95,7 @@ class ProvenanceService:
|
||||
}
|
||||
for ip, post, src, art in rows
|
||||
],
|
||||
"attachments": attachments,
|
||||
}
|
||||
|
||||
async def for_post(self, post_id: int) -> dict | None:
|
||||
@@ -81,4 +113,5 @@ class ProvenanceService:
|
||||
"post": _post_dict(post),
|
||||
"source": _source_dict(src),
|
||||
"artist": _artist_dict(art),
|
||||
"attachments": await self._attachments_for_posts([post.id]),
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user