Compare commits

..

No commits in common. "432f16ba080558a8ab8240174d1f27533acec819" and "523402d3ad6392a18bf463f1639219607535e749" have entirely different histories.

5 changed files with 102 additions and 230 deletions

View File

@ -1,38 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>STSG</title>
<link rel="stylesheet" href="/static/bulma.min.css" />
</head>
<body>
<!-- Header (Navbar) -->
<nav
class="navbar is-primary"
role="navigation"
aria-label="main navigation"
>
<div class="navbar-brand">
<a class="navbar-item" href="#">
<strong>Static Translated Site Generator</strong>
</a>
</div>
</nav>
<section class="section">
<div class="container">
<div class="column is-half is-offset-one-quarter">
{overview_cards}
</div>
</div>
</section>
<!-- Footer -->
<footer class="footer">
<div class="content has-text-centered">
<p><strong>STSG</strong> by Hazel. &copy; 2025</p>
</div>
</footer>
</body>
</html>

View File

@ -1,8 +0,0 @@
<div class="card mb-4">
<a href="{article_href}" class="card mb-4" style="color: inherit; text-decoration: none;">
<div class="card-content" href="{article_href}">
<p class="title">{article_language_flag} {article_language_name}</p>
<p class="content">{article_preview}</p>
</div>
</a>
</div>

View File

@ -26,7 +26,8 @@
<a href="../">Go Back</a> <a href="../">Go Back</a>
</div> </div>
<div class="content"> <div class="content">
{article_content}
<content />
</div> </div>
</section> </section>

View File

@ -1,100 +1,97 @@
from __future__ import annotations
import logging import logging
import shutil import shutil
from pathlib import Path from pathlib import Path
import os import os
import markdown import markdown
from typing import Optional, Union, Dict, Generator, List from typing import Optional
from .config import SOURCE_DIRECTORY, DIST_DIRECTORY, LANGUAGE_INFORMATION, ARTICLE_PREVIEW_LENGTH from .config import SOURCE_DIRECTORY, DIST_DIRECTORY, LANGUAGE_INFORMATION
logger = logging.getLogger("stsg.build") logger = logging.getLogger("stsg.build")
class CustomPath: def copy_static(src, dst):
def __init__(self, path: Path): if not os.path.exists(src):
self.path = path logger.warn("The static folder '%s' wasn't defined.", src)
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 return
logger.info("Copying static files from '%s' to '%s'", src, dst) logger.info("copying static files from '%s' to '%r'", src, dst)
dst.mkdir(parents=True, exist_ok=True) os.makedirs(dst, exist_ok=True)
for root, dirs, files in os.walk(src): for root, dirs, files in os.walk(src):
root_path = Path(root) if any(p.startswith(".") for p in Path(root).parts):
continue
if any(part.startswith(".") for part in root_path.parts): # Compute relative path from the source root
continue rel_path = os.path.relpath(root, src)
dest_dir = os.path.join(dst, rel_path)
rel_path = root_path.relative_to(src) os.makedirs(dest_dir, exist_ok=True)
dest_dir = dst / rel_path
dest_dir.mkdir(parents=True, exist_ok=True)
for file in files: for file in files:
if file.startswith("."): if file.startswith("."):
continue continue
src_file = root_path / file src_file = os.path.join(root, file)
dest_file = dest_dir / file dest_file = os.path.join(dest_dir, file)
shutil.copy2(src_file, dest_file) shutil.copy2(src_file, dest_file)
class Context:
def __init__(self, root: str = SOURCE_DIRECTORY):
self.file = None
current_root = Path(root)
while current_root.parts and self.file is None:
current_file = Path(current_root, "index.html")
if current_file.exists() and current_file.is_file:
self.file = current_file
current_root = current_root.parent
if self.file is None:
logger.error("couldn't find context for %s", root)
exit(1)
logger.info("%s found context %r", root, str(self.file))
def get_text(self, **placeholder_values: dict):
text = self.file.read_text()
for key, value in placeholder_values.items():
text = text.replace(f"<{key}/>", value)
text = text.replace(f"<{key} />", value)
return text
def convert_md(src: Path, dst: Path, context: Optional[Context] = None):
logger.info("converting %s", src)
html_content = markdown.markdown(src.read_text())
context = context or Context(str(src.parent))
full_page = context.get_text(content=html_content)
folder_dst = dst.parent / dst.name.replace(".md", "")
folder_dst.mkdir(parents=True, exist_ok=True)
with Path(folder_dst, "index.html").open("w") as f:
f.write(full_page)
class CustomLanguageCode: class CustomLanguageCode:
def __init__(self, language_code: str): def __init__(self, file: Path):
self.language_code = language_code self.file: Path = file
@property
def language_code(self) -> str:
return self.file.name.replace(".md", "")
@property
def relative_url(self) -> str:
return "/" + str(Path(self.file.parent, self.language_code))
def __repr__(self) -> str: def __repr__(self) -> str:
return f"{self.language_code}" return f"{self.language_code}"
@ -118,126 +115,45 @@ class CustomLanguageCode:
def native_name(self) -> str: def native_name(self) -> str:
return self._get_additional_data()["native_name"] 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 @property
def article_directory(self) -> Path: def html_code(self) -> str:
return self.dist_path.parent / self.stem return f'<ul><a href="{self.relative_url}"><bold>{self.flag} {self.native_name}</bold></a></ul>'
@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):
src_path = Path(SOURCE_DIRECTORY, root)
dst_path = Path(DIST_DIRECTORY, root)
def walk_directory(root: CustomPath, template: Optional[Template] = None): context = Context(src_path)
template_dir = root.get_child("_templates", force_directory=True) language_codes_found = []
if template_dir is not None:
template = Template(template_dir)
if template is None: for current_full_path in src_path.iterdir():
logger.error("Didn't find template for %d", root) current_name = Path(current_full_path).name
return current_dst = Path(dst_path, current_name)
current_src = Path(src_path, current_name)
for current_path in root.iterdir(): if current_name == "static":
if current_path.name.startswith("_") or current_path.name == "static": copy_static(current_src, current_dst)
continue
if current_name.endswith(".md"):
convert_md(current_src, current_dst, context=context)
language_codes_found.append(CustomLanguageCode(Path(root, current_name)))
continue continue
if current_path.source_path.is_file(): if current_src.is_dir():
template.build_article(Article(current_path)) walk_directory(Path(root, current_full_path.name))
continue
if current_path.source_path.is_dir(): content = f"""
walk_directory(current_path, template=template.copy()) <li>
{''.join(l.html_code for l in language_codes_found)}
template.build_overview(root=root) </li>
"""
static_dir = root.get_child("static", force_directory=True) with Path(dst_path, "index.html").open("w") as f:
if static_dir: f.write(context.get_text(content=content))
copy_static(static_dir)
def build(): def build():
logger.info("building static page") logger.info("building static page")
walk_directory(CustomPath(Path())) walk_directory("")

View File

@ -1,10 +1,11 @@
SOURCE_DIRECTORY = "src" SOURCE_DIRECTORY = "src"
DIST_DIRECTORY = "dist" DIST_DIRECTORY = "dist"
# config template stuff # relative to SOURCE_DIRECTORY / DIST_DIRECTORY
ARTICLE_PREVIEW_LENGTH = 200 STATIC_DIRECTORY = "static"
# FOR DEVELOPMENT # FOR DEVELOPMENT
CODE_DIRECTORY = "stsg" CODE_DIRECTORY = "stsg"
# LANGUAGE INFORMATION # LANGUAGE INFORMATION