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>
75 lines
2.1 KiB
Python
75 lines
2.1 KiB
Python
"""
|
||
save_keywords.py
|
||
================
|
||
Чете keywords_changes.json (генериран от браузъра)
|
||
и записва промените в PostgreSQL.
|
||
|
||
Стартирай с: python save_keywords.py
|
||
"""
|
||
|
||
import os, sys, json
|
||
from pathlib import Path
|
||
from datetime import datetime
|
||
|
||
try:
|
||
import psycopg2
|
||
except ImportError:
|
||
sys.exit("Инсталирай psycopg2: pip install psycopg2-binary")
|
||
|
||
CONN_STR = os.getenv(
|
||
"HELP_DB_CONN",
|
||
"host=192.168.88.18 port=5432 dbname=rip_help_system user=sa password=Parola~12345!!!"
|
||
)
|
||
CHANGES_FILE = Path(__file__).parent / "keywords_changes.json"
|
||
|
||
|
||
def main():
|
||
if not CHANGES_FILE.exists():
|
||
print("Файлът keywords_changes.json не е намерен.")
|
||
print("Запази промените от браузъра първо.")
|
||
return
|
||
|
||
changes = json.loads(CHANGES_FILE.read_text(encoding="utf-8"))
|
||
if not changes:
|
||
print("Няма промени за запис.")
|
||
return
|
||
|
||
print(f"Записвам {len(changes)} промени в БД...")
|
||
conn = psycopg2.connect(CONN_STR)
|
||
cur = conn.cursor()
|
||
ok, err = 0, 0
|
||
|
||
for item in changes:
|
||
code = item.get("code", "").strip()
|
||
keywords = item.get("keywords", "").strip()
|
||
if not code:
|
||
continue
|
||
try:
|
||
cur.execute(
|
||
"UPDATE rip_help_sections SET keywords=%s, updated_at=NOW() WHERE code=%s",
|
||
(keywords, code)
|
||
)
|
||
if cur.rowcount > 0:
|
||
ok += 1
|
||
print(f" ✓ {code}")
|
||
else:
|
||
print(f" ? {code} — не е намерен в БД")
|
||
except Exception as e:
|
||
print(f" ✗ {code} — {e}")
|
||
err += 1
|
||
|
||
conn.commit()
|
||
conn.close()
|
||
|
||
print(f"\nГотово: {ok} записани, {err} грешки.")
|
||
|
||
# Архивираме файла
|
||
ts = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||
archive = CHANGES_FILE.parent / f"keywords_changes_{ts}.json"
|
||
CHANGES_FILE.rename(archive)
|
||
print(f"Файлът е архивиран като: {archive.name}")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|