|
|
|
|
@@ -10,9 +10,12 @@ from collections import defaultdict
|
|
|
|
|
import toml
|
|
|
|
|
from datetime import datetime
|
|
|
|
|
import jinja2
|
|
|
|
|
from functools import cached_property
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
from . import config
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_first_header_content(content, fallback: str = ""):
|
|
|
|
|
soup = BeautifulSoup(content, 'html.parser')
|
|
|
|
|
for level in range(1, 7):
|
|
|
|
|
@@ -76,20 +79,7 @@ def shift_headings(html_string, header_shift=config.formatting.preview_header_sh
|
|
|
|
|
|
|
|
|
|
def get_preview_text(html_string: str):
|
|
|
|
|
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):
|
|
|
|
|
@@ -137,28 +127,66 @@ def compile_cross_article_context(cross_article_context):
|
|
|
|
|
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:
|
|
|
|
|
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):
|
|
|
|
|
self.file = file
|
|
|
|
|
self.article = article
|
|
|
|
|
self.file = file
|
|
|
|
|
|
|
|
|
|
self.context: Dict[str, Any] = {}
|
|
|
|
|
|
|
|
|
|
# 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.context = {}
|
|
|
|
|
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):
|
|
|
|
|
self.context["meta"] = self.article.context_shared
|
|
|
|
|
self.context["url"] = self.url
|
|
|
|
|
@@ -211,33 +239,70 @@ class ArticleTranslation:
|
|
|
|
|
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:
|
|
|
|
|
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.context: Dict[str, Any] = {}
|
|
|
|
|
self.context_shared: Dict[str, Any] = {}
|
|
|
|
|
if parent is not None:
|
|
|
|
|
self.context["parent"] = parent.context_shared
|
|
|
|
|
self.article_path: List[Article] = article_path or []
|
|
|
|
|
self.article_path.append(self)
|
|
|
|
|
|
|
|
|
|
# initializing the config values of the article
|
|
|
|
|
config_file = self.directory / "index.toml"
|
|
|
|
|
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.context: ArticleContext = {}
|
|
|
|
|
self.context_shared = {}
|
|
|
|
|
self.cross_article_context = CROSS_ARTICLE_CONTEXT[self.slug] = {}
|
|
|
|
|
|
|
|
|
|
ARTICLE_LAKE[self.slug] = self
|
|
|
|
|
|
|
|
|
|
self.location_in_tree: List[str] = location_in_tree or []
|
|
|
|
|
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
|
|
|
|
|
self.child_articles: List[Article] = []
|
|
|
|
|
self.article_translations_list: List[ArticleTranslation] = []
|
|
|
|
|
@@ -254,13 +319,13 @@ class Article:
|
|
|
|
|
elif c.is_dir():
|
|
|
|
|
self.child_articles.append(Article(
|
|
|
|
|
directory=c,
|
|
|
|
|
location_in_tree=self.location_in_tree.copy(),
|
|
|
|
|
article_path=self.article_path.copy(),
|
|
|
|
|
parent=self,
|
|
|
|
|
))
|
|
|
|
|
|
|
|
|
|
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):
|
|
|
|
|
self.context_shared["url"] = self.url
|
|
|
|
|
@@ -282,7 +347,7 @@ class Article:
|
|
|
|
|
child_article_list = self.context["children"] = []
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
|
|
|
|
|
for child_article in self.child_articles:
|
|
|
|
|
|