Spaces:
Running
Running
| import os, base64, re, json | |
| DEFAULT_CONTACTS = { | |
| "phone": "+380675745662", | |
| "phone_display": "+38 067 574 56 62", | |
| "tg_link": "https://t.me/unstop_retail", | |
| "tg_display": "Telegram", | |
| "map_iframe": 'https://www.google.com/maps/d/embed?mid=1pn9YLjrvuNJtrxwioRFoS-RC3eAdPAA&ehbc=2E312F&noprof=1', | |
| "address": "м. Харків, Основ'янський район" | |
| } | |
| def read_file(path, fallback=""): | |
| if os.path.exists(path): | |
| with open(path, "r", encoding="utf-8") as f: | |
| return f.read() | |
| return fallback | |
| def img_b64(path): | |
| ext = os.path.splitext(path)[1].lower() | |
| mime = "image/png" if ext == ".png" else "image/jpeg" if ext in [".jpg", ".jpeg"] else "image/webp" | |
| with open(path, "rb") as f: | |
| return f"data:{mime};base64," + base64.b64encode(f.read()).decode() | |
| def _read_config(base_dir): | |
| config_path = os.path.join(base_dir, "config.json") | |
| if os.path.exists(config_path): | |
| with open(config_path, "r", encoding="utf-8") as f: | |
| return json.load(f) | |
| return {"models": [], "contacts": DEFAULT_CONTACTS.copy()} | |
| def _write_config(base_dir, data): | |
| config_path = os.path.join(base_dir, "config.json") | |
| with open(config_path, "w", encoding="utf-8") as f: | |
| json.dump(data, f, ensure_ascii=False, indent=4) | |
| def get_contacts(base_dir): | |
| cfg = _read_config(base_dir) | |
| contacts = cfg.get("contacts", {}) | |
| # Fill missing keys with defaults | |
| for k, v in DEFAULT_CONTACTS.items(): | |
| contacts.setdefault(k, v) | |
| return contacts | |
| def save_contacts(base_dir, contacts: dict): | |
| cfg = _read_config(base_dir) | |
| cfg["contacts"] = contacts | |
| _write_config(base_dir, cfg) | |
| def get_models_data(base_dir): | |
| config_path = os.path.join(base_dir, "config.json") | |
| models_dir = os.path.join(base_dir, "models") | |
| if not os.path.exists(config_path): | |
| models_list = [] | |
| if os.path.exists(models_dir) and os.path.isdir(models_dir): | |
| for i, folder in enumerate(sorted(os.listdir(models_dir))): | |
| folder_path = os.path.join(models_dir, folder) | |
| if os.path.isdir(folder_path): | |
| opis = read_file(os.path.join(folder_path, "Opis.txt"), fallback="") | |
| price = read_file(os.path.join(folder_path, "Price.txt"), fallback="49 000 грн").strip() | |
| subtitle = read_file(os.path.join(folder_path, "Subtitle.txt"), fallback="🔋 Надійна енергія для вашого бізнесу").strip() | |
| models_list.append({ | |
| "id": f"m_{i}", "folder": folder, "name": folder, | |
| "title": folder, "subtitle": subtitle, "price": price, "opis_raw": opis | |
| }) | |
| if not models_list: | |
| models_list.append({ | |
| "id": "m_main", "folder": "", "name": "Базова", | |
| "title": "Unstop Retail 4032W", | |
| "subtitle": "🔋 LiFePO4 • 4032 Wh • 8+ годин автономної роботи", | |
| "price": "49 000 грн", | |
| "opis_raw": read_file(os.path.join(base_dir, "Opis.txt"), fallback="") | |
| }) | |
| _write_config(base_dir, {"models": models_list, "contacts": DEFAULT_CONTACTS.copy()}) | |
| cfg = _read_config(base_dir) | |
| models = cfg.get("models", []) | |
| for m in models: | |
| folder_name = m.get("folder", "") | |
| folder_path = os.path.join(models_dir, folder_name) if folder_name else base_dir | |
| images = [] | |
| if os.path.exists(folder_path): | |
| images = sorted([ | |
| os.path.join(folder_path, img) for img in os.listdir(folder_path) | |
| if img.lower().endswith(('.webp', '.png', '.jpg', '.jpeg')) | |
| ]) | |
| if not images and not folder_name: | |
| candidates = ["image (1).webp", "image (2).webp", "image.webp"] | |
| images = [os.path.join(base_dir, p) for p in candidates if os.path.exists(os.path.join(base_dir, p))] | |
| m["images"] = images | |
| return models | |
| def save_config(base_dir, models_data): | |
| cfg = _read_config(base_dir) | |
| clean_models = [] | |
| for m in models_data: | |
| clean_models.append({ | |
| "id": m.get("id"), "folder": m.get("folder", ""), | |
| "name": m.get("name", ""), "title": m.get("title", ""), | |
| "subtitle": m.get("subtitle", ""), "price": m.get("price", ""), | |
| "opis_raw": m.get("opis_raw", "") | |
| }) | |
| cfg["models"] = clean_models | |
| _write_config(base_dir, cfg) | |
| def opis_to_html(text): | |
| if not text: return "<p>Опис не знайдено.</p>" | |
| lines, parts, in_tbl, tbuf = text.split("\n"), [], False, [] | |
| for line in lines: | |
| if line.startswith("## "): parts.append(f"<h2>{line[3:].strip()}</h2>"); continue | |
| if line.startswith("### "): parts.append(f"<h3>{line[4:].strip()}</h3>"); continue | |
| if line.strip() in ("---", "***", "___"): parts.append("<hr>"); continue | |
| if "|" in line and line.strip().startswith("|"): | |
| if not in_tbl: in_tbl, tbuf = True, [] | |
| tbuf.append(line); continue | |
| if in_tbl: | |
| parts.append(_tbl(tbuf)); in_tbl = False | |
| if line.startswith("> "): | |
| parts.append(f'<blockquote>{line[2:].strip()}</blockquote>') | |
| elif line.strip(): | |
| l = re.sub(r"\*\*(.+?)\*\*", r"<strong>\1</strong>", line) | |
| l = re.sub(r"\*(.+?)\*", r"<em>\1</em>", l) | |
| if l.strip().startswith("- "): | |
| parts.append(f"<li>{l.strip()[2:]}</li>") | |
| else: | |
| parts.append(f"<p>{l.strip()}</p>") | |
| else: | |
| parts.append("") | |
| if in_tbl: parts.append(_tbl(tbuf)) | |
| res = "\n".join(parts) | |
| return re.sub(r"((?:<li>.*?</li>\n?)+)", r"<ul>\1</ul>", res) | |
| def _tbl(rows): | |
| html_tbl = "<table>" | |
| for i, row in enumerate(rows): | |
| cells = [c.strip() for c in row.strip().strip("|").split("|")] | |
| if all(set(c) <= set("-: ") for c in cells): continue | |
| tag = "th" if i == 0 else "td" | |
| html_tbl += "<tr>" + "".join(f"<{tag}>{c}</{tag}>" for c in cells) + "</tr>" | |
| return html_tbl + "</table>" | |