""" 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 = '
00 / Home
' editor_tab_cls = "tab" editor_panel_cls = "panel" home_panel_html = ( '
' f'
Home
' '
' ) tab_index_list = "['home','editor','search','generator']" initial_tab = "home" else: home_tab_html = "" editor_tab_cls = "tab active" editor_panel_cls = "panel active" home_panel_html = "" tab_index_list = "['editor','search','generator']" initial_tab = "editor" return f""" Help Viewer

BG16RFPR001-1.001-0068

| генериран: {generated}
{home_tab_html}
01 / Редактор
02 / Търсене
03 / Генератор
{home_panel_html}
Код Заглавие Ключови думи Source файл Обновен
Няма избрани секции
Избери секции от таб Търсене

ГЕНЕРИРАЙ ДОКУМЕНТ

Подреди секциите с drag & drop преди генериране.

За Word и PDF е нужен Python backend — засега се генерира HTML.
0 промени
""" if __name__ == "__main__": ap = argparse.ArgumentParser(description="Генерира help_viewer.html от БД") ap.add_argument( "--prefix", default=os.getenv("HELP_PREFIX"), help="Филтрира viewer-а по prefix (например 'HLP', 'PROJ_X'). " "Ако липсва, показва всички префикси." ) ap.add_argument( "--out", default=str(OUT_HTML), help=f"Изходен HTML път (default: {OUT_HTML.name})." ) ap.add_argument( "--home", default=None, help="Път към изображение, което да се покаже като Home таб (пръв). " "Ако липсва — няма Home таб (трите стандартни таба остават)." ) args = ap.parse_args() print("Четем от базата данни...") if args.prefix: print(f" Филтър по prefix: {args.prefix}") if args.home: print(f" Home image: {args.home}") try: sections = fetch_sections(prefix=args.prefix) except Exception as e: sys.exit(f"Грешка при свързване с БД: {e}") print(f"Намерени {len(sections)} секции.") html = build_html(sections, home_image=args.home) out_path = Path(args.out) out_path.write_text(html, encoding="utf-8") print(f"Генериран: {out_path}") import webbrowser webbrowser.open(out_path.as_uri()) print("Отворен в браузъра.")