401 lines
19 KiB
Python
401 lines
19 KiB
Python
"""Usage text and AI prompt files for the GUI pages."""
|
||
|
||
from __future__ import annotations
|
||
|
||
import sys
|
||
from pathlib import Path
|
||
|
||
from . import package_dir
|
||
|
||
USAGE_HELP = """md2gost — Markdown → DOCX (ТЗ МИРЭА / ГОСТ 7.32)
|
||
|
||
КАК ПОЛЬЗОВАТЬСЯ ОКНОМ
|
||
1. Перетащите .md в верхнюю область (или кликните по ней).
|
||
2. Выберите тип документа и параметры в блоке «Основные».
|
||
Шаблон / титул / задание — Настройки → Файлы.
|
||
ФИО и группа студента — Настройки → Студент… (спрашивают при первом запуске).
|
||
Свойства DOCX (автор, название, примечание) — Настройки → Метаданные….
|
||
PlantUML / Mermaid / Kroki — Настройки → Диаграммы.
|
||
Свои UML-схемы — меню «Шаблоны UML».
|
||
3. Нажмите «Конвертировать». Документ сохранится рядом с исходником (или по пути «Выходной DOCX»).
|
||
Если файл уже есть и вы откажетесь перезаписывать — сохранится как имя_гггг-мм-дд-ЧЧ-ММ.docx.
|
||
Дебаг (меню сверху) — следующая сборка с отладочными данными в документе.
|
||
|
||
Типы: practice (по умолчанию) / coursework / vkr — ГОСТ МИРЭА; PIS_custom — отчёт по практикам ПИС; APID_coursework — курсовая АПИД.
|
||
|
||
Полезные галочки
|
||
• «—» → «-» — заменить типографское тире на дефис (по умолчанию **выкл.**; методичка требует «—»).
|
||
• --- → разрыв страницы — по умолчанию выкл. (строка --- игнорируется). Вкл. — page break в Word.
|
||
• Смещение страниц — начальный номер PAGE в теле (пусто = сквозной; 1 = тело с 1).
|
||
• Проверить по ТЗ — замечания по структуре, «рис.», источникам.
|
||
• Проверить вёрстку в Word — полупустые страницы (эвристика, возможны ложные срабатывания; нужны Word + pywin32).
|
||
• Титул / задание — отдельные DOCX, вставляются перед телом отчёта.
|
||
Автотитул — галочка «Автотитул» / CLI `--auto-title` (по умолчанию **выкл.**):
|
||
info_conv.yaml рядом с .md + опционально блок ```title (number, year…);
|
||
ФИО/группа — Настройки → Студент… или --student / --group.
|
||
Явный путь к титулу отключает генератор. Подробно: docs/title.md.
|
||
• Свойства файла Word — Настройки → Метаданные… / --doc-author-from, --doc-author, --doc-title, --doc-comments….
|
||
По умолчанию автор = имя Windows, примечание «Создано при помощи md2gost (ТЗ МИРЭА)».
|
||
|
||
CLI (тот же движок)
|
||
python -m md2gost report.md -o report.docx --type coursework --check
|
||
python -m md2gost report.md --auto-title --student "Иванов И. И." --group ИНБО-31-23
|
||
python -m md2gost report.md --doc-author-from student --doc-comments ""
|
||
python -m md2gost report.docx -o report.md
|
||
python -m word2md report.docx
|
||
python -m md2gost --gui
|
||
md2gost.exe report.md --type PIS_custom --title title.docx
|
||
md2gost.exe report.md --schemes path/to/md2gost.schemes.json
|
||
md2gost.exe report.md --styles path/to/md2gost.styles.json
|
||
|
||
Импорт Word → Markdown
|
||
Перетащите .docx или кнопка «Импорт DOCX→MD». Подписи, таблицы, спецразделы
|
||
переводятся в диалект md2gost. UML/Mermaid из картинок не восстанавливаются.
|
||
Подробнее: docs/word2md.md.
|
||
|
||
Стили JSON (опционально)
|
||
Оверлей поверх пресета типа документа (--type). Файл md2gost.styles.json рядом с .md
|
||
или --styles / Настройки → Файлы → «Стили JSON». Меняет поля страницы и параметры
|
||
стилей абзацев (Normal, Heading 1–3, подписи…). В шаблон DOCX стили руками добавлять не нужно.
|
||
Подробнее: docs/styles.md.
|
||
|
||
СИНТАКСИС MARKDOWN
|
||
|
||
Спецразделы (без номера, ПРОПИСНЫЕ, звёздочка):
|
||
# *СОДЕРЖАНИЕ
|
||
[TOC]
|
||
# *ВВЕДЕНИЕ
|
||
# *ЗАКЛЮЧЕНИЕ
|
||
# *СПИСОК ИСПОЛЬЗОВАННЫХ ИСТОЧНИКОВ
|
||
# *ПРИЛОЖЕНИЯ
|
||
(перечень «Приложение А — …» собирается сам из ## Приложение А Название;
|
||
можно написать вручную — тогда автосписок не дублируется)
|
||
## Приложение А Название
|
||
|
||
Нумерованные разделы: # 1 Название ## 1.1 Подраздел
|
||
Точку в конце заголовка не ставить.
|
||
СОДЕРЖАНИЕ и СПИСОК — по центру; Введение / Заключение / ПРИЛОЖЕНИЯ — слева.
|
||
|
||
Рисунок
|
||
Текст со ссылкой на @Рисунок:arch.
|
||

|
||
В тексте пишите «Рисунок», не «рис.»
|
||
|
||
Таблица
|
||
%tbl1 Название
|
||
| A | B |
|
||
|---|---|
|
||
| 1 | 2 |
|
||
Ссылка: @Таблица:tbl1
|
||
Склеивание: ^ — ячейка сверху (rowspan), > — ячейка слева (colspan).
|
||
Не ставить ^/> в шапке; > — не в первом столбце.
|
||
|
||
Листинг
|
||
%code1 Название
|
||
```python
|
||
print("ok")
|
||
```
|
||
|
||
Диаграмма (PlantUML / Mermaid / схемы) → рисунок
|
||
%usecase1 Диаграмма прецедентов +listing
|
||
```uml
|
||
@startuml
|
||
actor User
|
||
User --> (Login)
|
||
@enduml
|
||
```
|
||
|
||
Широкая схема/таблица на альбомной странице:
|
||
%arch1 Архитектура +landscape
|
||
```uml-c4
|
||
…
|
||
```
|
||
|
||
Mermaid (локально: браузер / QuickJS; иначе Kroki):
|
||
%seq Последовательность +listing
|
||
```mermaid
|
||
sequenceDiagram
|
||
Alice->>Bob: hello
|
||
```
|
||
|
||
IDEF0 (контекст A-0, рамка NIST):
|
||
%a0 Контекст +landscape
|
||
```idef0
|
||
title Распорядиться товаром
|
||
node A-0
|
||
[A0] Распорядиться товаром
|
||
<- Спрос
|
||
^ Нормативная документация
|
||
-> Товар
|
||
v Персонал
|
||
```
|
||
|
||
DFD (pbauermeister/dfd → Graphviz вшитый/PATH / Kroki):
|
||
%dfd0 Контекстная DFD
|
||
```dfd
|
||
style context
|
||
entity Client Клиент
|
||
process System Система учёта заявок
|
||
Client --> System заявка
|
||
System --> Client статус
|
||
```
|
||
|
||
+listing — ещё и листинг с исходником.
|
||
+landscape — отдельная альбомная страница (A4, повёрт на 90°) вокруг рисунка/таблицы, затем снова книжная.
|
||
Языки: uml, plantuml, mermaid / mmd, idef0, dfd / data-flow-diagram, или uml-<id> / <id> из файла схем (c4, usecase, archimate, …).
|
||
Формат в Word: PNG по умолчанию (PlantUML, локальный Mermaid, IDEF0 и DFD ~2× для чёткости); --diagram-format svg — вектор + PNG-запасной (Word 2016+).
|
||
Подробнее — Справка → Схемы и раздел ниже в инструкции.
|
||
|
||
Формула (номер только если есть ссылка)
|
||
%eq1
|
||
$$ E = mc^2 $$
|
||
См. @Формула:eq1
|
||
|
||
Источники
|
||
В тексте: [1] (в DOCX — ссылка на пункт списка)
|
||
В списке: [1]: Иванов И. И. Название. — М.: Наука, 2023. — 120 с.
|
||
|
||
Разрыв страницы
|
||
По умолчанию --- игнорируется.
|
||
Галочка «--- → разрыв страницы» или --hr-pagebreak: пустая строка, ---, пустая строка.
|
||
|
||
Нумерация заголовков
|
||
manual — цифры уже в md (# 1 …); auto — нумерует Word.
|
||
|
||
Содержание
|
||
native — поле Word (обновить при открытии); manual — собирает md2gost.
|
||
|
||
Продолжение таблиц / листингов
|
||
word — по умолчанию: после сборки Word COM режет по реальной пагинации и
|
||
вставляет «Продолжение…» (нужны Windows + Word + pywin32). Также уводит
|
||
таблицу целиком, если внизу страницы зависли только название или
|
||
название + шапка граф (методичка: рис. Г.3).
|
||
off — одна таблица, Word сам переносит строки; без авто«Продолжение».
|
||
Защита от сирот: keep_with_next у подписи/шапки и оценка
|
||
«подпись+шапка+1 строка» при рендере (без COM слабее, чем word).
|
||
legacy / caption — режем по оценке высоты в md2gost (может не совпасть с Word).
|
||
|
||
Промпт для ИИ — Справка → Промпт для ИИ: выберите промпт, при необходимости
|
||
включите схемы (C4, usecase, …) кнопками — макросы допишутся в конец — скопируйте
|
||
в ChatGPT / Cursor / Copilot, затем дайте тему и черновик.
|
||
|
||
Документация — Справка → Документация: встроенный просмотр docs/*.md
|
||
(вшито в exe; внешняя папка docs/ не обязательна).
|
||
"""
|
||
|
||
SCHEMES_HELP = """СХЕМЫ ДИАГРАММ (PlantUML)
|
||
|
||
Зачем
|
||
В markdown пишете только «тело» диаграммы. Обёртка (@startuml, !include, тема)
|
||
берётся из схемы в файле md2gost.schemes.json.
|
||
|
||
Первый запуск
|
||
Рядом с программой (рядом с md2gost.exe или в текущей папке при python -m)
|
||
создаётся md2gost.schemes.json из встроенного шаблона.
|
||
При каждом запуске схемы с author=md2gost синхронизируются с шаблоном приложения
|
||
(добавление новых id, обновление, удаление исчезнувших вроде bpmn).
|
||
Схемы с пустым или другим author не трогаются.
|
||
Если правите встроенную схему в GUI и сохраняете — author сбрасывается,
|
||
чтобы следующая миграция не затёрла ваши правки.
|
||
|
||
Оградка в markdown
|
||
```uml — обычный PlantUML (или ```plantuml)
|
||
```uml-c4 — схема с id «c4» (то же, что ```c4)
|
||
```uml-usecase — схема «usecase»
|
||
```uml-archimate — ArchiMate 3.2 (макросы Business_*, Rel_Serving, …)
|
||
```mermaid / ```mmd — Mermaid локально (браузер / Chromium / QuickJS) или через Kroki
|
||
```idef0 — IDEF0 (FIPS 183): боксы, стрелки ICOM, рамка NIST; локальный Pillow
|
||
```dfd / ```data-flow-diagram — DFD (pbauermeister/dfd): process/entity/store, --> ;
|
||
Graphviz (вшитый / «Скачать Graphviz» / PATH) или Kroki
|
||
Встроенные пресеты PlantUML: c4, c4context, c4component, usecase.
|
||
|
||
Пример
|
||
|
||
%arch Архитектура +listing
|
||
|
||
```uml-c4
|
||
Person(user, "Студент")
|
||
System(app, "Портал")
|
||
Rel(user, app, "логин")
|
||
```
|
||
|
||
Поля схемы в JSON
|
||
title — подпись в GUI
|
||
version — версия схемы
|
||
author — автор
|
||
docs — шпаргалка синтаксиса (чтобы вспомнить макросы)
|
||
ai-prompt — заготовка промпта для ИИ / будущего MCP
|
||
includes — список файлов или http(s):// URL на .puml
|
||
prefix — текст перед телом (часто @startuml)
|
||
postfix — текст после тела (часто @enduml)
|
||
theme — опционально !theme …
|
||
|
||
Свои схемы
|
||
1. Меню «Шаблоны UML» в GUI — добавьте / отредактируйте и сохраните.
|
||
2. Или откройте md2gost.schemes.json в редакторе («Открыть JSON» / «Открыть файл схем»).
|
||
3. CLI: --schemes путь.json; также подхватывается md2gost.schemes.json рядом с .md.
|
||
|
||
Кэш includes из интернета
|
||
URL из includes (и !include https://… внутри .puml) при первом рендере
|
||
скачиваются. Индекс — md2gost.include-cache.json (только пары URL → файл).
|
||
Файлы лежат в папке include-cache/. Уже скачанные файлы не перезаписываются.
|
||
Повторный рендер без сети берёт путь из индекса.
|
||
«Сбросить кэш includes» в Настройки → Диаграммы или в «Шаблоны UML» удаляет индекс
|
||
и только файлы, перечисленные в нём.
|
||
|
||
Чего нет
|
||
Балансировка родитель/потомок DFD между уровнями не проверяется автоматически.
|
||
|
||
Рендер
|
||
UML: Java + plantuml.jar (локально) → иначе локальный Kroki → иначе kroki.io.
|
||
Mermaid: системный Chrome/Edge → Playwright Chromium (кнопка / --install-chromium) →
|
||
QuickJS (mermaidx, офлайн) → локальный Kroki → kroki.io.
|
||
IDEF0: Pillow, без сети и Java.
|
||
DFD: data-flow-diagram → Graphviz (dot/neato) или Kroki graphviz.
|
||
Нужны: Graphviz (вшитый / «Скачать Graphviz» / PATH) либо Kroki;
|
||
пакет data-flow-diagram ставится с md2gost.
|
||
Формат: PNG по умолчанию (PlantUML / локальный Mermaid ~scale 2 для чёткости, размер на странице как при 1; --diagram-scale); --diagram-format svg — PNG + SVG (svgBlip в Word 2016+).
|
||
+landscape у %подписи — альбомная страница под широкий рисунок/таблицу.
|
||
"""
|
||
|
||
PROMPT_FILES = (
|
||
("generate-md.md", "Markdown для md2gost"),
|
||
("generate-mirea-report.md", "МИРЭА / ГОСТ (курсовая, практика, ВКР)"),
|
||
("generate-pis-custom-report.md", "ПИС — отчёт по практическим работам"),
|
||
("emulate-student.md", "Стиль: человечный текст, не отчёт ИИ"),
|
||
)
|
||
|
||
|
||
def prompt_search_dirs() -> list[Path]:
|
||
dirs: list[Path] = []
|
||
here = Path(package_dir())
|
||
dirs.append(here / "prompts")
|
||
dirs.append(here.parent / "prompts")
|
||
if getattr(sys, "frozen", False):
|
||
mei = getattr(sys, "_MEIPASS", None)
|
||
if mei:
|
||
dirs.append(Path(mei) / "prompts")
|
||
dirs.append(Path(sys.executable).resolve().parent / "prompts")
|
||
seen: set[str] = set()
|
||
out: list[Path] = []
|
||
for path in dirs:
|
||
key = str(path.resolve()) if path.exists() else str(path)
|
||
if key in seen:
|
||
continue
|
||
seen.add(key)
|
||
out.append(path)
|
||
return out
|
||
|
||
|
||
def load_prompt_catalog() -> list[tuple[str, str, str]]:
|
||
"""Return list of (filename, title, text). Missing files are skipped."""
|
||
catalog: list[tuple[str, str, str]] = []
|
||
dirs = prompt_search_dirs()
|
||
for name, title in PROMPT_FILES:
|
||
text = None
|
||
for folder in dirs:
|
||
candidate = folder / name
|
||
if candidate.is_file():
|
||
text = candidate.read_text(encoding="utf-8")
|
||
break
|
||
if text:
|
||
catalog.append((name, title, text))
|
||
return catalog
|
||
|
||
|
||
def scheme_prompt_block(scheme) -> str:
|
||
"""Format one DiagramScheme for appending to an AI prompt."""
|
||
sid = getattr(scheme, "id", "") or ""
|
||
title = (getattr(scheme, "title", None) or sid).strip()
|
||
lines = [
|
||
f"## Схема: {title} (`{sid}`)",
|
||
"",
|
||
f"Оградка в markdown: ```uml-{sid} или ```{sid}",
|
||
"",
|
||
]
|
||
ai = (getattr(scheme, "ai_prompt", None) or "").strip()
|
||
if ai:
|
||
lines.append(ai)
|
||
lines.append("")
|
||
docs = (getattr(scheme, "docs", None) or "").strip()
|
||
if docs:
|
||
lines.append("Макросы / шпаргалка:")
|
||
lines.append(docs)
|
||
lines.append("")
|
||
return "\n".join(lines).rstrip() + "\n"
|
||
|
||
|
||
def compose_prompt(base: str, schemes: list | None = None) -> str:
|
||
"""
|
||
Base prompt text plus optional scheme blocks (order preserved).
|
||
schemes: iterable of DiagramScheme (or objects with id/title/docs/ai_prompt).
|
||
"""
|
||
text = (base or "").rstrip()
|
||
if not schemes:
|
||
return text + ("\n" if text else "")
|
||
parts = [text, "", "---", "", "# Дополнение: выбранные схемы диаграмм", ""]
|
||
for scheme in schemes:
|
||
parts.append(scheme_prompt_block(scheme))
|
||
parts.append("")
|
||
return "\n".join(parts).rstrip() + "\n"
|
||
|
||
|
||
def docs_search_dirs() -> list[Path]:
|
||
dirs: list[Path] = []
|
||
here = Path(package_dir())
|
||
dirs.append(here / "docs")
|
||
dirs.append(here.parent / "docs")
|
||
if getattr(sys, "frozen", False):
|
||
mei = getattr(sys, "_MEIPASS", None)
|
||
if mei:
|
||
dirs.append(Path(mei) / "docs")
|
||
dirs.append(Path(sys.executable).resolve().parent / "docs")
|
||
seen: set[str] = set()
|
||
out: list[Path] = []
|
||
for path in dirs:
|
||
key = str(path.resolve()) if path.exists() else str(path)
|
||
if key in seen:
|
||
continue
|
||
seen.add(key)
|
||
out.append(path)
|
||
return out
|
||
|
||
|
||
def _doc_title_from_text(filename: str, text: str) -> str:
|
||
for line in text.splitlines():
|
||
stripped = line.strip()
|
||
if stripped.startswith("#"):
|
||
return stripped.lstrip("#").strip() or Path(filename).stem
|
||
return Path(filename).stem
|
||
|
||
|
||
def load_docs_catalog() -> list[tuple[str, str, str]]:
|
||
"""
|
||
Return list of (filename, title, text) from the first existing docs/ folder.
|
||
README.md first, then other *.md alphabetically.
|
||
"""
|
||
folder: Path | None = None
|
||
for path in docs_search_dirs():
|
||
if path.is_dir():
|
||
folder = path
|
||
break
|
||
if folder is None:
|
||
return []
|
||
|
||
files = sorted(p for p in folder.glob("*.md") if p.is_file())
|
||
if not files:
|
||
return []
|
||
|
||
readme = [p for p in files if p.name.lower() == "readme.md"]
|
||
rest = [p for p in files if p.name.lower() != "readme.md"]
|
||
ordered = readme + rest
|
||
|
||
catalog: list[tuple[str, str, str]] = []
|
||
for path in ordered:
|
||
try:
|
||
text = path.read_text(encoding="utf-8")
|
||
except OSError:
|
||
continue
|
||
catalog.append((path.name, _doc_title_from_text(path.name, text), text))
|
||
return catalog
|