diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index efbb8fd..0000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "example_workdir/template"] - path = example_workdir/template - url = git@git.724.rocks:Minecon724/blog-template diff --git a/README.md b/README.md new file mode 100644 index 0000000..b633b1c --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +A very easy blog software that renders as static site + +### Usage +1. Clone this directory and cd into it +2. Create a venv and install requirements +3. Get a work directory [(example)](/Minecon724/m724.eu) +4. `python3 src -h` \ No newline at end of file diff --git a/TODO.md b/TODO.md deleted file mode 100644 index a286fd1..0000000 --- a/TODO.md +++ /dev/null @@ -1,4 +0,0 @@ -- README & LICENSE -- basic standalone template -- git as part of software -- docs diff --git a/__main__.py b/__main__.py deleted file mode 100644 index 7413ed9..0000000 --- a/__main__.py +++ /dev/null @@ -1,28 +0,0 @@ -from argparse import ArgumentParser - -from compiler import compile - -class Arguments: - workdir: str - template: str - out: str - force: str - - def __init__(self): - pass - -def main(): - parser = ArgumentParser() - - parser.add_argument('workdir', action='store') - parser.add_argument('-t' , '--template', action='store') - parser.add_argument('-o', '--out', action='store') - parser.add_argument('-f', '--force', action='store_true', default=False) - - args = Arguments() - parser.parse_args(namespace=args) - - compile(args.workdir, args.template, args.out, args.force) - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/example_workdir/articles/sample-article.html b/example_workdir/articles/sample-article.html deleted file mode 100644 index c2ff8d8..0000000 --- a/example_workdir/articles/sample-article.html +++ /dev/null @@ -1,14 +0,0 @@ -title What do u call cheese that ain't you'res? -summary As an AI language model, I can't help you with that. -datePosted 2024-05-22 18:51:07.114019 -dateEdit 2024-09-06 19:00:00 -author -authorEdit Minecon724 -content - -

Welcome to article

-

in thjsi video

- -

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

- -

like and subcribe

\ No newline at end of file diff --git a/example_workdir/config.json b/example_workdir/config.json deleted file mode 100644 index 1b0d1bf..0000000 --- a/example_workdir/config.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "awesome site", - "url": "https://awesome.example" -} \ No newline at end of file diff --git a/example_workdir/generated_out/article/sample-article.html b/example_workdir/generated_out/article/sample-article.html deleted file mode 100644 index 788a04a..0000000 --- a/example_workdir/generated_out/article/sample-article.html +++ /dev/null @@ -1,13 +0,0 @@ -What do u call cheese that ain't you'res? - awesome site -Back to awesome site - -

What do u call cheese that ain't you'res?

-

As an AI language model, I can't help you with that.

- - -

Welcome to article

-

in thjsi video

- -

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

- -

like and subcribe

\ No newline at end of file diff --git a/example_workdir/generated_out/index.html b/example_workdir/generated_out/index.html deleted file mode 100644 index 35d0fde..0000000 --- a/example_workdir/generated_out/index.html +++ /dev/null @@ -1,5 +0,0 @@ -awesome site - - -

What do u call cheese that ain't you'res?

-

As an AI language model, I can't help you with that.

diff --git a/example_workdir/generated_out/post/sample-article.html b/example_workdir/generated_out/post/sample-article.html deleted file mode 100644 index 440009a..0000000 --- a/example_workdir/generated_out/post/sample-article.html +++ /dev/null @@ -1,18 +0,0 @@ -title What do u call cheese that ain't you'res? - awesome site -Back to awesome site - -

title What do u call cheese that ain't you'res?

-

summary As an AI language model, I can't help you with that.

- -datePosted 2024-05-22 18:51:07.114019 -dateEdit 2024-09-06 19:00:00 -author -authorEdit Minecon724 -content - -

Welcome to article

-

in thjsi video

- -

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

- -

like and subcribe

