Программа принимает папку с документами, извлекает из них текст и метаданные и строит каталог: краткое описание, тезисы и ключевые слова для каждого файла плюс поиск по всему собранному. Ядро: - extract: PDF, DJVU, DOC(X), RTF, ODT, CHM, EPUB, FB2, PPT(X), XLS(X), TXT; PDF передаётся MuPDF потоком в память (работают длинные пути) и ограничен по времени — повреждённый файл иначе чинится минутами; - summarize: экстрактивное реферирование по TF-IDF, посчитанному на самой библиотеке, с лёгким стеммером для русского и английского; - db: SQLite с полнотекстовым индексом FTS5, морфологический поиск с BM25; - scanner: инкрементальный многопоточный обход, счётчик попыток защищает от файла, обрывающего разбор; - report: автономный HTML с поиском, Markdown, JSON, CSV. Интерфейс: - окно PySide6 в тёмной теме: вкладки разбора и поиска, фоновый поток, карточка документа, правка своих ключевых слов; - командная строка: build, scan, analyze, report, search, show, tag, stats; - у каждой папки свой каталог, последняя обработанная запоминается. Сборка: scripts/build_exe.py даёт Catalogizer.exe в Windows и Catalogizer.app в macOS, иконки .ico и .icns рисуются кодом. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
255 lines
16 KiB
Markdown
255 lines
16 KiB
Markdown
# Каталогизатор документов
|
||
|
||
Программа принимает папку с документами, разбирает её и строит каталог:
|
||
для каждого файла — **краткое описание**, **тезисы** и **ключевые слова**,
|
||
плюс **поиск по каталогу** — в окне программы, в командной строке и в
|
||
автономной HTML-странице.
|
||
|
||
Всё работает локально: файлы только читаются, никаких обращений в сеть.
|
||
|
||
Программа работает в Windows, macOS и Linux — исходники одни и те же.
|
||
|
||
## Запуск
|
||
|
||
**Windows** — двойной щелчок по `CATALOG.bat` или `dist\Catalogizer.exe`.
|
||
**macOS** — двойной щелчок по `CATALOG.command` или `dist/Catalogizer.app`.
|
||
Ни Python, ни библиотек при этом не требуется.
|
||
|
||
**Из исходников** (любая система):
|
||
|
||
```bash
|
||
pip install -r requirements.txt
|
||
python catalog_gui.py
|
||
```
|
||
|
||
## Окно программы
|
||
|
||
Две вкладки:
|
||
|
||
* **Разбор папки** — поле пути и «Обзор…», параметры (потоков чтения, страниц
|
||
на документ, «перечитать всё заново»), кнопки «Построить каталог» и
|
||
«Остановить», индикатор хода работы и журнал. Разбор идёт в фоновом потоке,
|
||
окно не подвисает; остановка безопасна — разобранное уже в базе, следующий
|
||
запуск продолжит с того же места.
|
||
* **Каталог и поиск** — строка поиска, фильтры по формату, языку и разделу,
|
||
таблица найденного и карточка документа с описанием, тезисами, ключевыми
|
||
словами и фрагментом текста с подсветкой. Двойной щелчок открывает документ,
|
||
«Показать в папке» — проводник на нём.
|
||
|
||
Кнопки «Пересобрать отчёты» и «Открыть HTML-каталог» — на вкладке разбора.
|
||
|
||
## Командная строка
|
||
|
||
Папка — обычный аргумент:
|
||
|
||
```bash
|
||
python catalog.py build "E:\Yandex.Disk\литература"
|
||
```
|
||
|
||
| Команда | Что делает |
|
||
|---|---|
|
||
| `gui [ПАПКА]` | графическое окно (то же, что запуск без аргументов) |
|
||
| `scan ПАПКА` | обход папки, извлечение текста и метаданных в базу |
|
||
| `analyze` | ключевые слова, тезисы и описания по всей библиотеке |
|
||
| `report` | выгрузка `catalog.html`, `catalog.md`, `catalog.json`, `catalog.csv` |
|
||
| `build ПАПКА` | полный цикл: `scan` + `analyze` + `report` |
|
||
| `search ЗАПРОС` | поиск по каталогу |
|
||
| `show ID\|ИМЯ` | карточка одного документа |
|
||
| `tag ДОКУМЕНТ СЛОВА` | свои ключевые слова документа |
|
||
| `stats` | сводка: форматы, разделы, частые темы |
|
||
|
||
Последняя обработанная папка запоминается, поэтому дальше путь можно не
|
||
повторять: `python catalog.py search "инвертор"`. Обратиться к другому
|
||
каталогу — ключ `-c`: `python catalog.py -c "D:\books" stats`.
|
||
|
||
У каждой папки своя база (`catalogs/<имя>-<хеш>.db`) и своя папка отчётов
|
||
(`out/<имя>-<хеш>/`), каталоги разных папок не смешиваются.
|
||
|
||
Полезные ключи `scan`/`build`:
|
||
|
||
```bash
|
||
python catalog.py scan --ext pdf djvu # только выбранные форматы
|
||
python catalog.py scan --max-pages 40 # читать больше страниц из каждого файла
|
||
python catalog.py scan --jobs 8 # потоков чтения: HDD 2-4, SSD 8-16
|
||
python catalog.py scan --limit 50 # пробный прогон на 50 файлах
|
||
python catalog.py scan --force # перечитать всё заново
|
||
python catalog.py scan --retry-broken # снова попробовать «битые» файлы
|
||
```
|
||
|
||
## Поиск
|
||
|
||
```bash
|
||
python catalog.py search "инвертор"
|
||
python catalog.py search "цифровая обработка сигналов" -n 30
|
||
python catalog.py search stm32 dma --ext pdf
|
||
python catalog.py search "операционный усилитель" --lang ru --year-from 2010
|
||
python catalog.py search "\"частотное преобразование\"" # точная фраза
|
||
python catalog.py search fpga -verilog # минус — исключить слово
|
||
python catalog.py search can --section datasheet # только в разделе
|
||
```
|
||
|
||
Поиск морфологический: слова приводятся к основе, поэтому «инвертор» находит
|
||
«инверторы» и «инвертора». Ранжирование — BM25 с повышенным весом названия и
|
||
ключевых слов; в выдаче показывается фрагмент текста с подсветкой.
|
||
|
||
## Свои ключевые слова
|
||
|
||
Кроме слов, вычисленных из текста, к любому документу можно добавить свои —
|
||
рубрику, проект, пометку «нужное». Они полноправно участвуют в поиске и даже
|
||
весят в нём больше вычисленных.
|
||
|
||
В окне: выбрать документ, вписать слова через запятую в строку «Свои
|
||
ключевые слова» под карточкой и нажать «Сохранить» (или Enter, или Ctrl+S).
|
||
При вводе подсказываются слова, уже заведённые в этом каталоге.
|
||
|
||
В командной строке:
|
||
|
||
```bash
|
||
python catalog.py tag opamp "силовая электроника, к проекту А" # добавить
|
||
python catalog.py tag 137 --remove "к проекту А" # убрать
|
||
python catalog.py tag 137 --set "только это" # заменить список
|
||
python catalog.py tag 137 --set # очистить
|
||
python catalog.py tag 137 # показать
|
||
python catalog.py tag --all # все слова каталога
|
||
python catalog.py search "к проекту А" # найти помеченные
|
||
```
|
||
|
||
Документ указывается номером `id` или частью имени, пути либо названия.
|
||
|
||
Слова хранятся отдельно от результатов разбора и привязаны к пути файла,
|
||
поэтому переживают повторный разбор папки и перестроение каталога. В отчёты
|
||
они попадают отдельной строкой («Свои ключевые слова»), в HTML-каталоге
|
||
выделены цветом, в CSV — отдельной колонкой.
|
||
|
||
## Отчёты
|
||
|
||
После `report` в папке `out/`:
|
||
|
||
* **`catalog.html`** — автономная страница: поиск по мере ввода, фильтры,
|
||
тезисы и ключевые слова у каждого документа, ссылки на файлы. Открывается
|
||
двойным щелчком, без сервера;
|
||
* `catalog.md` — каталог по разделам для чтения и печати;
|
||
* `catalog.json`, `catalog.csv` — выгрузка для других программ (CSV с BOM,
|
||
открывается в Excel).
|
||
|
||
## Как строятся описания и тезисы
|
||
|
||
Реферирование экстрактивное — фразы берутся из самого документа, ничего не
|
||
досочиняется:
|
||
|
||
1. из файла читаются первые `--max-pages` страниц, оглавление и метаданные;
|
||
2. слова приводятся к основе (лёгкий стеммер для русского и английского),
|
||
считаются частоты; слова из названия и оглавления получают больший вес;
|
||
3. по всей библиотеке считается IDF — так отсеиваются слова, общие для всей
|
||
техлитературы, и остаются характерные именно для этого документа;
|
||
4. **ключевые слова** — верхние основы по TF-IDF, показанные самой короткой
|
||
частой словоформой;
|
||
5. **тезисы** — предложения с наибольшей плотностью ключевых слов, с бонусом
|
||
началу документа (аннотация, введение) и оборотам вида «рассматривается»,
|
||
«предназначено», `this book describes`; близкие по составу фразы отбрасываются;
|
||
6. **описание** — тип издания, язык, объём, темы и первый тезис.
|
||
|
||
Для сканов без текстового слоя тезисы берутся из оглавления PDF, а если его
|
||
нет — документ попадает в каталог с пометкой «скан» и описанием по имени файла.
|
||
Найти такие файлы: `python catalog.py stats`.
|
||
|
||
## Форматы и внешние утилиты
|
||
|
||
Разбираются PDF, DJVU, DOC, DOCX, RTF, ODT, CHM, EPUB, FB2, PPT, PPTX, XLS,
|
||
XLSX, TXT, MD. Обязательна только `pymupdf` (PDF); дополнительно, если есть
|
||
в PATH:
|
||
|
||
* `antiword` — качественный разбор старых `.doc` (иначе грубая выборка строк);
|
||
* `djvutxt` из DjVuLibre — текстовый слой `.djvu` (иначе для DJVU остаются
|
||
только название, число страниц и раздел).
|
||
|
||
## Сборка исполняемого файла
|
||
|
||
Сборщик один — `scripts/build_exe.py`; что получится, зависит от системы,
|
||
**на которой идёт сборка**: PyInstaller не умеет собирать под чужую
|
||
платформу. Программу для macOS собирают на Mac, для Windows — на Windows.
|
||
|
||
**Windows** → `dist\Catalogizer.exe`:
|
||
|
||
```bash
|
||
BUILD_EXE.bat --setup # один раз: .venv с PySide6, pymupdf, PyInstaller
|
||
BUILD_EXE.bat # сборка
|
||
```
|
||
|
||
**macOS** → `dist/Catalogizer.app`:
|
||
|
||
```bash
|
||
chmod +x BUILD_APP.command CATALOG.command # один раз, после переноса файлов
|
||
./BUILD_APP.command --setup # один раз: .venv и зависимости
|
||
./BUILD_APP.command # сборка
|
||
```
|
||
|
||
Оба сценария можно просто запустить двойным щелчком. Ключи передаются в
|
||
`scripts/build_exe.py`: `--onedir` (папка вместо одного файла, запускается
|
||
быстрее; в macOS так всегда — `.app` и есть папка), `--console` (оставить
|
||
консоль для traceback), `--cli` (собрать ещё и консольную программу
|
||
`catalog`). После сборки автоматически выполняется самопроверка `--check`:
|
||
окно строится целиком и программа выходит.
|
||
|
||
Иконки генерируются кодом — `python scripts/make_icon.py` создаёт
|
||
`ico/catalog.ico` для Windows и `ico/catalog.icns` для macOS.
|
||
|
||
Куда программа кладёт базы и отчёты: в Windows и Linux — **рядом с
|
||
исполняемым файлом**, в macOS — в `~/Library/Application Support/Catalogizer`,
|
||
потому что содержимое `.app` считается неизменяемым.
|
||
|
||
### Что учтено для macOS
|
||
|
||
* пути и `\?\`-префикс применяются только в Windows;
|
||
* «Показать в папке» открывает Finder на файле (`open -R`), «Открыть
|
||
документ» — `open`;
|
||
* ссылки на файлы в HTML-каталоге строятся из абсолютного пути и работают
|
||
и с `/Users/...`, и с `E:\...`;
|
||
* в теме заданы системные шрифты (`SF Pro Text`, `SF Mono`) как замена
|
||
windows-овским;
|
||
* папка по умолчанию — `~/Documents`, если жёстко прописанной библиотеки нет;
|
||
* `.command`-файлы сохранены с переводами строк LF, иначе bash их не примет.
|
||
|
||
Первый запуск неподписанного `.app` macOS блокирует: правой кнопкой по
|
||
`Catalogizer.app` → «Открыть» → «Открыть» в диалоге. Либо снять карантин:
|
||
`xattr -dr com.apple.quarantine dist/Catalogizer.app`.
|
||
|
||
## Устройство
|
||
|
||
```
|
||
CATALOG.bat запуск окна в Windows
|
||
CATALOG.command запуск окна в macOS
|
||
BUILD_EXE.bat сборка exe в Windows
|
||
BUILD_APP.command сборка .app в macOS
|
||
catalog_gui.py точка входа окна (её упаковывает PyInstaller)
|
||
catalog.py точка входа командной строки
|
||
catalogizer/
|
||
app.py сборка QApplication, ключ --check
|
||
config.py папка по умолчанию, форматы, ограничения разбора
|
||
extract.py чтение PDF, DJVU, DOC(X), RTF, CHM, EPUB, FB2, XLS(X), TXT
|
||
textutil.py язык, токенизация, стемминг, разбиение на предложения
|
||
summarize.py TF-IDF, ключевые слова, тезисы, описание
|
||
scanner.py обход папки, многопоточное извлечение
|
||
analyze.py проход анализа по всей базе
|
||
search.py запросы к полнотекстовому индексу
|
||
report.py HTML, Markdown, JSON, CSV
|
||
db.py SQLite + FTS5
|
||
ui/ тёмная тема, главное окно, вкладки, фоновый поток
|
||
scripts/build_exe.py сборка exe/.app — под ту систему, где запущен
|
||
scripts/make_icon.py генерация иконок .ico и .icns
|
||
```
|
||
|
||
## Замечания по работе
|
||
|
||
* Файлы длиннее 260 символов пути читаются через префикс `\\?\`; PDF
|
||
передаются MuPDF потоком в память, поэтому нестандартные пути не мешают.
|
||
* Кодировка русских `.txt` определяется перебором (utf-8, cp1251, koi8-r, cp866).
|
||
* Разбор одного PDF ограничен по времени (`PDF_TIME_BUDGET`, 25 с):
|
||
повреждённый файл MuPDF чинит страницу за страницей и может занять минуты.
|
||
* Сбой на файле не останавливает обход: причина сохраняется в поле `error`
|
||
и видна в `stats`. Файл, трижды оборвавший разбор, дальше пропускается —
|
||
вернуть его в очередь можно ключом `--retry-broken`.
|
||
* Если папка лежит под управлением клиента синхронизации (Яндекс.Диск,
|
||
OneDrive), его драйвер может надолго придерживать открытые файлы. На время
|
||
первого разбора синхронизацию лучше приостановить.
|