diff --git a/kjvstudy_org/server.py b/kjvstudy_org/server.py index 0ba8147..5dcb536 100644 --- a/kjvstudy_org/server.py +++ b/kjvstudy_org/server.py @@ -189,27 +189,33 @@ templates = Jinja2Templates(directory=str(templates_dir)) # Register custom Jinja2 filters templates.env.filters['slugify'] = create_slug +# Initialize mistune for markdown rendering +import mistune + +# Create mistune instance for full markdown (with paragraphs) +_markdown = mistune.create_markdown(escape=False, hard_wrap=False) + +# Create inline renderer for markdown without paragraph wrapping +_inline_markdown = mistune.create_markdown( + renderer=mistune.HTMLRenderer(escape=False), + plugins=['strikethrough'] +) + def markdown_inline(text): - """Convert inline markdown to HTML (bold and italic only, no paragraph wrapping).""" + """Convert inline markdown to HTML (bold, italic, etc. - no paragraph wrapping).""" if not text: return text - # Convert **bold** to bold (must be done before italic) - text = re.sub(r'\*\*(.+?)\*\*', r'\1', text) - # Convert *italic* to italic - text = re.sub(r'\*(.+?)\*', r'\1', text) - return text + # Render and strip any outer

tags that mistune might add + html = _inline_markdown(text).strip() + if html.startswith('

') and html.endswith('

'): + html = html[3:-4] + return html def markdown_to_html(text): - """Convert simple markdown to HTML (bold, italic, and paragraphs).""" + """Convert markdown to HTML (bold, italic, paragraphs, etc.).""" if not text: return text - # First apply inline markdown - text = markdown_inline(text) - # Convert paragraphs (split on double newlines) - paragraphs = text.split('\n\n') - # Always wrap in

tags - text = ''.join(f'

{p.strip()}

' for p in paragraphs if p.strip()) - return text + return _markdown(text).strip() templates.env.filters['md'] = markdown_to_html templates.env.filters['mdi'] = markdown_inline diff --git a/kjvstudy_org/stories.py b/kjvstudy_org/stories.py index 7b40bde..4a2d0cb 100644 --- a/kjvstudy_org/stories.py +++ b/kjvstudy_org/stories.py @@ -86,14 +86,25 @@ def get_category_by_slug(category_slug: str) -> Optional[dict]: return None +# Cache story counts +_CACHED_STORY_COUNT = None +_CACHED_CATEGORY_COUNT = None + + def get_story_count() -> int: - """Get total number of stories.""" - return len(get_all_stories_flat()) + """Get total number of stories (cached).""" + global _CACHED_STORY_COUNT + if _CACHED_STORY_COUNT is None: + _CACHED_STORY_COUNT = len(get_all_stories_flat()) + return _CACHED_STORY_COUNT def get_category_count() -> int: - """Get total number of categories.""" - return len(load_all_stories()) + """Get total number of categories (cached).""" + global _CACHED_CATEGORY_COUNT + if _CACHED_CATEGORY_COUNT is None: + _CACHED_CATEGORY_COUNT = len(load_all_stories()) + return _CACHED_CATEGORY_COUNT # Pre-load categories on module import for faster access @@ -110,5 +121,7 @@ def get_categories() -> list[dict]: def refresh_stories(): """Refresh the cached stories (useful after adding new files).""" - global STORY_CATEGORIES + global STORY_CATEGORIES, _CACHED_STORY_COUNT, _CACHED_CATEGORY_COUNT STORY_CATEGORIES = load_all_stories() + _CACHED_STORY_COUNT = None + _CACHED_CATEGORY_COUNT = None diff --git a/kjvstudy_org/utils/helpers.py b/kjvstudy_org/utils/helpers.py index 737ab78..38e78e3 100644 --- a/kjvstudy_org/utils/helpers.py +++ b/kjvstudy_org/utils/helpers.py @@ -208,7 +208,7 @@ def get_chapter_popularity_score(book: str, chapter: int) -> int: if book in HIGH_READERSHIP_BOOKS: default_score += 1 - total_chapters = len([ch for bk, ch in bible.iter_chapters() if bk == book]) + total_chapters = len(bible.get_chapters_for_book(book)) if total_chapters <= 5: default_score += 1 diff --git a/pyproject.toml b/pyproject.toml index abf71df..42e2a45 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,6 +8,7 @@ dependencies = [ "biblepy>=0.1.3", "fastapi[standard]>=0.115.12", "ged4py>=0.5.2", + "mistune>=3.0.2", "parse>=1.20.2", "python-gedcom>=1.0.0", "requests>=2.32.3", diff --git a/uv.lock b/uv.lock index dd84656..33147a6 100644 --- a/uv.lock +++ b/uv.lock @@ -483,6 +483,7 @@ dependencies = [ { name = "biblepy" }, { name = "fastapi", extra = ["standard"] }, { name = "ged4py" }, + { name = "mistune" }, { name = "parse" }, { name = "python-gedcom" }, { name = "requests" }, @@ -506,6 +507,7 @@ requires-dist = [ { name = "biblepy", specifier = ">=0.1.3" }, { name = "fastapi", extras = ["standard"], specifier = ">=0.115.12" }, { name = "ged4py", specifier = ">=0.5.2" }, + { name = "mistune", specifier = ">=3.0.2" }, { name = "parse", specifier = ">=1.20.2" }, { name = "pytest", marker = "extra == 'dev'", specifier = ">=8.3.5" }, { name = "pytest-cov", marker = "extra == 'dev'", specifier = ">=6.0.0" }, @@ -568,6 +570,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, ] +[[package]] +name = "mistune" +version = "3.1.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/02/a7fb8b21d4d55ac93cdcde9d3638da5dd0ebdd3a4fed76c7725e10b81cbe/mistune-3.1.4.tar.gz", hash = "sha256:b5a7f801d389f724ec702840c11d8fc48f2b33519102fc7ee739e8177b672164", size = 94588, upload-time = "2025-08-29T07:20:43.594Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7a/f0/8282d9641415e9e33df173516226b404d367a0fc55e1a60424a152913abc/mistune-3.1.4-py3-none-any.whl", hash = "sha256:93691da911e5d9d2e23bc54472892aff676df27a75274962ff9edc210364266d", size = 53481, upload-time = "2025-08-29T07:20:42.218Z" }, +] + [[package]] name = "packaging" version = "25.0"