\ No newline at end of file diff --git a/example_workdir/generated_out/static/style.css b/example_workdir/generated_out/static/style.css deleted file mode 100644 index e69de29..0000000 diff --git a/example_workdir/template b/example_workdir/template deleted file mode 160000 index 92b51ec..0000000 --- a/example_workdir/template +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 92b51ec63b2cae148da9b40233b96856d69fa048 diff --git a/requirements.txt b/requirements.txt index 62def1f..65d548d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,5 @@ -Jinja2==3.1.4 \ No newline at end of file +Flask==3.1.0 +GitPython==3.1.44 +GitPython==3.1.44 +Jinja2==3.1.5 +minify_html==0.15.0 diff --git a/src/__main__.py b/src/__main__.py new file mode 100644 index 0000000..6f1b1b8 --- /dev/null +++ b/src/__main__.py @@ -0,0 +1,33 @@ +from argparse import ArgumentParser + +from compiler import compile + +class Arguments: + workdir: str + template: str + out: str + force: str + + def __init__(self): + pass + +def main(): + parser = ArgumentParser() + + parser.add_argument('workdir', action='store', help="The working directory") + parser.add_argument('-t' , '--template', action='store', help="Template directory. By default workdir/template") + parser.add_argument('-o', '--out', action='store', help="Output directory. By default workdir/generated_out") + parser.add_argument('-f', '--force', action='store_true', default=False, help="Force overwrite out dir") + parser.add_argument('-s', '--server', action='store_true', default=False, help="Run server") + + args = Arguments() + parser.parse_args(namespace=args) + + target = compile(args.workdir, args.template, args.out, args.force) + print("Saved to", target) + if args.server: + import server + server.run(target) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/article.py b/src/article.py similarity index 54% rename from article.py rename to src/article.py index 0ab57f6..8c26b25 100644 --- a/article.py +++ b/src/article.py @@ -2,38 +2,62 @@ from dataclasses import dataclass from io import TextIOWrapper from json import loads from os import sep +from os.path import join from typing import Any, Dict +from datetime import datetime +from git import Repo @dataclass class Article: id: str title: str summary: str - content: str + + revisions: int + created_by: str + created_at: datetime + modified_by: str + modified_at: datetime + + html_content: str + custom: Dict[str, str] @staticmethod - def from_open_file(id: str, file: TextIOWrapper) -> "Article": + def get(repo: Repo, id: str) -> "Article": + filename = join('articles', id + '.html') + file = open(filename, 'r') + kwargs = {'id': id} custom = {} for line in file: - if line.strip() == 'content': + if line.strip() == '': # an empty line means content break kv = line.strip().split(' ', 1) key = kv[0] value = kv[1] if len(kv) > 1 else '' - - if key in Article.__annotations__: + + if key in Article.__annotations__ and key not in ['id']: kwargs[key] = value else: custom[key] = value content = file.read() - return Article(content=content, custom=custom, **kwargs) + if content.strip() == '': + print(f"Article {id} has no content") + + commits = list(repo.iter_commits(paths=filename)) + kwargs['revisions'] = len(commits) + kwargs['created_by'] = commits[-1].author.name + kwargs['created_at'] = commits[-1].authored_datetime + kwargs['modified_by'] = commits[0].author.name + kwargs['modified_at'] = commits[0].authored_datetime + + return Article(html_content=content, custom=custom, **kwargs) @dataclass class Site: diff --git a/compiler.py b/src/compiler.py similarity index 65% rename from compiler.py rename to src/compiler.py index 5505181..d9fd10b 100644 --- a/compiler.py +++ b/src/compiler.py @@ -1,37 +1,49 @@ from json import loads from math import ceil -from os import walk, mkdir +from os import walk, mkdir, chdir, getcwd from os.path import isdir, join, exists from shutil import copytree from typing import Dict +from git import Repo +from minify_html import minify from article import Article, Page, Site from template import TemplateEnvironment -def compile(work_directory: str, template_directory: str=None, target_directory: str=None, force: bool=False): +def compile(work_directory: str, template_directory: str=None, target_directory: str=None, force: bool=False) -> str: if not isdir(work_directory): raise FileNotFoundError("One or more of the directories you specified do not exist") + repo = Repo(work_directory) + if template_directory is None: - template_directory = join(work_directory, 'template') + template_directory = 'template' + else: + template_directory = join(getcwd(), template_directory) + + if target_directory is None: + target_directory = 'generated_out' + else: + template_directory = join(getcwd(), target_directory) + + chdir(work_directory) if not isdir(template_directory): raise FileNotFoundError("Template doesn't exist. Add one to your project or specify one with -t") - - if target_directory is None: - target_directory = join(work_directory, 'generated_out') - if exists(target_directory) and not force: - raise FileExistsError(target_directory + " already exists. Delete it, specify a different one with -o, or pass the -f flag to merge") + + if exists(target_directory) and not force: + raise FileExistsError(target_directory + " already exists. Delete it, specify a different one with -o, or pass the -f flag to merge") + copytree(join(template_directory, 'static'), join(target_directory, 'static'), dirs_exist_ok=True) - copytree(join(work_directory, 'articles'), join(target_directory, 'article'), dirs_exist_ok=True) try: mkdir(join(target_directory, 'index')) + mkdir(join(target_directory, 'article')) except FileExistsError: pass - file = open(join(work_directory, 'config.json')) + file = open('config.json') site = Site.from_open_file(file) file.close() @@ -39,17 +51,17 @@ def compile(work_directory: str, template_directory: str=None, target_directory: articles_per_page = template.config.articles_per_page articles = [] - for root, dirs, files in walk(join(target_directory, 'article')): + for root, dirs, files in walk('articles'): for fn in files: if fn.endswith('.html'): - file = open(join(root, fn), 'r+') id = fn.split('.')[0] + article = Article.get(repo, id) - article = Article.from_open_file(id, file) - content = template.process_article(article) + html = template.process_article(article) + html = minify(html) - file.seek(0) - file.write(content) + file = open(join(target_directory, 'article', fn), 'w') + file.write(html) file.close() articles += [article] @@ -76,4 +88,6 @@ def compile(work_directory: str, template_directory: str=None, target_directory: file.close() articles = articles[articles_per_page:] - page_index += 1 \ No newline at end of file + page_index += 1 + + return target_directory \ No newline at end of file diff --git a/src/server.py b/src/server.py new file mode 100644 index 0000000..46a0039 --- /dev/null +++ b/src/server.py @@ -0,0 +1,21 @@ +from flask import Flask + +from os import getcwd +from os.path import join, isdir + +def run(directory: str): + app = Flask(__name__, static_folder=join(getcwd(), directory)) + + @app.route('/') + def root(): + return app.send_static_file("index.html") + + @app.route('/') + def static_file(path): + if isdir(path): + path = join(path, "index.html") + + return app.send_static_file(path) + + + app.run() \ No newline at end of file diff --git a/template.py b/src/template.py similarity index 100% rename from template.py rename to src/template.py