Skip to content

Commit 19f04a7

Browse files
committed
Generate sitemap.xml when building.
1 parent 11545b3 commit 19f04a7

10 files changed

Lines changed: 134 additions & 25 deletions

File tree

coltrane/__init__.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ def _merge_installed_apps(
140140

141141
def _get_from_env(env_name: str) -> List[str]:
142142
"""
143-
Retrieves environment value that could potentially be an list of strings.
143+
Retrieves environment value that could potentially be a list of strings.
144144
"""
145145

146146
env_values = []
@@ -233,6 +233,11 @@ def _merge_settings(base_dir: Path, django_settings: Dict[str, Any]) -> Dict[str
233233
"SETTINGS_MODULE": "coltrane",
234234
}
235235

236+
coltrane_site = getenv("COLTRANE_SITE")
237+
238+
if coltrane_site:
239+
default_settings["COLTRANE"]["SITE"] = coltrane_site
240+
236241
if is_whitenoise_installed:
237242
default_settings["WHITENOISE_MANIFEST_STRICT"] = False
238243
default_settings[

coltrane/config/settings.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,7 @@
1919
"toc",
2020
]
2121

22-
DEFAULT_COLTRANE_SETTINGS = {
23-
"MARKDOWN_EXTRAS": DEFAULT_MARKDOWN_EXTRAS,
24-
}
22+
DEFAULT_COLTRANE_SETTINGS = {"MARKDOWN_EXTRAS": DEFAULT_MARKDOWN_EXTRAS}
2523

2624

2725
def get_coltrane_settings() -> Dict:

coltrane/management/commands/build.py

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@
66
from multiprocessing import cpu_count
77
from pathlib import Path
88
from types import SimpleNamespace
9-
from typing import Dict
9+
from typing import Dict, List
1010

1111
from django.conf import settings
12+
from django.contrib.sitemaps.views import sitemap
1213
from django.core import management
1314
from django.core.management.base import BaseCommand
1415

@@ -22,7 +23,9 @@
2223
get_output_static_directory,
2324
)
2425
from coltrane.manifest import Manifest, ManifestItem
26+
from coltrane.renderer import StaticRequest
2527
from coltrane.retriever import get_content_paths
28+
from coltrane.urls import sitemaps
2629
from coltrane.utils import threadpool
2730

2831

@@ -65,6 +68,15 @@ def add_arguments(self, parser):
6568
def _load_manifest(self) -> Manifest:
6669
return Manifest(manifest_file=get_output_json())
6770

71+
def _generate_sitemap(self) -> None:
72+
assert self.output_directory
73+
74+
sitemap_response = sitemap(
75+
request=StaticRequest(path="/", META={}), sitemaps=sitemaps
76+
)
77+
sitemap_xml = sitemap_response.render().content.decode()
78+
(self.output_directory / "sitemap.xml").write_text(sitemap_xml)
79+
6880
@threadpool
6981
def _call_collectstatic(self) -> str:
7082
stdout = StringIO()
@@ -163,12 +175,13 @@ def _set_output_directory(self, options: Dict) -> None:
163175
pass
164176

165177
def handle(self, *args, **options):
166-
self.is_force = False
167-
self.manifest = None
178+
self.is_force: bool = False
179+
self.manifest: Manifest = None
168180
self.output_result_counts.create_count = 0
169181
self.output_result_counts.update_count = 0
170182
self.output_result_counts.skip_count = 0
171-
self.errors = []
183+
self.output_directory: Path = None
184+
self.errors: List[str] = []
172185

173186
start_time = time.time()
174187

@@ -180,10 +193,10 @@ def handle(self, *args, **options):
180193

181194
collectstatic_future = self._call_collectstatic()
182195

183-
output_directory = get_output_directory()
196+
self.output_directory = get_output_directory()
184197
self._success("Use output directory of ", ending="")
185-
self.stdout.write(self.style.WARNING(str(output_directory)))
186-
output_directory.mkdir(exist_ok=True)
198+
self.stdout.write(self.style.WARNING(str(self.output_directory)))
199+
self.output_directory.mkdir(exist_ok=True)
187200

