hakyll.py 4.37 KB
Newer Older
1 2 3 4 5 6 7
"""
Shared hakyll-related functions
"""

import os
import re

8 9
import string_ops

10 11 12 13 14 15 16 17 18 19 20 21
SEPARATOR = "---"


def get_metadata(post_path: str) -> {str, str}:
    with open(post_path, "r") as f:
        num_separators = 0
        is_separator = lambda s: s == SEPARATOR
        metadata_dict = {}
        for line in f:
            l = line.strip()
            if not l:
                continue
22

23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
            if is_separator(l):
                num_separators += 1
                if num_separators >= 2:
                    break
            elif num_separators == 0:
                break
            else:
                k, v = l.split(":", 1)
                metadata_dict[k] = v.strip()
        return metadata_dict


def is_published(post_path: str) -> bool:
    return os.path.isfile(post_path) and "published" in get_metadata(post_path)


def replace_metadata(content: str, new_metadata: {str, str}) -> str:
40
    unquote_values(new_metadata)
41
    fix_tags(new_metadata)
42
    quote_values(new_metadata)
43 44 45
    metadata = '\n'.join(sorted([f"{k}: {v}"
                                 for k, v in new_metadata.items()]))
    return re.sub("^---$.+?^---$",
46
                  rf"---\n{metadata}\n---",
47 48 49
                  content,
                  flags=re.DOTALL | re.MULTILINE,
                  count=1)
50

51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
def replace_metadata_org_format(content: str, new_metadata: {str, str}) -> str:
    unquote_values(new_metadata)
    fix_tags(new_metadata)
    metadata = '\n'.join(sorted([f"#+{k.upper().replace('-', '_')}: {v}"
                                 for k, v in new_metadata.items()]))
    return re.sub("^---$.+?^---$",
                  metadata,
                  content,
                  flags=re.DOTALL | re.MULTILINE,
                  count=1)

def update_metadata_format(path: str):
    with open(path) as source:
        content = source.read()
    updated = replace_metadata_org_format(content, get_metadata(path))
    with open(path, "w") as f:
        f.write(updated)
68

69 70 71 72 73 74 75 76 77 78 79 80 81 82
def update_metadata(path: str, metadata: {str, str}):
    with open(path) as source:
        content = source.read()
    updated = replace_metadata(content, metadata)
    with open(path, "w") as f:
        f.write(updated)


def update_metadata_field(field: str, value: str, post: str):
    metadata = get_metadata(post)
    metadata[field] = value
    update_metadata(post, metadata)


83
def fix_tags(metadata: {str, str}) -> ({str, str}):
84 85 86
    if not "tags" in metadata:
        return metadata
    tags = metadata["tags"]
87
    metadata["tags"] = ", ".join(sorted(x.strip().lower() for x in tags.split(",")))
88 89 90
    return metadata


91 92
def unquote_values(metadata: {str, str}) -> {str, str}:
    for k, v in metadata.items():
93
        intermediate = string_ops.strip_surrounding_quotes(v)
94 95
        unescaped = re.sub(r'\\"', '"', intermediate)
        metadata[k] = unescaped
96 97 98 99
    return metadata


def quote_values(metadata: {str, str}) -> {str, str}:
100
    escape_quotes = lambda x: re.sub(r'(?<!\\)"', '\\"', x)
101 102

    def transform(a):
103
        b = escape_quotes(string_ops.strip_surrounding_quotes(a))
104
        return f'"{b}"' if b else ""
105 106 107 108 109 110 111

    for k, v in metadata.items():
        if v:
            metadata[k] = transform(v)
    return metadata


112 113
def is_new(post_path: str) -> bool:
    return not get_metadata(post_path)["published"]
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147


# unit tests


def assert_f(f, original: str, expected: str):
    dict = {"key": original}
    result = f(dict)
    print(original, "->", result["key"], "Should be:", expected)
    assert result["key"] == expected


def test_unquoting():
    def assert_unquoted(original: str, expected: str):
        assert_f(unquote_values, original, expected)

    assert_unquoted('"a"', 'a')
    assert_unquoted('""', '')
    assert_unquoted('a', 'a')
    assert_unquoted(r'"it unescapes escaped \"quotes\""',
                    r'it unescapes escaped "quotes"')
    assert_unquoted(r'it respects "quotes"', r'it respects "quotes"')


def test_quoting():
    def assert_quoted(original: str, expected: str):
        assert_f(quote_values, original, expected)

    assert_quoted('a', '"a"')
    assert_quoted(r'it respects \"escaped quotes\"',
                  r'"it respects \"escaped quotes\""')

    assert_quoted('', '')
    assert_quoted('\"\"', '')
148 149 150 151 152 153 154 155

def test_tag_sorting():
    dict = {"tags": "b, c, a, d"}
    assert fix_tags(dict)["tags"] == "a, b, c, d"

def test_tag_casing():
    dict = {"tags": "B, caT, a, Dog"}
    assert fix_tags(dict)["tags"] == "a, b, cat, dog"