# DSP Scanner (Python-версия)

Полный перенос PowerShell-скрипта **`dspscanner.ps1`** на Python с сохранением
100% исходного функционала, модульной архитектурой и современным
графическим интерфейсом на **PySide6 (Qt 6)**.

## Что делает программа

1. Рекурсивно ищет файлы `.docx`, `.doc`, `.txt`, `.pdf` в указанных папках,
   пропуская системные/скрытые каталоги, временные файлы (`~`, `~$`) и
   дубликаты путей.
2. Извлекает текст из каждого файла:
   - **DOCX** — через `python-docx` (текст, таблицы, колонтитулы) с резервным
     ручным разбором ZIP/XML при повреждённом архиве;
   - **DOC** — автоматический каскад: LibreOffice headless → MS Word COM
     (только Windows) → эвристический побайтовый разбор OLE2; отдельно
     распознаются файлы, реально являющиеся RTF/HTML/Word 2003 XML/DOCX под
     расширением `.doc`;
   - **TXT** — определение BOM/кодировки (UTF-8/UTF-16/CP1251, авто-детект
     через `charset-normalizer`);
   - **PDF** — **новый функционал** (в оригинале отсутствовал): извлечение
     текста через `PyMuPDF`, с опциональным OCR (Tesseract) для сканов без
     текстового слоя.
3. Ищет одно или несколько ключевых слов/фраз в извлечённом тексте, строит
   контекстные фрагменты вокруг каждого совпадения.
4. Показывает результаты в таблице, ведёт статистику и журнал ошибок.
5. Экспортирует отчёт в **Excel** (три листа: результаты, статистика,
   ошибки), **CSV** или **Markdown**.
6. Копирует найденные файлы в отдельную папку с защитой от коллизий имён и
   маппингом «оригинал → копия».
7. Позволяет **безопасно удалить** (многопроходная перезапись) или
   **безопасно переместить** оригиналы файлов, копии которых пользователь
   вручную удалил из папки назначения — так же, как в оригинальном скрипте.

## Что улучшено относительно оригинала

- **Параллельная обработка файлов** (`ThreadPoolExecutor`) вместо
  последовательной обработки одного файла за раз — на больших деревьях
  каталогов ускорение в разы.
- **PDF читается по-настоящему** (в ps1-версии эта функция была лишь
  заглушкой, всегда возвращавшей пустой результат).
- DOC читается через LibreOffice/MS Word, когда это возможно — значительно
  точнее, чем побайтовый эвристический разбор, который используется только
  как последний резервный вариант.
- Определение кодировки TXT через `charset-normalizer` вместо жёстко
  заданной CP1251.
- Безопасное удаление перезаписывает файл случайными данными (а не только
  нулями).
- Современный, отзывчивый GUI на Qt6 вместо консольного меню; сканирование
  выполняется в фоновом потоке, прогресс и журнал обновляются в реальном
  времени, есть отмена операции.
- Добавлен **таймаут на один файл** — защита от зависания на повреждённых
  или очень больших файлах (в оригинале скрипт мог "застрять" навсегда).
- MS Word COM инициализируется один раз на поток обработки, а не на каждый
  файл — устраняет накладные расходы.

## Архитектура интерфейса (v2)

Главное окно разделено на три зоны, что устраняет проблему «хаотичного
изменения границ» и «невидимого прогресса» из первой версии:

```
┌──────────────────────────────────────────────────────────────┐
│  Меню: Файл · Настройки · Справка                              │
├──────────────────────────────────────────────────────────────┤
│  [████████░░░░░░]  123/456 — file.docx           [■ Стоп]    │  ← Прогресс-бар
├────────────────────┬─────────────────────────────────────────┤
│  Левая панель      │  Правая панель (вкладки)                 │
│  ┌─ Пути ────┐    │  [Результаты] [Файлы] [Журнал]            │
│  │ список   │    │                                            │
│  └──────────┘    │  Таблица найденных совпадений с           │
│  ┌─ Слова ───┐   │  подсветкой найденного слова, фильтр,      │
│  │ textarea │   │  экспорт в Excel/CSV/Markdown              │
│  └──────────┘    │                                            │
│  ┌─ Параметры┐   │                                            │
│  │ типы,    │   │                                            │
│  │ метод .doc│   │                                            │
│  └──────────┘    │                                            │
│  [▶ Начать]     │                                            │
│  ┌─ Статистика┐  │                                            │
│  │ 8 карточек│   │                                            │
│  └──────────┘    │                                            │
├────────────────────┴─────────────────────────────────────────┤
│  Статус-бар: Готово / Сканирование...                        │
└──────────────────────────────────────────────────────────────┘
```

Ключевые решения:
- **Прогресс-бар всегда сверху**, виден на любой вкладке — пользователь
  всегда видит, на каком этапе находится сканирование.
- **Автопереключение вкладок отключено** — пользователь сам выбирает,
  какую вкладку смотреть (Результаты / Файлы / Журнал).
