2026-06-10 18:44:03 +02:00
|
|
|
"""Data access for the ``podcasts`` table.
|
|
|
|
|
|
|
|
|
|
A thin async repository so the service and tasks never write raw queries. It
|
|
|
|
|
only loads and persists rows; lifecycle rules and (de)serialization live in the
|
|
|
|
|
service.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
from sqlalchemy import select
|
|
|
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
|
|
|
|
|
|
from .models import Podcast
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class PodcastRepository:
|
|
|
|
|
"""Loads and stores :class:`Podcast` rows for one session."""
|
|
|
|
|
|
|
|
|
|
def __init__(self, session: AsyncSession) -> None:
|
|
|
|
|
self._session = session
|
|
|
|
|
|
|
|
|
|
async def get(self, podcast_id: int) -> Podcast | None:
|
|
|
|
|
return await self._session.get(Podcast, podcast_id)
|
|
|
|
|
|
|
|
|
|
async def add(self, podcast: Podcast) -> Podcast:
|
|
|
|
|
"""Persist a new row and assign its primary key."""
|
|
|
|
|
self._session.add(podcast)
|
|
|
|
|
await self._session.flush()
|
|
|
|
|
return podcast
|
|
|
|
|
|
refactor(backend): rename search_space -> workspace across app bulk (Phase 2 Wave D)
Scoped codemod over surfsense_backend/app (excluding routes/, Wave E): renames
search_space_id -> workspace_id, search_space -> workspace, SearchSpace -> Workspace
across services, utils, tasks, agents, gateway, event_bus, notifications, podcasts,
automations, observability params, and prompt .md files. Also flips the camelCase
payload key searchSpaceId -> workspaceId (no backend reader; hard cutover).
Preserved carve-outs (verbatim): Celery task names "delete_search_space_background"
and "ai_sort_search_space" (wire names), and the OTel/metric key "search_space.id"
(dashboards depend on it). Enum values 'SEARCH_SPACE' and SearchSourceConnector
untouched.
2026-06-26 18:30:47 +02:00
|
|
|
async def latest_with_spec(self, workspace_id: int) -> Podcast | None:
|
2026-06-10 18:44:03 +02:00
|
|
|
"""Most recent podcast in the space that has a stored brief.
|
|
|
|
|
|
|
|
|
|
Used to seed language/voice defaults for a new podcast from what the
|
|
|
|
|
user chose last.
|
|
|
|
|
"""
|
|
|
|
|
result = await self._session.execute(
|
|
|
|
|
select(Podcast)
|
|
|
|
|
.where(
|
refactor(backend): rename search_space -> workspace across app bulk (Phase 2 Wave D)
Scoped codemod over surfsense_backend/app (excluding routes/, Wave E): renames
search_space_id -> workspace_id, search_space -> workspace, SearchSpace -> Workspace
across services, utils, tasks, agents, gateway, event_bus, notifications, podcasts,
automations, observability params, and prompt .md files. Also flips the camelCase
payload key searchSpaceId -> workspaceId (no backend reader; hard cutover).
Preserved carve-outs (verbatim): Celery task names "delete_search_space_background"
and "ai_sort_search_space" (wire names), and the OTel/metric key "search_space.id"
(dashboards depend on it). Enum values 'SEARCH_SPACE' and SearchSourceConnector
untouched.
2026-06-26 18:30:47 +02:00
|
|
|
Podcast.workspace_id == workspace_id,
|
2026-06-10 18:44:03 +02:00
|
|
|
Podcast.spec.is_not(None),
|
|
|
|
|
)
|
|
|
|
|
.order_by(Podcast.created_at.desc())
|
|
|
|
|
.limit(1)
|
|
|
|
|
)
|
|
|
|
|
return result.scalars().first()
|