some reworking
This commit is contained in:
11
argparser.py
Normal file
11
argparser.py
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import argparse
|
||||||
|
from config import Config
|
||||||
|
|
||||||
|
argparser = argparse.ArgumentParser(
|
||||||
|
prog = Config.APP_NAME,
|
||||||
|
description = Config.APP_DESCRIPTION,
|
||||||
|
epilog = f"See {Config.APP_SRC_URL} for more info.")
|
||||||
|
|
||||||
|
argparser.add_argument('--init', action='store_true', help = 'Initialize new site')
|
||||||
|
argparser.add_argument('--content', '--edit', type = str, help = 'Create new content')
|
||||||
|
argparser.add_argument('--build', action='store_true', help = 'Build the site')
|
||||||
58
classes.py
Normal file
58
classes.py
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
from datetime import datetime
|
||||||
|
import shutil
|
||||||
|
import markdown
|
||||||
|
import frontmatter
|
||||||
|
from jinja2 import Environment, FileSystemLoader
|
||||||
|
from pathlib import Path
|
||||||
|
from logger import logger
|
||||||
|
from config import Config
|
||||||
|
|
||||||
|
# Prepare template rendering engine
|
||||||
|
env = Environment(loader=FileSystemLoader(Config.TEMPLATE_DIR))
|
||||||
|
env.globals['header_image'] = Config.HEADER_IMAGE
|
||||||
|
index_template = env.get_template("index.html")
|
||||||
|
content_item_template = env.get_template("content_item.html")
|
||||||
|
|
||||||
|
class DefaultFrontmatterHeader:
|
||||||
|
def __init__(self, title):
|
||||||
|
self.title = f"title: '{title}'\n"
|
||||||
|
self.date = f"date: '{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}'\n"
|
||||||
|
self.omit_second_title = f"omit_second_title: '{str(False)}'\n"
|
||||||
|
self.description = "description: \n"
|
||||||
|
self.author = "author: \n"
|
||||||
|
|
||||||
|
class ContentItemPrototype:
|
||||||
|
def render_content(self):
|
||||||
|
logger.debug(f"Rendering {self.source_filename} to {Config.OUTPUT_DIR}/{self.target_filename}")
|
||||||
|
if self.image_src_file.exists():
|
||||||
|
shutil.copyfile(self.image_src_file, Path(Config.OUTPUT_DIR) / self.image)
|
||||||
|
if self.custom_css_src_file.exists():
|
||||||
|
shutil.copyfile(self.custom_css_src_file,Path(Config.OUTPUT_DIR) / self.custom_css)
|
||||||
|
if self.custom_js_src_file.exists():
|
||||||
|
shutil.copyfile(self.custom_js_src_file, Path(Config.OUTPUT_DIR) / self.custom_js)
|
||||||
|
with self.content_output_path.open("w", encoding="utf-8") as f:
|
||||||
|
f.write(content_item_template.render(content_item = self, page_title = self.title))
|
||||||
|
|
||||||
|
def __init__(self, md_file):
|
||||||
|
logger.debug(f"Parsing {md_file}")
|
||||||
|
self.source_filename = md_file
|
||||||
|
self.slug = md_file.stem
|
||||||
|
self.target_filename = self.slug + ".html"
|
||||||
|
self.data = frontmatter.load(md_file)
|
||||||
|
self.html = markdown.markdown(self.data.content)
|
||||||
|
self.preview = self.html[:300] + "<a href=" + f"{self.slug}.html" + ">... read more</a>"
|
||||||
|
self.title = self.data.get("title", self.slug)
|
||||||
|
self.omit_second_title = self.data.get("omit_second_title", False)
|
||||||
|
self.date = self.data.get("data", "2000-01-01T00:00:00+03:00")
|
||||||
|
self.content_output_path = Path(Config.OUTPUT_DIR) / f"{self.slug}.html"
|
||||||
|
self.image_src_file = Path(f"{Config.CONTENT_DIR}/{md_file.stem}.jpg")
|
||||||
|
self.image = f"images/{md_file.stem}.jpg" if self.image_src_file.exists() else None
|
||||||
|
self.custom_css_src_file = Path(f"{Config.CONTENT_DIR}/{md_file.stem}.css")
|
||||||
|
self.custom_css = f"static/css/{md_file.stem}.css" if self.custom_css_src_file.exists() else None
|
||||||
|
logger.debug(f"Custom CSS: {self.slug}: {self.custom_css}")
|
||||||
|
self.custom_js_src_file = Path(f"{Config.CONTENT_DIR}/{md_file.stem}.js")
|
||||||
|
self.custom_js = f"static/js/{md_file.stem}.js" if self.custom_js_src_file.exists() else None
|
||||||
|
logger.debug(f"Custom JS: {self.slug}: {self.custom_js}")
|
||||||
|
# Special handling for the 'about' item
|
||||||
|
if self.slug == 'about':
|
||||||
|
self.image = 'static/about.jpg'
|
||||||
13
config.py
13
config.py
@@ -1,7 +1,16 @@
|
|||||||
import os
|
import os
|
||||||
# Main config section
|
# Main config section
|
||||||
class Config:
|
class Config:
|
||||||
|
MAIN_PAGE_TITLE = "hydrogen site"
|
||||||
|
|
||||||
APP_NAME = "hydrogen"
|
APP_NAME = "hydrogen"
|
||||||
MAIN_PAGE_TITLE = "Selected poems"
|
APP_DESCRIPTION = "Simplistic static site generator"
|
||||||
OUTPUT_DIR = os.environ.get('OUTPUT_DIR') or 'public'
|
APP_SRC_URL = f"https://git.exocortex.ru/Exocortex/{APP_NAME}"
|
||||||
|
OUTPUT_DIR = 'public'
|
||||||
|
OUTPUT_IMG_DIR = f"{OUTPUT_DIR}/images"
|
||||||
|
OUTPUT_CSS_DIR = f"{OUTPUT_DIR}/css"
|
||||||
|
OUTPUT_JS_DIR = f"{OUTPUT_DIR}/js"
|
||||||
|
TEMPLATE_DIR = 'templates'
|
||||||
|
CONTENT_DIR = 'content'
|
||||||
|
STATIC_DIR = 'static'
|
||||||
HEADER_IMAGE = 'static/header.jpg'
|
HEADER_IMAGE = 'static/header.jpg'
|
||||||
42
functions.py
Normal file
42
functions.py
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import os, subprocess
|
||||||
|
import shutil
|
||||||
|
from pathlib import Path
|
||||||
|
from classes import DefaultFrontmatterHeader
|
||||||
|
from config import Config
|
||||||
|
from logger import logger
|
||||||
|
|
||||||
|
def create_content(filename):
|
||||||
|
filename += '.md'
|
||||||
|
filename = Path(f"{Config.CONTENT_DIR}/{filename}")
|
||||||
|
if not filename.exists():
|
||||||
|
fh = DefaultFrontmatterHeader(filename.stem)
|
||||||
|
logger.debug(f"Creating new content {filename}")
|
||||||
|
with filename.open("w", encoding="utf-8") as f:
|
||||||
|
f.writelines(['---\n', fh.title, fh.date, fh.description, fh.author, fh.omit_second_title, '---\n', '\n', '\n'])
|
||||||
|
|
||||||
|
def edit_content(filename):
|
||||||
|
logger.debug(f"Editing content {filename}")
|
||||||
|
editor = os.environ.get('EDITOR', 'vim')
|
||||||
|
subprocess.call([editor, filename])
|
||||||
|
|
||||||
|
def init_site():
|
||||||
|
logger.debug(f"Initializing new site")
|
||||||
|
|
||||||
|
# Recreate output directory if necessary
|
||||||
|
output_dir = Path(Config.OUTPUT_DIR)
|
||||||
|
if output_dir.exists():
|
||||||
|
shutil.rmtree(output_dir)
|
||||||
|
output_dir.mkdir()
|
||||||
|
|
||||||
|
# Create output directory subtree
|
||||||
|
for dir in [ Path(Config.CONTENT_DIR), Path(Config.STATIC_DIR), Path(Config.TEMPLATE_DIR),
|
||||||
|
Path(Config.OUTPUT_IMG_DIR), Path(Config.OUTPUT_CSS_DIR), Path(Config.OUTPUT_JS_DIR)]:
|
||||||
|
dir.mkdir(exist_ok=True)
|
||||||
|
|
||||||
|
# Create "About.md" content item
|
||||||
|
create_content("about")
|
||||||
|
|
||||||
|
def build_site():
|
||||||
|
logger.debug(f"Starting build")
|
||||||
|
|
||||||
|
|
||||||
130
hydrogen.py
130
hydrogen.py
@@ -1,12 +1,13 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import frontmatter
|
|
||||||
import markdown
|
|
||||||
from jinja2 import Environment, FileSystemLoader
|
|
||||||
import shutil
|
import shutil
|
||||||
from config import Config
|
from config import Config
|
||||||
from logger import logger
|
from logger import logger
|
||||||
|
from argparser import argparser
|
||||||
|
from classes import *
|
||||||
|
from functions import *
|
||||||
|
|
||||||
content_dir = Path('content')
|
content_dir = Path('content')
|
||||||
template_dir = Path('templates')
|
template_dir = Path('templates')
|
||||||
@@ -17,82 +18,69 @@ assets_js_dir = Path(output_dir / 'static/js')
|
|||||||
static_dir = Path('static')
|
static_dir = Path('static')
|
||||||
header_image = Config.HEADER_IMAGE or '/static/header.jpg'
|
header_image = Config.HEADER_IMAGE or '/static/header.jpg'
|
||||||
|
|
||||||
class ContentItemPrototype:
|
|
||||||
def render_content(self):
|
|
||||||
if self.image_src_file.exists():
|
|
||||||
shutil.copyfile(self.image_src_file, output_dir / self.image)
|
|
||||||
if self.custom_css_src_file.exists():
|
|
||||||
shutil.copyfile(self.custom_css_src_file,output_dir / self.custom_css)
|
|
||||||
if self.custom_js_src_file.exists():
|
|
||||||
shutil.copyfile(self.custom_js_src_file, output_dir / self.custom_js)
|
|
||||||
with self.content_output_path.open("w", encoding="utf-8") as f:
|
|
||||||
f.write(content_item_template.render(content_item = self, page_title = self.title))
|
|
||||||
|
|
||||||
def __init__(self, md_file):
|
|
||||||
self.slug = md_file.stem
|
|
||||||
self.data = frontmatter.load(md_file)
|
|
||||||
self.html = markdown.markdown(self.data.content)
|
|
||||||
self.preview = self.html[:300] + "<a href=" + f"{self.slug}.html" + ">... read more</a>"
|
|
||||||
self.title = self.data.get("title", self.slug)
|
|
||||||
self.omit_second_title = self.data.get("omit_second_title", False)
|
|
||||||
self.date = self.data.get("data", "2000-01-01T00:00:00+03:00")
|
|
||||||
self.content_output_path = output_dir / f"{self.slug}.html"
|
|
||||||
self.image_src_file = Path(f"{content_dir}/{md_file.stem}.jpg")
|
|
||||||
self.image = f"images/{md_file.stem}.jpg" if self.image_src_file.exists() else None
|
|
||||||
self.custom_css_src_file = Path(f"{content_dir}/{md_file.stem}.css")
|
|
||||||
self.custom_css = f"static/css/{md_file.stem}.css" if self.custom_css_src_file.exists() else None
|
|
||||||
logger.debug(f"Custom CSS: {self.slug}: {self.custom_css}")
|
|
||||||
self.custom_js_src_file = Path(f"{content_dir}/{md_file.stem}.js")
|
|
||||||
self.custom_js = f"static/js/{md_file.stem}.js" if self.custom_js_src_file.exists() else None
|
|
||||||
logger.debug(f"Custom JS: {self.slug}: {self.custom_js}")
|
|
||||||
# Special handling for the 'about' item
|
|
||||||
if self.slug == 'about':
|
|
||||||
self.image = 'static/about.jpg'
|
|
||||||
|
|
||||||
logger.info(f"Building the '{Config.MAIN_PAGE_TITLE}' site.")
|
|
||||||
|
|
||||||
# Recreate the output dir if needed
|
|
||||||
if output_dir.exists():
|
|
||||||
shutil.rmtree(output_dir)
|
|
||||||
output_dir.mkdir()
|
|
||||||
|
|
||||||
# Create public subdirs
|
|
||||||
subdirs = [img_dir, assets_css_dir, assets_js_dir]
|
|
||||||
for subdir in subdirs:
|
|
||||||
subdir.mkdir(parents=True, exist_ok=True)
|
|
||||||
|
|
||||||
# Copy static files if exist
|
|
||||||
if static_dir.exists():
|
|
||||||
shutil.copytree(static_dir, output_dir / "static", dirs_exist_ok=True)
|
|
||||||
|
|
||||||
# Prepare template rendering engine
|
|
||||||
env = Environment(loader=FileSystemLoader(str(template_dir)))
|
|
||||||
env.globals['header_image'] = header_image
|
|
||||||
index_template = env.get_template("index.html")
|
|
||||||
content_item_template = env.get_template("content_item.html")
|
|
||||||
|
|
||||||
# Parse the content files
|
def build_site():
|
||||||
content_items = []
|
logger.info(f"Building the '{Config.MAIN_PAGE_TITLE}' site.")
|
||||||
for md_file in content_dir.glob("*.md"):
|
|
||||||
current_content_item = ContentItemPrototype(md_file)
|
|
||||||
current_content_item.render_content()
|
|
||||||
content_items.append({
|
|
||||||
"slug": current_content_item.slug,
|
|
||||||
"title": current_content_item.title,
|
|
||||||
"date": current_content_item.date,
|
|
||||||
"preview": markdown.markdown(current_content_item.preview),
|
|
||||||
"image": current_content_item.image,
|
|
||||||
})
|
|
||||||
|
|
||||||
# Render the index file
|
# Recreate the output dir if needed
|
||||||
with (output_dir / "index.html").open("w", encoding="utf-8") as f:
|
if output_dir.exists():
|
||||||
f.write(index_template.render(page_title = Config.MAIN_PAGE_TITLE, content_items=content_items))
|
shutil.rmtree(output_dir)
|
||||||
|
output_dir.mkdir()
|
||||||
|
|
||||||
# Render the about file
|
# Create public subdirs
|
||||||
about_content = ContentItemPrototype(Path('static/about.md'))
|
subdirs = [img_dir, assets_css_dir, assets_js_dir]
|
||||||
about_content.render_content()
|
for subdir in subdirs:
|
||||||
|
subdir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
# Move 'robots.txt' into output_dir
|
# Copy static files if exist
|
||||||
shutil.copyfile(static_dir / 'robots.txt', output_dir / 'robots.txt')
|
if static_dir.exists():
|
||||||
|
shutil.copytree(static_dir, output_dir / "static", dirs_exist_ok=True)
|
||||||
|
|
||||||
logger.info(f"Created {len(content_items)} content items.")
|
# Parse the content files
|
||||||
|
content_items = []
|
||||||
|
for md_file in content_dir.glob("*.md"):
|
||||||
|
current_content_item = ContentItemPrototype(md_file)
|
||||||
|
current_content_item.render_content()
|
||||||
|
content_items.append({
|
||||||
|
"slug": current_content_item.slug,
|
||||||
|
"title": current_content_item.title,
|
||||||
|
"date": current_content_item.date,
|
||||||
|
# "preview": markdown.markdown(current_content_item.preview),
|
||||||
|
"image": current_content_item.image,
|
||||||
|
})
|
||||||
|
|
||||||
|
# Render the index file
|
||||||
|
with (output_dir / "index.html").open("w", encoding="utf-8") as f:
|
||||||
|
f.write(index_template.render(page_title = Config.MAIN_PAGE_TITLE, content_items=content_items))
|
||||||
|
|
||||||
|
# Render the about file
|
||||||
|
about_content = ContentItemPrototype(Path('static/about.md'))
|
||||||
|
about_content.render_content()
|
||||||
|
|
||||||
|
# Move 'robots.txt' into output_dir
|
||||||
|
shutil.copyfile(static_dir / 'robots.txt', output_dir / 'robots.txt')
|
||||||
|
|
||||||
|
logger.info(f"Created {len(content_items)} content items.")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
args = argparser.parse_args()
|
||||||
|
if args.content:
|
||||||
|
content = args.content or args.edit
|
||||||
|
create_content(content)
|
||||||
|
edit_content(content)
|
||||||
|
if args.build:
|
||||||
|
build_site()
|
||||||
|
if args.init:
|
||||||
|
init_site()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
@@ -4,7 +4,7 @@ from config import Config
|
|||||||
# Logging config section
|
# Logging config section
|
||||||
|
|
||||||
LOG_TO = sys.stdout
|
LOG_TO = sys.stdout
|
||||||
LOG_LEVEL = logging.INFO
|
LOG_LEVEL = logging.DEBUG
|
||||||
|
|
||||||
logger = logging.getLogger(Config.APP_NAME)
|
logger = logging.getLogger(Config.APP_NAME)
|
||||||
logger.setLevel(LOG_LEVEL)
|
logger.setLevel(LOG_LEVEL)
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
argparse
|
||||||
python-frontmatter
|
python-frontmatter
|
||||||
jinja2
|
jinja2
|
||||||
markdown
|
markdown
|
||||||
|
|||||||
Reference in New Issue
Block a user