franca/crater.py

156 lines
5.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import csv
import os
import shutil
from functools import partial
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
import yaml
import frontmatter
import markdown
import fire
from common.functions import *
def load_translations(file_path, _dict):
with open(file_path) as f:
reader = csv.DictReader(f, delimiter='|') #
for row in reader: # проходим по строкам csv, каждая из которых является словарём
_dict[row['id']] = row # добавляем ключ - значение ключа id, значение - словарь row
del row['id'] # удаляем ключ по названием "id" из словаря row, так как его значение уже является ключом в словаре _dict
# Функции, доступные в теме
def translate(id, language):
global translations
return translations[id][language]
translations = {} # здесь будут переводы от темы и от сайта
config = yaml.safe_load(read_file('config.yaml')) # Чиатем конфиг сайта
running = False # нужно для проверки
# класс для watchdog.
# При обнаружении изменений в папках content и themes/{config['theme']}, перегенерировать папку public
class Develop(FileSystemEventHandler):
def on_modified(self, event):
print(f'event type: {event.event_type} path : {event.src_path}')
crater()
def on_created(self, event):
print(f'event type: {event.event_type} path : {event.src_path}')
crater()
def on_deleted(self, event):
print(f'event type: {event.event_type} path : {event.src_path}')
crater()
# Функция для запуска веб-сервера
def start_httpd(directory: Path, port: int = 8000):
print(f"Listen on port {port}, serving from {directory}...")
handler = partial(SimpleHTTPRequestHandler, directory=directory)
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
templates = Environment(loader=FileSystemLoader(
f"themes/{config['theme']}/templates/"), autoescape=select_autoescape())
# Load base jinja template
base = templates.get_template("base.j2")
# Load translations
global 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']:
content_dir = Path(f"content/{language}")
posts[language] = {}
# Create posts dict
for item in list(content_dir.rglob("*.md")):
post_data = frontmatter.loads(read_file(item))
post_path = str(item).replace(
"content", "public").rstrip(".md")
os.makedirs(post_path, exist_ok=True)
description = post_data.content.partition('<!--more-->')[0]
content = "{% import 'shortcodes.j2' as shortcodes %}" + \
markdown.markdown(post_data.content)
content = templates.from_string(content).render()
url = post_path.replace(f"public/{language}", "")
section = "/" if len(url.split('/')) == 2 else url.split('/')[1]
posts[language].setdefault(section, {})
posts[language][section][url] = {
'title': post_data['title'],
'description': description,
'date': post_data['date'],
'content': content
}
image = post_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():
if section != "/":
html = base.render(config=config, section=section,
language=language, posts=posts)
write_file(f"public/{language}/{section}/index.html", html)
for url, post in urls.items():
html = base.render(config=config, post=post,
language=language, url=url, posts=posts)
write_file(f"public/{language}{url}/index.html", html)
html = base.render(config=config, posts=posts,
language=language, home=True)
write_file(f"public/{language}/index.html", html)
for source_folder in ('static', f"themes/{config['theme']}/static"):
shutil.copytree(source_folder, 'public', dirs_exist_ok=True)
develop(prod)
if __name__ == '__main__':
fire.Fire(crater)