@josephyan/qingflow-app-user-mcp 0.2.0-beta.91 → 0.2.0-beta.93
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/src/qingflow_mcp/__init__.py +33 -1
- package/src/qingflow_mcp/builder_facade/service.py +228 -12
package/README.md
CHANGED
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
Install:
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
|
-
npm install @josephyan/qingflow-app-user-mcp@0.2.0-beta.
|
|
6
|
+
npm install @josephyan/qingflow-app-user-mcp@0.2.0-beta.93
|
|
7
7
|
```
|
|
8
8
|
|
|
9
9
|
Run:
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
|
-
npx -y -p @josephyan/qingflow-app-user-mcp@0.2.0-beta.
|
|
12
|
+
npx -y -p @josephyan/qingflow-app-user-mcp@0.2.0-beta.93 qingflow-app-user-mcp
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
Environment:
|
package/package.json
CHANGED
package/pyproject.toml
CHANGED
|
@@ -1,5 +1,37 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
from importlib.metadata import PackageNotFoundError, packages_distributions, version as _dist_version
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
3
6
|
__all__ = ["__version__"]
|
|
4
7
|
|
|
5
|
-
|
|
8
|
+
_FALLBACK_VERSION = "0.2.0b93"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _resolve_local_pyproject_version() -> str | None:
|
|
12
|
+
module_path = Path(__file__).resolve()
|
|
13
|
+
for parent in module_path.parents:
|
|
14
|
+
candidate = parent / "pyproject.toml"
|
|
15
|
+
if not candidate.is_file():
|
|
16
|
+
continue
|
|
17
|
+
for line in candidate.read_text(encoding="utf-8").splitlines():
|
|
18
|
+
stripped = line.strip()
|
|
19
|
+
if stripped.startswith("version = "):
|
|
20
|
+
return stripped.split("=", 1)[1].strip().strip('"')
|
|
21
|
+
break
|
|
22
|
+
return None
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _resolve_runtime_version() -> str:
|
|
26
|
+
local_version = _resolve_local_pyproject_version()
|
|
27
|
+
if local_version:
|
|
28
|
+
return local_version
|
|
29
|
+
for dist_name in packages_distributions().get("qingflow_mcp", []):
|
|
30
|
+
try:
|
|
31
|
+
return _dist_version(dist_name)
|
|
32
|
+
except PackageNotFoundError:
|
|
33
|
+
continue
|
|
34
|
+
return _FALLBACK_VERSION
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
__version__ = _resolve_runtime_version()
|
|
@@ -9865,8 +9865,9 @@ def _parse_field(question: dict[str, Any], *, field_id_hint: str | None = None)
|
|
|
9865
9865
|
"que_id": que_id,
|
|
9866
9866
|
"que_type": que_type,
|
|
9867
9867
|
"default_type": _coerce_positive_int(question.get("queDefaultType")) if "queDefaultType" in question else None,
|
|
9868
|
-
"default_value": question.get("queDefaultValue") if "queDefaultValue" in question else None,
|
|
9869
9868
|
}
|
|
9869
|
+
if "queDefaultValue" in question:
|
|
9870
|
+
field["default_value"] = question.get("queDefaultValue")
|
|
9870
9871
|
if field_type in {FieldType.single_select.value, FieldType.multi_select.value, FieldType.boolean.value}:
|
|
9871
9872
|
options = question.get("options")
|
|
9872
9873
|
if isinstance(options, list):
|
|
@@ -12753,6 +12754,176 @@ def _field_needs_question_rebuild(field: dict[str, Any]) -> bool:
|
|
|
12753
12754
|
return not isinstance(field.get("_question_template"), dict) or bool(field.get("_question_rebuild_required"))
|
|
12754
12755
|
|
|
12755
12756
|
|
|
12757
|
+
def _extract_template_row_lengths(schema: dict[str, Any]) -> tuple[dict[int, int], dict[str, int]]:
|
|
12758
|
+
lengths_by_que_id: dict[int, int] = {}
|
|
12759
|
+
lengths_by_title: dict[str, int] = {}
|
|
12760
|
+
|
|
12761
|
+
def remember_row(row: Any) -> None:
|
|
12762
|
+
if not isinstance(row, list):
|
|
12763
|
+
return
|
|
12764
|
+
questions = [question for question in row if isinstance(question, dict)]
|
|
12765
|
+
row_length = len(questions)
|
|
12766
|
+
if row_length <= 0:
|
|
12767
|
+
return
|
|
12768
|
+
for question in questions:
|
|
12769
|
+
que_id = _coerce_nonnegative_int(question.get("queId"))
|
|
12770
|
+
if que_id is not None:
|
|
12771
|
+
lengths_by_que_id[que_id] = row_length
|
|
12772
|
+
title = str(question.get("queTitle") or "").strip()
|
|
12773
|
+
if title:
|
|
12774
|
+
lengths_by_title[title] = row_length
|
|
12775
|
+
|
|
12776
|
+
for row in schema.get("formQues", []) or []:
|
|
12777
|
+
if not isinstance(row, list):
|
|
12778
|
+
continue
|
|
12779
|
+
if len(row) == 1 and isinstance(row[0], dict) and _coerce_positive_int(row[0].get("queType")) == 24:
|
|
12780
|
+
for inner_row in row[0].get("innerQuestions", []) or []:
|
|
12781
|
+
remember_row(inner_row)
|
|
12782
|
+
continue
|
|
12783
|
+
remember_row(row)
|
|
12784
|
+
return lengths_by_que_id, lengths_by_title
|
|
12785
|
+
|
|
12786
|
+
|
|
12787
|
+
def _field_template_row_length(
|
|
12788
|
+
field: dict[str, Any],
|
|
12789
|
+
*,
|
|
12790
|
+
lengths_by_que_id: dict[int, int],
|
|
12791
|
+
lengths_by_title: dict[str, int],
|
|
12792
|
+
) -> int | None:
|
|
12793
|
+
que_id = _coerce_nonnegative_int(field.get("que_id"))
|
|
12794
|
+
if que_id is not None and que_id in lengths_by_que_id:
|
|
12795
|
+
return lengths_by_que_id[que_id]
|
|
12796
|
+
template = field.get("_question_template")
|
|
12797
|
+
if isinstance(template, dict):
|
|
12798
|
+
template_que_id = _coerce_nonnegative_int(template.get("queId"))
|
|
12799
|
+
if template_que_id is not None and template_que_id in lengths_by_que_id:
|
|
12800
|
+
return lengths_by_que_id[template_que_id]
|
|
12801
|
+
template_title = str(template.get("queTitle") or "").strip()
|
|
12802
|
+
if template_title and template_title in lengths_by_title:
|
|
12803
|
+
return lengths_by_title[template_title]
|
|
12804
|
+
field_name = str(field.get("name") or "").strip()
|
|
12805
|
+
if field_name and field_name in lengths_by_title:
|
|
12806
|
+
return lengths_by_title[field_name]
|
|
12807
|
+
return None
|
|
12808
|
+
|
|
12809
|
+
|
|
12810
|
+
def _row_needs_width_reflow(expected_template_lengths: list[int], current_row_length: int) -> bool:
|
|
12811
|
+
if current_row_length <= 0:
|
|
12812
|
+
return False
|
|
12813
|
+
return any(length != current_row_length for length in expected_template_lengths)
|
|
12814
|
+
|
|
12815
|
+
|
|
12816
|
+
_FORM_SAVE_BASE_KEYS = (
|
|
12817
|
+
"formDesc",
|
|
12818
|
+
"formTheme",
|
|
12819
|
+
"formAttach",
|
|
12820
|
+
"formStyle",
|
|
12821
|
+
"serialNumType",
|
|
12822
|
+
"serialNumConfig",
|
|
12823
|
+
"attachVisibleOnlyConfig",
|
|
12824
|
+
"externalLang",
|
|
12825
|
+
"hideCopyright",
|
|
12826
|
+
)
|
|
12827
|
+
|
|
12828
|
+
_QUESTION_RELATION_SAVE_KEYS = (
|
|
12829
|
+
"queId",
|
|
12830
|
+
"relationType",
|
|
12831
|
+
"displayedQueId",
|
|
12832
|
+
"qlinkerAlias",
|
|
12833
|
+
"displayedQueInfo",
|
|
12834
|
+
"aliasConfig",
|
|
12835
|
+
"matchRules",
|
|
12836
|
+
"tableMatchRules",
|
|
12837
|
+
"matchRuleType",
|
|
12838
|
+
"matchRuleFormula",
|
|
12839
|
+
"sortConfig",
|
|
12840
|
+
)
|
|
12841
|
+
|
|
12842
|
+
|
|
12843
|
+
def _build_form_save_base_payload(current_schema: dict[str, Any], title: str) -> dict[str, Any]:
|
|
12844
|
+
payload: dict[str, Any] = {"formTitle": title}
|
|
12845
|
+
for key in _FORM_SAVE_BASE_KEYS:
|
|
12846
|
+
if key in current_schema:
|
|
12847
|
+
payload[key] = deepcopy(current_schema.get(key))
|
|
12848
|
+
payload["editVersionNo"] = int(current_schema.get("editVersionNo") or 1)
|
|
12849
|
+
payload["formQues"] = []
|
|
12850
|
+
payload["questionRelations"] = []
|
|
12851
|
+
return payload
|
|
12852
|
+
|
|
12853
|
+
|
|
12854
|
+
def _normalize_question_relations_for_save(question_relations: list[dict[str, Any]] | None) -> list[dict[str, Any]]:
|
|
12855
|
+
normalized: list[dict[str, Any]] = []
|
|
12856
|
+
for relation in question_relations or []:
|
|
12857
|
+
if not isinstance(relation, dict):
|
|
12858
|
+
continue
|
|
12859
|
+
item: dict[str, Any] = {}
|
|
12860
|
+
for key in _QUESTION_RELATION_SAVE_KEYS:
|
|
12861
|
+
if key not in relation:
|
|
12862
|
+
continue
|
|
12863
|
+
value = relation.get(key)
|
|
12864
|
+
if value is None:
|
|
12865
|
+
continue
|
|
12866
|
+
item[key] = deepcopy(value)
|
|
12867
|
+
if item:
|
|
12868
|
+
normalized.append(item)
|
|
12869
|
+
return normalized
|
|
12870
|
+
|
|
12871
|
+
|
|
12872
|
+
def _field_rename_maps(fields: list[dict[str, Any]]) -> tuple[dict[int, str], dict[str, str]]:
|
|
12873
|
+
by_que_id: dict[int, str] = {}
|
|
12874
|
+
by_title: dict[str, str] = {}
|
|
12875
|
+
for field in fields:
|
|
12876
|
+
if not isinstance(field, dict):
|
|
12877
|
+
continue
|
|
12878
|
+
template = field.get("_question_template")
|
|
12879
|
+
if not isinstance(template, dict):
|
|
12880
|
+
continue
|
|
12881
|
+
old_title = str(template.get("queTitle") or "").strip()
|
|
12882
|
+
new_title = str(field.get("name") or "").strip()
|
|
12883
|
+
if not old_title or not new_title or old_title == new_title:
|
|
12884
|
+
continue
|
|
12885
|
+
que_id = _coerce_nonnegative_int(field.get("que_id"))
|
|
12886
|
+
if que_id is None:
|
|
12887
|
+
que_id = _coerce_nonnegative_int(template.get("queId"))
|
|
12888
|
+
if que_id is not None:
|
|
12889
|
+
by_que_id[que_id] = new_title
|
|
12890
|
+
by_title[old_title] = new_title
|
|
12891
|
+
return by_que_id, by_title
|
|
12892
|
+
|
|
12893
|
+
|
|
12894
|
+
def _sync_question_title_references(value: Any, *, by_que_id: dict[int, str], by_title: dict[str, str]) -> None:
|
|
12895
|
+
if isinstance(value, list):
|
|
12896
|
+
for item in value:
|
|
12897
|
+
_sync_question_title_references(item, by_que_id=by_que_id, by_title=by_title)
|
|
12898
|
+
return
|
|
12899
|
+
if not isinstance(value, dict):
|
|
12900
|
+
return
|
|
12901
|
+
|
|
12902
|
+
title_keys = ("queTitle", "supQueTitle", "_field_id")
|
|
12903
|
+
que_id = _coerce_nonnegative_int(value.get("queId"))
|
|
12904
|
+
replacement = None
|
|
12905
|
+
if que_id is not None and que_id in by_que_id:
|
|
12906
|
+
replacement = by_que_id[que_id]
|
|
12907
|
+
elif que_id is None:
|
|
12908
|
+
for key in title_keys:
|
|
12909
|
+
current_title = str(value.get(key) or "").strip()
|
|
12910
|
+
if current_title and current_title in by_title:
|
|
12911
|
+
replacement = by_title[current_title]
|
|
12912
|
+
break
|
|
12913
|
+
if replacement is not None:
|
|
12914
|
+
for key in title_keys:
|
|
12915
|
+
current_title = str(value.get(key) or "").strip()
|
|
12916
|
+
if (que_id is not None and que_id in by_que_id and key in value) or (current_title and current_title in by_title):
|
|
12917
|
+
value[key] = replacement
|
|
12918
|
+
sup_id = _coerce_nonnegative_int(value.get("supId"))
|
|
12919
|
+
if sup_id is not None and sup_id in by_que_id and "supQueTitle" in value:
|
|
12920
|
+
value["supQueTitle"] = by_que_id[sup_id]
|
|
12921
|
+
|
|
12922
|
+
for child_value in value.values():
|
|
12923
|
+
if isinstance(child_value, (dict, list)):
|
|
12924
|
+
_sync_question_title_references(child_value, by_que_id=by_que_id, by_title=by_title)
|
|
12925
|
+
|
|
12926
|
+
|
|
12756
12927
|
def _materialize_preserved_question(field: dict[str, Any]) -> dict[str, Any] | None:
|
|
12757
12928
|
template = deepcopy(field.get("_question_template"))
|
|
12758
12929
|
if not isinstance(template, dict):
|
|
@@ -12802,12 +12973,21 @@ def _field_to_question(field: dict[str, Any], *, temp_id: int) -> dict[str, Any]
|
|
|
12802
12973
|
},
|
|
12803
12974
|
temp_id,
|
|
12804
12975
|
)
|
|
12976
|
+
relation_config_explicit = bool(field.get("_relation_config_explicit"))
|
|
12805
12977
|
relation_question_template = (
|
|
12806
12978
|
deepcopy(field.get("_question_template"))
|
|
12807
12979
|
if field.get("type") == FieldType.relation.value and isinstance(field.get("_question_template"), dict)
|
|
12808
12980
|
else None
|
|
12809
12981
|
)
|
|
12810
|
-
question =
|
|
12982
|
+
question = (
|
|
12983
|
+
relation_question_template
|
|
12984
|
+
if relation_question_template is not None and not relation_config_explicit
|
|
12985
|
+
else built_question
|
|
12986
|
+
)
|
|
12987
|
+
if relation_config_explicit and relation_question_template is not None:
|
|
12988
|
+
for key in ("queOriginType", "relationDisplayMode", "customRenderConfig"):
|
|
12989
|
+
if key in relation_question_template:
|
|
12990
|
+
question[key] = deepcopy(relation_question_template[key])
|
|
12811
12991
|
if _coerce_nonnegative_int(field.get("que_id")) is not None:
|
|
12812
12992
|
question["queId"] = field["que_id"]
|
|
12813
12993
|
question.pop("queTempId", None)
|
|
@@ -12826,19 +13006,28 @@ def _field_to_question(field: dict[str, Any], *, temp_id: int) -> dict[str, Any]
|
|
|
12826
13006
|
if field.get("type") == FieldType.relation.value:
|
|
12827
13007
|
preserved_reference = (
|
|
12828
13008
|
deepcopy(field.get("_reference_config_template"))
|
|
12829
|
-
if not
|
|
13009
|
+
if not relation_config_explicit and isinstance(field.get("_reference_config_template"), dict)
|
|
12830
13010
|
else None
|
|
12831
13011
|
)
|
|
12832
13012
|
if preserved_reference is not None:
|
|
12833
13013
|
preserved_reference["referAppKey"] = field.get("target_app_key")
|
|
12834
13014
|
question["referenceConfig"] = preserved_reference
|
|
12835
13015
|
else:
|
|
12836
|
-
reference =
|
|
13016
|
+
reference = (
|
|
13017
|
+
deepcopy(built_question.get("referenceConfig"))
|
|
13018
|
+
if relation_config_explicit and isinstance(built_question.get("referenceConfig"), dict)
|
|
13019
|
+
else deepcopy(question.get("referenceConfig"))
|
|
13020
|
+
if isinstance(question.get("referenceConfig"), dict)
|
|
13021
|
+
else {}
|
|
13022
|
+
)
|
|
12837
13023
|
built_reference = (
|
|
12838
13024
|
deepcopy(built_question.get("referenceConfig"))
|
|
12839
13025
|
if isinstance(built_question.get("referenceConfig"), dict)
|
|
12840
13026
|
else {}
|
|
12841
13027
|
)
|
|
13028
|
+
if relation_config_explicit:
|
|
13029
|
+
for stale_key in ("customButtonText", "customAdvancedSetting", "configShowForm", "dataShowForm"):
|
|
13030
|
+
reference.pop(stale_key, None)
|
|
12842
13031
|
for key in (
|
|
12843
13032
|
"referQueId",
|
|
12844
13033
|
"referQuestions",
|
|
@@ -12994,7 +13183,9 @@ def _build_form_payload_from_fields(
|
|
|
12994
13183
|
_apply_row_widths(row)
|
|
12995
13184
|
payload = default_form_payload(title, form_rows)
|
|
12996
13185
|
payload["editVersionNo"] = int(current_schema.get("editVersionNo") or 1)
|
|
12997
|
-
payload["questionRelations"] =
|
|
13186
|
+
payload["questionRelations"] = _normalize_question_relations_for_save(
|
|
13187
|
+
question_relations if question_relations is not None else (current_schema.get("questionRelations") or [])
|
|
13188
|
+
)
|
|
12998
13189
|
return payload
|
|
12999
13190
|
|
|
13000
13191
|
|
|
@@ -13007,6 +13198,7 @@ def _build_form_payload_for_edit_fields(
|
|
|
13007
13198
|
question_relations: list[dict[str, Any]] | None = None,
|
|
13008
13199
|
) -> dict[str, Any]:
|
|
13009
13200
|
_, section_templates = _extract_question_templates(current_schema)
|
|
13201
|
+
template_row_lengths_by_que_id, template_row_lengths_by_title = _extract_template_row_lengths(current_schema)
|
|
13010
13202
|
fields_by_name = {
|
|
13011
13203
|
str(field.get("name") or ""): field
|
|
13012
13204
|
for field in fields
|
|
@@ -13017,18 +13209,26 @@ def _build_form_payload_for_edit_fields(
|
|
|
13017
13209
|
|
|
13018
13210
|
for row in layout.get("root_rows", []) or []:
|
|
13019
13211
|
questions: list[dict[str, Any]] = []
|
|
13212
|
+
expected_template_lengths: list[int] = []
|
|
13020
13213
|
row_preserved = True
|
|
13021
13214
|
for name in row:
|
|
13022
13215
|
field = fields_by_name.get(str(name))
|
|
13023
13216
|
if field is None:
|
|
13024
13217
|
continue
|
|
13218
|
+
template_row_length = _field_template_row_length(
|
|
13219
|
+
field,
|
|
13220
|
+
lengths_by_que_id=template_row_lengths_by_que_id,
|
|
13221
|
+
lengths_by_title=template_row_lengths_by_title,
|
|
13222
|
+
)
|
|
13223
|
+
if template_row_length is not None:
|
|
13224
|
+
expected_template_lengths.append(template_row_length)
|
|
13025
13225
|
question, preserved = _materialize_edit_question(field, temp_id=temp_id)
|
|
13026
13226
|
questions.append(question)
|
|
13027
13227
|
row_preserved = row_preserved and preserved
|
|
13028
13228
|
temp_id -= 100
|
|
13029
13229
|
if not questions:
|
|
13030
13230
|
continue
|
|
13031
|
-
if not row_preserved:
|
|
13231
|
+
if not row_preserved or _row_needs_width_reflow(expected_template_lengths, len(questions)):
|
|
13032
13232
|
_apply_row_widths(questions)
|
|
13033
13233
|
form_rows.append(questions)
|
|
13034
13234
|
|
|
@@ -13036,18 +13236,26 @@ def _build_form_payload_for_edit_fields(
|
|
|
13036
13236
|
inner_rows: list[list[dict[str, Any]]] = []
|
|
13037
13237
|
for row in section.get("rows", []) or []:
|
|
13038
13238
|
questions: list[dict[str, Any]] = []
|
|
13239
|
+
expected_template_lengths: list[int] = []
|
|
13039
13240
|
row_preserved = True
|
|
13040
13241
|
for name in row:
|
|
13041
13242
|
field = fields_by_name.get(str(name))
|
|
13042
13243
|
if field is None:
|
|
13043
13244
|
continue
|
|
13245
|
+
template_row_length = _field_template_row_length(
|
|
13246
|
+
field,
|
|
13247
|
+
lengths_by_que_id=template_row_lengths_by_que_id,
|
|
13248
|
+
lengths_by_title=template_row_lengths_by_title,
|
|
13249
|
+
)
|
|
13250
|
+
if template_row_length is not None:
|
|
13251
|
+
expected_template_lengths.append(template_row_length)
|
|
13044
13252
|
question, preserved = _materialize_edit_question(field, temp_id=temp_id)
|
|
13045
13253
|
questions.append(question)
|
|
13046
13254
|
row_preserved = row_preserved and preserved
|
|
13047
13255
|
temp_id -= 100
|
|
13048
13256
|
if not questions:
|
|
13049
13257
|
continue
|
|
13050
|
-
if not row_preserved:
|
|
13258
|
+
if not row_preserved or _row_needs_width_reflow(expected_template_lengths, len(questions)):
|
|
13051
13259
|
_apply_row_widths(questions)
|
|
13052
13260
|
inner_rows.append(questions)
|
|
13053
13261
|
if not inner_rows:
|
|
@@ -13080,10 +13288,18 @@ def _build_form_payload_for_edit_fields(
|
|
|
13080
13288
|
wrapper["innerQuestions"] = inner_rows
|
|
13081
13289
|
form_rows.append([wrapper])
|
|
13082
13290
|
|
|
13083
|
-
|
|
13084
|
-
|
|
13291
|
+
rename_by_que_id, rename_by_title = _field_rename_maps(fields)
|
|
13292
|
+
if rename_by_que_id or rename_by_title:
|
|
13293
|
+
_sync_question_title_references(form_rows, by_que_id=rename_by_que_id, by_title=rename_by_title)
|
|
13294
|
+
normalized_relations = _normalize_question_relations_for_save(
|
|
13295
|
+
question_relations if question_relations is not None else (current_schema.get("questionRelations") or [])
|
|
13296
|
+
)
|
|
13297
|
+
if rename_by_que_id or rename_by_title:
|
|
13298
|
+
_sync_question_title_references(normalized_relations, by_que_id=rename_by_que_id, by_title=rename_by_title)
|
|
13299
|
+
|
|
13300
|
+
payload = _build_form_save_base_payload(current_schema, title)
|
|
13085
13301
|
payload["formQues"] = form_rows
|
|
13086
|
-
payload["questionRelations"] =
|
|
13302
|
+
payload["questionRelations"] = normalized_relations
|
|
13087
13303
|
payload["editVersionNo"] = int(current_schema.get("editVersionNo") or 1)
|
|
13088
13304
|
return payload
|
|
13089
13305
|
|
|
@@ -14497,9 +14713,9 @@ def _build_form_payload_from_existing_schema(
|
|
|
14497
14713
|
wrapper["queWidth"] = 100
|
|
14498
14714
|
form_rows.append([wrapper])
|
|
14499
14715
|
|
|
14500
|
-
payload =
|
|
14716
|
+
payload = _build_form_save_base_payload(current_schema, str(current_schema.get("formTitle") or "未命名应用"))
|
|
14501
14717
|
payload["formQues"] = form_rows
|
|
14502
|
-
payload["questionRelations"] =
|
|
14718
|
+
payload["questionRelations"] = _normalize_question_relations_for_save(current_schema.get("questionRelations") or [])
|
|
14503
14719
|
payload["editVersionNo"] = int(current_schema.get("editVersionNo") or 1)
|
|
14504
14720
|
payload.setdefault("formTitle", current_schema.get("formTitle") or "未命名应用")
|
|
14505
14721
|
return payload
|