Compare commits

...

5 Commits

Author SHA1 Message Date
Hazel Noack
632f47e017 refactored article translations with cached properties 2025-05-22 12:24:38 +02:00
Hazel Noack
2a7ebaa298 some refactoring 2025-05-22 12:06:53 +02:00
Hazel Noack
d43d6505b1 defined name 2025-05-22 12:02:42 +02:00
Hazel Noack
e7558d996b refactored some attributes as cached properties 2025-05-22 11:56:06 +02:00
Hazel Noack
15707ada59 feat: english documentation up to date 2025-05-22 11:18:09 +02:00
4 changed files with 141 additions and 53 deletions

View File

@ -10,7 +10,7 @@ dependencies = [
dynamic = [] dynamic = []
authors = [] authors = []
readme = "README.md" readme = "README.md"
requires-python = ">=3.10" requires-python = ">=3.8"
classifiers = [] classifiers = []
version = "0.0.0" version = "0.0.0"

View File

@ -4,3 +4,23 @@ Here you will learn how to get started making your own website.
1. fork the project and clone the fork 1. fork the project and clone the fork
2. {{installation.link}} 2. {{installation.link}}
## File structure
The files that define the content can be found in `src`. There are 3 subfolders. The style and functionality is defined in `templates`. If you can't code, or you don't know what your doing, leave it alone. The static files like stylesheets or pictures exist in `static`. This folder is simply copied on build. The interesting part starts with `articles`. Here are (like the name implies) all articles.
If something remains unclear, then the code of this documentation (should be found in your fork) could be helpful.
## Write articles
The text for the articles follows the following naming scheme `<language_code>.md`. In this case it would be `en.md` then you can write the article normally using [markdown](https://www.markdownguide.org/) for formatting.
If there should be subarticles, just create a new folder in the parent article folder. The folder name will be used as slug. That means it will appear in the url, and you can use it to link to other articles.
If you want to define the metadata for the article (the name, creation date, or author), then you can do so in `index.toml`. If none exists you can just create one. Here an example of such a file.
```toml
name="stsg"
datetime="2024-04-15 13:45:12.123456"
author="Hazel"
```

View File

@ -8,6 +8,9 @@ class config:
fallback_language = "en" fallback_language = "en"
preview_length = 400 preview_length = 400
preview_header_shift = 2 preview_header_shift = 2
markdown_extras = [
"fenced-code-blocks"
]
languages = { languages = {
"af": { "af": {

View File

@ -10,9 +10,12 @@ from collections import defaultdict
import toml import toml
from datetime import datetime from datetime import datetime
import jinja2 import jinja2
from functools import cached_property
from . import config from . import config
def get_first_header_content(content, fallback: str = ""): def get_first_header_content(content, fallback: str = ""):
soup = BeautifulSoup(content, 'html.parser') soup = BeautifulSoup(content, 'html.parser')
for level in range(1, 7): for level in range(1, 7):
@ -78,19 +81,6 @@ def get_preview_text(html_string: str):
return shift_headings(shorten_text_and_clean(html_string)) return shift_headings(shorten_text_and_clean(html_string))
def stem_to_language_code(stem: str) -> str:
language_code = stem.lower().replace("-", "_")
if language_code in config.languages:
return language_code
language_code = language_code.split("_")[0]
if language_code in config.languages:
return language_code
logger.error("Didn't recognize %s as a valid language code, add it to the config, or fix your structure.", stem)
exit(1)
class TemplateDict(dict): class TemplateDict(dict):
def __init__(self, folder: Path): def __init__(self, folder: Path):
@ -137,28 +127,66 @@ def compile_cross_article_context(cross_article_context):
cross_article_context["link"] = f'<a href="{url}">{title}</a>' cross_article_context["link"] = f'<a href="{url}">{title}</a>'
class ArticleTranslationContext(TypedDict):
slug: str
name: str
datetime: str
author: str
url: str
class ArticleTranslation: class ArticleTranslation:
article: Article
file: Path
@cached_property
def html_content(self) -> str:
html_content = self.file.read_text()
if self.file.suffix == ".md":
return markdown(html_content, extras=config.formatting.markdown_extras)
return html_content
@cached_property
def language_code(self) -> str:
language_code = self.file.stem.lower().replace("-", "_")
if language_code in config.languages:
return language_code
language_code = language_code.split("_")[0]
if language_code in config.languages:
return language_code
logger.error("Didn't recognize %s as a valid language code, add it to the config, or fix your structure.", stem)
exit(1)
@cached_property
def priority(self) -> int:
return LANGUAGES[self.language_code]["priority"]
@cached_property
def slug_path(self) -> List[str]:
return [self.language_code, *self.article.slug_path]
@cached_property
def url(self) -> str:
return "/" + "/".join(self.slug_path)
@cached_property
def dist_path(self) -> Path:
return Path(config.setup.dist_directory, *self.slug_path)
context: ArticleTranslationContext
cross_article_context: Dict[str, Any]
def __init__(self, file: Path, article: Article): def __init__(self, file: Path, article: Article):
self.file = file
self.article = article self.article = article
self.file = file
self.context: Dict[str, Any] = {} self.context = {}
# initializing the location of the article translation
self.language_code = stem_to_language_code(self.file.stem)
self.location_in_tree = [self.language_code, *self.article.location_in_tree]
self.url = "/" + "/".join(self.location_in_tree)
self.dist_path = Path(config.setup.dist_directory, *self.location_in_tree)
self.cross_article_context = TRANSLATED_CROSS_ARTICLE_CONTEXT[self.language_code][self.article.slug] = {} self.cross_article_context = TRANSLATED_CROSS_ARTICLE_CONTEXT[self.language_code][self.article.slug] = {}
self.priority = LANGUAGES[self.language_code]["priority"]
self.real_language_code = LANGUAGES[self.language_code]["code"]
self.html_content = self.file.read_text()
if self.file.suffix == ".md":
self.html_content = markdown(self.html_content, extras=["fenced-code-blocks"])
def __init_context__(self): def __init_context__(self):
self.context["meta"] = self.article.context_shared self.context["meta"] = self.article.context_shared
self.context["url"] = self.url self.context["url"] = self.url
@ -211,32 +239,69 @@ class ArticleTranslation:
f.write(TEMPLATE["article_translation"].render(self.context)) f.write(TEMPLATE["article_translation"].render(self.context))
class ArticleConfig(TypedDict):
slug: str
name: str
datetime: str
author: str
class ArticleContext(TypedDict):
slug: str
name: str
datetime: str
author: str
url: str
class Article: class Article:
def __init__(self, directory: Path, location_in_tree: Optional[List[str]] = None, is_root: bool = False, parent: Optional[Article] = None): directory: Path
@cached_property
def config(self) -> ArticleConfig:
config_file = self.directory / "index.toml"
return toml.load(config_file) if config_file.exists() else {}
@cached_property
def slug(self) -> str:
slug = self.config.get("name", self.directory.name)
if slug in ARTICLE_LAKE:
logger.error("two articles have the same name at %s and %r", ARTICLE_LAKE[slug].directory, self.directory)
exit(1)
return slug
@cached_property
def name(self) -> str:
return self.config.get("name", self.slug)
article_path: List[Article]
@cached_property
def slug_path(self) -> List[str]:
return [a.slug for a in self.article_path[1:]]
@cached_property
def url(self) -> str:
return "/" + "/".join(self.slug_path)
@cached_property
def dist_path(self) -> Path:
return Path(config.setup.dist_directory, *self.slug_path)
context: ArticleContext
context_shared: Dict[str, Any]
cross_article_context: Dict[str, Any]
def __init__(self, directory: Path, article_path: Optional[List[str]] = None, is_root: bool = False, parent: Optional[Article] = None):
self.directory = directory self.directory = directory
self.context: Dict[str, Any] = {} self.article_path: List[Article] = article_path or []
self.context_shared: Dict[str, Any] = {} self.article_path.append(self)
if parent is not None:
self.context["parent"] = parent.context_shared
# initializing the config values of the article self.context: ArticleContext = {}
config_file = self.directory / "index.toml" self.context_shared = {}
self.config = toml.load(config_file) if config_file.exists() else {}
# initializing the location and slug of the article
self.slug = self.config.get("name", self.directory.name)
if self.slug in ARTICLE_LAKE:
logger.error("two articles have the same name at %s and %r", ARTICLE_LAKE[self.slug].directory, self.directory)
exit(1)
self.cross_article_context = CROSS_ARTICLE_CONTEXT[self.slug] = {} self.cross_article_context = CROSS_ARTICLE_CONTEXT[self.slug] = {}
ARTICLE_LAKE[self.slug] = self
self.location_in_tree: List[str] = location_in_tree or [] ARTICLE_LAKE[self.slug] = self
if not is_root:
self.location_in_tree.append(self.slug)
self.url = "/" + "/".join(self.location_in_tree)
self.dist_path = Path(config.setup.dist_directory, *self.location_in_tree)
# build the tree # build the tree
self.child_articles: List[Article] = [] self.child_articles: List[Article] = []
@ -254,13 +319,13 @@ class Article:
elif c.is_dir(): elif c.is_dir():
self.child_articles.append(Article( self.child_articles.append(Article(
directory=c, directory=c,
location_in_tree=self.location_in_tree.copy(), article_path=self.article_path.copy(),
parent=self, parent=self,
)) ))
self.article_translations_list.sort(key=lambda a: a.priority, reverse=True) self.article_translations_list.sort(key=lambda a: a.priority, reverse=True)
logger.info("found %s at %s with the translations %s", self.slug, ".".join(list(self.location_in_tree)), ",".join(self.article_translations_map.keys())) logger.info("found %s at %s with the translations %s", self.slug, ".".join(list(self.slug_path)), ",".join(self.article_translations_map.keys()))
def __init_context__(self): def __init_context__(self):
self.context_shared["url"] = self.url self.context_shared["url"] = self.url
@ -282,7 +347,7 @@ class Article:
child_article_list = self.context["children"] = [] child_article_list = self.context["children"] = []
for article_translation in self.article_translations_list: for article_translation in self.article_translations_list:
self.context[article_translation.real_language_code] = article_translation.context self.context[article_translation.language_code] = article_translation.context
translation_list.append(article_translation.context) translation_list.append(article_translation.context)
for child_article in self.child_articles: for child_article in self.child_articles: