244 lines
7.2 KiB
Python
244 lines
7.2 KiB
Python
from __future__ import annotations
|
|
import logging
|
|
import shutil
|
|
from pathlib import Path
|
|
import os
|
|
import markdown
|
|
from typing import Optional, Union, Dict, Generator, List
|
|
|
|
from .config import SOURCE_DIRECTORY, DIST_DIRECTORY, LANGUAGE_INFORMATION, ARTICLE_PREVIEW_LENGTH
|
|
|
|
|
|
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 CustomLanguageCode:
|
|
def __init__(self, language_code: str):
|
|
self.language_code = language_code
|
|
|
|
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"]
|
|
|
|
|
|
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
|
|
|
|
@property
|
|
def language_code(self) -> CustomLanguageCode:
|
|
return CustomLanguageCode(self.stem)
|
|
|
|
def get_article_keys(self) -> Dict[str, str]:
|
|
article_content = self.get_content()
|
|
|
|
return {
|
|
"article_content": article_content,
|
|
"article_preview": article_content[:ARTICLE_PREVIEW_LENGTH],
|
|
"article_href": "/" + str(self.path.parent / self.stem),
|
|
"article_language_name": self.language_code.native_name,
|
|
"article_language_code": self.language_code.language_code,
|
|
"article_language_flag": self.language_code.flag,
|
|
}
|
|
|
|
class Template:
|
|
def __init__(self, root: CustomPath):
|
|
self.root = root
|
|
|
|
self.articles: List[Article] = []
|
|
|
|
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("{" + key + "}", value)
|
|
|
|
return text
|
|
|
|
def get_article_template(self) -> str:
|
|
article = self.root.get_child("article.html", force_file=True)
|
|
return article.source_path.read_text() if article else "{article_content}"
|
|
|
|
def build_article(self, path: Article):
|
|
logger.info("converting %s", path)
|
|
|
|
article_text = self._replace_keywords(
|
|
self.get_article_template(),
|
|
**path.get_article_keys(),
|
|
)
|
|
|
|
path.article_directory.mkdir(parents=True, exist_ok=True)
|
|
with Path(path.article_directory, "index.html").open("w") as f:
|
|
f.write(article_text)
|
|
|
|
self.articles.append(path)
|
|
|
|
def get_overview_card_template(self) -> str:
|
|
overview_card = self.root.get_child("overview_card.html", force_file=True)
|
|
return overview_card.source_path.read_text() if overview_card else "<a href=\"{article_href}\"> {article_language_flag} {article_language_name} </a>"
|
|
|
|
def get_overview_template(self) -> str:
|
|
overview = self.root.get_child("overview.html", force_file=True)
|
|
return overview.source_path.read_text() if overview else "{overview_cards}"
|
|
|
|
def build_overview(self, root: CustomPath):
|
|
if not len(self.articles):
|
|
return
|
|
|
|
overview_card_template = self.get_overview_card_template()
|
|
|
|
overview_cards = "\n".join(self._replace_keywords(
|
|
overview_card_template,
|
|
**a.get_article_keys(),
|
|
) for a in self.articles)
|
|
|
|
overview_text = self._replace_keywords(
|
|
self.get_overview_template(),
|
|
overview_cards = overview_cards,
|
|
)
|
|
|
|
with Path(root.dist_path, "index.html").open("w") as f:
|
|
f.write(overview_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.build_article(Article(current_path))
|
|
continue
|
|
|
|
if current_path.source_path.is_dir():
|
|
walk_directory(current_path, template=template.copy())
|
|
|
|
template.build_overview(root=root)
|
|
|
|
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()))
|