From 776e39370eaed40bfd3bf9f4a40e047a4fa16624 Mon Sep 17 00:00:00 2001 From: Kenneth Reitz Date: Wed, 26 Nov 2025 01:38:55 -0500 Subject: [PATCH] Add global resource and reading plan PDF exports --- kjvstudy_org/routes/resources.py | 339 ++++++++++++++++++ kjvstudy_org/server.py | 28 +- kjvstudy_org/templates/biblical_angels.html | 40 +++ .../templates/biblical_covenants.html | 40 +++ .../templates/biblical_festivals.html | 46 ++- kjvstudy_org/templates/biblical_prophets.html | 26 +- .../templates/biblical_prophets_pdf.html | 110 ++++++ kjvstudy_org/templates/fruits_of_spirit.html | 40 +++ kjvstudy_org/templates/names_of_god.html | 40 +++ .../templates/reading_plan_detail.html | 48 +++ kjvstudy_org/templates/reading_plan_pdf.html | 101 ++++++ kjvstudy_org/templates/resource_index.html | 26 +- .../templates/resource_index_pdf.html | 115 ++++++ kjvstudy_org/templates/twelve_apostles.html | 40 +++ .../templates/twelve_apostles_pdf.html | 110 ++++++ .../templates/women_of_the_bible.html | 40 +++ 16 files changed, 1161 insertions(+), 28 deletions(-) create mode 100644 kjvstudy_org/templates/biblical_prophets_pdf.html create mode 100644 kjvstudy_org/templates/reading_plan_pdf.html create mode 100644 kjvstudy_org/templates/resource_index_pdf.html create mode 100644 kjvstudy_org/templates/twelve_apostles_pdf.html diff --git a/kjvstudy_org/routes/resources.py b/kjvstudy_org/routes/resources.py index 35e4b34..1fbf623 100644 --- a/kjvstudy_org/routes/resources.py +++ b/kjvstudy_org/routes/resources.py @@ -147,6 +147,30 @@ def _resource_detail_pdf_response( ) +def _resource_index_pdf_response(resource_data: dict, page_title: str, page_subtitle: str, page_description: str): + """Generate PDF for resource index-style pages.""" + if not WEASYPRINT_AVAILABLE: + raise HTTPException( + status_code=503, + detail="PDF generation is not available. WeasyPrint system libraries are not installed." + ) + + html_content = templates.get_template("resource_index_pdf.html").render( + resource_data=resource_data, + page_title=page_title, + page_subtitle=page_subtitle, + page_description=page_description, + ) + + pdf_buffer = render_html_to_pdf(html_content) + filename = f"{create_slug(page_title)}.pdf" + return StreamingResponse( + pdf_buffer, + media_type="application/pdf", + headers={"Content-Disposition": f"attachment; filename={filename}"} + ) + + # ============================================================================ # BIBLICAL MAPS # ============================================================================ @@ -180,6 +204,7 @@ def biblical_angels_page(request: Request): { "books": get_books(), "angels_data": ANGELS_DATA, + "pdf_available": WEASYPRINT_AVAILABLE, "breadcrumbs": [ {"text": "Home", "url": "/"}, {"text": "Resources", "url": "/resources"}, @@ -189,6 +214,16 @@ def biblical_angels_page(request: Request): ) +@router.get("/biblical-angels/pdf") +def biblical_angels_page_pdf(): + return _resource_index_pdf_response( + ANGELS_DATA, + page_title="Biblical Angels", + page_subtitle="Heavenly messengers throughout Scripture", + page_description="Explore angels and angelic beings mentioned in the King James Bible, including Michael, Gabriel, and the heavenly host." + ) + + @router.get("/biblical-angels/{angel_slug}", response_class=HTMLResponse) def angel_detail(request: Request, angel_slug: str): """Individual biblical angels detail page.""" @@ -226,6 +261,7 @@ def biblical_prophets_page(request: Request): { "books": get_books(), "prophets_data": PROPHETS_DATA, + "pdf_available": WEASYPRINT_AVAILABLE, "breadcrumbs": [ {"text": "Home", "url": "/"}, {"text": "Resources", "url": "/resources"}, @@ -235,6 +271,25 @@ def biblical_prophets_page(request: Request): ) +@router.get("/biblical-prophets/pdf") +def biblical_prophets_pdf(): + """PDF export for the prophets index.""" + if not WEASYPRINT_AVAILABLE: + raise HTTPException( + status_code=503, + detail="PDF generation is not available. WeasyPrint system libraries are not installed." + ) + + html_content = templates.get_template("biblical_prophets_pdf.html").render(prophets_data=PROPHETS_DATA) + pdf_buffer = render_html_to_pdf(html_content) + + return StreamingResponse( + pdf_buffer, + media_type="application/pdf", + headers={"Content-Disposition": "attachment; filename=biblical-prophets.pdf"} + ) + + @router.get("/biblical-prophets/{prophet_slug}", response_class=HTMLResponse) def prophet_detail(request: Request, prophet_slug: str): """Individual biblical prophets detail page.""" @@ -272,6 +327,7 @@ def names_of_god_page(request: Request): { "books": get_books(), "names_data": NAMES_DATA, + "pdf_available": WEASYPRINT_AVAILABLE, "breadcrumbs": [ {"text": "Home", "url": "/"}, {"text": "Resources", "url": "/resources"}, @@ -281,6 +337,16 @@ def names_of_god_page(request: Request): ) +@router.get("/names-of-god/pdf") +def names_of_god_page_pdf(): + return _resource_index_pdf_response( + NAMES_DATA, + page_title="Names of God", + page_subtitle="Divine titles revealed in Scripture", + page_description="Explore the revelation of God's names throughout Scripture and their meanings." + ) + + @router.get("/names-of-god/{name_slug}", response_class=HTMLResponse) def name_of_god_detail(request: Request, name_slug: str): """Individual name of God detail page.""" @@ -384,6 +450,7 @@ def biblical_covenants_page(request: Request): { "books": get_books(), "covenants_data": COVENANTS_DATA, + "pdf_available": WEASYPRINT_AVAILABLE, "breadcrumbs": [ {"text": "Home", "url": "/"}, {"text": "Resources", "url": "/resources"}, @@ -393,6 +460,16 @@ def biblical_covenants_page(request: Request): ) +@router.get("/biblical-covenants/pdf") +def biblical_covenants_page_pdf(): + return _resource_index_pdf_response( + COVENANTS_DATA, + page_title="Biblical Covenants", + page_subtitle="Divine promises across redemptive history", + page_description="Survey the major covenants established between God and His people throughout Scripture." + ) + + @router.get("/biblical-covenants/{covenant_slug}", response_class=HTMLResponse) def covenant_detail(request: Request, covenant_slug: str): """Individual covenant detail page.""" @@ -430,6 +507,7 @@ def apostles_page(request: Request): { "books": get_books(), "apostles_data": APOSTLES_DATA, + "pdf_available": WEASYPRINT_AVAILABLE, "breadcrumbs": [ {"text": "Home", "url": "/"}, {"text": "Resources", "url": "/resources"}, @@ -439,6 +517,25 @@ def apostles_page(request: Request): ) +@router.get("/the-twelve-apostles/pdf") +def apostles_page_pdf(): + """PDF export for the apostles index.""" + if not WEASYPRINT_AVAILABLE: + raise HTTPException( + status_code=503, + detail="PDF generation is not available. WeasyPrint system libraries are not installed." + ) + + html_content = templates.get_template("twelve_apostles_pdf.html").render(apostles_data=APOSTLES_DATA) + pdf_buffer = render_html_to_pdf(html_content) + + return StreamingResponse( + pdf_buffer, + media_type="application/pdf", + headers={"Content-Disposition": "attachment; filename=twelve-apostles.pdf"} + ) + + @router.get("/the-twelve-apostles/{apostle_slug}", response_class=HTMLResponse) def apostle_detail(request: Request, apostle_slug: str): """Individual apostle detail page.""" @@ -476,6 +573,7 @@ def women_of_the_bible_page(request: Request): { "books": get_books(), "women_data": WOMEN_DATA, + "pdf_available": WEASYPRINT_AVAILABLE, "breadcrumbs": [ {"text": "Home", "url": "/"}, {"text": "Resources", "url": "/resources"}, @@ -485,6 +583,16 @@ def women_of_the_bible_page(request: Request): ) +@router.get("/women-of-the-bible/pdf") +def women_of_the_bible_page_pdf(): + return _resource_index_pdf_response( + WOMEN_DATA, + page_title="Women of the Bible", + page_subtitle="Faithful witnesses throughout redemptive history", + page_description="Explore the lives, faith, and legacies of notable women throughout Scripture." + ) + + @router.get("/women-of-the-bible/{woman_slug}", response_class=HTMLResponse) def woman_detail(request: Request, woman_slug: str): """Individual woman of the Bible detail page.""" @@ -522,6 +630,7 @@ def biblical_festivals_page(request: Request): { "books": get_books(), "festivals_data": FESTIVALS_DATA, + "pdf_available": WEASYPRINT_AVAILABLE, "breadcrumbs": [ {"text": "Home", "url": "/"}, {"text": "Resources", "url": "/resources"}, @@ -531,6 +640,16 @@ def biblical_festivals_page(request: Request): ) +@router.get("/biblical-festivals/pdf") +def biblical_festivals_page_pdf(): + return _resource_index_pdf_response( + FESTIVALS_DATA, + page_title="Biblical Festivals", + page_subtitle="Appointed feasts of the Lord", + page_description="Learn about the appointed feasts and holy days ordained in the Law of Moses." + ) + + @router.get("/biblical-festivals/{festival_slug}", response_class=HTMLResponse) def festival_detail(request: Request, festival_slug: str): """Individual biblical festival detail page.""" @@ -568,6 +687,7 @@ def fruits_of_the_spirit_page(request: Request): { "books": get_books(), "fruits_data": FRUITS_DATA, + "pdf_available": WEASYPRINT_AVAILABLE, "breadcrumbs": [ {"text": "Home", "url": "/"}, {"text": "Resources", "url": "/resources"}, @@ -577,6 +697,16 @@ def fruits_of_the_spirit_page(request: Request): ) +@router.get("/fruits-of-the-spirit/pdf") +def fruits_of_the_spirit_page_pdf(): + return _resource_index_pdf_response( + FRUITS_DATA, + page_title="Fruits of the Spirit", + page_subtitle="Developing Christian character", + page_description="Meditate on the Spirit-produced virtues described in Galatians 5." + ) + + @router.get("/fruits-of-the-spirit/{fruit_slug}", response_class=HTMLResponse) def fruit_detail(request: Request, fruit_slug: str): """Individual fruit of the Spirit detail page.""" @@ -681,6 +811,7 @@ def miracles_page(request: Request): "page_subtitle": "Signs and Wonders Manifesting Divine Authority", "page_description": "Explore the miracles of Jesus Christ recorded in the Gospels - healings, nature miracles, exorcisms, and raisings from the dead.", "base_url": "/miracles-of-jesus", + "pdf_available": WEASYPRINT_AVAILABLE, "breadcrumbs": [ {"text": "Home", "url": "/"}, {"text": "Resources", "url": "/resources"}, @@ -690,6 +821,16 @@ def miracles_page(request: Request): ) +@router.get("/miracles-of-jesus/pdf") +def miracles_page_pdf(): + return _resource_index_pdf_response( + MIRACLES_DATA, + page_title="Miracles of Jesus", + page_subtitle="Signs and Wonders Manifesting Divine Authority", + page_description="Explore the miracles of Jesus Christ recorded in the Gospels - healings, nature miracles, exorcisms, and raisings from the dead." + ) + + @router.get("/miracles-of-jesus/{miracle_slug}", response_class=HTMLResponse) def miracle_detail(request: Request, miracle_slug: str): """Individual miracle detail page.""" @@ -731,6 +872,7 @@ def prayers_page(request: Request): "page_subtitle": "Sacred Conversations with the Almighty", "page_description": "Explore the prayers recorded in Scripture - from the Psalms to the prayers of Jesus, Paul, and the early church.", "base_url": "/prayers-of-the-bible", + "pdf_available": WEASYPRINT_AVAILABLE, "breadcrumbs": [ {"text": "Home", "url": "/"}, {"text": "Resources", "url": "/resources"}, @@ -740,6 +882,16 @@ def prayers_page(request: Request): ) +@router.get("/prayers-of-the-bible/pdf") +def prayers_page_pdf(): + return _resource_index_pdf_response( + PRAYERS_DATA, + page_title="Prayers of the Bible", + page_subtitle="Sacred Conversations with the Almighty", + page_description="Explore the prayers recorded in Scripture - from the Psalms to the prayers of Jesus, Paul, and the early church." + ) + + @router.get("/prayers-of-the-bible/{prayer_slug}", response_class=HTMLResponse) def prayer_detail(request: Request, prayer_slug: str): """Individual prayer detail page.""" @@ -781,6 +933,7 @@ def beatitudes_page(request: Request): "page_subtitle": "The Blessings of the Kingdom", "page_description": "Explore the Beatitudes from Jesus's Sermon on the Mount - the foundational blessings that describe the character of kingdom citizens.", "base_url": "/beatitudes", + "pdf_available": WEASYPRINT_AVAILABLE, "breadcrumbs": [ {"text": "Home", "url": "/"}, {"text": "Resources", "url": "/resources"}, @@ -790,6 +943,16 @@ def beatitudes_page(request: Request): ) +@router.get("/beatitudes/pdf") +def beatitudes_page_pdf(): + return _resource_index_pdf_response( + BEATITUDES_DATA, + page_title="The Beatitudes", + page_subtitle="The Blessings of the Kingdom", + page_description="Explore the Beatitudes from Jesus's Sermon on the Mount - the foundational blessings that describe the character of kingdom citizens." + ) + + @router.get("/beatitudes/{beatitude_slug}", response_class=HTMLResponse) def beatitude_detail(request: Request, beatitude_slug: str): """Individual beatitude detail page.""" @@ -831,6 +994,7 @@ def ten_commandments_page(request: Request): "page_subtitle": "The Moral Law of God", "page_description": "Study the Ten Commandments given by God to Moses on Mount Sinai - the foundation of biblical morality and divine law.", "base_url": "/ten-commandments", + "pdf_available": WEASYPRINT_AVAILABLE, "breadcrumbs": [ {"text": "Home", "url": "/"}, {"text": "Resources", "url": "/resources"}, @@ -840,6 +1004,16 @@ def ten_commandments_page(request: Request): ) +@router.get("/ten-commandments/pdf") +def ten_commandments_page_pdf(): + return _resource_index_pdf_response( + TEN_COMMANDMENTS_DATA, + page_title="The Ten Commandments", + page_subtitle="The Moral Law of God", + page_description="Study the Ten Commandments given by God to Moses on Mount Sinai - the foundation of biblical morality and divine law." + ) + + @router.get("/ten-commandments/{commandment_slug}", response_class=HTMLResponse) def commandment_detail(request: Request, commandment_slug: str): """Individual commandment detail page.""" @@ -881,6 +1055,7 @@ def armor_of_god_page(request: Request): "page_subtitle": "Divine Equipment for Spiritual Warfare", "page_description": "Study the Armor of God from Ephesians 6 - the spiritual equipment believers need to stand against the wiles of the devil.", "base_url": "/armor-of-god", + "pdf_available": WEASYPRINT_AVAILABLE, "breadcrumbs": [ {"text": "Home", "url": "/"}, {"text": "Resources", "url": "/resources"}, @@ -890,6 +1065,16 @@ def armor_of_god_page(request: Request): ) +@router.get("/armor-of-god/pdf") +def armor_of_god_page_pdf(): + return _resource_index_pdf_response( + ARMOR_OF_GOD_DATA, + page_title="The Armor of God", + page_subtitle="Divine Equipment for Spiritual Warfare", + page_description="Study the Armor of God from Ephesians 6 - the spiritual equipment believers need to stand against the wiles of the devil." + ) + + @router.get("/armor-of-god/{armor_slug}", response_class=HTMLResponse) def armor_detail(request: Request, armor_slug: str): """Individual armor piece detail page.""" @@ -931,6 +1116,7 @@ def i_am_statements_page(request: Request): "page_subtitle": "Divine Self-Revelations in the Gospel of John", "page_description": "Explore the seven 'I Am' statements of Jesus in John's Gospel - profound declarations of His divine nature and mission.", "base_url": "/i-am-statements", + "pdf_available": WEASYPRINT_AVAILABLE, "breadcrumbs": [ {"text": "Home", "url": "/"}, {"text": "Resources", "url": "/resources"}, @@ -940,6 +1126,16 @@ def i_am_statements_page(request: Request): ) +@router.get("/i-am-statements/pdf") +def i_am_statements_page_pdf(): + return _resource_index_pdf_response( + I_AM_STATEMENTS_DATA, + page_title="I Am Statements of Jesus", + page_subtitle="Divine Self-Revelations in the Gospel of John", + page_description="Explore the seven 'I Am' statements of Jesus in John's Gospel - profound declarations of His divine nature and mission." + ) + + @router.get("/i-am-statements/{statement_slug}", response_class=HTMLResponse) def i_am_statement_detail(request: Request, statement_slug: str): """Individual I Am statement detail page.""" @@ -981,6 +1177,7 @@ def trinity_page(request: Request): "page_subtitle": "The Doctrine of One God in Three Persons", "page_description": "An expansive theological study of the Trinity - the doctrine that God eternally exists as Father, Son, and Holy Spirit, three distinct Persons sharing one divine essence.", "base_url": "/trinity", + "pdf_available": WEASYPRINT_AVAILABLE, "breadcrumbs": [ {"text": "Home", "url": "/"}, {"text": "Resources", "url": "/resources"}, @@ -990,6 +1187,16 @@ def trinity_page(request: Request): ) +@router.get("/trinity/pdf") +def trinity_page_pdf(): + return _resource_index_pdf_response( + TRINITY_DATA, + page_title="The Trinity", + page_subtitle="The Doctrine of One God in Three Persons", + page_description="An expansive theological study of the Trinity - the doctrine that God eternally exists as Father, Son, and Holy Spirit, three distinct Persons sharing one divine essence." + ) + + @router.get("/trinity/{item_slug}", response_class=HTMLResponse) def trinity_detail(request: Request, item_slug: str): """Individual Trinity topic detail page.""" @@ -1031,6 +1238,7 @@ def christology_page(request: Request): "page_subtitle": "The Doctrine of the Person and Work of Christ", "page_description": "An expansive theological study of Christology - the doctrine concerning Jesus Christ, His divine-human nature, His offices, and His saving work.", "base_url": "/christology", + "pdf_available": WEASYPRINT_AVAILABLE, "breadcrumbs": [ {"text": "Home", "url": "/"}, {"text": "Resources", "url": "/resources"}, @@ -1040,6 +1248,16 @@ def christology_page(request: Request): ) +@router.get("/christology/pdf") +def christology_page_pdf(): + return _resource_index_pdf_response( + CHRISTOLOGY_DATA, + page_title="Christology", + page_subtitle="The Doctrine of the Person and Work of Christ", + page_description="An expansive theological study of Christology - the doctrine concerning Jesus Christ, His divine-human nature, His offices, and His saving work." + ) + + @router.get("/christology/{item_slug}", response_class=HTMLResponse) def christology_detail(request: Request, item_slug: str): """Individual Christology topic detail page.""" @@ -1081,6 +1299,7 @@ def soteriology_page(request: Request): "page_subtitle": "The Doctrine of Salvation", "page_description": "An expansive theological study of Soteriology - the doctrine of salvation, covering election, atonement, regeneration, justification, sanctification, and glorification.", "base_url": "/soteriology", + "pdf_available": WEASYPRINT_AVAILABLE, "breadcrumbs": [ {"text": "Home", "url": "/"}, {"text": "Resources", "url": "/resources"}, @@ -1090,6 +1309,16 @@ def soteriology_page(request: Request): ) +@router.get("/soteriology/pdf") +def soteriology_page_pdf(): + return _resource_index_pdf_response( + SOTERIOLOGY_DATA, + page_title="Soteriology", + page_subtitle="The Doctrine of Salvation", + page_description="An expansive theological study of Soteriology - the doctrine of salvation, covering election, atonement, regeneration, justification, sanctification, and glorification." + ) + + @router.get("/soteriology/{item_slug}", response_class=HTMLResponse) def soteriology_detail(request: Request, item_slug: str): """Individual Soteriology topic detail page.""" @@ -1131,6 +1360,7 @@ def pneumatology_page(request: Request): "page_subtitle": "The Doctrine of the Holy Spirit", "page_description": "An expansive theological study of Pneumatology - the doctrine of the Holy Spirit, His person, deity, work in salvation, and ministry to believers.", "base_url": "/pneumatology", + "pdf_available": WEASYPRINT_AVAILABLE, "breadcrumbs": [ {"text": "Home", "url": "/"}, {"text": "Resources", "url": "/resources"}, @@ -1140,6 +1370,16 @@ def pneumatology_page(request: Request): ) +@router.get("/pneumatology/pdf") +def pneumatology_page_pdf(): + return _resource_index_pdf_response( + PNEUMATOLOGY_DATA, + page_title="Pneumatology", + page_subtitle="The Doctrine of the Holy Spirit", + page_description="An expansive theological study of Pneumatology - the doctrine of the Holy Spirit, His person, deity, work in salvation, and ministry to believers." + ) + + @router.get("/pneumatology/{item_slug}", response_class=HTMLResponse) def pneumatology_detail(request: Request, item_slug: str): """Individual Pneumatology topic detail page.""" @@ -1181,6 +1421,7 @@ def eschatology_page(request: Request): "page_subtitle": "The Doctrine of Last Things", "page_description": "An expansive theological study of Eschatology - the doctrine of death, resurrection, the second coming of Christ, final judgment, and eternal destinies.", "base_url": "/eschatology", + "pdf_available": WEASYPRINT_AVAILABLE, "breadcrumbs": [ {"text": "Home", "url": "/"}, {"text": "Resources", "url": "/resources"}, @@ -1190,6 +1431,16 @@ def eschatology_page(request: Request): ) +@router.get("/eschatology/pdf") +def eschatology_page_pdf(): + return _resource_index_pdf_response( + ESCHATOLOGY_DATA, + page_title="Eschatology", + page_subtitle="The Doctrine of Last Things", + page_description="An expansive theological study of Eschatology - the doctrine of death, resurrection, the second coming of Christ, final judgment, and eternal destinies." + ) + + @router.get("/eschatology/{item_slug}", response_class=HTMLResponse) def eschatology_detail(request: Request, item_slug: str): """Individual Eschatology topic detail page.""" @@ -1231,6 +1482,7 @@ def ecclesiology_page(request: Request): "page_subtitle": "The Doctrine of the Church", "page_description": "An expansive theological study of Ecclesiology - the doctrine of the church, its nature, marks, government, mission, and ordinances.", "base_url": "/ecclesiology", + "pdf_available": WEASYPRINT_AVAILABLE, "breadcrumbs": [ {"text": "Home", "url": "/"}, {"text": "Resources", "url": "/resources"}, @@ -1240,6 +1492,16 @@ def ecclesiology_page(request: Request): ) +@router.get("/ecclesiology/pdf") +def ecclesiology_page_pdf(): + return _resource_index_pdf_response( + ECCLESIOLOGY_DATA, + page_title="Ecclesiology", + page_subtitle="The Doctrine of the Church", + page_description="An expansive theological study of Ecclesiology - the doctrine of the church, its nature, marks, government, mission, and ordinances." + ) + + @router.get("/ecclesiology/{item_slug}", response_class=HTMLResponse) def ecclesiology_detail(request: Request, item_slug: str): """Individual Ecclesiology topic detail page.""" @@ -1281,6 +1543,7 @@ def types_and_shadows_page(request: Request): "page_subtitle": "Old Testament Figures Fulfilled in Christ", "page_description": "An expansive study of Old Testament types and shadows pointing to Christ - persons, events, and institutions that prefigure and find their fulfillment in Jesus.", "base_url": "/types-and-shadows", + "pdf_available": WEASYPRINT_AVAILABLE, "breadcrumbs": [ {"text": "Home", "url": "/"}, {"text": "Resources", "url": "/resources"}, @@ -1290,6 +1553,16 @@ def types_and_shadows_page(request: Request): ) +@router.get("/types-and-shadows/pdf") +def types_and_shadows_page_pdf(): + return _resource_index_pdf_response( + TYPES_AND_SHADOWS_DATA, + page_title="Types and Shadows of Christ", + page_subtitle="Old Testament Figures Fulfilled in Christ", + page_description="An expansive study of Old Testament types and shadows pointing to Christ - persons, events, and institutions that prefigure and find their fulfillment in Jesus." + ) + + @router.get("/types-and-shadows/{item_slug}", response_class=HTMLResponse) def types_and_shadows_detail(request: Request, item_slug: str): """Individual Types and Shadows topic detail page.""" @@ -1331,6 +1604,7 @@ def messianic_prophecies_page(request: Request): "page_subtitle": "Old Testament Predictions Fulfilled in Christ", "page_description": "An expansive study of Messianic prophecies - Old Testament predictions concerning the Messiah's coming, ministry, suffering, and triumph, all fulfilled in Jesus Christ.", "base_url": "/messianic-prophecies", + "pdf_available": WEASYPRINT_AVAILABLE, "breadcrumbs": [ {"text": "Home", "url": "/"}, {"text": "Resources", "url": "/resources"}, @@ -1340,6 +1614,16 @@ def messianic_prophecies_page(request: Request): ) +@router.get("/messianic-prophecies/pdf") +def messianic_prophecies_page_pdf(): + return _resource_index_pdf_response( + MESSIANIC_PROPHECIES_DATA, + page_title="Messianic Prophecies", + page_subtitle="Old Testament Predictions Fulfilled in Christ", + page_description="An expansive study of Messianic prophecies - Old Testament predictions concerning the Messiah's coming, ministry, suffering, and triumph, all fulfilled in Jesus Christ." + ) + + @router.get("/messianic-prophecies/{item_slug}", response_class=HTMLResponse) def messianic_prophecies_detail(request: Request, item_slug: str): """Individual Messianic Prophecy topic detail page.""" @@ -1381,6 +1665,7 @@ def blood_in_scripture_page(request: Request): "page_subtitle": "The Theology of Redemption Through Blood", "page_description": "An expansive study of the blood in Scripture - its significance, Old Testament foundations, and ultimate fulfillment in the blood of Christ for redemption, justification, and cleansing.", "base_url": "/blood-in-scripture", + "pdf_available": WEASYPRINT_AVAILABLE, "breadcrumbs": [ {"text": "Home", "url": "/"}, {"text": "Resources", "url": "/resources"}, @@ -1390,6 +1675,16 @@ def blood_in_scripture_page(request: Request): ) +@router.get("/blood-in-scripture/pdf") +def blood_in_scripture_page_pdf(): + return _resource_index_pdf_response( + BLOOD_IN_SCRIPTURE_DATA, + page_title="The Blood in Scripture", + page_subtitle="The Theology of Redemption Through Blood", + page_description="An expansive study of the blood in Scripture - its significance, Old Testament foundations, and ultimate fulfillment in the blood of Christ for redemption, justification, and cleansing." + ) + + @router.get("/blood-in-scripture/{item_slug}", response_class=HTMLResponse) def blood_in_scripture_detail(request: Request, item_slug: str): """Individual Blood in Scripture topic detail page.""" @@ -1431,6 +1726,7 @@ def kingdom_of_god_page(request: Request): "page_subtitle": "The Reign of God Through Christ", "page_description": "An expansive study of the Kingdom of God - its nature, King, entrance requirements, growth, and ultimate consummation at Christ's return.", "base_url": "/kingdom-of-god", + "pdf_available": WEASYPRINT_AVAILABLE, "breadcrumbs": [ {"text": "Home", "url": "/"}, {"text": "Resources", "url": "/resources"}, @@ -1440,6 +1736,16 @@ def kingdom_of_god_page(request: Request): ) +@router.get("/kingdom-of-god/pdf") +def kingdom_of_god_page_pdf(): + return _resource_index_pdf_response( + KINGDOM_OF_GOD_DATA, + page_title="The Kingdom of God", + page_subtitle="The Reign of God Through Christ", + page_description="An expansive study of the Kingdom of God - its nature, King, entrance requirements, growth, and ultimate consummation at Christ's return." + ) + + @router.get("/kingdom-of-god/{item_slug}", response_class=HTMLResponse) def kingdom_of_god_detail(request: Request, item_slug: str): """Individual Kingdom of God topic detail page.""" @@ -1481,6 +1787,7 @@ def names_of_christ_page(request: Request): "page_subtitle": "The Glorious Designations of Our Lord", "page_description": "An expansive study of the names and titles of Jesus Christ - divine names, messianic titles, redemptive designations, and relational names revealing His person and work.", "base_url": "/names-of-christ", + "pdf_available": WEASYPRINT_AVAILABLE, "breadcrumbs": [ {"text": "Home", "url": "/"}, {"text": "Resources", "url": "/resources"}, @@ -1490,6 +1797,16 @@ def names_of_christ_page(request: Request): ) +@router.get("/names-of-christ/pdf") +def names_of_christ_page_pdf(): + return _resource_index_pdf_response( + NAMES_OF_CHRIST_DATA, + page_title="Names and Titles of Christ", + page_subtitle="The Glorious Designations of Our Lord", + page_description="An expansive study of the names and titles of Jesus Christ - divine names, messianic titles, redemptive designations, and relational names revealing His person and work." + ) + + @router.get("/names-of-christ/{item_slug}", response_class=HTMLResponse) def names_of_christ_detail(request: Request, item_slug: str): """Individual Names of Christ topic detail page.""" @@ -1531,6 +1848,7 @@ def spirits_and_demons_page(request: Request): "page_subtitle": "Biblical Demonology and Spiritual Warfare", "page_description": "A comprehensive study of demons, Satan, evil spirits, and spiritual warfare in Scripture—from Legion to the Lake of Fire.", "base_url": "/spirits-and-demons", + "pdf_available": WEASYPRINT_AVAILABLE, "breadcrumbs": [ {"text": "Home", "url": "/"}, {"text": "Resources", "url": "/resources"}, @@ -1540,6 +1858,16 @@ def spirits_and_demons_page(request: Request): ) +@router.get("/spirits-and-demons/pdf") +def spirits_and_demons_page_pdf(): + return _resource_index_pdf_response( + SPIRITS_AND_DEMONS_DATA, + page_title="Spirits & Demons", + page_subtitle="Biblical Demonology and Spiritual Warfare", + page_description="A comprehensive study of demons, Satan, evil spirits, and spiritual warfare in Scripture—from Legion to the Lake of Fire." + ) + + @router.get("/spirits-and-demons/{item_slug}", response_class=HTMLResponse) def spirits_and_demons_detail(request: Request, item_slug: str): """Individual Spirits and Demons topic detail page.""" @@ -1581,6 +1909,7 @@ def personifications_page(request: Request): "page_subtitle": "Abstract Concepts Given Human Form", "page_description": "A study of biblical personifications—Wisdom, Folly, Death, Sin, and other abstract concepts portrayed as persons throughout Scripture.", "base_url": "/personifications", + "pdf_available": WEASYPRINT_AVAILABLE, "breadcrumbs": [ {"text": "Home", "url": "/"}, {"text": "Resources", "url": "/resources"}, @@ -1590,6 +1919,16 @@ def personifications_page(request: Request): ) +@router.get("/personifications/pdf") +def personifications_page_pdf(): + return _resource_index_pdf_response( + PERSONIFICATIONS_DATA, + page_title="Personifications in Scripture", + page_subtitle="Abstract Concepts Given Human Form", + page_description="A study of biblical personifications—Wisdom, Folly, Death, Sin, and other abstract concepts portrayed as persons throughout Scripture." + ) + + @router.get("/personifications/{item_slug}", response_class=HTMLResponse) def personifications_detail(request: Request, item_slug: str): """Individual Personification topic detail page.""" diff --git a/kjvstudy_org/server.py b/kjvstudy_org/server.py index 3bbc566..a6c1ada 100644 --- a/kjvstudy_org/server.py +++ b/kjvstudy_org/server.py @@ -1732,11 +1732,37 @@ def reading_plan_detail(request: Request, plan_id: str): "plan": plan, "plan_id": plan_id, "books": books, - "breadcrumbs": breadcrumbs + "breadcrumbs": breadcrumbs, + "pdf_available": WEASYPRINT_AVAILABLE, + "pdf_url": f"/reading-plans/{plan_id}/pdf" if WEASYPRINT_AVAILABLE else None } ) +@app.get("/reading-plans/{plan_id}/pdf") +def reading_plan_pdf(plan_id: str): + """Generate a PDF export for a reading plan.""" + if not WEASYPRINT_AVAILABLE: + raise HTTPException( + status_code=503, + detail="PDF generation is not available. WeasyPrint system libraries are not installed." + ) + + plan = get_plan(plan_id) + if not plan: + raise HTTPException(status_code=404, detail="Reading plan not found") + + html_content = templates.get_template("reading_plan_pdf.html").render(plan=plan) + pdf_buffer = render_html_to_pdf(html_content) + + filename = f"reading-plan-{plan_id}.pdf" + return StreamingResponse( + pdf_buffer, + media_type="application/pdf", + headers={"Content-Disposition": f"attachment; filename={filename}"} + ) + + @app.get("/topics", response_class=HTMLResponse) def topics_page(request: Request): """Browse topical index of Bible themes""" diff --git a/kjvstudy_org/templates/biblical_angels.html b/kjvstudy_org/templates/biblical_angels.html index 4e089c2..51d46f1 100644 --- a/kjvstudy_org/templates/biblical_angels.html +++ b/kjvstudy_org/templates/biblical_angels.html @@ -80,6 +80,35 @@ line-height: 1.8; } +.angels-actions { + margin: 1rem 0 1.5rem; +} + +.angels-download-btn { + display: inline-flex; + align-items: center; + gap: 0.35rem; + padding: 0.35rem 0.75rem; + font-size: 0.85rem; + color: var(--text-secondary, #666); + background: var(--code-bg, #f8f8f8); + border: 1px solid var(--border-color, #ddd); + border-radius: 4px; + text-decoration: none; + transition: all 0.2s; +} + +.angels-download-btn:hover { + background: var(--bg-color, #fff); + border-color: var(--link-color); + color: var(--link-color); +} + +.angels-download-btn svg { + width: 14px; + height: 14px; +} + .intro-text { max-width: 60%; font-size: 1.2rem; @@ -134,6 +163,17 @@

Biblical Angels

Angelic Beings in Holy Scripture

+{% if pdf_available %} +
+ + + + + Download All Angels (PDF) + +
+{% endif %} +