Initial SQL-only 1C adapter baseline
This commit is contained in:
@@ -0,0 +1,403 @@
|
||||
"""Semantic profiles for 1C managed form XML exports."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import xml.etree.ElementTree as ET
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
|
||||
FORM_XML_KIND_RU = {
|
||||
"Form": "Форма",
|
||||
"Event": "Событие",
|
||||
"Attribute": "Реквизит формы",
|
||||
"Column": "Колонка реквизита",
|
||||
"Command": "Команда формы",
|
||||
"AutoCommandBar": "Командная панель",
|
||||
"Button": "Кнопка",
|
||||
"ButtonGroup": "Группа кнопок",
|
||||
"CommandBar": "Командная панель",
|
||||
"Popup": "Подменю",
|
||||
"Pages": "Страницы",
|
||||
"ColumnGroup": "Группа колонок",
|
||||
"LabelField": "Поле надписи",
|
||||
"InputField": "Поле ввода",
|
||||
"CheckBoxField": "Поле флажка",
|
||||
"Table": "Таблица",
|
||||
"TableColumn": "Колонка таблицы",
|
||||
"Page": "Страница",
|
||||
"UsualGroup": "Группа",
|
||||
"DecorativeLabel": "Декорация надпись",
|
||||
"ContextMenu": "Контекстное меню",
|
||||
"ExtendedTooltip": "Расширенная подсказка",
|
||||
"SearchStringAddition": "Дополнение строки поиска",
|
||||
"ViewStatusAddition": "Дополнение состояния просмотра",
|
||||
"SearchControlAddition": "Дополнение управления поиском",
|
||||
}
|
||||
|
||||
FORM_XML_PROPERTY_NAMES = {
|
||||
"name": "Имя",
|
||||
"id": "Идентификатор",
|
||||
"Title": "Заголовок",
|
||||
"Type": "Вид",
|
||||
"DataPath": "ПутьКДанным",
|
||||
"TitleLocation": "ПоложениеЗаголовка",
|
||||
"Visible": "Видимость",
|
||||
"UserVisible": "ПользовательскаяВидимость",
|
||||
"Enabled": "Доступность",
|
||||
"ReadOnly": "ТолькоПросмотр",
|
||||
"SkipOnInput": "ПропускатьПриВводе",
|
||||
"DefaultItem": "АктивизироватьПоУмолчанию",
|
||||
"Importance": "ВажностьПриОтображении",
|
||||
"ServerUnavailabilityBehavior": "ПоведениеПриНедоступностиОсновногоСервера",
|
||||
"PasswordMode": "РежимПароля",
|
||||
"Hyperlink": "Гиперссылка",
|
||||
"CommandName": "ИмяКоманды",
|
||||
"WindowOpeningMode": "РежимОткрытияОкна",
|
||||
"AutoSaveDataInSettings": "АвтоСохранениеДанныхВНастройках",
|
||||
"CommandBarLocation": "ПоложениеКоманднойПанели",
|
||||
"ShowCommandBar": "ОтображатьКоманднуюПанель",
|
||||
"AutoCommandBar": "АвтоКоманднаяПанель",
|
||||
"SearchStringAddition": "ДополнениеСтрокиПоиска",
|
||||
"ViewStatusAddition": "ДополнениеСостоянияПросмотра",
|
||||
"SearchControlAddition": "ДополнениеУправленияПоиском",
|
||||
"Representation": "Отображение",
|
||||
"ChangeRowSet": "ИзменятьСоставСтрок",
|
||||
"RowSelectionMode": "РежимВыделенияСтроки",
|
||||
"HorizontalLinesBWA": "ГоризонтальныеЛинии",
|
||||
"VerticalLinesBWA": "ВертикальныеЛинии",
|
||||
"UseAlternationRowColorBWA": "ЧередованиеЦветовСтрок",
|
||||
"AutoInsertNewRow": "АвтоВставкаНовойСтроки",
|
||||
"EnableStartDrag": "РазрешитьНачалоПеретаскивания",
|
||||
"EnableDrag": "РазрешитьПеретаскивание",
|
||||
"RowFilter": "ОтборСтрок",
|
||||
"HeightInTableRows": "ВысотаВСтрокахТаблицы",
|
||||
"Footer": "Подвал",
|
||||
"FileDragMode": "РежимПеретаскиванияФайлов",
|
||||
"SearchStringLocation": "ПоложениеСтрокиПоиска",
|
||||
"ViewStatusLocation": "ПоложениеСостоянияПросмотра",
|
||||
"SearchControlLocation": "ПоложениеУправленияПоиском",
|
||||
"ButtonGroup": "СоставКоманд",
|
||||
"Group": "Группировка",
|
||||
"Behavior": "Поведение",
|
||||
"ShowTitle": "ПоказыватьЗаголовок",
|
||||
"Autofill": "Автозаполнение",
|
||||
"EditWarningRepresentation": "ОтображениеПредупрежденияПриРедактировании",
|
||||
"EditWarning": "ПредупреждениеПриРедактировании",
|
||||
"EditMode": "РежимРедактирования",
|
||||
"AutoEditMode": "АвтоРежимРедактирования",
|
||||
"AutoCellHeight": "АвтоВысотаЯчейки",
|
||||
"FixInTable": "ФиксацияВТаблице",
|
||||
"Shortcut": "СочетаниеКлавиш",
|
||||
"CommandSet": "СоставКоманд",
|
||||
"UseCopy": "ИспользоватьКопирование",
|
||||
"ShowInHeader": "ОтображатьВШапке",
|
||||
"ShowInFooter": "ОтображатьВПодвале",
|
||||
"ToolTip": "Подсказка",
|
||||
"ToolTipRepresentation": "ОтображениеПодсказки",
|
||||
"DropListButton": "КнопкаВыпадающегоСписка",
|
||||
"ChoiceButton": "КнопкаВыбора",
|
||||
"ClearButton": "КнопкаОчистки",
|
||||
"SpinButton": "КнопкаРегулирования",
|
||||
"OpenButton": "КнопкаОткрытия",
|
||||
"CreateButton": "КнопкаСоздания",
|
||||
"QuickChoice": "БыстрыйВыбор",
|
||||
"ChooseType": "ВыбиратьТип",
|
||||
"ChoiceList": "СписокВыбора",
|
||||
"IncompleteChoiceMode": "РежимВыбораНезаполненного",
|
||||
"TextEdit": "РедактированиеТекста",
|
||||
"TextEditUpdate": "ОбновлениеТекстаРедактирования",
|
||||
"MultiLine": "МногострочныйРежим",
|
||||
"AutoLineBreak": "АвтоПереносСтрок",
|
||||
"AutoMarkIncomplete": "АвтоОтметкаНезаполненного",
|
||||
"AutoChoiceIncomplete": "АвтоВыборНезаполненного",
|
||||
"InputHint": "ПодсказкаВвода",
|
||||
"ChoiceHistoryOnInput": "ИсторияВыбораПриВводе",
|
||||
"Picture": "Картинка",
|
||||
"HeaderPicture": "КартинкаШапки",
|
||||
"FooterPicture": "КартинкаПодвала",
|
||||
"BackColor": "ЦветФона",
|
||||
"TextColor": "ЦветТекста",
|
||||
"BorderColor": "ЦветРамки",
|
||||
"Font": "Шрифт",
|
||||
"Shape": "Фигура",
|
||||
"ShapeRepresentation": "ОтображениеФигуры",
|
||||
"PictureLocation": "ПоложениеКартинки",
|
||||
"HeaderBackColor": "ЦветФонаЗаголовка",
|
||||
"Width": "Ширина",
|
||||
"Height": "Высота",
|
||||
"AutoMaxWidth": "АвтоМаксимальнаяШирина",
|
||||
"AutoMaxHeight": "АвтоМаксимальнаяВысота",
|
||||
"MaxWidth": "МаксимальнаяШирина",
|
||||
"MaxHeight": "МаксимальнаяВысота",
|
||||
"TitleHeight": "ВысотаЗаголовка",
|
||||
"HorizontalAlign": "ГоризонтальноеПоложениеВГруппе",
|
||||
"VerticalAlign": "ВертикальноеПоложениеВГруппе",
|
||||
"HorizontalStretch": "РастягиватьПоГоризонтали",
|
||||
"VerticalStretch": "РастягиватьПоВертикали",
|
||||
"LocationInCommandBar": "ПоложениеВКоманднойПанели",
|
||||
"UniqueCommands": "УникальностьКоманд",
|
||||
"DefaultButton": "КнопкаПоУмолчанию",
|
||||
"Check": "Пометка",
|
||||
"MainAttribute": "ОсновнойРеквизит",
|
||||
"Action": "Действие",
|
||||
"AdditionSource": "ИсточникДополнения",
|
||||
"Columns": "Колонки",
|
||||
"Save": "СохраняемыеДанные",
|
||||
"ContextMenu": "КонтекстноеМеню",
|
||||
"ExtendedTooltip": "РасширеннаяПодсказка",
|
||||
"Events": "События",
|
||||
"Handler": "Обработчик",
|
||||
"ChildItems": "ПодчиненныеЭлементы",
|
||||
}
|
||||
|
||||
FORM_XML_PROPERTY_GROUPS = {
|
||||
"Основные": {
|
||||
"name",
|
||||
"id",
|
||||
"Title",
|
||||
"Type",
|
||||
"DataPath",
|
||||
"CommandName",
|
||||
"WindowOpeningMode",
|
||||
"AutoSaveDataInSettings",
|
||||
"CommandBarLocation",
|
||||
"ShowCommandBar",
|
||||
"AutoCommandBar",
|
||||
"SearchStringAddition",
|
||||
"ViewStatusAddition",
|
||||
"SearchControlAddition",
|
||||
"TitleLocation",
|
||||
"Visible",
|
||||
"UserVisible",
|
||||
"Enabled",
|
||||
"ReadOnly",
|
||||
"SkipOnInput",
|
||||
"DefaultItem",
|
||||
"Importance",
|
||||
"ServerUnavailabilityBehavior",
|
||||
"PasswordMode",
|
||||
"Hyperlink",
|
||||
"Representation",
|
||||
"ButtonGroup",
|
||||
"Group",
|
||||
"Behavior",
|
||||
"ShowTitle",
|
||||
"Autofill",
|
||||
},
|
||||
"Использование": {
|
||||
"EditWarningRepresentation",
|
||||
"EditWarning",
|
||||
"EditMode",
|
||||
"AutoEditMode",
|
||||
"AutoCellHeight",
|
||||
"FixInTable",
|
||||
"Shortcut",
|
||||
"CommandSet",
|
||||
"UseCopy",
|
||||
"ChangeRowSet",
|
||||
"RowSelectionMode",
|
||||
"AutoInsertNewRow",
|
||||
"EnableStartDrag",
|
||||
"EnableDrag",
|
||||
"RowFilter",
|
||||
"FileDragMode",
|
||||
"ShowInHeader",
|
||||
"ShowInFooter",
|
||||
"ToolTip",
|
||||
"ToolTipRepresentation",
|
||||
"DropListButton",
|
||||
"ChoiceButton",
|
||||
"ClearButton",
|
||||
"SpinButton",
|
||||
"OpenButton",
|
||||
"CreateButton",
|
||||
"QuickChoice",
|
||||
"ChooseType",
|
||||
"ChoiceList",
|
||||
"IncompleteChoiceMode",
|
||||
"TextEdit",
|
||||
"TextEditUpdate",
|
||||
"MultiLine",
|
||||
"AutoLineBreak",
|
||||
"AutoMarkIncomplete",
|
||||
"AutoChoiceIncomplete",
|
||||
"InputHint",
|
||||
"ChoiceHistoryOnInput",
|
||||
"MainAttribute",
|
||||
"Action",
|
||||
"Save",
|
||||
},
|
||||
"Оформление": {
|
||||
"Picture",
|
||||
"HeaderPicture",
|
||||
"FooterPicture",
|
||||
"BackColor",
|
||||
"TextColor",
|
||||
"BorderColor",
|
||||
"Font",
|
||||
"Shape",
|
||||
"ShapeRepresentation",
|
||||
"PictureLocation",
|
||||
"HeaderBackColor",
|
||||
"Footer",
|
||||
"HorizontalLinesBWA",
|
||||
"VerticalLinesBWA",
|
||||
"UseAlternationRowColorBWA",
|
||||
},
|
||||
"Расположение": {
|
||||
"Width",
|
||||
"Height",
|
||||
"HeightInTableRows",
|
||||
"SearchStringLocation",
|
||||
"ViewStatusLocation",
|
||||
"SearchControlLocation",
|
||||
"AutoMaxWidth",
|
||||
"AutoMaxHeight",
|
||||
"MaxWidth",
|
||||
"MaxHeight",
|
||||
"TitleHeight",
|
||||
"HorizontalAlign",
|
||||
"VerticalAlign",
|
||||
"HorizontalStretch",
|
||||
"VerticalStretch",
|
||||
"LocationInCommandBar",
|
||||
"UniqueCommands",
|
||||
"DefaultButton",
|
||||
"Check",
|
||||
"AdditionSource",
|
||||
"Columns",
|
||||
},
|
||||
}
|
||||
|
||||
FORM_XML_CONTAINER_TAGS = {"ChildItems", "CommandSet", "Events"}
|
||||
|
||||
|
||||
def local_name(tag: str) -> str:
|
||||
return tag.rsplit("}", 1)[-1]
|
||||
|
||||
|
||||
def property_group(tag: str) -> str:
|
||||
for group, names in FORM_XML_PROPERTY_GROUPS.items():
|
||||
if tag in names:
|
||||
return group
|
||||
return "Прочее"
|
||||
|
||||
|
||||
def xml_scalar(element: ET.Element) -> Any:
|
||||
text = (element.text or "").strip()
|
||||
if text:
|
||||
if text == "true":
|
||||
return True
|
||||
if text == "false":
|
||||
return False
|
||||
return text
|
||||
if "name" in element.attrib:
|
||||
return element.attrib.get("name")
|
||||
if "id" in element.attrib:
|
||||
return element.attrib.get("id")
|
||||
return None
|
||||
|
||||
|
||||
def add_property(groups: dict[str, list[dict[str, Any]]], xml_name: str, value: Any, *, source: str = "form_xml") -> None:
|
||||
if value is None:
|
||||
return
|
||||
group = property_group(xml_name)
|
||||
groups.setdefault(group, []).append(
|
||||
{
|
||||
"name": FORM_XML_PROPERTY_NAMES.get(xml_name, xml_name),
|
||||
"xml_name": xml_name,
|
||||
"value": value,
|
||||
"source": source,
|
||||
"status": "ok" if value not in {"", None} else "empty",
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def element_semantic(element: ET.Element) -> dict[str, Any]:
|
||||
tag = local_name(element.tag)
|
||||
groups: dict[str, list[dict[str, Any]]] = {}
|
||||
add_property(groups, "name", element.attrib.get("name"), source="form_xml_attribute")
|
||||
add_property(groups, "id", element.attrib.get("id"), source="form_xml_attribute")
|
||||
add_property(groups, "Type", FORM_XML_KIND_RU.get(tag, tag), source="form_xml_tag")
|
||||
if tag == "Event" and (element.text or "").strip():
|
||||
add_property(groups, "Handler", (element.text or "").strip(), source="form_xml_text")
|
||||
for child in list(element):
|
||||
child_tag = local_name(child.tag)
|
||||
if child_tag in FORM_XML_CONTAINER_TAGS:
|
||||
continue
|
||||
add_property(groups, child_tag, xml_scalar(child))
|
||||
mapped = sum(len(items) for items in groups.values())
|
||||
return {
|
||||
"groups": groups,
|
||||
"coverage": {
|
||||
"mapped": mapped,
|
||||
"unmapped": 0,
|
||||
"total": mapped,
|
||||
"status": "ok",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def walk_form_elements(root: ET.Element, *, limit: int = 5000) -> tuple[list[dict[str, Any]], int, bool]:
|
||||
records: list[dict[str, Any]] = []
|
||||
total = 0
|
||||
|
||||
def walk(
|
||||
element: ET.Element,
|
||||
path: list[int],
|
||||
additional_columns_table: str | None = None,
|
||||
owner_name: str | None = None,
|
||||
) -> None:
|
||||
nonlocal total
|
||||
tag = local_name(element.tag)
|
||||
if tag == "AdditionalColumns" and element.attrib.get("table"):
|
||||
additional_columns_table = element.attrib.get("table")
|
||||
if "name" in element.attrib:
|
||||
total += 1
|
||||
if len(records) < limit:
|
||||
records.append(
|
||||
{
|
||||
"name": element.attrib.get("name"),
|
||||
"id": element.attrib.get("id"),
|
||||
"kind": tag,
|
||||
"kind_ru": FORM_XML_KIND_RU.get(tag, tag),
|
||||
"xml_path": ".".join(str(part) for part in path),
|
||||
"semantic": element_semantic(element),
|
||||
**({"additional_columns_table": additional_columns_table} if additional_columns_table else {}),
|
||||
**({"owner": owner_name} if tag == "Event" and owner_name else {}),
|
||||
}
|
||||
)
|
||||
child_owner = element.attrib.get("name") if "name" in element.attrib and tag != "Event" else owner_name
|
||||
for index, child in enumerate(list(element)):
|
||||
walk(child, [*path, index], additional_columns_table, child_owner)
|
||||
|
||||
walk(root, [])
|
||||
return records, total, total > len(records)
|
||||
|
||||
|
||||
def decode_form_xml(xml: str | bytes | Path, *, max_items: int = 5000) -> dict[str, Any]:
|
||||
if isinstance(xml, Path):
|
||||
root = ET.parse(xml).getroot()
|
||||
source = {"kind": "xml_file", "path": str(xml)}
|
||||
else:
|
||||
root = ET.fromstring(xml)
|
||||
source = {"kind": "xml_text"}
|
||||
items, total, truncated = walk_form_elements(root, limit=max_items)
|
||||
return {
|
||||
"schema": "onec_form_xml_profile.v1",
|
||||
"status": "ok",
|
||||
"source": source,
|
||||
"form": {
|
||||
"version": root.attrib.get("version"),
|
||||
"kind": local_name(root.tag),
|
||||
"kind_ru": FORM_XML_KIND_RU.get(local_name(root.tag), local_name(root.tag)),
|
||||
"semantic": element_semantic(root),
|
||||
},
|
||||
"items": items,
|
||||
"counts": {
|
||||
"items": len(items),
|
||||
"items_total": total,
|
||||
"items_truncated": truncated,
|
||||
},
|
||||
}
|
||||
Reference in New Issue
Block a user