Andrey Epifancev 801d9948ca synthgen: каркас, настройки, копии модулей #35 (#38)
- synthgen/config.py: Config, load_config (YAML + --set), dump_config, проверки диапазонов §3
- synthgen/rng.py: rng_for (SHA-256 → Random), stable_hash
- synthgen/__main__.py: разбор --config/--set/--out/--force, ошибка конфигурации → код 2
- synthgen/examples/default.yaml: все ключи §3 со значениями по умолчанию
- копии из synthetic-group/gen (#35): inn, xlsx, ledger, chart (+80.09, 91.09 из seed arm-core)
- тесты: config, inn, ledger; conftest с маркерами parser/slow
- .gitignore: out/
- спека: решения 15–16

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
2026-11-12 23:35:17 +04:00

38 lines
1.5 KiB
Python

"""Источник: synthetic-group/gen/xlsx.py (#35). Детерминированное сохранение xlsx: фиксированные метки книги и записей zip."""
import datetime
import io
import re
import zipfile
from pathlib import Path
import openpyxl
FIXED_TIME = datetime.datetime(2026, 1, 1, 0, 0, 0)
ZIP_DATE_TIME = (2026, 1, 1, 0, 0, 0)
CREATOR = "arm-ka synthetic"
_FIXED_W3CDTF = FIXED_TIME.strftime("%Y-%m-%dT%H:%M:%SZ").encode()
_CORE_PART = "docProps/core.xml"
_MODIFIED_RE = re.compile(rb"(<dcterms:modified[^>]*>)[^<]*(</dcterms:modified>)")
def save_deterministic(wb: openpyxl.Workbook, path: Path) -> None:
wb.properties.creator = CREATOR
wb.properties.lastModifiedBy = CREATOR
wb.properties.created = FIXED_TIME
wb.properties.modified = FIXED_TIME
raw = io.BytesIO()
wb.save(raw)
out = io.BytesIO()
with zipfile.ZipFile(raw) as source, zipfile.ZipFile(out, "w", zipfile.ZIP_DEFLATED) as target:
for info in source.infolist():
clone = zipfile.ZipInfo(info.filename, date_time=ZIP_DATE_TIME)
clone.compress_type = zipfile.ZIP_DEFLATED
clone.external_attr = info.external_attr
content = source.read(info.filename)
if info.filename == _CORE_PART:
content = _MODIFIED_RE.sub(rb"\g<1>" + _FIXED_W3CDTF + rb"\g<2>", content)
target.writestr(clone, content)
path.parent.mkdir(parents=True, exist_ok=True)
path.write_bytes(out.getvalue())