diff --git a/src/epub_builder.py b/src/epub_builder.py index ca227bc..dd98de3 100644 --- a/src/epub_builder.py +++ b/src/epub_builder.py @@ -1,7 +1,7 @@ import json import os import re -from datetime import date +from datetime import date, datetime from bs4 import BeautifulSoup from ebooklib import epub @@ -67,6 +67,7 @@ def build_epub( issue_type: str = "weekly", ) -> str: os.makedirs(output_dir, exist_ok=True) + ts = datetime.now().strftime("%Y%m%d%H%M%S") articles = ( Article.query @@ -91,7 +92,7 @@ def build_epub( ) book = epub.EpubBook() - book.set_identifier(f"pi-{week_start.isoformat()}") + book.set_identifier(f"pi-{week_start.isoformat()}-{issue_type}-{ts}") book.set_title(title) book.set_language("en") book.add_author("Plymouth Independent") @@ -99,6 +100,11 @@ def build_epub( with open(cover_path, "rb") as f: book.set_cover("cover.jpg", f.read()) + for item in book.get_items(): + if item.get_name() == "cover.xhtml": + item.is_linear = True + break + style = epub.EpubItem( uid="style", file_name="style/default.css", media_type="text/css", content=EPUB_CSS.encode("utf-8"), @@ -162,10 +168,16 @@ def build_epub( book.add_item(epub.EpubNcx()) book.add_item(epub.EpubNav()) - book.spine = ["nav"] + chapters + book.spine = ["cover", "nav"] + chapters iso_week = week_start.isocalendar()[1] - filename = f"plymouth-independent-{week_start.year}-W{iso_week:02d}.epub" + if issue_type == "multi_week": + w2 = week_end.isocalendar()[1] + filename = f"plymouth-independent-{week_start.year}-W{iso_week:02d}-W{w2:02d}-{ts}.epub" + elif issue_type == "single_article": + filename = f"plymouth-independent-single-{ts}.epub" + else: + filename = f"plymouth-independent-{week_start.year}-W{iso_week:02d}-{ts}.epub" epub_path = os.path.join(output_dir, filename) epub.write_epub(epub_path, book) diff --git a/src/routes/issues.py b/src/routes/issues.py index 0b45a00..abe46b4 100644 --- a/src/routes/issues.py +++ b/src/routes/issues.py @@ -37,6 +37,15 @@ def download(issue_id): ) +@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) @@ -73,6 +82,22 @@ def read(issue_id): ) +@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) diff --git a/static/style.css b/static/style.css index 7d93b93..d7949fd 100644 --- a/static/style.css +++ b/static/style.css @@ -1,5 +1,7 @@ :root { --pico-font-size: 16px; + --pico-font-family-sans-serif: system-ui, -apple-system, "Segoe UI", + Helvetica, Arial, sans-serif, var(--pico-font-family-emoji); } .stats-grid { @@ -31,6 +33,18 @@ display: flex; gap: 0.5rem; flex-wrap: wrap; + align-items: center; + margin-bottom: 1.5rem; +} +.action-buttons form { margin: 0; } +.action-buttons a[role="button"], +.action-buttons button { + white-space: nowrap; + padding: 0.5rem 1rem; + font-size: 0.95rem; + line-height: 1.4; + margin: 0; + width: auto; } .hidden { display: none !important; } @@ -123,6 +137,29 @@ nav .brand { font-weight: bold; font-size: 1.1rem; } border-bottom: 1px solid var(--pico-muted-border-color); } +/* Issue actions */ +.issue-actions { + display: flex; + flex-wrap: wrap; + gap: 0.35rem; + align-items: center; +} +.issue-actions form { margin: 0; } +.issue-actions a[role="button"], +.issue-actions button { + padding: 0.25rem 0.6rem; + font-size: 0.8rem; + line-height: 1.4; + margin: 0; + width: auto; +} +.btn-danger { color: var(--pico-del-color); } +.btn-danger-fill { + background: var(--pico-del-color); + border-color: var(--pico-del-color); + color: #fff; +} + /* Consistent interactive elements */ button, [role="button"], input[type="submit"], diff --git a/templates/base.html b/templates/base.html index 9c2d0aa..0e4fe93 100644 --- a/templates/base.html +++ b/templates/base.html @@ -4,6 +4,7 @@ {% block title %}PI Weekly{% endblock %} — Plymouth Independent + @@ -36,6 +37,48 @@
PI Weekly Newspaper Generator
+ +
+
+ +

Confirm

+
+

+
+ + +
+
+
+ {% block scripts %}{% endblock %} diff --git a/templates/dashboard.html b/templates/dashboard.html index 32208d0..162af2b 100644 --- a/templates/dashboard.html +++ b/templates/dashboard.html @@ -34,7 +34,7 @@ View - Publish New + Publish New {% if latest_issue %} diff --git a/templates/issues.html b/templates/issues.html index a3e665e..781820b 100644 --- a/templates/issues.html +++ b/templates/issues.html @@ -26,16 +26,16 @@ {{ item.article_count }} {{ item.issue.cover_method }} {{ item.issue.created_at.strftime('%b %d, %Y %H:%M') }} - - - Read - - - Download - -
+ + Read + Download +
+
+ +
{% endfor %} diff --git a/templates/reader.html b/templates/reader.html index e7d5bed..b427b65 100644 --- a/templates/reader.html +++ b/templates/reader.html @@ -4,6 +4,7 @@ {{ title }} — PI Weekly Reader +