feat(mxl-compile,mxl-decompile,mxl-validate): группы строк и колонок

Документные ключи rowGroups и columnGroups описывают сворачиваемые
группы: диапазон, имя, признак свёрнутости и расположение заголовка.

Форма плоская, как в формате: вложенность выражена вхождением одного
диапазона в другой. Дерево было бы читаемее, но платформа хранит плоско,
и для правки чужого макета дерево дороже — чтобы добавить группу внутрь
существующей, пришлось бы искать родителя. Частичных пересечений
платформа не порождает (корпус: 40 620 886 пар непересекающихся,
599 958 вложенных, ни одного частичного), поэтому компилятор их
отвергает.

Число уровней вложенности не задаётся: оно совпало с <vgLevels> у всех
1797 макетов корпуса. Порядок записи компилятор приводит к
платформенному — родитель раньше вложенных, по возрастанию начала
(совпало у 1798 макетов из 1798).

Имя группы вывести нельзя, хотя 143 455 имён корпуса выглядят как
автоматические R<номер строки>: 16 666 из них протухли после вставки
строк, а 10 287 групп имени не имеют вовсе. Расположение заголовка взяло
ключ titleLocation — то же свойство и то же имя, что в DSL форм.

mxl-validate: проверка 14 — перевёрнутый диапазон, частичное пересечение,
начало группы за пределами документа.

Стенд Группировки собирается байт в байт обоими портами и добавлен
в платформенные фикстуры.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Nick Shirokov
2026-08-15 15:50:18 +03:00
co-authored by Claude Opus 5
parent 4005f36cce
commit 1e0b82d419
19 changed files with 1173 additions and 13 deletions
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
# mxl-decompile v1.23 — Decompile 1C spreadsheet to JSON
# mxl-decompile v1.24 — Decompile 1C spreadsheet to JSON
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
@@ -431,6 +431,36 @@ def main():
return None
return raw_formats[idx - 1]
# --- 4b. Группировки строк и колонок ---
# Платформа хранит их плоским списком диапазонов, родитель раньше детей. Число уровней
# (<vgLevels>) не читаем: оно выводится из вложенности — совпало на всех 1797 макетах.
def read_groups(tag, axis):
out = []
for g in findall(root, "d:" + tag):
b = int_of(find(g, "d:b"))
e_node = find(g, "d:e")
e = int_of(e_node, b) if e_node is not None else b
item = OrderedDict()
item[axis] = (b + 1) if e == b else f'{b + 1}-{e + 1}'
t_el = find(g, "d:t")
if t_el is not None:
by_lang = OrderedDict()
for it in findall(t_el, "v8:item"):
lang = text_of(find(it, "v8:lang")) or ''
by_lang[lang] = text_of(find(it, "v8:content")) or ''
if by_lang:
item["name"] = by_lang
if (text_of(find(g, "d:o")) or 'true').strip() == 'false':
item["collapsed"] = True
place = text_of(find(g, "d:g"))
if place:
item["titleLocation"] = place.strip().lower()
out.append(item)
return out
row_groups = read_groups("vg", "rows")
col_groups = read_groups("hg", "cols")
# --- 5. Extract columns and default width ---
# Колоночная раскладка («индивидуальная ширина колонок» для группы строк) — элемент <columns>.
@@ -1433,6 +1463,15 @@ def main():
sets_out[cs["Id"]] = entry
result["columnSets"] = sets_out
# Имя группировки — та же мультиязычная строка, что текст ячейки, и сворачивается так же.
for _g in row_groups + col_groups:
if "name" in _g:
_g["name"] = get_dsl_text(_g["name"])
if row_groups:
result["rowGroups"] = row_groups
if col_groups:
result["columnGroups"] = col_groups
result["areas"] = dsl_areas
# Именованные области, не выразимые блоком, — координатами. Тип не пишем: он выводится