@josephyan/qingflow-app-user-mcp 0.2.0-beta.92 → 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 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.92
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.92 qingflow-app-user-mcp
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@josephyan/qingflow-app-user-mcp",
3
- "version": "0.2.0-beta.92",
3
+ "version": "0.2.0-beta.93",
4
4
  "description": "Operational end-user MCP for Qingflow records, tasks, comments, and directory workflows.",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/pyproject.toml CHANGED
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "qingflow-mcp"
7
- version = "0.2.0b92"
7
+ version = "0.2.0b93"
8
8
  description = "User-authenticated MCP server for Qingflow"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -5,7 +5,7 @@ from pathlib import Path
5
5
 
6
6
  __all__ = ["__version__"]
7
7
 
8
- _FALLBACK_VERSION = "0.2.0b92"
8
+ _FALLBACK_VERSION = "0.2.0b93"
9
9
 
10
10
 
11
11
  def _resolve_local_pyproject_version() -> str | None:
@@ -12813,6 +12813,117 @@ def _row_needs_width_reflow(expected_template_lengths: list[int], current_row_le
12813
12813
  return any(length != current_row_length for length in expected_template_lengths)
12814
12814
 
12815
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
+
12816
12927
  def _materialize_preserved_question(field: dict[str, Any]) -> dict[str, Any] | None:
12817
12928
  template = deepcopy(field.get("_question_template"))
12818
12929
  if not isinstance(template, dict):
@@ -13072,7 +13183,9 @@ def _build_form_payload_from_fields(
13072
13183
  _apply_row_widths(row)
13073
13184
  payload = default_form_payload(title, form_rows)
13074
13185
  payload["editVersionNo"] = int(current_schema.get("editVersionNo") or 1)
13075
- payload["questionRelations"] = deepcopy(question_relations if question_relations is not None else (current_schema.get("questionRelations") or []))
13186
+ payload["questionRelations"] = _normalize_question_relations_for_save(
13187
+ question_relations if question_relations is not None else (current_schema.get("questionRelations") or [])
13188
+ )
13076
13189
  return payload
13077
13190
 
13078
13191
 
@@ -13175,10 +13288,18 @@ def _build_form_payload_for_edit_fields(
13175
13288
  wrapper["innerQuestions"] = inner_rows
13176
13289
  form_rows.append([wrapper])
13177
13290
 
13178
- payload = deepcopy(current_schema)
13179
- payload["formTitle"] = title
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)
13180
13301
  payload["formQues"] = form_rows
13181
- payload["questionRelations"] = deepcopy(question_relations if question_relations is not None else (current_schema.get("questionRelations") or []))
13302
+ payload["questionRelations"] = normalized_relations
13182
13303
  payload["editVersionNo"] = int(current_schema.get("editVersionNo") or 1)
13183
13304
  return payload
13184
13305
 
@@ -14592,9 +14713,9 @@ def _build_form_payload_from_existing_schema(
14592
14713
  wrapper["queWidth"] = 100
14593
14714
  form_rows.append([wrapper])
14594
14715
 
14595
- payload = deepcopy(current_schema)
14716
+ payload = _build_form_save_base_payload(current_schema, str(current_schema.get("formTitle") or "未命名应用"))
14596
14717
  payload["formQues"] = form_rows
14597
- payload["questionRelations"] = deepcopy(current_schema.get("questionRelations") or [])
14718
+ payload["questionRelations"] = _normalize_question_relations_for_save(current_schema.get("questionRelations") or [])
14598
14719
  payload["editVersionNo"] = int(current_schema.get("editVersionNo") or 1)
14599
14720
  payload.setdefault("formTitle", current_schema.get("formTitle") or "未命名应用")
14600
14721
  return payload