index : livejrnl.git

ascending towards madness

author holly sparkles <sparkles@holly.sh> 2023-02-07 12:32:04.0 +00:00:00
committer holly sparkles <sparkles@holly.sh> 2023-02-07 12:32:04.0 +00:00:00
commit
d48f27122a3940849fea05d38700279738819820 [patch]
tree
6b22da97b6a80545e3c81529b6c0ac7a5e27a430
parent
c960d38a0f89468d5b5aeafbf6f5a0f0ef66c846
download
d48f27122a3940849fea05d38700279738819820.tar.gz

feat: add script source files



Diff

 requirements.txt |  13 ++++++-
 src/filters.py   |  47 +++++++++++++++++++++-
 src/livejrnl.py  | 123 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 183 insertions(+)

diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..552bd07
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,13 @@
arrow==1.2.3
beautifulsoup4==4.11.2
jinja-markdown==1.210911
Jinja2==3.1.2
jinja2-time==0.2.0
Markdown==3.4.1
MarkupSafe==2.1.2
Pygments==2.14.0
pymdown-extensions==9.9.2
python-dateutil==2.8.2
six==1.16.0
soupsieve==2.3.2.post1
strip-markdown==1.3
diff --git a/src/filters.py b/src/filters.py
new file mode 100644
index 0000000..a3be80a
--- /dev/null
+++ b/src/filters.py
@@ -0,0 +1,47 @@
import strip_markdown
from datetime import datetime
import markdown


class TemplateFilters():
    """A collection of filters to use with Jinja2 templates"""

    def str_to_datetime(value: str, format: str = "%a, %d %b %Y %H:%M:%S %z", dt_offset="+0100") -> str:
        """
        Formats a Jrnl date to the desired date string.

        Keyword arguments:
        value -- the string to format
        format -- the format to format the string to
        dt_offset -- the timezone offset to use for DateTime formatting
        """
        extracted_date: datetime = datetime.strptime(
            value + dt_offset, "%Y-%m-%d %H:%M%z")
        return extracted_date.strftime(format)

    def strip_markdown(value: str) -> str:
        """
        Strips Markdown formatting from a string

        Keyword arguments:
        value -- the string to strip formatting from
        """
        return strip_markdown.strip_markdown(value)

    def strip_entry_tag(value: str) -> str:
        """
        Removes the first character from a tag string.

        Keyword arguments:
        value -- the string to strip the tag from
        """
        return value[1:]

    def markdown_to_html(value: str) -> str:
        """
        Formats Markdown as HTML

        Keyword arguments:
        value -- the string to format as HTML
        """
        return markdown.markdown(value)
diff --git a/src/livejrnl.py b/src/livejrnl.py
new file mode 100644
index 0000000..0ae28c1
--- /dev/null
+++ b/src/livejrnl.py
@@ -0,0 +1,123 @@
from argparse import ArgumentParser
import subprocess
import json
from jinja2 import Environment, Template, FileSystemLoader
from filters import TemplateFilters
from pathlib import Path


def parse_args() -> dict:
    """Parse arguments passed to the script and add it to a dictionary"""
    parser: ArgumentParser = ArgumentParser(
        prog="LiveJrnl",
        description="Renders a Jrnl journal as a static site."
    )

    parser.add_argument("-t", "--template", type=str,
                        help="the template file to use to build the output", required=True)
    parser.add_argument("-o", "--output", type=str,
                        help="the output file to write to", required=True)
    parser.add_argument("-x", "--cutoff", type=int,
                        help="the maximum number of items to render", default=-1)
    parser.add_argument("-c", "--config", type=str,
                        help="the configuration file to use for building your journal")

    args: dict = parser.parse_args()
    return args


def get_journal() -> dict:
    """Load a jrnl journal and return it as JSON"""
    json_data: str = subprocess.getoutput("jrnl --format json")
    return json.loads(json_data)


def get_default_config() -> dict:
    """Create a default configuration for use with the included default template"""
    default_config: dict = {
        "title": "Ashley Robin's Journal",
        "base_url": "https://localhost",
        "description": "Write a bit about your website here.",
        "author": "Ashley Robin",
        "author_link": "https://localhost/arobin",
        "year": "2023",
        "language": "en",
        "rss_language": "en-gb"
    }
    return default_config


def load_config(args: dict) -> str:
    """Load JSON configuration from file or create a default one.

    Keyword arguments:
    args -- arguments passed from parse_args()
    """
    if args.config is None:
        return get_default_config()
    else:
        with open(args.config, 'r') as config_file:
            data = config_file.read()
            return json.loads(data)


def generate_from_template(template: str, loaded_config: str) -> str:
    """Generate output from a supplied template

    Keyword arguments:
    template -- the path to the template to use for output
    loaded_config -- a dictionary of extra information to render
    """
    template_path: Path = Path(template)
    if template_path.exists():
        jrnl_json: dict = get_journal()

        environment: Environment = Environment(loader=FileSystemLoader(template_path.parent), extensions=[
                                               'jinja2_time.TimeExtension', 'jinja_markdown.MarkdownExtension'])
        environment.filters['datetime'] = TemplateFilters.str_to_datetime
        environment.filters['md2html'] = TemplateFilters.markdown_to_html
        environment.filters['tagstrip'] = TemplateFilters.strip_entry_tag

        loaded_template: Template = environment.get_template(
            template_path.name)
        return loaded_template.render(jrnl_json, config=loaded_config)
    return str()


def remove_empty_lines(string: str) -> str:
    """Strip empty/blank lines from a string.

    Keyword arguments:
    string -- the string to clean up
    """

    # https://stackoverflow.com/a/46416167
    return "".join([s for s in string.splitlines(True) if s.strip()])


def write_output_file(filename: str, contents: str) -> None:
    """Write data to file

    Keyword arguments:
    filename -- the full path to the file to write to
    contents -- the contents to write to file
    """
    output_path: Path = Path(filename)
    # create the output directory structure
    output_path.parent.mkdir(parents=True, exist_ok=True)
    # write the file, stripping empty lines
    with open(output_path, "w") as output_file:
        output_file.write(remove_empty_lines(contents))


if __name__ == '__main__':
    # get args
    args: dict = parse_args()
    # load config from file or default
    config = load_config(args)
    # add feed cutoff to the config if defined
    if not args.cutoff is None:
        config["cutoff"] = int(args.cutoff)
    # generate the file and write to disk
    contents = generate_from_template(args.template, config)
    write_output_file(args.output, contents)