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, categories_index_template class ContentItem: def render_content(self): logger.debug(f"Rendering {self.source_filename} to {self.target_filename}") try: logger.debug(f"Title: {self.title}") 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) 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)) 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.url = " ...read more" self.preview = self.data.content.replace('\n', '
')[:300] + self.url 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 = self.data.get("categories", str([])) 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) logger.info("Initializing new site") 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): # 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(f"Scanning {Path(Config.CONTENT_DIR)}") for md_file in Path(Config.CONTENT_DIR).glob("*.md"): logger.debug(f"Loading {md_file}") 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) print(self.categories) 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] 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) # Parse the content files logger.debug("Getting content items") self.get_content_items() logger.debug(f"Content items: {self.content_items}") for content_item in self.content_items: content_item.render_content() # Build categories map self.map_categories() # Render the index file 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=self.content_items, categories = self.categories)) # Render the about file about_content = ContentItem(Path('static/about.md')) about_content.parse_content() about_content.render_content() logger.info(f"Created {len(self.content_items)} content items.")