feat(translation): expose translated fields in post feed + provenance payloads (#143 step 4)
post_feed_service (card + detail) and provenance_service._post_dict now include post_title_translated, description_translated (card-truncated / detail-uncapped) and translated_source_lang, keeping the originals for the toggle. Feed serialization test. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CDgx8bQS5YrGRK76v8HUnM
This commit is contained in:
@@ -200,6 +200,8 @@ class PostFeedService:
|
||||
atts_map = await self._attachments_for([post.id])
|
||||
item = self._to_dict(post, artist, source, thumbs_map, atts_map)
|
||||
item["description_full"] = html_to_plain(post.description)
|
||||
# Full (uncapped) translated description for the detail view (#143).
|
||||
item["description_translated_full"] = post.description_translated
|
||||
# Sanitized HTML body for faithful (semantic) rendering in the post view;
|
||||
# detail-only (the feed list stays lightweight plain text). None when the
|
||||
# post has no body. Inline `<img>` sources are remapped to locally-served
|
||||
@@ -371,6 +373,12 @@ class PostFeedService:
|
||||
description_plain, truncated = None, False
|
||||
else:
|
||||
description_plain, truncated = truncate_at_word(plain_full, DESCRIPTION_LIMIT)
|
||||
# Translation (#143): the stored translated description is already plain
|
||||
# text; truncate it the same way for the card.
|
||||
desc_trans = post.description_translated
|
||||
desc_trans_short = (
|
||||
truncate_at_word(desc_trans, DESCRIPTION_LIMIT)[0] if desc_trans else None
|
||||
)
|
||||
thumbs_entry = thumbs_map.get(post.id, {"thumbs": [], "more": 0})
|
||||
# `source` is null for filesystem-imported posts with no live
|
||||
# subscription (alembic 0030). Frontend renders that as a
|
||||
@@ -384,6 +392,11 @@ class PostFeedService:
|
||||
"downloaded_at": post.downloaded_at.isoformat(),
|
||||
"description_plain": description_plain,
|
||||
"description_truncated": truncated,
|
||||
# Translation-forward fields (#143): shown by default when present;
|
||||
# UI toggles back to the originals above. Source lang labels the toggle.
|
||||
"post_title_translated": post.post_title_translated,
|
||||
"description_translated": desc_trans_short,
|
||||
"translated_source_lang": post.translated_source_lang,
|
||||
"artist": {"id": artist.id, "name": artist.name, "slug": artist.slug},
|
||||
"source": (
|
||||
{"id": source.id, "platform": source.platform}
|
||||
|
||||
@@ -29,6 +29,12 @@ def _post_dict(p: Post) -> dict:
|
||||
"date": p.post_date.isoformat() if p.post_date else None,
|
||||
"description_html": sanitize_post_html(p.description),
|
||||
"attachment_count": p.attachment_count,
|
||||
# Translation (#143): the English title/description shown by default when
|
||||
# a translation exists; the UI toggles to the original. Source lang labels
|
||||
# the original.
|
||||
"title_translated": p.post_title_translated,
|
||||
"description_translated": p.description_translated,
|
||||
"translated_source_lang": p.translated_source_lang,
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -283,6 +283,30 @@ async def test_scroll_item_shape_minimal(db):
|
||||
assert "description_full" not in item
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_scroll_surfaces_translation_fields(db):
|
||||
# #143: a translated post exposes the translated title/description + source
|
||||
# lang, with the originals still present for the toggle.
|
||||
artist = await _seed_artist(db, "alice-tr")
|
||||
src = await _seed_source(db, artist.id, "pixiv", "https://p/alice-tr")
|
||||
p = await _seed_post(
|
||||
db, src.id, external_id="TR", post_date=datetime.now(UTC),
|
||||
title="ねこ", description="<p>かわいい</p>",
|
||||
)
|
||||
p.post_title_translated = "cat"
|
||||
p.description_translated = "cute"
|
||||
p.translated_source_lang = "ja"
|
||||
await db.commit()
|
||||
|
||||
page = await PostFeedService(db).scroll(cursor=None, limit=10)
|
||||
item = page["items"][0]
|
||||
assert item["post_title_translated"] == "cat"
|
||||
assert item["description_translated"] == "cute"
|
||||
assert item["translated_source_lang"] == "ja"
|
||||
assert item["post_title"] == "ねこ" # original kept for the toggle
|
||||
assert item["description_plain"] == "かわいい"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_scroll_description_truncates_long_text(db):
|
||||
artist = await _seed_artist(db, "alice-long")
|
||||
|
||||
Reference in New Issue
Block a user