fix: update regenerate route to use image_paths for mosaic cover
Made-with: Cursor
This commit is contained in:
@@ -3,7 +3,7 @@ import json
|
|||||||
from flask import Blueprint, render_template, send_file, redirect, url_for, flash
|
from flask import Blueprint, render_template, send_file, redirect, url_for, flash
|
||||||
|
|
||||||
from app import db
|
from app import db
|
||||||
from src.models import Issue, Article
|
from src.models import Issue, Article, Image
|
||||||
from src.cover import generate_cover
|
from src.cover import generate_cover
|
||||||
from src.epub_builder import build_epub
|
from src.epub_builder import build_epub
|
||||||
import config
|
import config
|
||||||
@@ -112,14 +112,22 @@ def regenerate(issue_id):
|
|||||||
a.title for a in articles_for_issue
|
a.title for a in articles_for_issue
|
||||||
if "Obituaries" not in json.loads(a.categories)
|
if "Obituaries" not in json.loads(a.categories)
|
||||||
]
|
]
|
||||||
categories_list = []
|
|
||||||
|
image_paths = []
|
||||||
for a in articles_for_issue:
|
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)
|
||||||
|
|
||||||
|
cover_method = issue.cover_method
|
||||||
|
if cover_method == "ai":
|
||||||
|
issue.cover_method = "mosaic"
|
||||||
|
cover_method = "mosaic"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
cover_path = generate_cover(
|
cover_path = generate_cover(
|
||||||
issue.cover_method, config.ISSUES_DIR,
|
cover_method, config.ISSUES_DIR,
|
||||||
issue.week_start, issue.week_end, headlines, categories_list
|
issue.week_start, issue.week_end, headlines, image_paths
|
||||||
)
|
)
|
||||||
epub_path = build_epub(
|
epub_path = build_epub(
|
||||||
issue.week_start, issue.week_end, article_ids,
|
issue.week_start, issue.week_end, article_ids,
|
||||||
|
|||||||
142
tests/test_issues.py
Normal file
142
tests/test_issues.py
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
import json
|
||||||
|
import os
|
||||||
|
from datetime import date, datetime
|
||||||
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
|
from PIL import Image as PILImage
|
||||||
|
|
||||||
|
from app import db
|
||||||
|
from src.models import Article, Image, Issue
|
||||||
|
|
||||||
|
|
||||||
|
def test_regenerate_passes_ordered_image_paths_to_generate_cover(app, client, db, tmp_path):
|
||||||
|
"""First image per article, in pub_date 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="<p>x</p>",
|
||||||
|
)
|
||||||
|
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="<p>y</p>",
|
||||||
|
)
|
||||||
|
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,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
id1, id2 = a1.id, a2.id
|
||||||
|
issue = Issue(
|
||||||
|
week_start=date(2026, 4, 6),
|
||||||
|
week_end=date(2026, 4, 12),
|
||||||
|
cover_method="mosaic",
|
||||||
|
cover_path=str(tmp_path / "old-cover.jpg"),
|
||||||
|
epub_path=str(tmp_path / "old.epub"),
|
||||||
|
article_ids=json.dumps([id2, id1]),
|
||||||
|
status="published",
|
||||||
|
)
|
||||||
|
db.session.add(issue)
|
||||||
|
db.session.commit()
|
||||||
|
issue_id = issue.id
|
||||||
|
|
||||||
|
mock_cover = MagicMock(return_value=str(tmp_path / "new-cover.jpg"))
|
||||||
|
mock_epub = MagicMock(return_value=str(tmp_path / "new.epub"))
|
||||||
|
|
||||||
|
with patch("src.routes.issues.generate_cover", mock_cover), patch(
|
||||||
|
"src.routes.issues.build_epub", mock_epub
|
||||||
|
):
|
||||||
|
resp = client.post(f"/issues/{issue_id}/regenerate")
|
||||||
|
|
||||||
|
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]
|
||||||
|
|
||||||
|
|
||||||
|
def test_regenerate_maps_ai_cover_method_to_mosaic(app, client, db, tmp_path):
|
||||||
|
"""Legacy ai issues use mosaic generation and persist cover_method=mosaic."""
|
||||||
|
os.makedirs(tmp_path, exist_ok=True)
|
||||||
|
img1 = str(tmp_path / "a1.jpg")
|
||||||
|
PILImage.new("RGB", (100, 100), color="blue").save(img1, format="JPEG")
|
||||||
|
|
||||||
|
with app.app_context():
|
||||||
|
a1 = Article(
|
||||||
|
guid="g-ai",
|
||||||
|
title="Some Article",
|
||||||
|
author="A",
|
||||||
|
pub_date=datetime(2026, 4, 6, 10, 0),
|
||||||
|
categories=json.dumps(["Government"]),
|
||||||
|
link="http://example.com/1",
|
||||||
|
content_html="<p>x</p>",
|
||||||
|
)
|
||||||
|
db.session.add(a1)
|
||||||
|
db.session.flush()
|
||||||
|
db.session.add(
|
||||||
|
Image(
|
||||||
|
article_id=a1.id,
|
||||||
|
original_url="https://example.com/1.jpg",
|
||||||
|
local_path=img1,
|
||||||
|
width=100,
|
||||||
|
height=100,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
issue = Issue(
|
||||||
|
week_start=date(2026, 4, 6),
|
||||||
|
week_end=date(2026, 4, 12),
|
||||||
|
cover_method="ai",
|
||||||
|
cover_path=str(tmp_path / "old-cover.jpg"),
|
||||||
|
epub_path=str(tmp_path / "old.epub"),
|
||||||
|
article_ids=json.dumps([a1.id]),
|
||||||
|
status="published",
|
||||||
|
)
|
||||||
|
db.session.add(issue)
|
||||||
|
db.session.commit()
|
||||||
|
issue_id = issue.id
|
||||||
|
|
||||||
|
mock_cover = MagicMock(return_value=str(tmp_path / "new-cover.jpg"))
|
||||||
|
mock_epub = MagicMock(return_value=str(tmp_path / "new.epub"))
|
||||||
|
|
||||||
|
with patch("src.routes.issues.generate_cover", mock_cover), patch(
|
||||||
|
"src.routes.issues.build_epub", mock_epub
|
||||||
|
):
|
||||||
|
client.post(f"/issues/{issue_id}/regenerate")
|
||||||
|
|
||||||
|
mock_cover.assert_called_once()
|
||||||
|
assert mock_cover.call_args[0][0] == "mosaic"
|
||||||
|
|
||||||
|
with app.app_context():
|
||||||
|
updated = db.session.get(Issue, issue_id)
|
||||||
|
assert updated.cover_method == "mosaic"
|
||||||
Reference in New Issue
Block a user