[REFACTOR] реестр титульных листов вместо одного макета

This commit is contained in:
Arseny
2026-09-02 03:17:53 +03:00
parent cd1b8d98c3
commit 10528c6688
7 changed files with 114 additions and 3 deletions
+2 -2
View File
@@ -1,4 +1,4 @@
from md2gost.latex.preamble import END_DOCUMENT, PREAMBLE from md2gost.latex.preamble import END_DOCUMENT, PREAMBLE
from md2gost.latex.titlepage import render_titlepage from md2gost.latex.titlepages import TITLEPAGES, render_titlepage
__all__ = ["END_DOCUMENT", "PREAMBLE", "render_titlepage"] __all__ = ["END_DOCUMENT", "PREAMBLE", "TITLEPAGES", "render_titlepage"]
+32
View File
@@ -0,0 +1,32 @@
"""Реестр макетов титульного листа.
Макет выбирается полем front matter ``титульник``; по умолчанию ``default`` —
исторический макет, поэтому существующие документы продолжают собираться
без изменений.
"""
from __future__ import annotations
from typing import Callable
from md2gost.latex.titlepages import default, ptp_report, ptp_task
TITLEPAGES: dict[str, Callable[[dict[str, str]], str]] = {
"default": default.render,
"ptp-report": ptp_report.render,
"ptp-task": ptp_task.render,
}
__all__ = ["TITLEPAGES", "render_titlepage"]
def render_titlepage(fm: dict[str, str]) -> str:
name = fm.get("титульник", "default")
try:
layout = TITLEPAGES[name]
except KeyError:
available = ", ".join(sorted(TITLEPAGES))
raise ValueError(
f"front matter: неизвестный титульник '{name}'; доступны: {available}"
) from None
return layout(fm)
+39
View File
@@ -0,0 +1,39 @@
"""Общие части титульных листов: доступ к полям и шапка по бланку кафедры."""
from __future__ import annotations
from md2gost.handlers.inline import escape_latex
MINISTRY_DEFAULTS = {
"министерство": "Министерство науки и высшего образования Российской Федерации",
"вуз_тип": "Федеральное государственное автономное образовательное учреждение",
"вуз_тип2": "высшего образования",
"вуз": "Московский государственный технический университет",
"вуз_имени": "имени Н.Э. Баумана",
"вуз_статус": "национальный исследовательский университет",
"вуз_аббревиатура": "МГТУ им. Н.Э. Баумана",
}
def field(fm: dict[str, str], key: str, default: str = "") -> str:
"""Значение поля front matter, экранированное для LaTeX."""
return escape_latex(fm.get(key, default))
def ministry_lines(fm: dict[str, str]) -> str:
"""Семь центрированных строк шапки в точности по бланку кафедры.
Отличается от макета ``default`` разбивкой строк: закрывающая кавычка стоит
после статуса вуза, а скобки закрыты.
"""
get = lambda key: fm.get(key, MINISTRY_DEFAULTS[key]) # noqa: E731
lines = [
get("министерство"),
get("вуз_тип"),
get("вуз_тип2"),
f"<<{get('вуз')}",
get("вуз_имени"),
f"({get('вуз_статус')})>>",
f"({get('вуз_аббревиатура')})",
]
return "\n".join(f"\\vspace*{{-0.7ex}}\n\\centerline{{{line}}}" for line in lines)
@@ -1,9 +1,15 @@
"""Исторический макет титульного листа.
Перенесён без изменений: вывод обязан совпадать байт в байт с прежним,
иначе сломаются уже сданные документы.
"""
from __future__ import annotations from __future__ import annotations
from md2gost.handlers.inline import escape_latex from md2gost.handlers.inline import escape_latex
def render_titlepage(fm: dict[str, str]) -> str: def render(fm: dict[str, str]) -> str:
required = {"название": "заголовок работы", "тип": "тип работы"} required = {"название": "заголовок работы", "тип": "тип работы"}
for key, desc in required.items(): for key, desc in required.items():
if key not in fm: if key not in fm:
+5
View File
@@ -0,0 +1,5 @@
from __future__ import annotations
def render(fm: dict[str, str]) -> str:
raise NotImplementedError
+5
View File
@@ -0,0 +1,5 @@
from __future__ import annotations
def render(fm: dict[str, str]) -> str:
raise NotImplementedError
+24
View File
@@ -1,3 +1,6 @@
import pytest
from md2gost.latex.titlepages import TITLEPAGES
from md2gost.parser import Parser from md2gost.parser import Parser
from md2gost.render import Renderer from md2gost.render import Renderer
@@ -9,6 +12,12 @@ def render(md: str) -> str:
return Renderer(parser.tokens, parser.front_matter).render() return Renderer(parser.tokens, parser.front_matter).render()
def render_fm(extra: str, body: str = "Текст.\n") -> str:
head = f"---\nназвание: Тестовая работа\nтип: Отчёт\n{extra}---\n\n"
parser = Parser(head + body)
return Renderer(parser.tokens, parser.front_matter).render()
def test_heading_numbering() -> None: def test_heading_numbering() -> None:
tex = render("# Исследовательская часть\n\n## Обзор\n") tex = render("# Исследовательская часть\n\n## Обзор\n")
assert "\\section{Исследовательская часть}" in tex assert "\\section{Исследовательская часть}" in tex
@@ -139,3 +148,18 @@ def _table_block(tex: str) -> str:
start = tex.index("\\begin{table}") start = tex.index("\\begin{table}")
end = tex.index("\\end{table}") + len("\\end{table}") end = tex.index("\\end{table}") + len("\\end{table}")
return tex[start:end] return tex[start:end]
def test_titlepage_default_is_used_when_field_absent() -> None:
tex = render("Текст.\n")
assert "\\begin{titlepage}" in tex
assert "ОТЧЁТ ПО УЧЕБНОЙ ПРАКТИКЕ" not in tex
def test_titlepage_registry_lists_three_layouts() -> None:
assert set(TITLEPAGES) == {"default", "ptp-report", "ptp-task"}
def test_titlepage_unknown_name_raises() -> None:
with pytest.raises(ValueError, match="ptp-report"):
render_fm("титульник: несуществующий\n")