Files
rip-help-system/docs/razdeljane-na-sekcii.md
2026-09-11 15:04:49 +03:00

196 lines
11 KiB
Markdown
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.
# Разделяне на произволен текст на секции
Този документ описва **реалното** поведение на RIP Help System при разделяне на help-файл на секции. Източникът е `help_processor.py` след последните корекции (вкл. `merge_short_sections`). Не е маркетингово резюме.
Генерирането на ключови думи е **извън обхвата**, освен един ред в края.
За да прегенерираш PDF-а:
```
python docs/md_to_pdf.py
```
---
## 1. Място в обработката
За всеки файл `process_file()` прави:
1. Избира парсер по разширение (`.html` / `.htm`, `.docx`, `.doc`, `.pdf`, `.txt`).
2. Парсерът връща списък от `Section(title, text, level, images, html_text)`.
3. Върху списъка се вика `merge_short_sections()`.
4. Празните блокове се филтрират при запис; заглавие без тяло се запазва (виж §8).
ZIP не е формат за разделяне: разопакова се и всеки вътрешен файл минава по същия път.
Нива: `1` = H1, `2` = H2, `3` = H3 (по-дълбоки HTML/Word заглавия се режат до 3), `0` = без заглавие.
---
## 2. Какво е граница на секция
**Граница = открито заглавие.** Текущата секция се затваря (`flush`) и започва нова. Текстът на заглавието става `title` и **не** се копира в тялото на секцията.
Всичко, което не е заглавие — параграфи, списъци, таблици, картинки — се добавя към **текущата** секция. Таблица никога не отваря нова секция.
Ако преди първото заглавие има съдържание, то образува секция с **празно** `title`.
Ако парсерът не открие нито една секция, файлът става **една** секция без заглавие, с целия извлечен текст.
---
## 3. Word (.docx)
Обхождат се децата на `w:body` в document-order: параграфи **и** таблици (`_iter_docx_blocks`). Стандартният `Document.paragraphs` пропуска таблиците, затова не се ползва за сегментиране.
### 3.1. Стилово заглавие
`_docx_heading_level()` гледа `style_id` и `style.name`, после `base_style` нагоре по веригата. Токенът се нормализира (без интервали, `_`, `-`, без значение на регистъра).
Разпознават се поне:
| Токен (примери) | Ниво |
|---|---|
| Heading 1 / heading1 | 1 |
| Heading 2 / 3 | 2 / 3 |
| Title | 1 |
| Subtitle | 2 |
| **Заглавие** / **Заглавие 1** | 1 |
| Заглавие 2 / 3 | 2 / 3 |
| Подзаглавие | 2 |
| Наименование | 1 |
| Überschrift / Überschrift 1 | 1 |
| MsoHeading1 / 2 / 3 | 1 / 2 / 3 |
Число над 3 се ограничава до 3. `Subtitle` / `Подзаглавие` без цифра → ниво 2; останалите именувани стилове без цифра → ниво 1.
Ако стилът не е heading, има fallback: Word `outlineLvl` 0–2 **и** текст под 120 символа → ниво = `outlineLvl + 1`.
### 3.2. Bold като заглавие
Ако няма стилово ниво, параграфът се приема за заглавие ниво 2, когато:
- видимият текст е непразен и ≤ 120 символа;
- всички run-ове с текст са **bold**;
- стилът **не** започва с `list`;
- в параграфа **няма** картинки.
### 3.3. Таблици и картинки
Таблица: всеки ред се сплесква до `клетка | клетка | клетка` и редовете се добавят в тялото на текущата секция.
Картинки в параграф: записват се като `[IMG: img_NN]` в същото тяло.
Празен параграф без картинка се пропуска.
### 3.4. Fallback
Ако след обхождането няма секции: една секция от всички непразни параграфи; ако и те са празни — от таблиците.
---
## 4. Стар .doc
Конвертира се към `.docx` с LibreOffice (`soffice`) или, на Windows, MS Word през COM. После се вика същият `parse_docx`. Ако и двете конверсии пропаднат, файлът се чете като обикновен текст (`parse_txt`).
---
## 5. HTML / HTM (вкл. Word „Запиши като уеб“)
Премахват се `script`, `style`, `nav`, `footer`, `header`, `noscript`.
Събират се **top-level** блокове: `h1`–`h6`, `p`, `ul`, `ol`, `table`, `dl`, `pre`, `blockquote`, `figure`, `hr`, самостоятелни `img`, и `div` **само** ако изглежда като заглавие. Вложен блок не се обработва втори път.
### 5.1. Заглавие
1. Тагове: `h1`→1, `h2`→2, `h3`–`h6`→3.
2. CSS class съдържа `heading`, `заглавие`, `title`, `subtitle`, `msoheading` или `überschrift`, с опционална цифра. Така `MsoHeading1` / `MsoNormal` **не** се бъркат: само heading-класовете режат секция. Пример: Word HTML `<p class="MsoHeading1">`.
3. `p` или `div` с текст < 120 символа, изцяло обвит в `<b>` / `<strong>` → ниво 2.
### 5.2. Съдържание
Списъци и таблици влизат в текущата секция. В plain text редовете им са с нов ред (не сплескани в един ред). Декоративни атрибути (`class`, `style`, `on*`, `data-*` …) се махат от запазения HTML.
Картинки: локални и `data:` URI; HTTP(S) се пропускат. Дребни иконки под 50×50 px се изхвърлят.
Ако няма нито една секция: цялото `body` като една секция без заглавие.
---
## 6. Обикновен текст (.txt)
Кодирането се детектира с chardet.
Ред е граница, ако след `strip()`:
- съвпада с Markdown заглавие `#{1,3} текст` — нивото е броят `#`, заглавието е текстът след тях; **или**
- съвпада с номерирано заглавие: `1. …`, `2) …`, `I. …`, `А. …` (1–2 цифри / римски / една главна буква, после `.` или `)`), дължина на реда < 120.
Всички останали редове отиват в тялото (десният край се реже, водещите интервали се пазят).
Ако няма такива редове: **една** секция с целия файл.
---
## 7. PDF
Няма стилове. Текстът се събира като визуални редове по Y (не по PDF stream), буквите от drop-cap се слепват, сплескани списъци от вида `1 Foo 2 Bar` се разбиват на редове.
Граница: група редове с почти еднакъв размер на шрифта, ако размерът е **по-голям от предишния с повече от 1 pt** и текстът е **под 150 символа** → нова секция, ниво 2.
Това е по-ненадеждно от Word/HTML. Картинките се кропват от страницата и се редят в текущата секция по вертикална позиция.
---
## 8. Сливане на кратки секции (`merge_short_sections`)
Константа: `MIN_SECTION_TOKENS = 60` (думи в **тялото**, `str.split()`).
Правило след последните корекции:
- Секция **с непразно заглавие** **не се слива**, дори тялото да е късо или празно.
- Секция **без заглавие** и с под 60 думи се залепва към предишната (текст, картинки, HTML).
- Няма предишна секция → късият untitled блок остава сам.
Следствия:
- Последователни заглавия без тяло („Глава 1“ веднага следвано от „1.1 Увод“) остават **две** секции.
- Къс абзац без заглавие след секция се присъединява към нея, вместо да стане отделен къс запис.
- Untitled текст в началото на файла остава отделна секция (докато не е под прага *и* няма към какво да се слее — първият елемент не се слива).
При запис в `process_file()`:
- Ако няма текст, картинки и HTML, но има заглавие → тялото става самото заглавие. Затова „голо“ заглавие оцелява като секция.
- Ако няма нито текст, нито заглавие, нито картинки → секцията се пропуска.
`clean_text()` срива поредици от интервали/табове, но пази нови редове (списъци и абзаци).
---
## 9. Текст без заглавие
Типични случаи:
- Увод преди първия Heading 1.
- HTML/DOCX без нито едно разпознато заглавие → целият файл е една untitled секция.
- TXT без markdown/номерация.
- Къс untitled остатък след заглавие се слива с предишната секция (§8).
Празното `title` по-късно се попълва от класификатора (извън този документ).
---
## 10. Таблици — обобщение
| Формат | Поведение |
|---|---|
| DOCX | Редове `a \| b \| c` в текущата секция; не са граница |
| HTML | Целият `<table>` е блок в текущата секция |
| TXT / PDF | Няма отделен table parser; таблицата е просто текст/редове |
---
## 11. Ключови думи (един ред)
След като секциите са вече разделени, всяка получава заглавие и ключови думи чрез Claude или fallback; ръчните думи от таб „Сканиране“ се записват първи във всяка секция.