Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions coltrane/manifest.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,8 @@ def render_html(self):
# Mock an HttpRequest when generating the HTML for static sites
request = StaticRequest(path=self.url_slug)

(template, context) = render_markdown(self.slug, request)
rendered_html = render_to_string(template, context)
rendered_markdown = render_markdown(self.slug, request)
rendered_html = render_to_string(rendered_markdown.content, rendered_markdown.metadata)

return rendered_html

Expand Down
71 changes: 40 additions & 31 deletions coltrane/renderer.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import logging
from dataclasses import dataclass, field
from pathlib import Path
from typing import Dict, Optional, Tuple, Union

from django.conf import settings
from django.http import HttpRequest
from django.template import engines
from django.utils.html import mark_safe # type: ignore
Expand Down Expand Up @@ -56,6 +56,12 @@ def is_secure(self) -> bool:
return self.path.startswith("https://")


@dataclass
class RenderedMarkdown:
content: str
metadata: Dict


def _parse_and_update_metadata(content: Markdown) -> dict:
"""
Add new, parse and/or cast existing values to metadata.
Expand All @@ -80,59 +86,60 @@ def _parse_and_update_metadata(content: Markdown) -> dict:
return metadata


def render_markdown_path(path) -> Tuple[str, Optional[Dict]]:
def _get_markdown_content_as_html(slug: str) -> RenderedMarkdown:
"""
Converts markdown file based on the slug into HTML.
"""

path = get_content_directory() / f"{slug}.md"

return render_markdown_path(path)


def render_markdown_path(path: Path) -> RenderedMarkdown:
"""
Renders the markdown file located at path.
"""

markdown_extras = get_markdown_extras()

content = markdown_path(
path,
extras=markdown_extras,
)

metadata = _parse_and_update_metadata(content)

return (str(content), metadata)
return RenderedMarkdown(str(content), metadata)


def render_markdown_text(text: str) -> Tuple[str, Optional[Dict]]:
markdown_extras = get_markdown_extras()

content = markdown(text, extras=markdown_extras)

metadata = _parse_and_update_metadata(content)

return (str(content), metadata)


def _get_markdown_content_as_html(slug: str) -> Tuple[str, Optional[Dict]]:
def render_markdown_text(text: str) -> RenderedMarkdown:
"""
Converts markdown file based on the slug into HTML.
Renders the markdown text.
"""

path = get_content_directory() / f"{slug}.md"
markdown_extras = get_markdown_extras()
content = markdown(text, extras=markdown_extras)
metadata = _parse_and_update_metadata(content)

return render_markdown_path(path)
return RenderedMarkdown(str(content), metadata)


def render_html_with_django(
html: str, context: Dict, request: HttpRequest = None
rendered_markdown: RenderedMarkdown, request: HttpRequest = None
) -> str:
"""
Takes the rendered HTML from the markdown uses Django to fill in any template
variables from the `context` dictionary.
"""

django_engine = engines["django"]
template = django_engine.from_string(html)
template = django_engine.from_string(rendered_markdown.content)

return str(template.render(context=context, request=request))
return str(template.render(context=rendered_markdown.metadata, request=request))


def get_html_and_markdown(slug: str) -> Tuple[str, Dict]:
(html, metadata) = _get_markdown_content_as_html(slug)
def get_html_and_markdown(slug: str) -> RenderedMarkdown:
rendered_markdown = _get_markdown_content_as_html(slug)
metadata = rendered_markdown.metadata

if metadata is None:
metadata = {}
Expand All @@ -142,28 +149,30 @@ def get_html_and_markdown(slug: str) -> Tuple[str, Dict]:

metadata["slug"] = slug

return (html, metadata)
rendered_markdown.metadata = metadata

return rendered_markdown


def render_markdown(
slug: str, request: Union[HttpRequest, StaticRequest]
) -> Tuple[str, Dict]:
) -> RenderedMarkdown:
"""
Renders the markdown from the `slug` by:
1. Rendering the markdown file into HTML
2. Passing the HTML through Django to fill in template variables based on
data in JSON files and markdown frontmatter

