commit 4a3074d4dde0120b257e5e80597307dbfaca8770 Author: Digital Studium Date: Sat Jul 22 16:38:20 2023 +0300 First commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b694934 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.venv \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..7f3d3af --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +# Crater + +Multilingual static site generator based on Jinja templates. + +Example theme: https://git.digitalstudium.com/digitalstudium/crater-theme-digitalstudium + +Example website: https://git.digitalstudium.com/digitalstudium/digitalstudium.com \ No newline at end of file diff --git a/crater.py b/crater.py new file mode 100644 index 0000000..a7c47f7 --- /dev/null +++ b/crater.py @@ -0,0 +1,139 @@ +import csv +import os +import pathlib +import shutil +import http.server +import socketserver + +from jinja2 import Environment, FileSystemLoader, select_autoescape +import yaml +import frontmatter +import markdown +import fire + + +translations = {} + +def copy_file(src, dst): + os.makedirs(os.path.dirname(dst), exist_ok=True) + shutil.copy2(src, dst) + + +def load_translations(file_path, _dict): + with open(file_path) as f: + reader = csv.DictReader(f, delimiter='|') + for row in reader: + _dict[row['id']] = row + del row['id'] + + +def translate(id, language): + global translations + return translations[id][language] + + +def develop(): + PORT = 8000 + os.chdir('public') + handler = http.server.SimpleHTTPRequestHandler + server = socketserver.TCPServer(("", PORT), handler) + print("Server started at port 8000. Press CTRL+C to close the server.") + try: + server.serve_forever() + except KeyboardInterrupt: + server.server_close() + print("Server Closed") + + +def site_creator(prod=False): + global translations + # Read config of site + with open('config.yaml', 'r') as file: + config = yaml.safe_load(file) + + # Load theme's jinja templates + templates = Environment(loader=FileSystemLoader( + f"themes/{config['theme']}/templates/"), autoescape=select_autoescape()) + + # Load base jinja template + base = templates.get_template("base.j2") + + # Load translations + load_translations(f"themes/{config['theme']}/i18n.csv", translations) + load_translations("i18n.csv", translations) + + # Add functions to base template + base.globals.update({"translate": translate}) + + # Remove public folder if exists before generating new content + shutil.rmtree('public', ignore_errors=True) + + posts = {} + # Create new public folder + for language in config['languages']: + path = f"content/{language}" + content_dir = pathlib.Path(path) + content_files = content_dir.rglob("*.md") + posts[language] = {} + + # Create posts dict + for item in list(content_files): + with open(item, 'r') as f: + data = frontmatter.loads(f.read()) + + post_path = str(item).replace( + "content", "public").rstrip(".md") + os.makedirs(post_path, exist_ok=True) + + content = "{% import 'shortcodes.j2' as shortcodes %}" + \ + markdown.markdown(data.content) + + content = templates.from_string(content).render() + description = content.partition('')[0] + url = post_path.replace(f"public/{language}", "") + section = url.split('/')[1] + + if not os.path.dirname(url) == "/": + posts[language].setdefault(section, {}) + + posts[language][section][url] = { + 'title': data['title'], + 'description': description, + 'date': data['date'], + 'content': content + } + + image = data.get('image', None) + if image: + posts[language][section][url]['image'] = image + copy_file(f'assets{image}', f'public{image}') + + for section, urls in posts[language].items(): + html = base.render(config=config, section=section, + language=language, posts=posts) + + with open(f"public/{language}/{section}/index.html", 'w') as f: + f.write(html) + + for url, post in urls.items(): + html = base.render(config=config, post=post, + language=language, url=url, posts=posts) + + with open(f"public/{language}{url}/index.html", 'w') as f: + f.write(html) + + html = base.render(config=config, posts=posts, + language=language, home=True) + + with open(f"public/{language}/index.html", 'w') as f: + f.write(html) + + shutil.copytree('static', 'public', dirs_exist_ok=True) + shutil.copytree( + f"themes/{config['theme']}/static", 'public', dirs_exist_ok=True) + if not prod: + develop() + + +if __name__ == '__main__': + fire.Fire(site_creator) diff --git a/deploy.sh b/deploy.sh new file mode 100755 index 0000000..3190ea2 --- /dev/null +++ b/deploy.sh @@ -0,0 +1,5 @@ +pip install -r requirements.txt +python -m PyInstaller --onefile crater.py +chmod +x dist/crater +sudo mv dist/crater /usr/local/bin/ +rm -rf dist build crater.spec \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..b43d1fc --- /dev/null +++ b/requirements.txt @@ -0,0 +1,11 @@ +altgraph==0.17.3 +fire==0.5.0 +Jinja2==3.1.2 +Markdown==3.4.3 +MarkupSafe==2.1.3 +pyinstaller==5.13.0 +pyinstaller-hooks-contrib==2023.6 +python-frontmatter==1.0.0 +PyYAML==6.0.1 +six==1.16.0 +termcolor==2.3.0