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>
</div>
<div class="content">
{article_content}
<content />
</div>
</section>

View File

@ -1,100 +1,97 @@
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 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")
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)
def copy_static(src, dst):
if not os.path.exists(src):
logger.warn("The static folder '%s' wasn't defined.", src)
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):
root_path = Path(root)
if any(part.startswith(".") for part in root_path.parts):
if any(p.startswith(".") for p in Path(root).parts):
continue
rel_path = root_path.relative_to(src)
dest_dir = dst / rel_path
dest_dir.mkdir(parents=True, exist_ok=True)
# Compute relative path from the source root
rel_path = os.path.relpath(root, src)
dest_dir = os.path.join(dst, rel_path)
os.makedirs(dest_dir, exist_ok=True)
for file in files:
if file.startswith("."):
continue
src_file = root_path / file
dest_file = dest_dir / file
src_file = os.path.join(root, file)
dest_file = os.path.join(dest_dir, 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:
def __init__(self, language_code: str):
self.language_code = language_code
def __init__(self, file: Path):
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:
return f"{self.language_code}"
@ -118,126 +115,45 @@ class CustomLanguageCode:
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 html_code(self) -> str:
return f'<ul><a href="{self.relative_url}"><bold>{self.flag} {self.native_name}</bold></a></ul>'
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):
template_dir = root.get_child("_templates", force_directory=True)
if template_dir is not None:
template = Template(template_dir)
context = Context(src_path)
language_codes_found = []
if template is None:
logger.error("Didn't find template for %d", root)
return
for current_full_path in src_path.iterdir():
current_name = Path(current_full_path).name
current_dst = Path(dst_path, current_name)
current_src = Path(src_path, current_name)
for current_path in root.iterdir():
if current_path.name.startswith("_") or current_path.name == "static":
if current_name == "static":
copy_static(current_src, current_dst)
continue
if current_path.source_path.is_file():
template.build_article(Article(current_path))
if current_name.endswith(".md"):
convert_md(current_src, current_dst, context=context)
language_codes_found.append(CustomLanguageCode(Path(root, current_name)))
continue
if current_path.source_path.is_dir():
walk_directory(current_path, template=template.copy())
if current_src.is_dir():
walk_directory(Path(root, current_full_path.name))
template.build_overview(root=root)
static_dir = root.get_child("static", force_directory=True)
if static_dir:
copy_static(static_dir)
content = f"""
<li>
{''.join(l.html_code for l in language_codes_found)}
</li>
"""
with Path(dst_path, "index.html").open("w") as f:
f.write(context.get_text(content=content))
def build():
logger.info("building static page")
walk_directory(CustomPath(Path()))
walk_directory("")

View File

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