import os import json from flask import Blueprint, render_template, send_file, redirect, url_for, flash from app import db from src.models import Issue, Article from src.cover import generate_cover from src.epub_builder import build_epub import config issues_bp = Blueprint("issues", __name__) @issues_bp.route("/issues") def index(): issues = Issue.query.order_by(Issue.created_at.desc()).all() issue_data = [] for issue in issues: article_count = len(json.loads(issue.article_ids)) issue_data.append({ "issue": issue, "article_count": article_count, }) return render_template("issues.html", issues=issue_data) @issues_bp.route("/issues//download") def download(issue_id): issue = Issue.query.get_or_404(issue_id) if not os.path.exists(issue.epub_path): flash("ePub file not found.", "error") return redirect(url_for("issues.index")) return send_file( issue.epub_path, as_attachment=True, download_name=os.path.basename(issue.epub_path), ) @issues_bp.route("/issues//epub") def epub_file(issue_id): issue = Issue.query.get_or_404(issue_id) if not os.path.exists(issue.epub_path): flash("ePub file not found.", "error") return redirect(url_for("issues.index")) return send_file(issue.epub_path, mimetype="application/epub+zip") @issues_bp.route("/issues//cover") def cover_image(issue_id): issue = Issue.query.get_or_404(issue_id) if not issue.cover_path or not os.path.exists(issue.cover_path): flash("Cover image not found.", "error") return redirect(url_for("issues.index")) return send_file(issue.cover_path, mimetype="image/jpeg") @issues_bp.route("/issues//read") def read(issue_id): issue = Issue.query.get_or_404(issue_id) if not os.path.exists(issue.epub_path): flash("ePub file not found.", "error") return redirect(url_for("issues.index")) article_count = len(json.loads(issue.article_ids)) if issue.issue_type == "single_article": article_ids = json.loads(issue.article_ids) article = Article.query.get(article_ids[0]) if article_ids else None title = article.title if article else "Single Article" elif issue.issue_type == "multi_week": w1 = issue.week_start.isocalendar()[1] w2 = issue.week_end.isocalendar()[1] title = f"Weeks {w1}\u2013{w2}" else: title = f"Week {issue.week_start.isocalendar()[1]}" return render_template( "reader.html", issue=issue, title=title, article_count=article_count, ) @issues_bp.route("/issues//delete", methods=["POST"]) def delete(issue_id): issue = Issue.query.get_or_404(issue_id) if issue.epub_path and os.path.exists(issue.epub_path): os.remove(issue.epub_path) if issue.cover_path and os.path.exists(issue.cover_path): os.remove(issue.cover_path) db.session.delete(issue) db.session.commit() flash("Issue deleted.") return redirect(url_for("issues.index")) @issues_bp.route("/issues//regenerate", methods=["POST"]) def regenerate(issue_id): issue = Issue.query.get_or_404(issue_id) article_ids = json.loads(issue.article_ids) articles_for_issue = ( Article.query.filter(Article.id.in_(article_ids)) .order_by(Article.pub_date.asc()) .all() ) headlines = [ a.title for a in articles_for_issue if "Obituaries" not in json.loads(a.categories) ] categories_list = [] for a in articles_for_issue: categories_list.extend(json.loads(a.categories)) try: cover_path = generate_cover( issue.cover_method, config.ISSUES_DIR, issue.week_start, issue.week_end, headlines, categories_list ) epub_path = build_epub( issue.week_start, issue.week_end, article_ids, cover_path, config.ISSUES_DIR ) issue.cover_path = cover_path issue.epub_path = epub_path db.session.commit() flash("Issue regenerated successfully.") except Exception as e: flash(f"Regeneration failed: {e}", "error") return redirect(url_for("issues.index"))