This commit is contained in:
Digital Studium 2023-07-23 11:30:22 +03:00
parent 5fd1b91eba
commit c5d7d48492
2 changed files with 81 additions and 64 deletions

142
crater.py
View File

@ -1,10 +1,12 @@
import csv import csv
import os import os
import pathlib
import shutil import shutil
import http.server from functools import partial
import socketserver from http.server import HTTPServer, SimpleHTTPRequestHandler
from pathlib import Path
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
from jinja2 import Environment, FileSystemLoader, select_autoescape from jinja2 import Environment, FileSystemLoader, select_autoescape
import yaml import yaml
import frontmatter import frontmatter
@ -12,45 +14,65 @@ import markdown
import fire import fire
translations = {} from common.functions import *
def copy_file(src, dst):
os.makedirs(os.path.dirname(dst), exist_ok=True)
shutil.copy2(src, dst)
def load_translations(file_path, _dict): def load_translations(file_path, _dict):
with open(file_path) as f: with open(file_path) as f:
reader = csv.DictReader(f, delimiter='|') reader = csv.DictReader(f, delimiter='|') #
for row in reader: for row in reader: # проходим по строкам csv, каждая из которых является словарём
_dict[row['id']] = row _dict[row['id']] = row # добавляем ключ - значение ключа id, значение - словарь row
del row['id'] del row['id'] # удаляем ключ по названием "id" из словаря row, так как его значение уже является ключом в словаре _dict
# Функции, доступные в теме
def translate(id, language): def translate(id, language):
global translations global translations
return translations[id][language] return translations[id][language]
translations = {} # здесь будут переводы от темы и от сайта
config = yaml.safe_load(read_file('config.yaml')) # Чиатем конфиг сайта
running = False # нужно для проверки
def develop(): # класс для watchdog.
PORT = 8000 # При обнаружении изменений в папках content и themes/{config['theme']}, перегенерировать папку public
os.chdir('public') class Develop(FileSystemEventHandler):
handler = http.server.SimpleHTTPRequestHandler def on_modified(self, event):
server = socketserver.TCPServer(("", PORT), handler) print(f'event type: {event.event_type} path : {event.src_path}')
print("Server started at port 8000. Press CTRL+C to close the server.") crater()
try: def on_created(self, event):
server.serve_forever() print(f'event type: {event.event_type} path : {event.src_path}')
except KeyboardInterrupt: crater()
server.server_close() def on_deleted(self, event):
print("Server Closed") print(f'event type: {event.event_type} path : {event.src_path}')
crater()
def site_creator(prod=False): # Функция для запуска веб-сервера
global translations def start_httpd(directory: Path, port: int = 8000):
# Read config of site print(f"Listen on port {port}, serving from {directory}...")
with open('config.yaml', 'r') as file: handler = partial(SimpleHTTPRequestHandler, directory=directory)
config = yaml.safe_load(file) httpd = HTTPServer(('localhost', port), handler)
httpd.serve_forever()
# Функция для запуска разработки
def develop(prod):
global running
if not prod and not running:
event_handler = Develop()
observer = Observer()
observer.schedule(event_handler, path='content', recursive=True)
observer.schedule(event_handler, path=f"themes/{config['theme']}", recursive=True)
observer.start()
running = True
try:
start_httpd(directory='public')
except KeyboardInterrupt:
pass
# Функция для генерации сайта
def crater(prod=False):
# Load theme's jinja templates # Load theme's jinja templates
templates = Environment(loader=FileSystemLoader( templates = Environment(loader=FileSystemLoader(
f"themes/{config['theme']}/templates/"), autoescape=select_autoescape()) f"themes/{config['theme']}/templates/"), autoescape=select_autoescape())
@ -59,6 +81,7 @@ def site_creator(prod=False):
base = templates.get_template("base.j2") base = templates.get_template("base.j2")
# Load translations # Load translations
global translations
load_translations(f"themes/{config['theme']}/i18n.csv", translations) load_translations(f"themes/{config['theme']}/i18n.csv", translations)
load_translations("i18n.csv", translations) load_translations("i18n.csv", translations)
@ -71,69 +94,62 @@ def site_creator(prod=False):
posts = {} posts = {}
# Create new public folder # Create new public folder
for language in config['languages']: for language in config['languages']:
path = f"content/{language}" content_dir = Path(f"content/{language}")
content_dir = pathlib.Path(path)
content_files = content_dir.rglob("*.md")
posts[language] = {} posts[language] = {}
# Create posts dict # Create posts dict
for item in list(content_files): for item in list(content_dir.rglob("*.md")):
with open(item, 'r') as f: post_data = frontmatter.loads(read_file(item))
data = frontmatter.loads(f.read())
post_path = str(item).replace( post_path = str(item).replace(
"content", "public").rstrip(".md") "content", "public").rstrip(".md")
os.makedirs(post_path, exist_ok=True) os.makedirs(post_path, exist_ok=True)
description = post_data.content.partition('<!--more-->')[0]
content = "{% import 'shortcodes.j2' as shortcodes %}" + \ content = "{% import 'shortcodes.j2' as shortcodes %}" + \
markdown.markdown(data.content) markdown.markdown(post_data.content)
content = templates.from_string(content).render() content = templates.from_string(content).render()
description = content.partition('<!--more-->')[0]
url = post_path.replace(f"public/{language}", "") url = post_path.replace(f"public/{language}", "")
section = url.split('/')[1] section = "/" if len(url.split('/')) == 2 else url.split('/')[1]
if not os.path.dirname(url) == "/": posts[language].setdefault(section, {})
posts[language].setdefault(section, {})
posts[language][section][url] = { posts[language][section][url] = {
'title': data['title'], 'title': post_data['title'],
'description': description, 'description': description,
'date': data['date'], 'date': post_data['date'],
'content': content 'content': content
} }
image = data.get('image', None) image = post_data.get('image', None)
if image: if image:
posts[language][section][url]['image'] = image posts[language][section][url]['image'] = image
copy_file(f'assets{image}', f'public{image}') copy_file(f'assets{image}', f'public{image}')
for section, urls in posts[language].items(): for section, urls in posts[language].items():
html = base.render(config=config, section=section, if section != "/":
language=language, posts=posts) html = base.render(config=config, section=section,
language=language, posts=posts)
with open(f"public/{language}/{section}/index.html", 'w') as f: write_file(f"public/{language}/{section}/index.html", html)
f.write(html)
for url, post in urls.items(): for url, post in urls.items():
html = base.render(config=config, post=post, html = base.render(config=config, post=post,
language=language, url=url, posts=posts) language=language, url=url, posts=posts)
with open(f"public/{language}{url}/index.html", 'w') as f: write_file(f"public/{language}{url}/index.html", html)
f.write(html)
html = base.render(config=config, posts=posts, html = base.render(config=config, posts=posts,
language=language, home=True) language=language, home=True)
with open(f"public/{language}/index.html", 'w') as f: write_file(f"public/{language}/index.html", html)
f.write(html)
shutil.copytree('static', 'public', dirs_exist_ok=True) for source_folder in ('static', f"themes/{config['theme']}/static"):
shutil.copytree( shutil.copytree(source_folder, 'public', dirs_exist_ok=True)
f"themes/{config['theme']}/static", 'public', dirs_exist_ok=True)
if not prod: develop(prod)
develop()
if __name__ == '__main__': if __name__ == '__main__':
fire.Fire(site_creator) fire.Fire(crater)

View File

@ -9,3 +9,4 @@ python-frontmatter==1.0.0
PyYAML==6.0.1 PyYAML==6.0.1
six==1.16.0 six==1.16.0
termcolor==2.3.0 termcolor==2.3.0
watchdog==3.0.0