from __future__ import annotations import logging import shutil from pathlib import Path import os import markdown from typing import Optional, Union, Dict, Generator from .config import SOURCE_DIRECTORY, DIST_DIRECTORY, LANGUAGE_INFORMATION logger = logging.getLogger("stsg.build") class CustomPath: def __init__(self, path: Path): self.path = path def __repr__(self) -> str: return str(self.path) @property def source_path(self) -> Path: return Path(SOURCE_DIRECTORY, self.path) @property def dist_path(self) -> Path: return Path(DIST_DIRECTORY, self.path) @property def name(self) -> str: return Path(self.path).name @property def parent(self) -> CustomPath: return CustomPath(Path(self.path).parent) @property def stem(self) -> str: return Path(self.path).stem def iterdir(self) -> Generator[CustomPath, None, None]: for p in self.source_path.iterdir(): yield CustomPath(Path(self.path, p.name)) def get_child(self, name: str, force_directory: bool = False, force_file: bool = False) -> Optional[CustomPath]: child = Path(self.source_path, name) if not child.exists(): return None if force_directory and not child.is_dir(): return None if force_file and not child.is_file(): return None return CustomPath(Path(self.path, name)) def read_text(self) -> str: return self.source_path.read_text() def copy_static(path: CustomPath): src = path.source_path dst = path.dist_path if not src.exists(): logger.warning("The static folder '%s' wasn't defined.", src) return logger.info("Copying static files from '%s' to '%s'", src, dst) dst.mkdir(parents=True, exist_ok=True) for root, dirs, files in os.walk(src): root_path = Path(root) if any(part.startswith(".") for part in root_path.parts): continue rel_path = root_path.relative_to(src) dest_dir = dst / rel_path dest_dir.mkdir(parents=True, exist_ok=True) for file in files: if file.startswith("."): continue src_file = root_path / file dest_file = dest_dir / file shutil.copy2(src_file, dest_file) class Article(CustomPath): def __init__(self, path: CustomPath): super().__init__(path.path) def get_content(self) -> str: if self.name.endswith(".md"): return markdown.markdown(self.read_text()) return self.read_text() @property def article_directory(self) -> Path: return self.dist_path.parent / self.stem class CustomLanguageCode: def __init__(self, file: CustomPath): self.file = file @property def language_code(self) -> str: return self.file.stem @property def relative_url(self) -> str: return "/" + str(self) def __repr__(self) -> str: return f"{self.language_code}" def _get_additional_data(self) -> dict: parsed_language_code = self.language_code.lower().replace("-", "_") if parsed_language_code in LANGUAGE_INFORMATION: return LANGUAGE_INFORMATION[parsed_language_code] parsed_language_code = parsed_language_code.split("_")[0] if parsed_language_code in LANGUAGE_INFORMATION: return LANGUAGE_INFORMATION[parsed_language_code] return {} @property def flag(self) -> str: return self._get_additional_data()["flag"] @property def native_name(self) -> str: return self._get_additional_data()["native_name"] @property def html_code(self) -> str: return f'' class Template: def __init__(self, root: CustomPath): self.root = root self.articles = [] def copy(self): return Template(root=self.root) def _replace_keywords(self, text: str, **placeholder_values: str) -> str: for key, value in placeholder_values.items(): text = text.replace(f"<{key}/>", value) text = text.replace(f"<{key} />", value) return text @property def article_template(self) -> str: article = self.root.get_child("article.html", force_file=True) return article.source_path.read_text() if article else "" def convert_article(self, path: Article): logger.info("converting %s", path) article_text = self._replace_keywords( self.article_template, content = path.get_content(), ) path.article_directory.mkdir(parents=True, exist_ok=True) with Path(path.article_directory, "index.html").open("w") as f: f.write(article_text) def walk_directory(root: CustomPath, template: Optional[Template] = None): template_dir = root.get_child("_templates", force_directory=True) if template_dir is not None: template = Template(template_dir) if template is None: logger.error("Didn't find template for %d", root) return for current_path in root.iterdir(): if current_path.name.startswith("_") or current_path.name == "static": continue if current_path.source_path.is_file(): template.convert_article(Article(current_path)) continue if current_path.source_path.is_dir(): walk_directory(current_path, template=template.copy()) static_dir = root.get_child("static", force_directory=True) if static_dir: copy_static(static_dir) def build(): logger.info("building static page") walk_directory(CustomPath(Path()))