Returns:
Tuple of template file name (i.e. `coltrane/content.html`) and context dictionary.
`RenderedMarkdown` with template file name (i.e. `coltrane/content.html`) and context dictionary.
"""

(html, metadata) = get_html_and_markdown(slug)
rendered_markdown = get_html_and_markdown(slug)

context = {}

# Start with any metadata from the markdown frontmatter
context.update(metadata)
context.update(rendered_markdown.metadata)

# Add JSON data to the context
data = get_data()
Expand All @@ -173,8 +182,8 @@ def render_markdown(
context["request"] = request

# Add rendered content to the context
content = render_html_with_django(html, context, request)
content = render_html_with_django(rendered_markdown, request)
context["content"] = mark_safe(content)
template = context["template"]

return (template, context)
return RenderedMarkdown(template, context)
7 changes: 4 additions & 3 deletions coltrane/retriever.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,9 @@ def get_content_items(skip_draft: bool = True) -> Iterable[ContentItem]:
content_directory_path_length = len(str(content_directory))

for path in paths:
(html, metadata) = render_markdown_path(path)
rendered_markdown = render_markdown_path(path)

if skip_draft and "draft" in metadata and metadata["draft"] is True:
if skip_draft and "draft" in rendered_markdown.metadata and rendered_markdown.metadata["draft"] is True:
continue

path_str = str(path)
Expand All @@ -119,7 +119,8 @@ def get_content_items(skip_draft: bool = True) -> Iterable[ContentItem]:
relative_url = relative_url[:-6]

content_item = ContentItem(
path=path, metadata=metadata, html=html, relative_url=relative_url
path=path, metadata=rendered_markdown.metadata,
html=rendered_markdown.content, relative_url=relative_url
)
_items.append(content_item)

Expand Down
17 changes: 9 additions & 8 deletions coltrane/templatetags/coltrane_tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,9 @@ def directory_contents(
if exclude == content_slug:
continue

(_, metadata) = get_html_and_markdown(content_slug)
rendered_markdown = get_html_and_markdown(content_slug)

contents.append(metadata)
contents.append(rendered_markdown.metadata)

return contents

Expand Down Expand Up @@ -109,14 +109,14 @@ def render(self, context):
template = context.template.engine.select_template((template_name,))
cache[template_name] = template

(html, metadata) = render_markdown_path(template.origin.name)
rendered_markdown = render_markdown_path(template.origin.name)

for c in context:
for key, value in c.items():
if key not in metadata:
metadata[key] = value
if key not in rendered_markdown.metadata:
rendered_markdown.metadata[key] = value

return render_html_with_django(html, metadata)
return render_html_with_django(rendered_markdown)


@register.tag("include_md")
Expand Down Expand Up @@ -146,7 +146,8 @@ def do_include_md(parser, token):

@register.filter(takes_context=True)
def to_html(context: dict, text: str) -> str:
(html, metadata) = render_markdown_text(text)
rendered_markdown = render_markdown_text(text)

return mark_safe(
render_html_with_django(html, metadata, request=context["request"])
render_html_with_django(rendered_markdown, request=context["request"])
)
34 changes: 15 additions & 19 deletions coltrane/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from django.utils.cache import patch_response_headers

from .config.cache import ViewCache
from .renderer import render_markdown
from .renderer import RenderedMarkdown, render_markdown


logger = logging.getLogger(__name__)
Expand All @@ -28,26 +28,25 @@ def _normalize_slug(slug: str) -> str:
return slug


def _get_from_cache_if_enabled(slug: str) -> Tuple[str, Dict]:
def _get_from_cache_if_enabled(slug: str) -> RenderedMarkdown:
"""
Gets the slug from the cache if it's enabled.
"""

template = None
context = None
rendered_markdown = None
view_cache = ViewCache()

if view_cache.is_enabled:
cache_key = f"{view_cache.cache_key_namespace}{slug}"
cached_value = view_cache.cache.get(cache_key)

if cached_value:
(template, context) = cached_value
rendered_markdown = cached_value

return (template, context)
return rendered_markdown


def _set_in_cache_if_enabled(slug: str, template: str, context: Dict) -> None:
def _set_in_cache_if_enabled(slug: str, rendered_markdown: RenderedMarkdown) -> None:
"""
Sets everything in the cache if it's enabled.
"""
Expand All @@ -58,7 +57,7 @@ def _set_in_cache_if_enabled(slug: str, template: str, context: Dict) -> None:
cache_key = f"{view_cache.cache_key_namespace}{slug}"
view_cache.cache.set(
cache_key,
(template, context),
rendered_markdown,
view_cache.seconds,
)

Expand All @@ -70,28 +69,25 @@ def content(request: HttpRequest, slug: str = "index"):
Will cache the rendered content if enabled.
"""

