Files
rip-help-system/README.md
Sabo Sabev 9613420d1d Migrate to PostgreSQL + add FastAPI webapp for Coolify deploy
Backend migration:
- Replace pyodbc/SQL Server with psycopg2/PostgreSQL throughout
- Rewrite Database class with portable SQL: SERIAL, ON CONFLICT, NOW()
- Lowercase table names (rip_help_files, rip_help_sections) - Postgres convention
- libpq connection string format in HELP_DB_CONN

Webapp (webapp/):
- FastAPI app: GET /, GET /images/<f>, GET /home-image, GET /api/sections,
  POST /api/keywords/<code>, GET /healthz
- Jinja2 template extracted from generate_html.py with HTTP image URLs
- Direct keyword save to DB (no JSON download detour)
- Same prefix scoping as CLI tools (?prefix=RIP)

Deployment:
- Dockerfile (python:3.12-slim + uvicorn)
- docker-compose.yml for local dev
- requirements-webapp.txt (minimal, no Windows-only deps)
- .dockerignore excludes pipeline scripts and BAT files
- README updated with webapp section and Coolify deploy guide

Also: switch AI model to claude-haiku-4-5 (~3x cheaper, same quality for this task)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 17:00:44 +03:00

185 lines
8.4 KiB
Markdown
Raw Permalink 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-файл декомпозитор и viewer
Обработва help-файлове (`.html`, `.htm`, `.docx`, `.doc`, `.pdf`, `.txt`), декомпозира ги на секции, извлича картинки, класифицира секциите с Claude API (заглавие + ключови думи), и записва всичко в SQL Server. После генерира интерактивен HTML viewer.
## Архитектура
```
Входни файлове → help_processor.py → SQL Server → generate_html.py → help_viewer.html
(.docx, .html, (RIP_help_*) (Home / Редактор /
.pdf, .doc) Търсене / Генератор)
save_keywords.py ← keywords_changes.json
(от Редактора на viewer-а)
```
## Инсталация
```
pip install -r requirements.txt
```
За стар `.doc` формат — едно от:
- **LibreOffice** в PATH (кросплатформено)
- **MS Word** (Windows, чрез pywin32 COM — автоматичен fallback)
## Конфигурация
Копирай `.env.example` като `.env` и попълни:
```
ANTHROPIC_API_KEY=sk-ant-...
HELP_DB_CONN=DRIVER={ODBC Driver 18 for SQL Server};TrustServerCertificate=yes;SERVER=host,port;DATABASE=db;UID=user;PWD=password
```
`.env` е gitignore-нат. Bat файловете го зареждат автоматично през `_load_env.bat`.
## Употреба (Windows)
### Обработка на нов проект
Първо създай `<PROJECT>_load.bat` и `<PROJECT>_view.bat` (вж. `RIP_load.bat`, `RIP_view.bat` като образец).
| BAT | Какво прави |
|---|---|
| `RIP_load.bat` | Incremental — обработва само нови/променени файлове по SHA-256 hash |
| `RIP_load_force.bat` | `--force --purge-missing` — преобработва всичко, изтрива orphans |
| `RIP_view.bat` | Генерира `help_viewer.html` за prefix=RIP и го отваря в браузъра |
### Директно от CLI
```
python help_processor.py --prefix=<PREFIX> <input_dir> <output_dir>
python help_processor.py --prefix=<PREFIX> --force --purge-missing <input_dir> <output_dir>
python generate_html.py --prefix=<PREFIX> # без Home таб
python generate_html.py --prefix=<PREFIX> --home img.png # с Home таб
```
## Prefix scoping
Всеки проект има свой `--prefix` (напр. `RIP`, `INEX_TM`). Прави следните неща изолирани между проектите:
- Кодовете на секциите: `RIP_0001_SEC_0001` vs `INEX_TM_0001_SEC_0001`
- skip-by-hash (incremental) — само в рамките на prefix-а
- `--purge-missing` — изтрива orphans само в текущия prefix
- `generate_html.py --prefix=X` — viewer-а филтрира по prefix
## Структура на базата
### `RIP_help_files`
| Поле | Тип | Описание |
|---|---|---|
| id | INT IDENTITY | PK |
| prefix | NVARCHAR(50) | Project scope |
| file_path | NVARCHAR(1000) | Пълен път до файла |
| file_hash | CHAR(64) | SHA-256 за incremental |
| processed_at | DATETIME2 | Последна обработка |
| section_count | INT | Брой секции |
UNIQUE constraint: `(prefix, file_path)`
### `RIP_help_sections`
| Поле | Тип | Описание |
|---|---|---|
| id | INT IDENTITY | PK |
| prefix | NVARCHAR(50) | Project scope |
| code | NVARCHAR(80) | `<PREFIX>_NNNN_SEC_NNNN` (UNIQUE) |
| source_file | NVARCHAR(1000) | Източник |
| title | NVARCHAR(500) | AI-генерирано заглавие |
| keywords | NVARCHAR(300) | До 5 ключови думи |
| char_count | INT | Размер на чистия текст |
| output_path | NVARCHAR(1000) | Път до `.txt` файла |
| images | NVARCHAR(MAX) | JSON масив с относителни пътища |
| html_text | NVARCHAR(MAX) | Rich HTML с форматиране (само за `.html` източници) |
| created_at, updated_at | DATETIME2 | |
## HTML Viewer — 3 / 4 таба
- **Home** (опционален, ако `--home <image>` е подаден) — началов екран с изображение
- **Редактор** — таблица със секции; inline редактиране на ключови думи; ✓ Save → JSON download → `save_keywords.py` → UPDATE в БД
- **Търсене** — карти със секции; multi-keyword (intervals = AND, "phrase" = literal); preview с картинки
- **Генератор** — drag & drop ordering → export като HTML (self-contained, всички картинки base64-embed-нати)
## Картинки
Извличат се по време на парсване:
- `.docx``<a:blip>` в paragraph drawings → bytes от related_parts
- `.html` — локални файлове и `data:` URLs; HTTP пропуска
- `.pdf``pdfplumber.page.crop(bbox).to_image()` като PNG
- `.doc` — след LibreOffice/MS Word конверсия до `.docx`
Филтър ≥ 50×50 px (PIL детектва), за да отрязва иконки/булети.
Записват се в `<output_dir>/images/<code>_img_NN.<ext>`. В текста placeholder `[IMG: images/...]`. В DB `images` колоната съдържа JSON масив с пътищата.
## Constants (в `help_processor.py`)
| Константа | Default | Описание |
|---|---|---|
| `MIN_SECTION_TOKENS` | 60 | Под този праг секцията се слива с предишната |
| `MAX_AI_CHARS` | 4000 | Символи, пращани към Claude |
| `AI_MODEL` | claude-haiku-4-5 | Модел за класификация |
| `MIN_IMAGE_PX` | 50 | Картинки под NxN px се пропускат |
---
## Webapp (FastAPI)
`webapp/` съдържа FastAPI app за server deploy (Coolify / Docker).
### Endpoint-и
| Метод | Път | Описание |
|---|---|---|
| GET | `/` | HTML viewer (Home/Editor/Search/Generator); query `?prefix=RIP&home=1` |
| GET | `/images/<file>` | Сервира картинка от `OUTPUT_DIR/images/` |
| GET | `/home-image` | Home image (от `HOME_IMAGE` env var или `Bairaci.png`) |
| GET | `/api/sections` | JSON списък със секции; query `?prefix=` |
| POST | `/api/keywords/<code>` | `{keywords: "..."}` → UPDATE в DB |
| GET | `/healthz` | Health check (DB ping) |
### Env vars
| Var | Default | Описание |
|---|---|---|
| `HELP_DB_CONN` | — | libpq формат за Postgres (задължително) |
| `OUTPUT_DIR` | `./data` | Директория с `images/` подпапка |
| `HOME_IMAGE` | — | Абсолютен път към home картинка (опционален) |
### Локален dev
```
pip install -r requirements-webapp.txt
$env:HELP_DB_CONN="host=192.168.88.18 port=5432 dbname=rip_help_system user=sa password=..."
$env:OUTPUT_DIR="q:\RIP_Help_Source\Output"
python -m uvicorn webapp.main:app --reload
# отвори http://127.0.0.1:8000/?prefix=RIP&home=1
```
### Coolify deploy
1. В Coolify: New Resource → Application → Public/Private Repository
2. URL: `https://git.inex-project.net/sabo/rip-help-system.git`
3. Build pack: **Dockerfile**
4. Environment variables (в Coolify UI, не в git):
- `HELP_DB_CONN=host=... port=5432 dbname=rip_help_system user=... password=...`
- `OUTPUT_DIR=/data/help_output`
- `HOME_IMAGE=/data/help_output/Bairaci.png` (по избор)
5. Persistent storage (volume): `/data/help_output` (там качваш картинките с WinSCP/rsync)
6. Port: 8000
7. Custom domain → Coolify прави HTTPS (Let's Encrypt) автоматично
### Качване на картинки на сървъра
Локалният `help_processor.py` пише в `q:\RIP_Help_Source\Output\` (включително `images/` подпапка). За deploy:
```
# с WinSCP — sync на цялата директория
local: q:\RIP_Help_Source\Output\
remote: /home/sabo/share/help_output/ ← Coolify volume mount
```
Базата (Postgres на 192.168.88.18) се ползва от webapp-а директно.