Fix movie/series API responses never surfacing company logo_path
CI / lint (push) Canceled after 0s
CI / typecheck (push) Canceled after 0s
CI / unit-tests (push) Canceled after 0s
CI / integration-tests (push) Canceled after 0s
CI / container-build (push) Canceled after 0s
CI / dependency-audit (push) Canceled after 0s
CI / lint (push) Canceled after 0s
CI / typecheck (push) Canceled after 0s
CI / unit-tests (push) Canceled after 0s
CI / integration-tests (push) Canceled after 0s
CI / container-build (push) Canceled after 0s
CI / dependency-audit (push) Canceled after 0s
movie_service.get_movie_detail() hardcoded companies[].logo_path = None instead of looking up the company's logo artwork, and tv_service's networks[]/companies[] never set logo_path at all -- both just built CompanyBriefOut without the artwork lookup that company_service.py (and _season_brief for posters) already does correctly. Ingestion and storage were working the whole time; the API layer just never read the result. Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
2138d4afc3
commit
73488a0d80
@@ -47,6 +47,18 @@ async def get_movie_detail(session: AsyncSession, movie_aurora_id: str) -> Movie
|
||||
|
||||
cast, crew = split_credits(credits_)
|
||||
|
||||
company_out: list[CompanyBriefOut] = []
|
||||
for company, _role in companies:
|
||||
logos = await polymorphic.get_artwork(session, "company", company.id, ArtworkType.LOGO)
|
||||
company_out.append(
|
||||
CompanyBriefOut(
|
||||
id=aurora_id("company", str(company.id)),
|
||||
name=company.name,
|
||||
company_type=company.company_type.value,
|
||||
logo_path=primary_artwork_path(logos, ArtworkType.LOGO),
|
||||
)
|
||||
)
|
||||
|
||||
return MovieDetailOut(
|
||||
id=aurora_id("movie", str(movie.id)),
|
||||
title=movie.title,
|
||||
@@ -67,15 +79,7 @@ async def get_movie_detail(session: AsyncSession, movie_aurora_id: str) -> Movie
|
||||
keywords=[KeywordOut(id=aurora_id("keyword", str(k.id)), name=k.name) for k in keywords],
|
||||
cast=cast,
|
||||
crew=crew,
|
||||
companies=[
|
||||
CompanyBriefOut(
|
||||
id=aurora_id("company", str(company.id)),
|
||||
name=company.name,
|
||||
company_type=company.company_type.value,
|
||||
logo_path=None,
|
||||
)
|
||||
for company, _role in companies
|
||||
],
|
||||
companies=company_out,
|
||||
collection=(
|
||||
CollectionBriefOut(
|
||||
id=aurora_id("collection", str(movie.collection.id)), name=movie.collection.name
|
||||
|
||||
+14
-12
@@ -53,6 +53,16 @@ async def _season_brief(session: AsyncSession, season) -> SeasonBriefOut:
|
||||
)
|
||||
|
||||
|
||||
async def _company_brief(session: AsyncSession, company) -> CompanyBriefOut:
|
||||
logos = await polymorphic.get_artwork(session, "company", company.id, ArtworkType.LOGO)
|
||||
return CompanyBriefOut(
|
||||
id=aurora_id("company", str(company.id)),
|
||||
name=company.name,
|
||||
company_type=company.company_type.value,
|
||||
logo_path=primary_artwork_path(logos, ArtworkType.LOGO),
|
||||
)
|
||||
|
||||
|
||||
async def get_series_detail(session: AsyncSession, series_aurora_id: str) -> SeriesDetailOut:
|
||||
series_id = _resolve_id(series_aurora_id, "series", SeriesNotFoundError)
|
||||
series = await tv_repository.get_series_by_id(session, series_id)
|
||||
@@ -75,6 +85,8 @@ async def get_series_detail(session: AsyncSession, series_aurora_id: str) -> Ser
|
||||
_, creators = split_credits([c for c in credits_ if c.department == "Creator"])
|
||||
|
||||
season_briefs = [await _season_brief(session, s) for s in seasons]
|
||||
network_briefs = [await _company_brief(session, c) for c, _role in networks]
|
||||
company_briefs = [await _company_brief(session, c) for c, _role in companies]
|
||||
|
||||
return SeriesDetailOut(
|
||||
id=aurora_id("series", str(series.id)),
|
||||
@@ -96,18 +108,8 @@ async def get_series_detail(session: AsyncSession, series_aurora_id: str) -> Ser
|
||||
creators=creators,
|
||||
cast=cast,
|
||||
crew=crew,
|
||||
networks=[
|
||||
CompanyBriefOut(
|
||||
id=aurora_id("company", str(c.id)), name=c.name, company_type=c.company_type.value
|
||||
)
|
||||
for c, _role in networks
|
||||
],
|
||||
companies=[
|
||||
CompanyBriefOut(
|
||||
id=aurora_id("company", str(c.id)), name=c.name, company_type=c.company_type.value
|
||||
)
|
||||
for c, _role in companies
|
||||
],
|
||||
networks=network_briefs,
|
||||
companies=company_briefs,
|
||||
seasons=season_briefs,
|
||||
images=to_images_out(artwork),
|
||||
videos=[to_video_out(v) for v in videos],
|
||||
|
||||
@@ -11,6 +11,7 @@ import pytest
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.core.ids import aurora_id
|
||||
from app.models.artwork import Artwork
|
||||
from app.models.company import Company
|
||||
from app.models.credit import Credit
|
||||
@@ -18,6 +19,7 @@ from app.models.genre import Genre
|
||||
from app.models.movie import Movie
|
||||
from app.models.provider import ProviderRecord
|
||||
from app.providers.base import MetadataProvider
|
||||
from app.services import movie_service
|
||||
from app.services.ingestion.movie_ingestion import ingest_movie
|
||||
from tests.unit.test_normalizer import FAKE_MOVIE
|
||||
|
||||
@@ -128,3 +130,21 @@ async def test_ingest_movie_ingests_company_logo(db_session: AsyncSession):
|
||||
assert logo.artwork_type.value == "logo"
|
||||
assert logo.is_primary is True
|
||||
assert logo.original_url.endswith("/village-roadshow-logo.png")
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_movie_detail_surfaces_company_logo_path(db_session: AsyncSession):
|
||||
"""The ingested logo must actually reach the API response -- movie_service
|
||||
used to hardcode companies[].logo_path = None instead of looking up the
|
||||
company's artwork, so ingestion could succeed while the API still showed
|
||||
logo_path: None for every company."""
|
||||
|
||||
provider = FakeTMDbProvider(FAKE_MOVIE)
|
||||
movie_id = await ingest_movie(db_session, provider, "603")
|
||||
await db_session.commit()
|
||||
|
||||
detail = await movie_service.get_movie_detail(db_session, aurora_id("movie", str(movie_id)))
|
||||
|
||||
roadshow = next(c for c in detail.companies if c.name == "Village Roadshow Pictures")
|
||||
assert roadshow.logo_path is not None
|
||||
assert roadshow.logo_path.endswith("/village-roadshow-logo.png")
|
||||
|
||||
@@ -11,8 +11,10 @@ import pytest
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.core.ids import aurora_id
|
||||
from app.models.tv import Episode, Season, Series
|
||||
from app.providers.base import MetadataProvider, ProviderNotFoundError
|
||||
from app.services import tv_service
|
||||
from app.services.ingestion.series_ingestion import ingest_season, ingest_series
|
||||
from tests.unit.test_normalizer import FAKE_SEASON, FAKE_SERIES
|
||||
|
||||
@@ -115,3 +117,23 @@ async def test_ingest_season_is_idempotent(db_session: AsyncSession):
|
||||
)
|
||||
# Re-running must not duplicate episode rows.
|
||||
assert len(episodes) == 2
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_series_detail_surfaces_network_logo_path(db_session: AsyncSession):
|
||||
"""Same class of bug as movie companies: tv_service used to build
|
||||
networks[]/companies[] without ever looking up their logo artwork, so
|
||||
logo_path was always None regardless of what ingestion had stored."""
|
||||
|
||||
provider = FakeTMDbSeriesProvider(FAKE_SERIES, FAKE_SEASON)
|
||||
series_id = await ingest_series(db_session, provider, "1396")
|
||||
await db_session.commit()
|
||||
|
||||
detail = await tv_service.get_series_detail(db_session, aurora_id("series", str(series_id)))
|
||||
|
||||
amc = next(n for n in detail.networks if n.name == "AMC")
|
||||
assert amc.logo_path is not None
|
||||
assert amc.logo_path.endswith("/amc-logo.png")
|
||||
|
||||
gran_via = next(c for c in detail.companies if c.name == "Gran Via Productions")
|
||||
assert gran_via.logo_path is None
|
||||
|
||||
Reference in New Issue
Block a user