- **Stat cards имеют фиксированные размеры** (через QSS) — цифры разной
  длины больше не «прыгают» при обновлении.
- **Таблица результатов**: колонка «Контекст» в режиме `Interactive` с
  минимальной шириной (без `Stretch`) — устраняет перерасчёт геометрии
  при добавлении строк. Подсветка найденного слова через делегат.
- **Кнопка «Стоп»** в строке прогресс-бара, всегда доступна во время
  сканирования.

## Установка

```bash
cd python_app
python -m venv .venv
# Windows: .venv\Scripts\activate       Linux/Mac: source .venv/bin/activate
pip install -r requirements.txt
```

Дополнительно (по желанию, необязательно для базовой работы):

- **LibreOffice** — для максимально точного чтения `.doc` без Windows/MS Word.
- **Tesseract OCR** — для распознавания сканированных PDF без текстового слоя.

Пути к LibreOffice и Tesseract можно задать в диалоге **«Настройки → Параметры»**
(если они не в PATH — автоопределение работает только для стандартных установок).

## Запуск

```bash
python main.py
```

## Горячие клавиши

- `Ctrl+S` — сохранить конфигурацию поиска (пути, слова, опции) в JSON.
- `Ctrl+O` — загрузить конфигурацию поиска.
- `Ctrl+Q` — выход.

## Архитектура проекта

```
python_app/
├── main.py                     # точка входа, инициализация Qt-приложения
├── requirements.txt
└── app/
    ├── config.py                # константы, датаклассы (ScanSettings, ScanStats, ...)
    ├── settings_store.py        # глобальные настройки (внешние программы, таймауты)
    ├── logging_utils.py         # логирование в файл + трансляция в GUI
    ├── readers/                 # извлечение текста из файлов
    │   ├── base.py               # общий интерфейс BaseReader / ReaderOutcome
    │   ├── docx_reader.py        # DOCX (python-docx + резервный XML-парсер)
    │   ├── doc_reader.py         # DOC: диспетчер (LibreOffice/COM/эвристика)
    │   ├── doc_formats.py        # определение формата + RTF/HTML/XML/OLE2 парсеры
    │   ├── txt_reader.py         # TXT с автоопределением кодировки
    │   └── pdf_reader.py         # PDF (PyMuPDF + опциональный OCR) — новое
    ├── scanning/                 # поиск файлов и текстовый поиск
    │   ├── file_finder.py         # безопасный рекурсивный обход ФС
    │   ├── search_engine.py       # поиск слов + построение контекста
    │   └── scanner.py             # оркестратор (пул потоков, таймаут, статистика)
    ├── fileops/
    │   └── file_operations.py    # копирование, безопасное удаление/перемещение
    ├── export/
    │   ├── excel_export.py       # экспорт в Excel (openpyxl)
    │   └── text_exports.py       # экспорт в CSV / Markdown
    └── gui/
        ├── theme.py               # современная тёмная Qt-тема (QSS, фикс. размеры)
        ├── results_model.py       # модель таблицы результатов + делегат подсветки
        ├── workers.py             # фоновые QThread для сканирования/файловых операций
        ├── settings_dialog.py     # диалог "Параметры" (LibreOffice, Tesseract, ...)
        └── main_window.py         # главное окно (прогресс сверху, сплиттер, вкладки)
```

## О тестировании

В окружении, где готовился этот проект, нет возможности запускать
интерпретатор Python — есть только инструменты создания и редактирования
файлов. Поэтому код был:

- написан по проверенным, стабильным API широко используемых библиотек
  (`python-docx`, `olefile`, `striprtf`, `PyMuPDF`, `openpyxl`, `PySide6`);
- тщательно вычитан вручную на предмет синтаксических и логических ошибок;
- структурирован так, чтобы каждый читатель формата (`docx_reader`,
  `doc_reader`, `pdf_reader`, `txt_reader`) можно было запустить и
  протестировать независимо, например:

```python
from pathlib import Path
from app.readers.docx_reader import DocxReader
from app.readers.doc_reader import DocReader
from app.config import ScanSettings

# Тест чтения одного файла
outcome = DocxReader().extract_text(Path("Закупка KSS.docx"))
print(outcome.text[:500] if outcome.text else outcome.error)

# Тест полного сканирования
from app.scanning.scanner import DocumentScanner
settings = ScanSettings(
    paths=["."],
    words=["договор", "оплата"],
    file_types={".docx", ".doc", ".txt", ".pdf"},
)
report = DocumentScanner().run(settings)
print(f"Найдено: {len(report.results)}, ошибок: {report.stats.errors}")
```

Перед боевым использованием рекомендуется прогнать программу на реальных
тестовых `.docx`/`.doc`/`.pdf` файлах и свериться с журналом (вкладка
«Журнал» и файл `~/DSPScanner/logs/search_errors.log`).
