import markdown
import yaml
from pathlib import Path
import re
import frontmatter
from django.conf import settings
from blog.models import Author, Column, Post
from .crypto import encrypt_for_cryptojs
def slugify(text):
text = text.lower()
text = text.replace(".", "")
text = re.sub(r"[^a-z0-9]+", "-", text)
return text.strip("-")
def load_yaml(path: str) -> dict:
"""
Load yaml file and return a dict
"""
with open(path, "r", encoding="utf-8") as f:
return yaml.safe_load(f)
def load_authors():
authors = load_yaml(settings.CONFIG_FILE["authors"])
for author_id, author in authors.items():
Author.objects.update_or_create(
slug=author_id,
defaults={
"name": author.get("name", author_id),
"description": author.get("description", ""),
"avatar": author.get("avatar", ""),
"url": author.get("url", ""),
"featured": author.get("featured", False),
"is_staff": author.get("is_staff", False)
},
)
def load_columns():
columns = load_yaml(settings.CONFIG_FILE["columns"])
for column_id, column in columns.items():
Column.objects.update_or_create(
slug=column_id,
defaults={
"name": column.get("name", column_id),
"description": column.get("description", ""),
"unlisted": column.get("unlisted", False),
"icon": column.get("icon", "")
}
)
def load_single_post(md_path):
md_path = Path(md_path)
if not md_path.exists():
print(f"Skipping missing file {md_path}")
return
fm = frontmatter.load(md_path)
title = fm.get("title") or md_path.stem
description = fm.get("description", "")
slug = slugify(fm.get("slug") or title)
date_val = fm.get("date")
if not date_val:
print(f"Skipping {md_path}: no date")
return
authors_list = fm.get("authors")
if not authors_list:
print(f"Skipping {md_path}: no authors")
return
author_id = authors_list[0]
try:
author = Author.objects.get(slug=author_id)
except Author.DoesNotExist:
print(f"Skipping {md_path}: unknown author '{author_id}'")
return
categories = fm.get("categories")
if not categories:
print(f"Skipping {md_path}: no categories")
return
column_name = categories[0]
column, _ = Column.objects.get_or_create(
name=column_name,
slug=slugify(column_name),
)
source = fm.get("source")
image = fm.get("image")
disable_comments = fm.get("disable_comments")
content_raw = fm.content
aside_html = None
if "<!-- ASIDE -->" in content_raw and "<!-- /ASIDE -->" in content_raw:
before, remainder = content_raw.split("<!-- ASIDE -->", 1)
aside, after = remainder.split("<!-- /ASIDE -->", 1)
aside_html = aside.strip()
content_raw = after.strip()
content_html = markdown.markdown(
content_raw,
extensions=["fenced_code", "tables"],
)
encrypt_password = fm.get("encrypt_password")
encrypted_content = None
encrypted_aside = None
if encrypt_password:
encrypted_content = encrypt_for_cryptojs(content_html, encrypt_password)
content_html = ""
if aside_html:
encrypted_aside = encrypt_for_cryptojs(aside_html, encrypt_password)
aside_html = ""
Post.objects.update_or_create(
slug=slug,
date=date_val,
defaults={
"title": title,
"description": description,
"author": author,
"column": column,
"source": source,
"image": image,
"markdown_path": str(md_path),
"content_html": content_html,
"aside_html": aside_html,
"encrypted_content": None if not encrypted_content else encrypted_content,
"encrypted_aside": None if not encrypted_aside else encrypted_aside,
"disable_comments": True if disable_comments else False
},
)
print(f"Loaded: {title}")
def load_posts():
base = Path("content/blog")
for md_path in base.rglob("*.md"):
load_single_post(md_path)