{html_escape(notebook.title)}
' f'{html_escape(notebook.summary)}
' f'{audience_html}' f'from __future__ import annotations from dataclasses import dataclass, field from typing import Any from .content import ContentCard, SiteContent from .render import html_escape @dataclass(slots=True) class NotebookApp: title: str href: str description: str = "" @dataclass(slots=True) class Notebook: notebook_id: str title: str summary: str = "" audience: str = "" goals: list[str] = field(default_factory=list) apps: list[NotebookApp] = field(default_factory=list) source_kinds: list[str] = field(default_factory=list) max_items: int = 8 def load_notebooks(config: dict[str, Any]) -> list[Notebook]: notebooks: list[Notebook] = [] for item in config.get("notebooks", []): if not isinstance(item, dict): continue apps = [ NotebookApp( title=str(app.get("title") or app.get("href") or "App"), href=str(app.get("href") or "#"), description=str(app.get("description") or ""), ) for app in item.get("apps", []) if isinstance(app, dict) ] notebooks.append( Notebook( notebook_id=str(item.get("id") or item.get("notebook_id") or item.get("title") or "notebook"), title=str(item.get("title") or "Notebook"), summary=str(item.get("summary") or item.get("description") or ""), audience=str(item.get("audience") or ""), goals=[str(goal) for goal in item.get("goals", [])], apps=apps, source_kinds=[str(kind) for kind in item.get("source_kinds", [])], max_items=int(item.get("max_items") or 8), ) ) return notebooks def select_notebook_cards(notebook: Notebook, site_content: SiteContent) -> list[ContentCard]: cards = ( site_content.section_cards + site_content.app_cards + site_content.feature_cards + site_content.bibliography_entries ) if notebook.source_kinds: allowed = set(notebook.source_kinds) cards = [card for card in cards if card.kind in allowed or card.meta in allowed] return cards[: notebook.max_items] def render_notebooks(notebooks: list[Notebook], site_content: SiteContent) -> str: if not notebooks: return "" return "\n".join(render_notebook(notebook, site_content) for notebook in notebooks) def render_notebook(notebook: Notebook, site_content: SiteContent) -> str: cards = select_notebook_cards(notebook, site_content) goals_html = "".join(f"
{html_escape(card.body)}
' '{html_escape(notebook.summary)}
' f'{audience_html}' f'