"""
generate_html.py
================
Чете секциите от SQL Server и генерира help_viewer.html.
Стартирай с: python generate_html.py
"""
import os, sys, json, re, base64, mimetypes, argparse
from pathlib import Path
from datetime import datetime
from typing import Optional
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!!!"
)
OUT_HTML = Path(__file__).parent / "help_viewer.html"
_IMG_PLACEHOLDER_RE = re.compile(r"\[IMG:\s*([^\]]+?)\s*\]")
def _esc(s: str) -> str:
return (s.replace("&", "&")
.replace("<", "<")
.replace(">", ">")
.replace('"', """))
def _img_src(rel: str, output_dir: Path, embed: bool) -> str:
"""file:// URI или base64 data URI за картинка."""
abs_path = (output_dir / rel).resolve()
if not abs_path.exists():
return _esc(rel)
if embed:
try:
mime = mimetypes.guess_type(str(abs_path))[0] or "image/png"
b64 = base64.b64encode(abs_path.read_bytes()).decode("ascii")
return f"data:{mime};base64,{b64}"
except Exception:
return abs_path.as_uri()
return abs_path.as_uri()
def _text_to_html(text: str, output_dir: Path, embed: bool = False) -> str:
"""Конвертира [IMG: images/foo.png] към ; escape-ва останалия текст."""
parts = []
last = 0
for m in _IMG_PLACEHOLDER_RE.finditer(text):
parts.append(_esc(text[last:m.start()]))
rel = m.group(1).strip().replace("\\", "/")
src = _img_src(rel, output_dir, embed)
parts.append(
f'
'
)
last = m.end()
parts.append(_esc(text[last:]))
return "".join(parts).replace("\n", "
")
def _rich_html_with_images(html: str, output_dir: Path, embed: bool = False) -> str:
"""Същото като _text_to_html, но входът е вече HTML — НЕ escape-ва."""
def sub(m):
rel = m.group(1).strip().replace("\\", "/")
src = _img_src(rel, output_dir, embed)
return (f'')
return _IMG_PLACEHOLDER_RE.sub(sub, html)
def fetch_sections(prefix: Optional[str] = None):
conn = psycopg2.connect(CONN_STR)
cur = conn.cursor()
if prefix:
cur.execute("""
SELECT s.prefix, s.code, s.title, s.keywords, s.char_count,
s.source_file, s.output_path, s.updated_at,
s.images, s.html_text, f.section_count
FROM rip_help_sections s
LEFT JOIN rip_help_files f
ON f.file_path = s.source_file AND f.prefix = s.prefix
WHERE s.prefix = %s
ORDER BY s.code
""", (prefix,))
else:
cur.execute("""
SELECT s.prefix, s.code, s.title, s.keywords, s.char_count,
s.source_file, s.output_path, s.updated_at,
s.images, s.html_text, f.section_count
FROM rip_help_sections s
LEFT JOIN rip_help_files f
ON f.file_path = s.source_file AND f.prefix = s.prefix
ORDER BY s.prefix, s.code
""")
cols = [c[0] for c in cur.description]
rows = []
for r in cur.fetchall():
d = dict(zip(cols, r))
d["updated_at"] = str(d["updated_at"])[:16] if d["updated_at"] else ""
# парсваме images JSON
try:
d["images"] = json.loads(d["images"]) if d.get("images") else []
except Exception:
d["images"] = []
# прочитаме текста от .txt файла ако съществува
d["text"] = ""
d["text_html"] = "" # file:// — за viewer-а
d["text_html_embed"] = "" # base64 data: — за export (self-contained)
out_dir = Path(d["output_path"]).parent if d.get("output_path") else None
if d.get("output_path") and Path(d["output_path"]).exists():
try:
txt_path = Path(d["output_path"])
raw = txt_path.read_text(encoding="utf-8")
parts = raw.split("─" * 60, 1)
body = parts[1].strip() if len(parts) > 1 else raw
d["text"] = body[:800]
except Exception:
pass
# rich HTML от БД има приоритет; иначе fallback към plain text
if d.get("html_text") and out_dir:
d["text_html"] = _rich_html_with_images(d["html_text"], out_dir, embed=False)
d["text_html_embed"] = _rich_html_with_images(d["html_text"], out_dir, embed=True)
elif out_dir and d["text"]:
d["text_html"] = _text_to_html(d["text"][:1200], out_dir, embed=False)
d["text_html_embed"] = _text_to_html(d["text"], out_dir, embed=True)
rows.append(d)
conn.close()
return rows
def _home_image_data_uri(home_path: Optional[str]) -> Optional[str]:
"""Връща data: URI ако файлът съществува, иначе None."""
if not home_path:
return None
p = Path(home_path).expanduser()
if not p.is_absolute():
p = (Path(__file__).parent / p).resolve()
if not p.is_file():
print(f" [home] файлът не е намерен: {p}", file=sys.stderr)
return None
mime = mimetypes.guess_type(str(p))[0] or "image/png"
b64 = base64.b64encode(p.read_bytes()).decode("ascii")
return f"data:{mime};base64,{b64}"
def build_html(sections, home_image: Optional[str] = None):
data_json = json.dumps(sections, ensure_ascii=False)
generated = datetime.now().strftime("%d.%m.%Y %H:%M")
home_uri = _home_image_data_uri(home_image)
if home_uri:
home_tab_html = '
| Код | Заглавие | Ключови думи | Source файл | Обновен |
|---|