BigUpdate
Python application / build (push) Has been cancelled

This commit is contained in:
Igor20264
2026-09-03 10:44:08 +03:00
parent d2da20fdb2
commit 516abe7b83
177 changed files with 40178 additions and 2 deletions
+380
View File
@@ -0,0 +1,380 @@
"""Unit tests for MIREA TZ checker and numbering helpers."""
from md2gost.checker import check_markdown
from md2gost.numberer import Numberer
from md2gost.profiles import preprocess_markdown, find_formula_refs
from md2gost.bibliography import find_citations, extract_year
SAMPLE_OK = """
# *СОДЕРЖАНИЕ
[TOC]
# *ВВЕДЕНИЕ
Текст введения без ссылок на источники.
# Анализ предметной области
Согласно исследованиям [1] применяется подход, показанный на @Рисунок:arch.
![схема](img.png "%arch Архитектура системы")
%tbl1 Сравнение подходов
| Подход | Оценка |
|--------|--------|
| A | 1 |
| B | 2 |
Далее см. Таблицу — @Таблица:tbl1.
# *ЗАКЛЮЧЕНИЕ
Выводы по работе.
# *СПИСОК ИСПОЛЬЗОВАННЫХ ИСТОЧНИКОВ
[1]: Иванов И. И. Информационные системы. — М.: Наука, 2023. — 120 с.
[2]: Петров П. П. Базы данных. — СПб.: Питер, 2022. — 200 с.
[3]: ГОСТ 7.32-2017. Отчет о НИР. — М., 2022.
[4]: Сидоров С. С. Python. — М.: ДМК, 2024. — 300 с.
[5]: Козлов К. К. Анализ данных. — М.: Бином, 2022. — 150 с.
# *ПРИЛОЖЕНИЯ
## Приложение А Листинг модуля
Код.
"""
def test_numberer_section_scoped():
n = Numberer()
n.enter_section(1)
assert n.next_number("Рисунок", "a") == "1.1"
assert n.next_number("Рисунок", "b") == "1.2"
n.enter_section(2)
assert n.next_number("Рисунок") == "2.1"
n.enter_appendix("Б")
assert n.next_number("Рисунок", "x") == "Б.1"
def test_numberer_continuous_pis():
n = Numberer(mode="continuous")
n.enter_section(1)
assert n.next_number("Рисунок", "a") == "1"
assert n.next_number("Таблица", "t") == "1"
n.enter_section(2)
assert n.next_number("Рисунок", "b") == "2"
n.enter_appendix("А")
assert n.next_number("Рисунок", "c") == "3"
SAMPLE_PIS = """
# *СОДЕРЖАНИЕ
[TOC]
# Практическая работа №1. Формирование требований к системе
Цель работы — сформировать требования.
## Описание предметной области
Текст с рисунком @Рисунок:arch.
![схема](img.png "%arch Архитектура")
# Практическая работа №2. Моделирование процессов
## Построение модели
%tbl1 Сравнение подходов
| A | B |
|---|---|
| 1 | 2 |
См. @Таблица:tbl1.
"""
def test_checker_ok_pis_custom():
issues = check_markdown(SAMPLE_PIS, "PIS_custom")
errors = [i for i in issues if i.severity == "error"]
assert not errors, errors
def test_checker_pis_requires_practical():
text = SAMPLE_PIS.replace("Практическая работа №1.", "Раздел 1.")
text = text.replace("Практическая работа №2.", "Раздел 2.")
issues = check_markdown(text, "PIS_custom")
assert any(i.id == "structure.practical" for i in issues)
def test_pis_styles_h1_centered():
import docx
from md2gost.styles import apply_pis_custom_styles
from docx.enum.text import WD_ALIGN_PARAGRAPH
doc = docx.Document(r"md2gost/Template.docx")
apply_pis_custom_styles(doc)
h1 = doc.styles["Heading 1"]
assert h1.paragraph_format.alignment == WD_ALIGN_PARAGRAPH.CENTER
assert h1.font.all_caps is True
h2 = doc.styles["Heading 2"]
assert h2.paragraph_format.first_line_indent is not None
assert abs(h2.paragraph_format.first_line_indent.cm - 1.25) < 0.01
assert h2.paragraph_format.left_indent is None or h2.paragraph_format.left_indent.cm == 0
def test_soderzhanie_excluded_from_toc_outline():
import docx
from md2gost.renderable.heading import Heading
from md2gost.styles import apply_mirea_styles
doc = docx.Document(r"md2gost/Template.docx")
doc._body.clear_content()
apply_mirea_styles(doc)
h = Heading(doc._body, 1, False)
h.add_run("СОДЕРЖАНИЕ")
h.exclude_from_toc()
levels = h._docx_paragraph._p.pPr.xpath("./w:outlineLvl")
assert levels and levels[0].get(
"{http://schemas.openxmlformats.org/wordprocessingml/2006/main}val"
) == "9"
def test_heading_font_is_tnr_black():
import docx
from docx.oxml.ns import qn
from md2gost.styles import apply_mirea_styles
doc = docx.Document(r"md2gost/Template.docx")
apply_mirea_styles(doc)
rPr = doc.styles["Heading 1"].element.find(qn("w:rPr"))
rFonts = rPr.find(qn("w:rFonts"))
assert rFonts.get(qn("w:ascii")) == "Times New Roman"
assert rFonts.get(qn("w:hAnsi")) == "Times New Roman"
assert rFonts.get(qn("w:eastAsia")) == "Times New Roman"
color = rPr.find(qn("w:color"))
assert color is not None
assert color.get(qn("w:val")) == "000000"
assert color.get(qn("w:themeColor")) is None
def test_checker_ok_coursework():
issues = check_markdown(SAMPLE_OK, "coursework")
errors = [i for i in issues if i.severity == "error"]
assert not errors, errors
def test_checker_ris_forbidden():
text = SAMPLE_OK.replace("@Рисунок:arch", "рис. 1.1")
issues = check_markdown(text, "coursework")
assert any(i.id == "ref.ris" for i in issues)
def test_checker_cite_in_intro():
text = SAMPLE_OK.replace("Текст введения без ссылок на источники.", "См. [1].")
issues = check_markdown(text, "coursework")
assert any(i.id == "cite.intro" for i in issues)
def test_checker_vkr_graphic():
issues = check_markdown(SAMPLE_OK, "vkr")
assert any(i.id == "structure.graphic" for i in issues)
def test_preprocess_quotes():
assert "«тест»" in preprocess_markdown('Он сказал "тест" вслух.')
def test_formula_refs():
assert find_formula_refs("см. @Формула:eq1 и далее") == {"eq1"}
def test_citations_order():
assert find_citations("a [2] b [1] c [2, 3]") == ["2", "1", "3"]
def test_extract_year():
assert extract_year("М.: Наука, 2023. — 120 с.") == 2023
def test_biblio_entries_from_concatenated_paragraph():
from md2gost.biblio_processor import entries_from_text, extract_bibliography_from_markdown
entries = entries_from_text(
"[1]: Первый источник. — М., 2024.[2]: Второй. URL: https://a.com/x-y/[3]: Третий."
)
assert [e.key for e in entries] == ["1", "2", "3"]
assert "https://a.com/x-y/" in entries[1].text
md = """# *ВВЕДЕНИЕ
Текст.
# *СПИСОК ИСПОЛЬЗОВАННЫХ ИСТОЧНИКОВ
[1]: Ааа. — М., 2024.
[2]: Ббб. — СПб., 2023.
# *ПРИЛОЖЕНИЯ
"""
flat = extract_bibliography_from_markdown(md)
assert len(flat) == 2
assert flat[0].key == "1"
def test_emdash_to_hyphen_preprocess():
from md2gost.profiles import preprocess_markdown, set_emdash_to_hyphen, dash_separator
out = preprocess_markdown("Цель — тест. Слово - слово.", emdash_to_hyphen=True)
assert "" not in out
assert "Цель - тест" in out
assert dash_separator() == " - "
out2 = preprocess_markdown("Цель — тест.", emdash_to_hyphen=False)
assert "" in out2
assert dash_separator() == ""
set_emdash_to_hyphen(False)
def test_native_toc_inserts_word_field():
import docx
from md2gost.renderable.toc import ToC
doc = docx.Document(r"md2gost/Template.docx")
doc._body.clear_content()
toc = ToC(doc._body, toc_mode="native")
toc.fill()
xml = toc._paragraph._docx_paragraph._p.xml
assert "TOC" in xml
assert "fldChar" in xml
def test_reference_ignores_trailing_punctuation():
from md2gost.extended_markdown import markdown
def refs(text):
doc = markdown.parse(text)
out = []
def walk(node):
if node.__class__.__name__ == "Reference":
out.append((node.type, node.name))
kids = getattr(node, "children", None)
if isinstance(kids, list):
for c in kids:
walk(c)
walk(doc)
return out
assert ("Рисунок", "submitted") in refs("@Рисунок:submitted, @Рисунок:lmsafter")
assert ("Рисунок", "deploy") in refs("см. (@Рисунок:deploy). Далее")
def test_heading_manual_kills_word_numbering():
import docx
from md2gost.renderable.heading import Heading
doc = docx.Document(r'md2gost/Template.docx')
doc._body.clear_content()
h = Heading(doc._body, 1, True, numbering_mode='manual')
h.add_run('1 Разработка')
h.apply_numbering_mode('manual')
assert h.text.startswith('1 ')
num_pr = h._docx_paragraph._p.pPr.xpath('.//w:numId')
assert num_pr and num_pr[0].get('{http://schemas.openxmlformats.org/wordprocessingml/2006/main}val') == '0'
def test_heading_auto_strips_leading_number():
import docx
from md2gost.renderable.heading import Heading
doc = docx.Document(r'md2gost/Template.docx')
doc._body.clear_content()
h = Heading(doc._body, 2, True, numbering_mode='auto')
h.add_run('1.1 Описание')
h.apply_numbering_mode('auto')
assert h.text == 'Описание'
def test_toc_tab_stops_match_text_area():
import docx
from md2gost.styles import apply_mirea_styles
doc = docx.Document(r'md2gost/Template.docx')
apply_mirea_styles(doc)
section = doc.sections[0]
usable = section.page_width.mm - section.left_margin.mm - section.right_margin.mm
for name in ('toc 1', 'toc 2'):
tabs = list(doc.styles[name].paragraph_format.tab_stops)
right_tabs = [t for t in tabs if 'RIGHT' in str(t.alignment)]
assert len(right_tabs) == 1, (name, tabs)
assert abs(right_tabs[0].position.mm - usable) < 0.05, (name, right_tabs[0].position.mm, usable)
edge = section.left_margin.mm + right_tabs[0].position.mm
assert abs(edge - (section.page_width.mm - section.right_margin.mm)) < 0.05
SAMPLE_APID = """
# *СОДЕРЖАНИЕ
[TOC]
# *ВВЕДЕНИЕ
Текст введения без ссылок.
# 1 Теоретические аспекты разработки архитектуры приложений и данных: описание микросервисной архитектуры
Согласно [1] архитектура задаёт структуру системы. Далее [2].
# 2 Прикладные аспекты разработки архитектуры приложений и данных web-сервиса «SMEZHNO»
## 2.1 Описание проекта команды и разрабатываемого программного приложения «SMEZHNO»
Текст проекта [3].
## 2.2 Описание роли «Неавторизованный пользователь» web-сервиса «SMEZHNO»
Роль.
## 2.3 Описание архитектуры программного приложения и данных web-сервиса «SMEZHNO» для роли «Неавторизованный пользователь»
Архитектура [4].
## 2.4 Варианты развития архитектуры программного приложения «SMEZHNO»
Альтернативы [5].
# *ЗАКЛЮЧЕНИЕ
Выводы.
# *СПИСОК ИСПОЛЬЗОВАННЫХ ИСТОЧНИКОВ
[1]: ISO/IEC/IEEE 42010:2022. Architecture description. — Geneva: ISO, 2022.
[2]: The Open Group. ArchiMate 3.2 Specification. — 2023.
[3]: Аншина М. Л. Материалы лекционного курса. — М.: РТУ МИРЭА, 2024.
[4]: Ньюмен С. Создание микросервисов. — СПб.: Питер, 2023. — 624 с.
[5]: The Open Group. The TOGAF Standard, 10th Edition. — 2022.
[6]: PostgreSQL 16 Documentation. URL: https://www.postgresql.org/docs/16/ (дата обращения: 01.05.2025).
[7]: Kubernetes Documentation. URL: https://kubernetes.io/docs/home/ (дата обращения: 01.05.2025).
"""
def test_checker_apid_ok():
issues = check_markdown(SAMPLE_APID, "APID_coursework")
errors = [i for i in issues if i.severity == "error"]
assert not errors, errors
def test_checker_apid_requires_sections():
text = SAMPLE_APID.replace("## 2.4 Варианты развития", "## 2.4 Прочее")
issues = check_markdown(text, "APID_coursework")
assert any(i.id == "structure.apid" for i in issues)
def test_apid_biblio_min_seven():
text = SAMPLE_APID
# drop last three sources -> 4 left
text = text.split("[5]:")[0] + "\n"
issues = check_markdown(text, "APID_coursework")
assert any(i.id == "biblio.count" for i in issues)