import csv import os import shutil 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 markdown2 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 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) content = markdown2.markdown(post_data.content, extras=['fenced-code-blocks']) description = content.partition('')[0] content = "{% import 'shortcodes.j2' as shortcodes %}" + content 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': templates.from_string(content).render() } 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)