From: Danilo M. Date: Sun, 3 May 2026 08:01:57 +0000 (+0200) Subject: feat: article scanner with translation pair detection X-Git-Tag: v1.0~25 X-Git-Url: https://git.danix.xyz/?a=commitdiff_plain;h=823703d848e810ee1dbe00d5470c60302a59a422;p=publisher.git feat: article scanner with translation pair detection --- diff --git a/core/article_scanner.py b/core/article_scanner.py new file mode 100644 index 0000000..ea6579a --- /dev/null +++ b/core/article_scanner.py @@ -0,0 +1,34 @@ +from __future__ import annotations +from pathlib import Path +from core.models import Article +from core.frontmatter import parse_frontmatter + +def scan_articles(blog_root: Path) -> list[Article]: + articles: list[Article] = [] + by_slug: dict[tuple[str, str], Path] = {} + + for lang in ("it", "en"): + content_dir = blog_root / "content" / lang / "articles" + if not content_dir.exists(): + continue + for index_md in sorted(content_dir.glob("*/index.md")): + slug = index_md.parent.name + by_slug[(lang, slug)] = index_md + + for (lang, slug), path in by_slug.items(): + other_lang = "en" if lang == "it" else "it" + translation_path = by_slug.get((other_lang, slug)) + try: + fm, _ = parse_frontmatter(path) + except (ValueError, Exception): + fm = {} + articles.append(Article( + slug=slug, + lang=lang, + path=path, + frontmatter=fm, + has_translation=translation_path is not None, + translation_path=translation_path, + )) + + return articles diff --git a/tests/test_article_scanner.py b/tests/test_article_scanner.py new file mode 100644 index 0000000..66daf83 --- /dev/null +++ b/tests/test_article_scanner.py @@ -0,0 +1,41 @@ +# tests/test_article_scanner.py +from pathlib import Path +from core.article_scanner import scan_articles + +def _make_article(base: Path, lang: str, slug: str, fm_extra: str = "") -> Path: + p = base / "content" / lang / "articles" / slug + p.mkdir(parents=True) + (p / "index.md").write_text(f"+++\ntitle = \"{slug}\"\ntype = \"Tech\"\n{fm_extra}+++\n\nBody.\n") + return p / "index.md" + +def test_scan_finds_articles(tmp_path): + _make_article(tmp_path, "it", "primo-post") + _make_article(tmp_path, "en", "first-post") + articles = scan_articles(tmp_path) + slugs = [a.slug for a in articles] + assert "primo-post" in slugs + assert "first-post" in slugs + +def test_scan_detects_translation_pair(tmp_path): + _make_article(tmp_path, "it", "shared-slug") + _make_article(tmp_path, "en", "shared-slug") + articles = scan_articles(tmp_path) + it_art = next(a for a in articles if a.lang == "it" and a.slug == "shared-slug") + en_art = next(a for a in articles if a.lang == "en" and a.slug == "shared-slug") + assert it_art.has_translation is True + assert it_art.translation_path == en_art.path + assert en_art.has_translation is True + +def test_scan_detects_missing_translation(tmp_path): + _make_article(tmp_path, "it", "solo-italiano") + articles = scan_articles(tmp_path) + art = next(a for a in articles if a.slug == "solo-italiano") + assert art.has_translation is False + assert art.translation_path is None + +def test_scan_parses_frontmatter(tmp_path): + _make_article(tmp_path, "it", "con-fm", 'tags = ["linux"]\n') + articles = scan_articles(tmp_path) + art = next(a for a in articles if a.slug == "con-fm") + assert art.frontmatter["title"] == "con-fm" + assert "linux" in art.frontmatter["tags"]