Big update
This commit is contained in:
parent
d70911b37e
commit
5ff8b7bd17
17 changed files with 127 additions and 114 deletions
3
.gitmodules
vendored
3
.gitmodules
vendored
|
@ -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
7
README.md
Normal 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`
|
4
TODO.md
4
TODO.md
|
@ -1,4 +0,0 @@
|
|||
- README & LICENSE
|
||||
- basic standalone template
|
||||
- git as part of software
|
||||
- docs
|
28
__main__.py
28
__main__.py
|
@ -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()
|
|
@ -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>
|
|
@ -1,4 +0,0 @@
|
|||
{
|
||||
"name": "awesome site",
|
||||
"url": "https://awesome.example"
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
<title>What do u call cheese that ain't you'res? - awesome site</title>
|
||||
<a href="https://awesome.example">Back to awesome site</a>
|
||||
|
||||
<h1>What do u call cheese that ain't you'res?</h1>
|
||||
<h4>As an AI language model, I can'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>
|
|
@ -1,5 +0,0 @@
|
|||
<title>awesome site</title>
|
||||
|
||||
|
||||
<h3>What do u call cheese that ain't you'res?</h3>
|
||||
<p>As an AI language model, I can't help you with that.</p>
|
|
@ -1,18 +0,0 @@
|
|||
<title>title What do u call cheese that ain't you'res? - awesome site</title>
|
||||
<a href="https://awesome.example">Back to awesome site</a>
|
||||
|
||||
<h1>title What do u call cheese that ain't you'res?</h1>
|
||||
<h4>summary As an AI language model, I can'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
|
|
@ -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
33
src/__main__.py
Normal 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()
|
|
@ -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:
|
|
@ -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
|
||||
page_index += 1
|
||||
|
||||
return target_directory
|
21
src/server.py
Normal file
21
src/server.py
Normal 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()
|
Reference in a new issue