diff --git a/src/routes/publish.py b/src/routes/publish.py index f714bba..866cd9c 100644 --- a/src/routes/publish.py +++ b/src/routes/publish.py @@ -4,7 +4,7 @@ from calendar import monthrange from flask import Blueprint, render_template, request, redirect, url_for, flash, jsonify from app import db -from src.models import Article, Issue +from src.models import Article, Issue, Image from src.cover import generate_cover from src.epub_builder import build_epub import config @@ -168,14 +168,17 @@ def create_issue(): a.title for a in articles_for_issue if "Obituaries" not in json.loads(a.categories) ] - categories_list = [] + + image_paths = [] for a in articles_for_issue: - categories_list.extend(json.loads(a.categories)) + first_image = Image.query.filter_by(article_id=a.id).first() + if first_image: + image_paths.append(first_image.local_path) try: cover_path = generate_cover( cover_method, config.ISSUES_DIR, week_start, week_end, - headlines, categories_list + headlines, image_paths ) epub_path = build_epub( week_start, week_end, included_ids, cover_path, diff --git a/tests/test_publish.py b/tests/test_publish.py new file mode 100644 index 0000000..4f9cd85 --- /dev/null +++ b/tests/test_publish.py @@ -0,0 +1,88 @@ +import json +import os +from datetime import datetime +from unittest.mock import MagicMock, patch + +from PIL import Image as PILImage +from werkzeug.datastructures import MultiDict + +from src.models import Article, Image + + +def test_publish_route_passes_ordered_image_paths_to_generate_cover(app, client, db, tmp_path): + """First image per article, in pub_date order (not form order), passed to generate_cover.""" + os.makedirs(tmp_path, exist_ok=True) + img1 = str(tmp_path / "a1.jpg") + img2 = str(tmp_path / "a2.jpg") + for p in (img1, img2): + PILImage.new("RGB", (100, 100), color="blue").save(p, format="JPEG") + + with app.app_context(): + a1 = Article( + guid="g1", + title="Earlier Article", + author="A", + pub_date=datetime(2026, 4, 6, 10, 0), + categories=json.dumps(["Government"]), + link="http://example.com/1", + content_html="

x

", + ) + a2 = Article( + guid="g2", + title="Later Article", + author="B", + pub_date=datetime(2026, 4, 7, 10, 0), + categories=json.dumps(["Culture"]), + link="http://example.com/2", + content_html="

y

", + ) + db.session.add_all([a1, a2]) + db.session.flush() + db.session.add_all( + [ + Image( + article_id=a1.id, + original_url="https://example.com/1.jpg", + local_path=img1, + width=100, + height=100, + ), + Image( + article_id=a2.id, + original_url="https://example.com/2.jpg", + local_path=img2, + width=100, + height=100, + ), + ] + ) + db.session.commit() + id1, id2 = a1.id, a2.id + + mock_cover = MagicMock(return_value="/fake/cover.jpg") + mock_epub = MagicMock(return_value="/fake/issue.epub") + + with patch("src.routes.publish.generate_cover", mock_cover), patch( + "src.routes.publish.build_epub", mock_epub + ): + resp = client.post( + "/publish", + data=MultiDict( + [ + ("week_start", "2026-04-06"), + ("week_end", "2026-04-12"), + ("cover_method", "mosaic"), + ("article_ids", str(id2)), + ("article_ids", str(id1)), + ] + ), + ) + + assert resp.status_code in (302, 303) + mock_cover.assert_called_once() + args = mock_cover.call_args[0] + assert args[0] == "mosaic" + assert args[4] == ["Earlier Article", "Later Article"] + assert args[5] == [img1, img2] + + mock_epub.assert_called_once()