[ADD] титульный лист по бланку отчёта об учебной практике

This commit is contained in:
Arseny
2026-09-02 03:22:39 +03:00
parent 10528c6688
commit 6aed7f186d
4 changed files with 199 additions and 2 deletions
+31
View File
@@ -96,6 +96,37 @@ PREAMBLE = r"""\documentclass[a4paper, 14pt]{extarticle}
\end{tabular}%
}
\newcommand{\ptpfieldw}[4][4.5cm]{%
\par\noindent
\makebox[#1][l]{#2}%
\begin{tabular}[t]{@{}c@{}}%
\makebox[#4][c]{#3} \\[-1.05em]%
\rule{#4}{0.4pt}%
\end{tabular}\par}
\newcommand{\ptpfield}[3][4.5cm]{%
\ptpfieldw[#1]{#2}{#3}{\dimexpr\textwidth-#1\relax}}
\newcommand{\ptpfieldnote}[4][4.5cm]{%
\ptpfield[#1]{#2}{#3}%
\par\vspace{-0.7em}%
\hspace*{#1}\makebox[\dimexpr\textwidth-#1\relax][c]{\footnotesize\itshape #4}\par}
\newcommand{\ptpfieldpar}[3][4.5cm]{%
\par\noindent
\makebox[#1][l]{#2}%
\begin{tabular}[t]{@{}c@{}}%
\parbox{\dimexpr\textwidth-#1\relax}{\centering #3} \\[-0.55em]%
\rule{\dimexpr\textwidth-#1\relax}{0.4pt}%
\end{tabular}\par}
\newcommand{\ptpsig}[3]{%
\par\noindent
\begin{tabularx}{\textwidth}{@{}l X c c@{}}%
#1 & & \sigline[4.2cm]{}{\itshape подпись, дата}
& \sigline[4.2cm]{#2}{\itshape #3} \\%
\end{tabularx}\par}
\begin{document}"""
END_DOCUMENT = r"\end{document}" + "\n"
+7 -1
View File
@@ -36,4 +36,10 @@ def ministry_lines(fm: dict[str, str]) -> str:
f"({get('вуз_статус')})>>",
f"({get('вуз_аббревиатура')})",
]
return "\n".join(f"\\vspace*{{-0.7ex}}\n\\centerline{{{line}}}" for line in lines)
# Первая строка без отрицательного отступа: иначе она вылезает за верхнюю
# границу рамки, в которую шапку заключает бланк отчёта.
head = f"\\centerline{{{lines[0]}}}"
rest = "\n".join(
f"\\vspace*{{-0.7ex}}\n\\centerline{{{line}}}" for line in lines[1:]
)
return f"{head}\n{rest}"
+93 -1
View File
@@ -1,5 +1,97 @@
"""Титульный лист по бланку «ОТЧЁТ ПО УЧЕБНОЙ ПРАКТИКЕ» кафедры ИУ9."""
from __future__ import annotations
from md2gost.latex.titlepages._common import field, ministry_lines
def render(fm: dict[str, str]) -> str:
raise NotImplementedError
faculty = field(fm, "факультет")
department = field(fm, "кафедра")
student_short = field(fm, "студент")
student_full = field(fm, "студент_полностью") or student_short
group = field(fm, "группа")
practice = field(fm, "тип_практики")
company = field(fm, "предприятие")
team = field(fm, "команда")
repo = field(fm, "хранилище")
advisor = field(fm, "руководитель")
year = field(fm, "год", "2026")
team_line = f"{team} (хранилище {repo})" if repo else team
return (
"\\begin{titlepage}\n"
"\\thispagestyle{empty}\n"
"\n"
"% --- Шапка в рамке с разделителем, как в бланке ---\n"
"\\vspace*{-45pt}\n"
"\\noindent\\begin{tabular}"
"{|@{\\hspace{4pt}}c@{\\hspace{4pt}}|@{\\hspace{4pt}}c@{\\hspace{4pt}}|}\n"
"\\hline\n"
"\\begin{minipage}{0.16\\textwidth}\\centering\\vspace{3pt}\n"
"\\includegraphics[width=0.92\\textwidth]{emblem}\n"
"\\vspace{3pt}\\end{minipage}\n"
"&\n"
"\\begin{minipage}{0.775\\textwidth}\\small\\bfseries\n"
"\\vspace*{2pt}\n"
f"{ministry_lines(fm)}\n"
"\\vspace*{0.5ex}\n"
"\\end{minipage} \\\\\n"
"\\hline\n"
"\\end{tabular}\n"
"\n"
"% --- Полосы ---\n"
"\\vspace{6pt}\n"
"\\noindent\\rule{\\textwidth}{2.3pt}\n"
"\n"
"\\vspace*{-18pt}\n"
"\\noindent\\rule{\\textwidth}{0.4pt}\n"
"\n"
"% --- Факультет и кафедра ---\n"
"\\vspace{2.5ex}\n"
f"\\ptpfield[2.8cm]{{ФАКУЛЬТЕТ}}{{{faculty}}}\n"
"\n"
"\\vspace{1.5ex}\n"
f"\\ptpfield[2.8cm]{{КАФЕДРА}}{{{department}}}\n"
"\n"
"% --- Заголовок ---\n"
"\\vspace{3.5em}\n"
"\\begin{center}\n"
"{\\Large\\bfseries\\underline{ОТЧЁТ ПО УЧЕБНОЙ ПРАКТИКЕ}}\n"
"\\end{center}\n"
"\n"
"% --- Поля ---\n"
"\\vspace{2.5em}\n"
f"\\ptpfieldnote[2.4cm]{{Студент}}{{{student_full}}}{{фамилия, имя, отчество}}\n"
"\n"
"\\vspace{1.8ex}\n"
f"\\ptpfieldw[2.4cm]{{Группа}}{{{group}}}{{5cm}}\n"
"\n"
"\\vspace{1.8ex}\n"
f"\\ptpfield[4.6cm]{{Тип практики}}{{{practice}}}\n"
"\n"
"\\vspace{1.8ex}\n"
f"\\ptpfield[4.6cm]{{Название предприятия}}{{{company}}}\n"
"\n"
"\\vspace{1.8ex}\n"
f"\\ptpfieldpar[2.4cm]{{Команда}}{{{team_line}}}\n"
"\n"
"% --- Подписи ---\n"
"\\vfill\n"
f"\\ptpsig{{Студент}}{{{student_short}}}{{фамилия, и.о.}}\n"
"\n"
"\\vspace{2em}\n"
f"\\ptpsig{{Руководитель практики}}{{{advisor}}}{{фамилия, и.о.}}\n"
"\n"
"% --- Оценка ---\n"
"\\vspace{2em}\n"
"\\noindent Оценка \\rule{0.5\\textwidth}{0.4pt}\n"
"\n"
"\\vfill\n"
"\\begin{center}\n"
f"\\textsl{{{year} г.}}\n"
"\\end{center}\n"
"\n"
"\\end{titlepage}"
)
+68
View File
@@ -163,3 +163,71 @@ def test_titlepage_registry_lists_three_layouts() -> None:
def test_titlepage_unknown_name_raises() -> None:
with pytest.raises(ValueError, match="ptp-report"):
render_fm("титульник: несуществующий\n")
PTP_REPORT_FM = (
"титульник: ptp-report\n"
"факультет: Информатика и системы управления (ИУ)\n"
"кафедра: Теоретическая информатика и компьютерные технологии (ИУ9)\n"
"студент: И. И. Камалетдинов\n"
"студент_полностью: Камалетдинов Ильяс Илнарович\n"
"группа: ИУ9-22Б\n"
"тип_практики: проектно-технологическая практика\n"
"предприятие: МГТУ имени Н. Э. Баумана\n"
"команда: Система мониторинга радиоэфира\n"
"хранилище: https://monitor.yss.su/IU9/radio-serv\n"
"руководитель: Д. П. Посевин\n"
"год: 2026\n"
)
def test_ptp_report_has_blank_headings() -> None:
tex = render_fm(PTP_REPORT_FM)
for heading in (
"ОТЧЁТ ПО УЧЕБНОЙ ПРАКТИКЕ",
"Тип практики",
"Название предприятия",
"Команда",
"Оценка",
"Руководитель практики",
):
assert heading in tex
def test_ptp_report_fills_all_fields() -> None:
tex = render_fm(PTP_REPORT_FM)
for value in (
"Камалетдинов Ильяс Илнарович",
"И. И. Камалетдинов",
"ИУ9-22Б",
"проектно-технологическая практика",
"МГТУ имени Н. Э. Баумана",
"Д. П. Посевин",
"2026",
):
assert value in tex
def test_ptp_report_team_line_includes_repository() -> None:
tex = render_fm(PTP_REPORT_FM)
assert "Система мониторинга радиоэфира (хранилище" in tex
assert "monitor.yss.su/IU9/radio-serv" in tex
def test_ptp_report_team_line_without_repository() -> None:
fm = PTP_REPORT_FM.replace(
"хранилище: https://monitor.yss.su/IU9/radio-serv\n", ""
)
tex = render_fm(fm)
assert "хранилище" not in tex
def test_ptp_report_has_no_hardcoded_names() -> None:
"""Имена, группы и предприятия приходят только из front matter."""
import inspect
from md2gost.latex.titlepages import ptp_report
source = inspect.getsource(ptp_report)
for forbidden in ("Посевин", "Коновалов", "Камалетдинов", "ИУ9-2"):
assert forbidden not in source