161 lines
7.5 KiB
Python
161 lines
7.5 KiB
Python
from datetime import datetime
|
|
import os
|
|
import shutil
|
|
from collections import defaultdict
|
|
import markdown
|
|
import frontmatter
|
|
from pathlib import Path
|
|
from logger import logger
|
|
from config import Config
|
|
from jinja_env import env, content_item_template, index_template
|
|
|
|
class ContentItem:
|
|
def render_content(self, categories):
|
|
logger.debug(f"Rendering {self.source_filename} to {self.target_filename}")
|
|
try:
|
|
if self.image_file and self.image_file.exists():
|
|
logger.debug(f"Copying {self.image_file} to {Path(Config.OUTPUT_DIR) / self.image_file}")
|
|
shutil.copyfile(self.image_file, Path(Config.OUTPUT_DIR) / self.image_file)
|
|
self.image_file = f"{self.image_file.stem}.jpg"
|
|
if self.css_file and self.css_file.exists():
|
|
css_targetfile = Path(f"{Config.OUTPUT_DIR}/{Config.STATIC_DIR}/css/{self.css_file.name}")
|
|
logger.debug(f"Copying {self.css_file} to {css_targetfile}")
|
|
shutil.copyfile(self.css_file, css_targetfile)
|
|
if self.js_file and self.js_file.exists():
|
|
shutil.copyfile(self.js_file, Path(Config.OUTPUT_DIR) / self.js_file)
|
|
with self.target_filename.open("w", encoding="utf-8") as f:
|
|
f.write(content_item_template.render(content_item = self, page_title = self.title, parent_path = '../', categories = categories))
|
|
except Exception as e:
|
|
logger.error(e)
|
|
|
|
def parse_content(self):
|
|
logger.debug(f"Parsing file {self.source_filename}")
|
|
try:
|
|
self.source_filename = Path(self.source_filename)
|
|
self.subdir = self.source_filename.parent
|
|
self.slug = self.source_filename.stem
|
|
self.target_filename = Path(f"{Config.OUTPUT_DIR}/{self.source_filename.parent}/{self.source_filename.stem}.html")
|
|
self.data = frontmatter.load(self.source_filename)
|
|
self.preview = self.data.content.replace('\n', '<br>')[:300]
|
|
self.title = self.data.get("title", self.slug)
|
|
self.omit_second_title = self.data.get("omit_second_title", False)
|
|
self.date = self.data.get("date", "2000-01-01T00:00:00+03:00")
|
|
self.categories = [c for c in self.data.get("categories", str([])) if c != 'default']
|
|
self.hidden = self.data.get("hidden", str(False))
|
|
self.data.content = self.data.content.replace('\n', ' \n')
|
|
self.html = markdown.markdown(self.data.content)
|
|
cover_image_path = Path(self.source_filename.parent) / Path(self.source_filename.stem + ".jpg")
|
|
self.image_file = cover_image_path if cover_image_path.exists() else None
|
|
css_filepath = Path(f"{self.source_filename.parent}/{self.source_filename.stem}.css")
|
|
self.css_file = css_filepath if css_filepath.exists() else None
|
|
self.js_file = Path(self.source_filename.stem + ".js") if Path(self.source_filename.stem + ".js").exists else None
|
|
except Exception as e:
|
|
logger.error(e)
|
|
|
|
def create_content(self):
|
|
with open(self.source_filename, mode="w", encoding="utf-8") as f:
|
|
f.writelines([
|
|
"---\n",
|
|
f"title: {self.source_filename.stem}\n",
|
|
f"date: '{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}'\n",
|
|
"description: ''\n",
|
|
"author: ''\n",
|
|
"categories: ['default']\n",
|
|
"hidden: False\n",
|
|
"omit_second_title: False\n",
|
|
"---\n",
|
|
"\n\n\n"
|
|
])
|
|
|
|
def __init__(self, filename):
|
|
self.source_filename = filename
|
|
|
|
class Site:
|
|
def __init__(self):
|
|
self.output_dir = Path(Config.OUTPUT_DIR)
|
|
self.content_dir = Path(Config.CONTENT_DIR)
|
|
self.static_dir = Path(Config.STATIC_DIR)
|
|
self.templates_dir = Path(Config.TEMPLATES_DIR)
|
|
self.images_dir = Path(f"{Config.STATIC_DIR}/images")
|
|
self.css_dir = Path(f"{Config.STATIC_DIR}/css")
|
|
self.js_dir = Path(f"{Config.STATIC_DIR}/js")
|
|
self.output_dir = Path(Config.OUTPUT_DIR)
|
|
self.content_items = []
|
|
self.categories = defaultdict(list)
|
|
|
|
def init_site(self):
|
|
logger.info("Initializing new site")
|
|
# Create directories
|
|
for subdir in [self.content_dir, self.static_dir, self.templates_dir,
|
|
self.images_dir, self.css_dir, self.js_dir]:
|
|
os.makedirs(subdir, exist_ok=True)
|
|
# Create templates from literals
|
|
import templates
|
|
template_names = [t for t in dir(templates) if not t.startswith('_')]
|
|
for template_name in template_names:
|
|
template_content = getattr(templates, template_name)
|
|
with open(self.templates_dir / f"{template_name}.html", "w", encoding="utf8") as f:
|
|
f.write(template_content)
|
|
# Create static/about.md
|
|
|
|
def get_content_items(self):
|
|
logger.debug("Getting content items")
|
|
self.get_content_items = []
|
|
logger.debug(f"Scanning {Path(Config.CONTENT_DIR)}")
|
|
for md_file in Path(Config.CONTENT_DIR).glob("*.md"):
|
|
content_item = ContentItem(md_file)
|
|
content_item.parse_content()
|
|
self.content_items.append(content_item)
|
|
|
|
def map_categories(self):
|
|
for content_item in self.content_items:
|
|
for category in content_item.data.get("categories"):
|
|
if not category == "default":
|
|
self.categories[category].append(content_item.slug)
|
|
|
|
def build(self):
|
|
# Recreate the output dir if needed
|
|
if self.output_dir.exists():
|
|
shutil.rmtree(self.output_dir)
|
|
self.output_dir.mkdir()
|
|
|
|
# Create public subdirs
|
|
subdirs = [self.images_dir, self.css_dir, self.js_dir, self.content_dir, "categories"]
|
|
for subdir in subdirs:
|
|
subdir = self.output_dir / subdir
|
|
subdir.mkdir(parents=True, exist_ok=True)
|
|
|
|
# Copy static files if exist
|
|
if self.static_dir.exists():
|
|
shutil.copytree(self.static_dir, self.output_dir / self.static_dir, dirs_exist_ok=True)
|
|
|
|
# Get content items
|
|
self.get_content_items()
|
|
|
|
# Build categories map
|
|
self.map_categories()
|
|
|
|
# Render the content files
|
|
for content_item in self.content_items:
|
|
content_item.render_content(categories = self.categories)
|
|
|
|
# Render the about file
|
|
about_content = ContentItem(Path('static/about.md'))
|
|
about_content.parse_content()
|
|
about_content.render_content(categories = self.categories)
|
|
|
|
# Render the index file
|
|
visible_content_items = [c for c in self.content_items if c.data.get("hidden") != True]
|
|
with (self.output_dir / "index.html").open("w", encoding="utf-8") as f:
|
|
f.write(index_template.render(page_title = Config.MAIN_PAGE_TITLE, content_items=visible_content_items, categories = self.categories))
|
|
|
|
# Render the categories indices
|
|
visible_content_items = [c for c in self.content_items if c.data.get("hidden") != True]
|
|
for category in self.categories:
|
|
category_index = Path(f"{self.output_dir}/categories/{category}.html")
|
|
category_items = [i for i in visible_content_items if category in i.data.get("categories")]
|
|
with (category_index).open(mode="w", encoding="utf-8") as f:
|
|
f.write(index_template.render(page_title=category, content_items=category_items, categories = self.categories, parent_path = '../'))
|
|
|
|
logger.info(f"Created {len(self.content_items)} content items.")
|
|
|