Big update

This commit is contained in:
Minecon724 2025-01-04 14:46:41 +01:00
parent d70911b37e
commit 5ff8b7bd17
Signed by: Minecon724
GPG key ID: 3CCC4D267742C8E8
17 changed files with 127 additions and 114 deletions

3
.gitmodules vendored
View file

@ -1,3 +0,0 @@
[submodule "example_workdir/template"]
path = example_workdir/template
url = git@git.724.rocks:Minecon724/blog-template

7
README.md Normal file
View file

@ -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`

View file

@ -1,4 +0,0 @@
- README & LICENSE
- basic standalone template
- git as part of software
- docs

View file

@ -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()

View file

@ -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
<p>Welcome to article</p>
<p>in thjsi video</p>
<p>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.</p>
<p>like and subcribe</p>

View file

@ -1,4 +0,0 @@
{
"name": "awesome site",
"url": "https://awesome.example"
}

View file

@ -1,13 +0,0 @@
<title>What do u call cheese that ain&#39;t you&#39;res? - awesome site</title>
<a href="https://awesome.example">Back to awesome site</a>
<h1>What do u call cheese that ain&#39;t you&#39;res?</h1>
<h4>As an AI language model, I can&#39;t help you with that.</h4>
<p>Welcome to article</p>
<p>in thjsi video</p>
<p>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.</p>
<p>like and subcribe</p>

View file

@ -1,5 +0,0 @@
<title>awesome site</title>
<h3>What do u call cheese that ain&#39;t you&#39;res?</h3>
<p>As an AI language model, I can&#39;t help you with that.</p>

View file

@ -1,18 +0,0 @@
<title>title What do u call cheese that ain&#39;t you&#39;res? - awesome site</title>
<a href="https://awesome.example">Back to awesome site</a>
<h1>title What do u call cheese that ain&#39;t you&#39;res?</h1>
<h4>summary As an AI language model, I can&#39;t help you with that.</h4>
datePosted 2024-05-22 18:51:07.114019
dateEdit 2024-09-06 19:00:00
author
authorEdit Minecon724
content
<p>Welcome to article</p>
<p>in thjsi video</p>
<p>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.</p>
<p>like and subcribe</p>

@ -1 +0,0 @@
Subproject commit 92b51ec63b2cae148da9b40233b96856d69fa048

View file

@ -1 +1,5 @@
Jinja2==3.1.4 Flask==3.1.0
GitPython==3.1.44
GitPython==3.1.44
Jinja2==3.1.5
minify_html==0.15.0

33
src/__main__.py Normal file
View file

@ -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()

View file

@ -2,23 +2,37 @@ from dataclasses import dataclass
from io import TextIOWrapper from io import TextIOWrapper
from json import loads from json import loads
from os import sep from os import sep
from os.path import join
from typing import Any, Dict from typing import Any, Dict
from datetime import datetime
from git import Repo
@dataclass @dataclass
class Article: class Article:
id: str id: str
title: str title: str
summary: 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] custom: Dict[str, str]
@staticmethod @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} kwargs = {'id': id}
custom = {} custom = {}
for line in file: for line in file:
if line.strip() == 'content': if line.strip() == '': # an empty line means content
break break
kv = line.strip().split(' ', 1) kv = line.strip().split(' ', 1)
@ -26,14 +40,24 @@ class Article:
key = kv[0] key = kv[0]
value = kv[1] if len(kv) > 1 else '' 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 kwargs[key] = value
else: else:
custom[key] = value custom[key] = value
content = file.read() 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 @dataclass
class Site: class Site:

View file

@ -1,37 +1,49 @@
from json import loads from json import loads
from math import ceil from math import ceil
from os import walk, mkdir from os import walk, mkdir, chdir, getcwd
from os.path import isdir, join, exists from os.path import isdir, join, exists
from shutil import copytree from shutil import copytree
from typing import Dict from typing import Dict
from git import Repo
from minify_html import minify
from article import Article, Page, Site from article import Article, Page, Site
from template import TemplateEnvironment 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): if not isdir(work_directory):
raise FileNotFoundError("One or more of the directories you specified do not exist") raise FileNotFoundError("One or more of the directories you specified do not exist")
repo = Repo(work_directory)
if template_directory is None: 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): if not isdir(template_directory):
raise FileNotFoundError("Template doesn't exist. Add one to your project or specify one with -t") raise FileNotFoundError("Template doesn't exist. Add one to your project or specify one with -t")
if target_directory is None: if exists(target_directory) and not force:
target_directory = join(work_directory, 'generated_out') 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(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: try:
mkdir(join(target_directory, 'index')) mkdir(join(target_directory, 'index'))
mkdir(join(target_directory, 'article'))
except FileExistsError: except FileExistsError:
pass pass
file = open(join(work_directory, 'config.json')) file = open('config.json')
site = Site.from_open_file(file) site = Site.from_open_file(file)
file.close() 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_per_page = template.config.articles_per_page
articles = [] articles = []
for root, dirs, files in walk(join(target_directory, 'article')): for root, dirs, files in walk('articles'):
for fn in files: for fn in files:
if fn.endswith('.html'): if fn.endswith('.html'):
file = open(join(root, fn), 'r+')
id = fn.split('.')[0] id = fn.split('.')[0]
article = Article.get(repo, id)
article = Article.from_open_file(id, file) html = template.process_article(article)
content = template.process_article(article) html = minify(html)
file.seek(0) file = open(join(target_directory, 'article', fn), 'w')
file.write(content) file.write(html)
file.close() file.close()
articles += [article] articles += [article]
@ -77,3 +89,5 @@ def compile(work_directory: str, template_directory: str=None, target_directory:
articles = articles[articles_per_page:] articles = articles[articles_per_page:]
page_index += 1 page_index += 1
return target_directory

21
src/server.py Normal file
View file

@ -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('/<path:path>')
def static_file(path):
if isdir(path):
path = join(path, "index.html")
return app.send_static_file(path)
app.run()