franca/franca.py

197 lines
7.7 KiB
Python
Raw Normal View History

2023-07-22 13:38:20 +00:00
import csv
import os
import shutil
2023-07-23 08:30:22 +00:00
from pathlib import Path
2023-07-22 13:38:20 +00:00
2023-07-23 08:30:22 +00:00
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
2023-07-22 13:38:20 +00:00
from jinja2 import Environment, FileSystemLoader, select_autoescape
import yaml
import frontmatter
2023-07-23 12:54:37 +00:00
import markdown2
2023-07-22 13:38:20 +00:00
import fire
2023-07-28 17:35:34 +00:00
import minify_html
2023-07-22 13:38:20 +00:00
2023-07-23 08:30:22 +00:00
from common.functions import *
2023-07-22 13:38:20 +00:00
def load_translations(file_path, _dict):
with open(file_path) as f:
2023-07-23 08:30:22 +00:00
reader = csv.DictReader(f, delimiter='|') #
for row in reader: # проходим по строкам csv, каждая из которых является словарём
_dict[row['id']] = row # добавляем ключ - значение ключа id, значение - словарь row
2023-10-14 10:53:51 +00:00
del row[
'id'] # удаляем ключ по названием "id" из словаря row, так как его значение уже является ключом в словаре _dict
2023-07-22 13:38:20 +00:00
2023-07-23 08:30:22 +00:00
# Функции, доступные в теме
2023-07-22 13:38:20 +00:00
def translate(id, language):
global translations
return translations[id][language]
2023-10-14 10:53:51 +00:00
2023-07-23 08:30:22 +00:00
translations = {} # здесь будут переводы от темы и от сайта
2023-10-14 10:53:51 +00:00
config = yaml.safe_load(read_file('config.yaml')) # Читаем конфиг сайта
2023-07-23 08:30:22 +00:00
running = False # нужно для проверки
2023-10-14 10:53:51 +00:00
# класс для watchdog
# Во время разработки, при обнаружении изменений в папках content и themes/{config['theme']},
# перегенерировать папку public
2023-07-23 08:30:22 +00:00
class Develop(FileSystemEventHandler):
2023-10-14 10:53:51 +00:00
def on_modified(self, event):
2023-07-23 13:44:56 +00:00
print(f'event type: {event.event_type} path : {event.src_path}')
2023-07-28 17:35:34 +00:00
franca()
2023-10-14 10:53:51 +00:00
def on_created(self, event):
2023-07-23 13:44:56 +00:00
print(f'event type: {event.event_type} path : {event.src_path}')
2023-07-28 17:35:34 +00:00
franca()
2023-10-14 10:53:51 +00:00
def on_deleted(self, event):
2023-07-23 13:44:56 +00:00
print(f'event type: {event.event_type} path : {event.src_path}')
2023-07-28 17:35:34 +00:00
franca()
2023-07-23 08:30:22 +00:00
# Функция для запуска разработки
def develop(prod):
global running
2023-10-14 10:53:51 +00:00
if not prod and not running:
2023-07-23 08:30:22 +00:00
event_handler = Develop()
observer = Observer()
2023-10-14 10:53:51 +00:00
observer.schedule(event_handler, path='content', recursive=True)
observer.schedule(event_handler, path='assets', recursive=True)
observer.schedule(event_handler, path='static', recursive=True)
observer.schedule(event_handler, path='config.yaml')
observer.schedule(event_handler, path=f"themes/{config['theme']}", recursive=True)
2023-07-23 08:30:22 +00:00
observer.start()
running = True
try:
start_httpd(directory='public')
except KeyboardInterrupt:
pass
2023-10-14 10:53:51 +00:00
2023-07-23 08:30:22 +00:00
# Функция для генерации сайта
2023-07-28 17:35:34 +00:00
def franca(prod=False):
2023-10-14 10:53:51 +00:00
if not prod:
2023-07-23 19:46:55 +00:00
config['base_url'] = "http://127.0.0.1:8000"
2023-07-22 13:38:20 +00:00
# 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")
2023-07-23 17:47:37 +00:00
index = templates.get_template("index.j2")
2023-07-22 13:38:20 +00:00
# Load translations
2023-07-23 08:30:22 +00:00
global translations
2023-07-22 13:38:20 +00:00
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']:
2023-07-23 08:30:22 +00:00
content_dir = Path(f"content/{language}")
2023-07-22 13:38:20 +00:00
posts[language] = {}
# Create posts dict
2023-07-23 08:30:22 +00:00
for item in list(content_dir.rglob("*.md")):
post_data = frontmatter.loads(read_file(item))
2023-07-22 13:38:20 +00:00
post_path = str(item).replace(
"content", "public").rstrip(".md")
os.makedirs(post_path, exist_ok=True)
2023-07-23 12:54:37 +00:00
content = markdown2.markdown(post_data.content, extras=['fenced-code-blocks'])
description = content.partition('<!--more-->')[0]
content = "{% import 'shortcodes.j2' as shortcodes %}" + content
2023-07-22 13:38:20 +00:00
url = post_path.replace(f"public/{language}", "")
2023-07-23 08:30:22 +00:00
section = "/" if len(url.split('/')) == 2 else url.split('/')[1]
2023-10-14 10:53:51 +00:00
date = post_data['date']
posts[language].setdefault(date, {})
posts[language][date].setdefault(section, {})
2023-07-22 13:38:20 +00:00
2023-10-14 10:53:51 +00:00
posts[language][date][section][url] = {
2023-07-23 08:30:22 +00:00
'title': post_data['title'],
'description': description,
2023-10-14 10:53:51 +00:00
'date': date,
2023-07-23 12:54:37 +00:00
'content': templates.from_string(content).render()
2023-07-23 08:30:22 +00:00
}
2023-07-22 13:38:20 +00:00
2023-07-23 08:30:22 +00:00
image = post_data.get('image', None)
if image:
2023-10-14 10:53:51 +00:00
os.makedirs(os.path.dirname(f'public{image}'), exist_ok=True)
2023-07-23 19:46:55 +00:00
filename = image.split('/')[-1].split('.')[0]
2023-10-14 10:53:51 +00:00
create_thumbnail(f'assets{image}', f'public/images/{filename}_600.jpg', 600)
posts[language][date][section][url]['image'] = f'/images/{filename}_600.jpg'
2023-07-23 19:46:55 +00:00
2023-10-14 10:53:51 +00:00
create_thumbnail(f'assets{image}', f'public/images/{filename}_400.jpg', 400)
posts[language][date][section][url]['thumbnail'] = f'/images/{filename}_400.jpg'
2023-07-23 19:46:55 +00:00
2023-10-14 10:53:51 +00:00
posts[language] = dict(sorted(posts[language].items(), reverse=True))
2023-07-22 13:38:20 +00:00
2023-10-14 10:53:51 +00:00
for date, sections in posts[language].items():
for section, urls in sections.items():
if section != "/":
html = base.render(config=config, section=section,
language=language, posts=posts)
2023-07-22 13:38:20 +00:00
2023-10-14 10:53:51 +00:00
write_file(f"public/{language}/{section}/index.html", minify_html.minify(html, minify_js=True))
2023-07-22 13:38:20 +00:00
2023-10-14 10:53:51 +00:00
for url, post in urls.items():
html = base.render(config=config, post=post,
language=language, url=url, posts=posts)
2023-07-22 13:38:20 +00:00
2023-10-14 10:53:51 +00:00
write_file(f"public/{language}{url}/index.html", minify_html.minify(html, minify_js=True))
2023-07-22 13:38:20 +00:00
html = base.render(config=config, posts=posts,
language=language, home=True)
2023-07-28 17:35:34 +00:00
write_file(f"public/{language}/index.html", minify_html.minify(html, minify_js=True))
2023-07-23 08:30:22 +00:00
2023-07-23 14:30:20 +00:00
# copy images/css/js from theme
shutil.copytree(f"themes/{config['theme']}/static/images", 'public/images', dirs_exist_ok=True)
2023-07-28 17:35:34 +00:00
css = read_file(f"themes/{config['theme']}/static/css/style.css")
minify_css('public/css/style.css', css)
2023-07-23 14:56:27 +00:00
if 'css_includes' in config:
for include in config['css_includes']:
2023-07-28 17:35:34 +00:00
css = read_file(f"themes/{config['theme']}/static/css/{include}")
2023-10-14 10:53:51 +00:00
minify_css(f'public/css/{include}', css)
2023-07-23 14:30:20 +00:00
if 'js_includes' in config:
for include in config['js_includes']:
2023-10-14 10:53:51 +00:00
copy_file(f"themes/{config['theme']}/static/js/{include}", 'public/js/')
2023-07-23 14:30:20 +00:00
2023-10-14 10:53:51 +00:00
# copy css/images from site static folder
2023-07-23 14:30:20 +00:00
if 'custom_css' in config:
shutil.copytree('static/css', 'public/css', dirs_exist_ok=True)
copy_file('static/logo.svg', 'public/')
copy_file('static/favicon.ico', 'public/')
2023-10-14 10:53:51 +00:00
2023-07-23 17:47:37 +00:00
# Write main index.html
html = index.render(config=config)
2023-07-28 17:35:34 +00:00
write_file('public/index.html', minify_html.minify(html, minify_js=True))
2023-07-23 17:47:37 +00:00
# Write robots.txt
2023-10-14 10:53:51 +00:00
robots_content = "User-agent: *"
2023-07-28 17:35:34 +00:00
if not config.get('search_engines', None) == "allow":
robots_content += "\nDisallow: /"
2023-07-23 17:47:37 +00:00
write_file('public/robots.txt', robots_content)
2023-07-22 13:38:20 +00:00
2023-10-14 10:53:51 +00:00
if prod:
if 'pagefind' in config:
os.system("npx pagefind --source public") # build search index
2023-07-28 17:35:34 +00:00
2023-07-23 13:44:56 +00:00
develop(prod)
2023-07-22 13:38:20 +00:00
if __name__ == '__main__':
2023-07-28 17:35:34 +00:00
fire.Fire(franca)