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()