45 lines
1.8 KiB
Python
45 lines
1.8 KiB
Python
"""generator layout + pagination.
|
|
|
|
Per BR2.1/BR2.3, US1/NFR2: flows articles into a classic column-based
|
|
multi-page A4 layout with page breaks at boundaries, page numbers and running
|
|
headers. Deterministic, stdlib-only.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from .model import Article, Column, ColumnItem, Page
|
|
|
|
|
|
class ColumnFlow:
|
|
"""Flows placed items into columns/pages with running headers."""
|
|
|
|
def __init__(self, columns_per_page: int = 3, items_per_column: int = 4) -> None:
|
|
self.columns_per_page = columns_per_page
|
|
self.items_per_column = items_per_column
|
|
|
|
def flow(self, article_items: list[ColumnItem]) -> list[Page]:
|
|
pages: list[Page] = []
|
|
current: Column | None = None
|
|
page: Page | None = None
|
|
idx = 0
|
|
for item in article_items:
|
|
if page is None or current is None or len(current.items) >= self.items_per_column:
|
|
current = Column(column_index=0 if current is None else ((current.column_index + 1) % self.columns_per_page))
|
|
if page is None or (current.column_index == 0 and len(page.columns) >= self.columns_per_page):
|
|
page_number = len(pages) + 1
|
|
page = Page(page_number=page_number, columns=[], running_header=f"Page {page_number}")
|
|
pages.append(page)
|
|
page.columns.append(current)
|
|
idx += 1
|
|
current.items.append(item)
|
|
return pages or [Page(page_number=1, columns=[Column(column_index=0)], running_header="Page 1")]
|
|
|
|
|
|
def build_layout(articles: list[Article]) -> list[Page]:
|
|
"""Map articles into column items and flow them (BR2.1)."""
|
|
items: list[ColumnItem] = []
|
|
for a in articles:
|
|
items.append(ColumnItem(item_id=a.article_id, kind="article", source_ref=a.article_id))
|
|
flow = ColumnFlow()
|
|
return flow.flow(items)
|