template = ""
context = {}
slug = _normalize_slug(slug)

(template, context) = _get_from_cache_if_enabled(slug)
rendered_markdown = _get_from_cache_if_enabled(slug)

try:
if not template or not context:
(template, context) = render_markdown(slug, request=request)
_set_in_cache_if_enabled(slug, template, context)
if not rendered_markdown:
rendered_markdown = render_markdown(slug, request=request)
_set_in_cache_if_enabled(slug, rendered_markdown)
except FileNotFoundError:
try:
slug_with_index = f"{slug}/index"
(template, context) = render_markdown(slug_with_index, request=request)
_set_in_cache_if_enabled(slug_with_index, template, context)
rendered_markdown = render_markdown(slug_with_index, request=request)
_set_in_cache_if_enabled(slug_with_index, rendered_markdown)
except FileNotFoundError:
raise Http404(f"{slug} cannot be found")

response = render(
request,
template,
context=context,
rendered_markdown.content,
context=rendered_markdown.metadata,
)

view_cache = ViewCache()
Expand Down
34 changes: 17 additions & 17 deletions tests/renderer/test_get_html_and_markdown.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
from pathlib import Path
from unittest.mock import patch

from coltrane.renderer import get_html_and_markdown
from coltrane.renderer import RenderedMarkdown, get_html_and_markdown


@patch(
"coltrane.renderer._get_markdown_content_as_html",
return_value=("some-content", None),
return_value=(RenderedMarkdown("some-content", None)),
)
def test_handle_none_metadata(_get_markdown_content_as_html):
get_html_and_markdown("some-slug")
Expand All @@ -25,10 +25,10 @@ def test_publish_date_in_metadata(settings, tmp_path: Path):
"""
)

(_, metadata) = get_html_and_markdown("test-1")
rendered_markdown = get_html_and_markdown("test-1")

assert isinstance(metadata["publish_date"], datetime)
assert metadata["publish_date"] == datetime(2022, 2, 26, 10, 26, 2, 0)
assert isinstance(rendered_markdown.metadata["publish_date"], datetime)
assert rendered_markdown.metadata["publish_date"] == datetime(2022, 2, 26, 10, 26, 2, 0)


def test_draft_true_in_metadata(settings, tmp_path: Path):
Expand All @@ -43,10 +43,10 @@ def test_draft_true_in_metadata(settings, tmp_path: Path):
"""
)

(_, metadata) = get_html_and_markdown("test-1")
rendered_markdown = get_html_and_markdown("test-1")

assert isinstance(metadata["draft"], bool)
assert metadata["draft"]
assert isinstance(rendered_markdown.metadata["draft"], bool)
assert rendered_markdown.metadata["draft"]


def test_draft_false_in_metadata(settings, tmp_path: Path):
Expand All @@ -61,10 +61,10 @@ def test_draft_false_in_metadata(settings, tmp_path: Path):
"""
)

(_, metadata) = get_html_and_markdown("test-1")
rendered_markdown = get_html_and_markdown("test-1")

assert isinstance(metadata["draft"], bool)
assert not metadata["draft"]
assert isinstance(rendered_markdown.metadata["draft"], bool)
assert not rendered_markdown.metadata["draft"]


def test_draft_string_in_metadata(settings, tmp_path: Path):
Expand All @@ -79,10 +79,10 @@ def test_draft_string_in_metadata(settings, tmp_path: Path):
"""
)

(_, metadata) = get_html_and_markdown("test-1")
rendered_markdown = get_html_and_markdown("test-1")

assert isinstance(metadata["draft"], bool)
assert not metadata["draft"]
assert isinstance(rendered_markdown.metadata["draft"], bool)
assert not rendered_markdown.metadata["draft"]


def test_draft_1_in_metadata(settings, tmp_path: Path):
Expand All @@ -97,7 +97,7 @@ def test_draft_1_in_metadata(settings, tmp_path: Path):
"""
)

(_, metadata) = get_html_and_markdown("test-1")
rendered_markdown = get_html_and_markdown("test-1")

assert isinstance(metadata["draft"], bool)
assert not metadata["draft"]
assert isinstance(rendered_markdown.metadata["draft"], bool)
assert not rendered_markdown.metadata["draft"]
Loading