Skip to content

Commit

Permalink
Build drafts into /draft/uuid/ and not the regular URL
Browse files Browse the repository at this point in the history
  • Loading branch information
Siecje committed Feb 4, 2024
1 parent bebb8bc commit 14b57cd
Show file tree
Hide file tree
Showing 6 changed files with 88 additions and 40 deletions.
34 changes: 5 additions & 29 deletions htmd/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@

import click
from flask import Flask
from flask_flatpages import FlatPages, Page
from flask_flatpages import FlatPages

from .utils import (
combine_and_minify_css,
combine_and_minify_js,
copy_missing_templates,
copy_site_file,
create_directory,
set_post_metadata,
)


Expand Down Expand Up @@ -97,37 +98,12 @@ def verify() -> None:
sys.exit(1)


def set_post_time(
app: Flask,
post: Page,
field: str,
date_time: datetime.datetime,
) -> None:
file_path = (
Path(app.config['FLATPAGES_ROOT'])
/ (post.path + app.config['FLATPAGES_EXTENSION'])
)
with file_path.open('r') as file:
lines = file.readlines()

found = False
with file_path.open('w') as file:
for line in lines:
if not found and field in line:
# Update datetime value
line = f'{field}: {date_time.isoformat()}\n' # noqa: PLW2901
found = True
elif not found and '...' in line:
# Write field and value before '...'
file.write(f'{field}: {date_time.isoformat()}\n')
found = True
file.write(line)


def set_posts_datetime(app: Flask, posts: FlatPages) -> None:
# Ensure each post has a published date
# set time for correct date field
for post in posts:
if post.meta.get('draft', False):
continue
if 'updated' not in post.meta:
published = post.meta.get('published')
if isinstance(published, datetime.datetime):
Expand All @@ -147,7 +123,7 @@ def set_posts_datetime(app: Flask, posts: FlatPages) -> None:
else:
post_datetime = now
post.meta[field] = post_datetime
set_post_time(app, post, field, post_datetime)
set_post_metadata(app, post, field, post_datetime.isoformat())


@cli.command('build', short_help='Create static version of the site.')
Expand Down
4 changes: 3 additions & 1 deletion htmd/example_site/templates/post.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@

{# Open Graph Tags #}
<meta property="og:type" content="article">
<meta property="og:url" content="{{ url_for('post', year=post.meta['published'].year, month=post.meta['published'].strftime('%m'), day=post.meta['published'].strftime('%d'), path=post.path, _external=True) }}">
{% if 'draft' not in post.meta %}
<meta property="og:url" content="{{ url_for('post', year=post.meta['published'].year, month=post.meta['published'].strftime('%m'), day=post.meta['published'].strftime('%d'), path=post.path, _external=True) }}">
{% endif %}
<meta property="og:title" content="{{post.title}}">
<meta property="og:description" content="{{post.description}}">
{% if post.image or SITE_LOGO %}
Expand Down
28 changes: 22 additions & 6 deletions htmd/site.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import sys
import tomllib
import typing
import uuid

from bs4 import BeautifulSoup
from feedwerk.atom import AtomFeed
Expand All @@ -14,6 +15,8 @@
from htmlmin import minify
from jinja2 import ChoiceLoader, FileSystemLoader

from .utils import set_post_metadata, valid_uuid


this_dir = Path(__file__).parent

Expand All @@ -34,6 +37,7 @@ def get_project_dir() -> Path:

project_dir = get_project_dir()


app = Flask(
__name__,
static_folder=project_dir / 'static',
Expand All @@ -48,6 +52,7 @@ def get_project_dir() -> Path:
msg = 'Can not find config.toml'
sys.exit(msg)


# Flask configs are flat, config.toml is not
# Define the configuration keys and their default values
# 'Flask config': [section, key, default]
Expand All @@ -71,12 +76,12 @@ def get_project_dir() -> Path:
'DEFAULT_AUTHOR_TWITTER': ('author', 'default_twitter', ''),
'DEFAULT_AUTHOR_FACEBOOK': ('author', 'default_facebook', ''),
}

# Update app.config using the configuration keys
for flask_key, (table, key, default) in config_keys.items():
app.config[flask_key] = htmd_config.get(table, {}).get(key, default)
assert app.static_folder is not None


# To avoid full paths in config.toml
app.config['FLATPAGES_ROOT'] = (
project_dir / app.config['POSTS_FOLDER']
Expand All @@ -95,6 +100,7 @@ def get_project_dir() -> Path:
published_posts = [p for p in posts if not p.meta.get('draft', False)]
freezer = Freezer(app)


# Allow config settings (even new user created ones) to be used in templates
for key in app.config:
app.jinja_env.globals[key] = app.config[key]
Expand All @@ -113,6 +119,7 @@ def truncate_post_html(post_html: str) -> str:
app.jinja_loader, # type: ignore[list-item]
])