188201
spinner.start("Load manifest")
189202
self.manifest = self._load_manifest()
@@ -250,6 +263,10 @@ def handle(self, *args, **options):
250263
spinner.succeed(result_msg)
251264

252265
if self.manifest.is_dirty:
266+
spinner.start("Update sitemap.xml")
267+
self._generate_sitemap()
268+
spinner.succeed()
269+
253270
spinner.start("Update manifest")
254271
self.manifest.write_data()
255272
spinner.succeed()

coltrane/manifest.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,6 @@ def render_html(self):
113113

114114
(template, context) = render_markdown(self.slug, request)
115115
rendered_html = render_to_string(template, context)
116-
print("rendered_htmlrendered_htmlrendered_html", rendered_html)
117116

118117
return rendered_html
119118

coltrane/renderer.py

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import logging
2-
from dataclasses import dataclass
2+
from dataclasses import dataclass, field
33
from typing import Dict, Optional, Tuple, Union
44

5+
from django.conf import settings
56
from django.http import HttpRequest
67
from django.template import engines
78
from django.utils.html import mark_safe # type: ignore
@@ -29,7 +30,22 @@ class StaticRequest:
2930
"""
3031

3132
path: str
32-
META: Dict
33+
META: Dict = field(default_factory=dict)
34+
GET: Dict = field(default_factory=dict)
35+
36+
@property
37+
def site(self):
38+
_site = settings.COLTRANE.get("SITE")
39+
assert _site, "COLTRANE_SITE is required in .env"
40+
41+
return _site
42+
43+
@property
44+
def scheme(self) -> str:
45+
return self.site.split("://")[0]
46+
47+
def get_host(self) -> str:
48+
return self.site.split("://")[1]
3349

3450

3551
def render_markdown_path(path) -> Dict[str, Optional[Dict]]:
@@ -48,6 +64,17 @@ def render_markdown_path(path) -> Dict[str, Optional[Dict]]:
4864

4965
metadata = content.metadata
5066

67+
if metadata is None:
68+
metadata = {}
69+
70+
if "date" in metadata:
71+
metadata["date"] = dateparser.parse(metadata["date"])
72+
73+
if "draft" in metadata:
74+
metadata["draft"] = metadata["draft"] == "true"
75+
76+
metadata["now"] = now()
77+
5178
return (str(content), metadata)
5279

5380

@@ -86,14 +113,6 @@ def get_html_and_markdown(slug: str) -> Tuple[str, Dict]:
86113

87114
metadata["slug"] = slug
88115

89-
if "date" in metadata:
90-
metadata["date"] = dateparser.parse(metadata["date"])
91-
92-
if "draft" in metadata:
93-
metadata["draft"] = metadata["draft"] == "true"
94-
95-
metadata["now"] = now
96-
97116
return (html, metadata)
98117

99118

coltrane/sitemaps.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from django.contrib.sitemaps import Sitemap
44

5+
from coltrane.renderer import render_markdown_path
56
from coltrane.retriever import get_content_paths
67

78

@@ -11,7 +12,19 @@ class ContentSitemap(Sitemap):
1112

1213
def items(self):
1314
# TODO: This could get cached so it isn't re-generated every time
14-
return list(get_content_paths())
15+
paths = get_content_paths()
16+
_items = []
17+
18+
for path in paths:
19+
(_, metadata) = render_markdown_path(path)
20+
21+
if "draft" in metadata:
22+
if metadata["draft"]:
23+
continue
24+
25+
_items.append(path)
26+
27+
return _items
1528

1629
def location(self, path: Path):
1730
relative_url = str(path)[7:-3]

conftest.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,23 @@
44
from django.conf import settings
55

66
from coltrane import _get_default_template_settings
7+
from coltrane.config.settings import DEFAULT_COLTRANE_SETTINGS
78

89

910
def pytest_configure():
1011
base_dir = Path(".")
1112

1213
templates = deepcopy(_get_default_template_settings(base_dir))
14+
coltrane_settings = deepcopy(DEFAULT_COLTRANE_SETTINGS)
15+
coltrane_settings["SITE"] = "http://localhost"
1316

1417
settings.configure(
1518
BASE_DIR=base_dir,
1619
SECRET_KEY="this-is-a-secret",
1720
TEMPLATES=templates,
1821
ROOT_URLCONF="coltrane.urls",
1922
INSTALLED_APPS=[
23+
"django.contrib.sitemaps",
2024
"django_fastdev.django_app.FastDevConfig",
2125
"coltrane",
2226
],
@@ -27,4 +31,5 @@ def pytest_configure():
2731
},
2832
STATIC_ROOT=base_dir / "output" / "static",
2933
SETTINGS_MODULE="coltrane",
34+
COLTRANE=coltrane_settings,
3035
)

tests/renderer/test_get_markdown_content_as_html.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from pathlib import Path
2+
from unittest.mock import ANY
23

34
from coltrane.config.settings import DEFAULT_MARKDOWN_EXTRAS
45
from coltrane.renderer import _get_markdown_content_as_html
@@ -18,7 +19,7 @@ def test_get_markdown_content_as_html_with_frontmatter(settings, tmp_path: Path)
1819
)
1920

2021
rendered_html = "<p>test data</p>\n"
21-
context = {"template": "test-template.html"}
22+
context = {"template": "test-template.html", "now": ANY}
2223
expected = (rendered_html, context)
2324
actual = _get_markdown_content_as_html("test-1")
2425

@@ -43,7 +44,7 @@ def test_get_markdown_content_as_html_extras_settings(settings, tmp_path: Path):
4344
)
4445

4546
rendered_html = "<p>test data</p>\n"
46-
context = {"template": "test-template.html"}
47+
context = {"template": "test-template.html", "now": ANY}
4748
expected = (rendered_html, context)
4849
actual = _get_markdown_content_as_html("test-1")
4950

tests/renderer/test_render_markdown.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
from dataclasses import dataclass
12
from pathlib import Path
3+
from typing import Dict
4+
from unittest.mock import patch
25

36
from coltrane.renderer import StaticRequest, render_markdown
47

@@ -28,3 +31,19 @@ def test_render_markdown(settings, tmp_path: Path):
2831
assert actual_context.get("content") == expected_content
2932
assert actual_context.get("data") == expected_data
3033
assert actual_context.get("template") == expected_template
34+
35+
36+
@dataclass
37+
class MarkdownContent:
38+
metadata: Dict
39+
40+
def __str__(self):
41+
return "test-content"
42+
43+
44+
@patch("coltrane.renderer.markdown_path", return_value=MarkdownContent(metadata=None))
45+
def test_render_markdown_metadata(settings, tmp_path: Path):
46+
static_request = StaticRequest(path="/", META={})
47+
48+
(_, metadata) = render_markdown("test-2", static_request)
49+
assert metadata.get("content") == "test-content"
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import pytest
2+
3+
from coltrane.renderer import StaticRequest
4+
5+
6+
def test_static_request_scheme(settings):
7+
settings.COLTRANE = {"SITE": "http://localhost"}
8+
request = StaticRequest(path="/")
9+
10+
assert request.scheme == "http"
11+
12+
13+
def test_static_request_get_host(settings):
14+
settings.COLTRANE = {"SITE": "http://localhost"}
15+
request = StaticRequest(path="/")
16+
17+
assert request.get_host() == "localhost"
18+
19+
20+
def test_static_request_missing_site(settings):
21+
settings.COLTRANE = {}
22+
request = StaticRequest(path="/")
23+
24+
with pytest.raises(AssertionError):
25+
request.scheme
26+
27+
28+
def test_static_request_missing_coltrane(settings):
29+
delattr(settings, "COLTRANE")
30+
request = StaticRequest(path="/")
31+
32+
with pytest.raises(AttributeError):
33+
request.scheme

0 commit comments

Comments
 (0)