from __future__ import annotations import datetime as dt import hashlib import json from pathlib import Path from openpyxl import load_workbook SOURCE_DIR = Path(r"C:\Users\rblbk\Downloads") OUTPUT = Path(r"S:\EVENT TECH\wtchat-event-tech-tilda-block.html") SOURCE_CANDIDATES = [ "WTChat_event_tech_clean_final_table.xlsx", "WTChat_пересобранная_таблица_оценки_event_tech_FIXED.xlsx", ] def clean_value(value): if value is None: return "" if isinstance(value, (dt.datetime, dt.date)): return value.isoformat() return value def row_values(ws, row_idx: int): return [clean_value(ws.cell(row_idx, col_idx).value) for col_idx in range(1, ws.max_column + 1)] def detect_header_row(ws) -> int: num_column = "\u2116" site_column = "\u0421\u0430\u0439\u0442" solution_columns = {"\u0420\u0435\u0448\u0435\u043d\u0438\u0435", "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435"} for row_idx in range(1, min(ws.max_row, 12) + 1): values = [str(value).strip() for value in row_values(ws, row_idx) if value not in ("", None)] if num_column in values and site_column in values and any(column in values for column in solution_columns): return row_idx return 1 def build_payload(source: Path): wb = load_workbook(source, data_only=True, read_only=False) sheets = [] for ws in wb.worksheets: header_row = detect_header_row(ws) original_headers = row_values(ws, header_row) headers = [str(value) if value != "" else f"Column {idx + 1}" for idx, value in enumerate(original_headers)] raw_rows = [] for row_idx in range(1, ws.max_row + 1): raw_rows.append({"rowNumber": row_idx, "values": row_values(ws, row_idx)}) records = [] for row_idx in range(header_row + 1, ws.max_row + 1): values = row_values(ws, row_idx) if any(value != "" for value in values): records.append( { "rowNumber": row_idx, "cells": {headers[idx]: values[idx] if idx < len(values) else "" for idx in range(len(headers))}, "values": values, } ) sheets.append( { "name": ws.title, "rowCount": ws.max_row, "columnCount": ws.max_column, "headerRow": header_row, "headers": headers, "originalHeaders": original_headers, "prefaceRows": raw_rows[: header_row - 1], "rawRows": raw_rows, "records": records, } ) stable_payload = { "sourceFileName": source.name, "sourceFileSize": source.stat().st_size, "sourceModified": dt.datetime.fromtimestamp(source.stat().st_mtime).isoformat(), "sheets": sheets, } payload = {**stable_payload, "generatedAt": dt.datetime.now().isoformat()} raw = json.dumps(stable_payload, ensure_ascii=False, separators=(",", ":")) payload["dataHash"] = hashlib.sha256(raw.encode("utf-8")).hexdigest()[:12] return payload HTML_TEMPLATE = r'''

WTChat / Event Tech

Рабочая таблица оценки event tech

Интерфейс для просмотра, фильтрации, локального редактирования и экспорта данных из Excel без backend.

0всего строк
0отфильтровано
0высокий приоритет
0на проверке / частично
Изменений нет
Колонки
''' def main(): matches = [SOURCE_DIR / name for name in SOURCE_CANDIDATES if (SOURCE_DIR / name).exists()] if not matches: matches = sorted(SOURCE_DIR.glob("WTChat*event*tech*.xlsx")) if not matches: raise FileNotFoundError("XLSX source file was not found in Downloads") source = matches[0] payload = build_payload(source) encoded = json.dumps(payload, ensure_ascii=False, separators=(",", ":")) OUTPUT.write_text(HTML_TEMPLATE.replace("__WTCHAT_DATA__", encoded), encoding="utf-8") print(OUTPUT) print(f"bytes={OUTPUT.stat().st_size}") print(f"sheets={', '.join(sheet['name'] for sheet in payload['sheets'])}") print(f"hash={payload['dataHash']}") if __name__ == "__main__": main()