MONTHS = {
'01': 'January',
'02': 'February',
Expand All @@ -128,6 +135,7 @@ def truncate_post_html(post_html: str) -> str:
'12': 'December',
}


pages = Blueprint(
'pages',
__name__,
Expand Down Expand Up @@ -226,6 +234,14 @@ def post(year: str, month: str, day: str, path: str) -> ResponseReturnValue:
return render_template('post.html', post=post)


@app.route('/draft/<post_uuid>/')
def draft(post_uuid: str) -> ResponseReturnValue:
for post in posts:
if str(post.meta.get('draft', '')) == post_uuid:
return render_template('post.html', post=post)
abort(404) # noqa: RET503


@app.route('/tags/')
def all_tags() -> ResponseReturnValue:
tag_counts: dict[str, int] = {}
Expand Down Expand Up @@ -365,14 +381,14 @@ def day_view() -> Iterator[dict]: # noqa: F811


@freezer.register_generator # type: ignore[no-redef]
def post() -> Iterator[dict]: # noqa: F811
def draft() -> Iterator[dict]: # noqa: F811
draft_posts = [p for p in posts if p.meta.get('draft', False)]
for post in draft_posts:
if not valid_uuid(str(post.meta['draft'])):
post.meta['draft'] = uuid.uuid4()
set_post_metadata(app, post, 'draft', post.meta['draft'])
yield {
'day': post.meta.get('published').strftime('%d'),
'month': post.meta.get('published').strftime('%m'),
'year': post.meta.get('published').year,
'path': post.path,
'post_uuid': str(post.meta['draft']),
}


Expand Down
39 changes: 39 additions & 0 deletions htmd/utils.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
from importlib.resources import as_file, files
from pathlib import Path
import shutil
import uuid

import click
from csscompressor import compress
from flask import Flask
from flask_flatpages import Page
from jsmin import jsmin


Expand Down Expand Up @@ -93,3 +96,39 @@ def copy_site_file(directory: Path, filename: str) -> None:

with as_file(source_path) as file:
copy_file(file, destination_path)


def set_post_metadata(
app: Flask,
post: Page,
field: str,
value: str,
) -> None:
file_path = (
Path(app.config['FLATPAGES_ROOT'])
/ (post.path + app.config['FLATPAGES_EXTENSION'])
)
with file_path.open('r') as file:
lines = file.readlines()

found = False
with file_path.open('w') as file:
for line in lines:
if not found and field in line:
# Update datetime value
line = f'{field}: {value}\n' # noqa: PLW2901
found = True
elif not found and '...' in line:
# Write field and value before '...'
file.write(f'{field}: {value}\n')
found = True
file.write(line)


def valid_uuid(string: str) -> bool:
try:
uuid.UUID(string, version=4)
except ValueError:
return False
else:
return True
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ order-by-type = false
section-order = ["future", "standard-library", "third-party", "first-party", "local-folder"]

[tool.ruff.lint.per-file-ignores]
"htmd/utils.py" = ["I001"]
"tests/test_app.py" = ["ARG001"]
"tests/test_build.py" = ["I001"]
"tests/test_drafts.py" = ["ARG001", "I001"]
Expand Down
22 changes: 18 additions & 4 deletions tests/test_drafts.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,15 @@ def set_example_as_draft() -> None:
post_file.write(line)


def get_example_draft_uuid() -> str:
draft_path = Path('posts') / 'example.md'
with draft_path.open('r') as draft_file:
for line in draft_file.readlines():
if 'draft' in line:
return line.replace('draft:', '').strip()
return ''


@pytest.fixture(scope='module')
def build_draft() -> Generator[None, None, None]: # noqa: PT004
runner = CliRunner()
Expand All @@ -35,8 +44,11 @@ def build_draft() -> Generator[None, None, None]: # noqa: PT004

def test_draft_is_built(build_draft: None) -> None:
post_path = Path('build') / '2014' / '10' / '30' / 'example' / 'index.html'
with post_path.open('r') as post_page:
assert 'Example Post' in post_page.read()
assert post_path.exists() is False

draft_uuid = get_example_draft_uuid()
draft_path = Path('build') / 'draft' / draft_uuid / 'index.html'
assert draft_path.is_file() is True


def test_no_drafts_home(build_draft: None) -> None:
Expand Down Expand Up @@ -86,10 +98,12 @@ def test_no_drafts_for_day(build_draft: None) -> None:
assert (Path('build') / '2014' / '10' / '30' / 'index.html').exists() is False


def test_draft_without_published(run_start: CliRunner):
expected_output = ''
def test_draft_without_published(run_start: CliRunner) -> None:
set_example_as_draft()
remove_fields_from_example_post(('published', 'updated'))
result = run_start.invoke(build)
assert result.exit_code == 0
assert re.search(SUCCESS_REGEX, result.output)
draft_uuid = get_example_draft_uuid()
draft_path = Path('build') / 'draft' / draft_uuid / 'index.html'
assert draft_path.is_file() is True

0 comments on commit 14b57cd

Please sign in to comment.