Files
md_to_gost/md2gost/__main__.py
T
Igor20264 818a044aa1
Python application / build (push) Has been cancelled
update 0.4.4
- update документация
- промт для ии полу конфигурируемый
2026-09-06 11:04:01 +03:00

263 lines
11 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python
from argparse import ArgumentParser, BooleanOptionalAction
import sys
from .pipeline import ConvertRequest, convert, should_launch_gui
from .profiles import (
DEFAULT_HEADING_NUMBERING,
DEFAULT_TABLE_CONTINUATION,
DEFAULT_LISTING_CONTINUATION,
DEFAULT_TOC_MODE,
DOC_TYPES,
HEADING_NUMBERING_MODES,
TABLE_CONTINUATION_MODES,
LISTING_CONTINUATION_MODES,
TOC_MODES,
)
def build_parser() -> ArgumentParser:
parser = ArgumentParser(
prog="md2gost",
description=(
"Генерация DOCX-отчётов из Markdown по ТЗ МИРЭА / ГОСТ. "
"Типы: coursework/practice/vkr, APID_coursework, PIS_custom. "
"Без файла или с --gui открывается окно. "
"FODT: python -m md2fodt …"
),
)
parser.add_argument(
"filename", nargs="?",
help="Путь до .md (или .docx с --check-pages). Без файла открывается GUI",
)
parser.add_argument(
"--gui", action="store_true",
help="Открыть графический интерфейс (можно сразу передать .md)",
)
parser.add_argument("-o", "--output", help="Путь до сгенерированного .docx")
parser.add_argument("-t", "--template", help="Путь до шаблона .docx")
parser.add_argument(
"--type", dest="doc_type", choices=DOC_TYPES, default="practice",
help="Тип: practice | coursework | vkr | PIS_custom | APID_coursework (по умолчанию practice)",
)
parser.add_argument(
"--heading-numbering",
choices=HEADING_NUMBERING_MODES,
default=DEFAULT_HEADING_NUMBERING,
help=(
"Нумерация заголовков разделов: "
"manual — цифры из markdown (# 1 … / ## 1.1 …), автонумерация Word отключена; "
"auto — нумерует Word, цифры в начале заголовка md снимаются. "
f"По умолчанию: {DEFAULT_HEADING_NUMBERING}."
),
)
parser.add_argument(
"--toc",
choices=TOC_MODES,
default=DEFAULT_TOC_MODE,
help=(
"Содержание: native — встроенное поле Word TOC (обновить при открытии); "
"manual — собрать в md2gost с номерами страниц из layout. "
f"По умолчанию: {DEFAULT_TOC_MODE}."
),
)
parser.add_argument(
"--table-continuation",
choices=TABLE_CONTINUATION_MODES,
default=DEFAULT_TABLE_CONTINUATION,
help=(
"Таблицы длиннее страницы: "
"word — после сохранения Word COM режет по реальной пагинации + «Продолжение…» "
"(по умолчанию; нужны Windows, Word, pywin32); "
"off/soft — одна таблица, пагинация Word, без авто«Продолжение»; "
"legacy/caption — режем по оценке высоты и вставляем «Продолжение…» "
"(оценка ≠ Word, возможны артефакты). "
f"По умолчанию: {DEFAULT_TABLE_CONTINUATION}."
),
)
parser.add_argument(
"--listing-continuation",
choices=LISTING_CONTINUATION_MODES,
default=DEFAULT_LISTING_CONTINUATION,
help=(
"Листинги длиннее страницы: "
"word — после сохранения Word COM + «Продолжение Листинга…» "
"(по умолчанию; нужны Windows, Word, pywin32); "
"off/soft — один блок, пагинация Word, без авто«Продолжение»; "
"legacy/caption — режем по оценке высоты и вставляем «Продолжение Листинга…» "
"(оценка ≠ Word, возможны артефакты). "
f"По умолчанию: {DEFAULT_LISTING_CONTINUATION}."
),
)
parser.add_argument(
"--table-repeat-header",
action=BooleanOptionalAction,
default=False,
help=(
"При --table-continuation word: повторять шапку таблицы на каждом фрагменте "
"после разрыва. По умолчанию выкл."
),
)
parser.add_argument(
"--emdash-to-hyphen",
action=BooleanOptionalAction,
default=False,
help=(
"Автозамена типографского тире «—» на дефис «-» в тексте и подписях. "
"По умолчанию выключено (тире «—» по методичке); включить: --emdash-to-hyphen."
),
)
parser.add_argument(
"--hr-pagebreak",
action=BooleanOptionalAction,
default=False,
help=(
"Строка «---» / «***» / «___» — разрыв страницы Word. "
"По умолчанию такие строки игнорируются; включить: --hr-pagebreak."
),
)
parser.add_argument("--title", help="DOCX титульного листа (вставляется перед телом)")
parser.add_argument("--assignment", help="DOCX бланка задания")
parser.add_argument("--check", help="Проверить markdown по ТЗ и вывести отчёт",
action="store_true")
parser.add_argument("--check-only", help="Только проверка, без генерации документа",
action="store_true")
parser.add_argument(
"--check-pages",
help=(
"После конвертации (или для готового .docx) проверить полупустые страницы "
"через Microsoft Word. Эвристика — возможны ложные срабатывания. "
"Нужны Windows, Word и pywin32."
),
action="store_true",
)
parser.add_argument("--strict", help="Код выхода 1 при ошибках проверки",
action="store_true")
parser.add_argument("--syntax-highlighting", help="Подсветка синтаксиса в листингах",
action=BooleanOptionalAction)
parser.add_argument(
"--plantuml-jar",
help="Путь к plantuml.jar (иначе env PLANTUML_JAR)",
)
parser.add_argument(
"--kroki-url",
help="URL локального Kroki (иначе env KROKI_URL, default http://localhost:8000)",
)
parser.add_argument(
"--diagram-fallback",
choices=["local", "remote", "off"],
default="remote",
help="Если локальный рендер UML недоступен: remote (kroki.io), local (ошибка), off",
)
parser.add_argument(
"--diagram-format",
choices=["png", "svg"],
default="png",
help="Формат схем в Word: png (по умолчанию) или svg (вектор + PNG-запасной, Word 2016+)",
)
parser.add_argument(
"--diagram-scale",
type=float,
default=2.0,
help="Масштаб рендера PlantUML PNG (качество); размер на странице как при 1. По умолчанию 2",
)
parser.add_argument(
"--schemes",
dest="schemes_path",
help="Путь к md2gost.schemes.json (иначе рядом с приложением / с .md)",
)
parser.add_argument(
"--styles",
dest="styles_path",
help=(
"Путь к md2gost.styles.json (оверлей оформления поверх пресета --type; "
"также подхватывается md2gost.styles.json рядом с .md)"
),
)
parser.add_argument("--debug", help="Добавляет отладочные данные в документ",
action="store_true")
return parser
def request_from_args(args) -> ConvertRequest:
return ConvertRequest(
filename=args.filename or "",
output=args.output,
template=args.template,
doc_type=args.doc_type,
heading_numbering=args.heading_numbering,
toc_mode=args.toc,
table_continuation=args.table_continuation,
listing_continuation=args.listing_continuation,
table_repeat_header=bool(getattr(args, "table_repeat_header", False)),
emdash_to_hyphen=args.emdash_to_hyphen,
hr_pagebreak=args.hr_pagebreak,
title=args.title,
assignment=args.assignment,
check=args.check,
check_only=args.check_only,
check_pages=bool(getattr(args, "check_pages", False)),
strict=args.strict,
syntax_highlighting=bool(args.syntax_highlighting),
plantuml_jar=args.plantuml_jar,
kroki_url=args.kroki_url,
diagram_fallback=args.diagram_fallback,
diagram_format=args.diagram_format,
diagram_scale=float(args.diagram_scale),
schemes_path=args.schemes_path,
styles_path=getattr(args, "styles_path", None),
debug=args.debug,
open_when_done=bool(args.debug),
)
def _argv_needs_console(argv: list[str]) -> bool:
"""Frozen windowed exe: attach a console for CLI / --help, not for GUI."""
args = argv[1:]
if not args:
return False
if "-h" in args or "--help" in args:
return True
if "--gui" in args:
return False
return any(not a.startswith("-") for a in args)
def _enable_windows_console() -> None:
if sys.platform != "win32" or not getattr(sys, "frozen", False):
return
try:
import ctypes
kernel32 = ctypes.windll.kernel32
if not kernel32.AttachConsole(0xFFFFFFFF):
if not kernel32.GetConsoleWindow():
kernel32.AllocConsole()
sys.stdout = open("CONOUT$", "w", encoding="utf-8", errors="replace")
sys.stderr = open("CONOUT$", "w", encoding="utf-8", errors="replace")
try:
sys.stdin = open("CONIN$", "r", encoding="utf-8", errors="replace")
except OSError:
pass
except Exception:
pass
def main():
if _argv_needs_console(sys.argv):
_enable_windows_console()
parser = build_parser()
args = parser.parse_args()
req = request_from_args(args)
if should_launch_gui(args.filename, args.gui):
from .gui import run_gui
run_gui(req)
return
result = convert(req)
sys.exit(result.exit_code)
if __name__ == "__main__":
main()