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.")