[REFACTOR] реестр титульных листов вместо одного макета
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
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"]
|
||||
|
||||
@@ -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)
|
||||
@@ -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 md2gost.handlers.inline import escape_latex
|
||||
|
||||
|
||||
def render_titlepage(fm: dict[str, str]) -> str:
|
||||
def render(fm: dict[str, str]) -> str:
|
||||
required = {"название": "заголовок работы", "тип": "тип работы"}
|
||||
for key, desc in required.items():
|
||||
if key not in fm:
|
||||
@@ -0,0 +1,5 @@
|
||||
from __future__ import annotations
|
||||
|
||||
|
||||
def render(fm: dict[str, str]) -> str:
|
||||
raise NotImplementedError
|
||||
@@ -0,0 +1,5 @@
|
||||
from __future__ import annotations
|
||||
|
||||
|
||||
def render(fm: dict[str, str]) -> str:
|
||||
raise NotImplementedError
|
||||
@@ -1,3 +1,6 @@
|
||||
import pytest
|
||||
|
||||
from md2gost.latex.titlepages import TITLEPAGES
|
||||
from md2gost.parser import Parser
|
||||
from md2gost.render import Renderer
|
||||
|
||||
@@ -9,6 +12,12 @@ def render(md: str) -> str:
|
||||
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:
|
||||
tex = render("# Исследовательская часть\n\n## Обзор\n")
|
||||
assert "\\section{Исследовательская часть}" in tex
|
||||
@@ -139,3 +148,18 @@ def _table_block(tex: str) -> str:
|
||||
start = tex.index("\\begin{table}")
|
||||
end = tex.index("\\end{table}") + len("\\end{table}")
|
||||
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")
|
||||
|
||||
Reference in New Issue
Block a user