@josephyan/qingflow-app-user-mcp 1.1.27 → 1.1.29
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/builder_facade/service.py +127 -6
package/README.md
CHANGED
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
Install:
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
|
-
npm install @josephyan/qingflow-app-user-mcp@1.1.
|
|
6
|
+
npm install @josephyan/qingflow-app-user-mcp@1.1.29
|
|
7
7
|
```
|
|
8
8
|
|
|
9
9
|
Run:
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
|
-
npx -y -p @josephyan/qingflow-app-user-mcp@1.1.
|
|
12
|
+
npx -y -p @josephyan/qingflow-app-user-mcp@1.1.29 qingflow-app-user-mcp
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
Environment:
|
package/package.json
CHANGED
package/pyproject.toml
CHANGED
|
@@ -157,6 +157,7 @@ MATCH_TYPE_ACCURACY = 1
|
|
|
157
157
|
MATCH_TYPE_QUESTION = 2
|
|
158
158
|
JUDGE_EQUAL = 0
|
|
159
159
|
JUDGE_UNEQUAL = 1
|
|
160
|
+
JUDGE_INCLUDE = 2
|
|
160
161
|
JUDGE_GREATER_OR_EQUAL = 5
|
|
161
162
|
JUDGE_LESS_OR_EQUAL = 7
|
|
162
163
|
JUDGE_EQUAL_ANY = 9
|
|
@@ -6855,6 +6856,11 @@ class AiBuilderFacade:
|
|
|
6855
6856
|
if (field_id := _coerce_nonnegative_int(entry.get("field_id"))) is not None
|
|
6856
6857
|
}
|
|
6857
6858
|
query_conditions = _extract_view_query_conditions_config(config, question_entries_by_id=question_entries_by_id)
|
|
6859
|
+
filter_groups = _public_view_filter_groups_from_match_rules(
|
|
6860
|
+
config.get("viewgraphLimit") if isinstance(config, dict) else [],
|
|
6861
|
+
question_entries_by_id=question_entries_by_id,
|
|
6862
|
+
)
|
|
6863
|
+
filters = filter_groups[0] if len(filter_groups) == 1 else []
|
|
6858
6864
|
return {
|
|
6859
6865
|
"status": "success",
|
|
6860
6866
|
"error_code": None,
|
|
@@ -6870,6 +6876,9 @@ class AiBuilderFacade:
|
|
|
6870
6876
|
"warnings": warnings,
|
|
6871
6877
|
"verification": verification,
|
|
6872
6878
|
"verified": all(bool(value) for value in verification.values()),
|
|
6879
|
+
"filters": filters,
|
|
6880
|
+
"filter_groups": filter_groups,
|
|
6881
|
+
"filter_logic": "or" if len(filter_groups) > 1 else ("and" if filter_groups else "none"),
|
|
6873
6882
|
"query_conditions": query_conditions,
|
|
6874
6883
|
"buttons_config": buttons_config,
|
|
6875
6884
|
"associated_resources_config": associated_resources_config,
|
|
@@ -12580,7 +12589,8 @@ def _match_mapping_operator_to_judge_type(operator: Any, *, reason_path: str) ->
|
|
|
12580
12589
|
"le": JUDGE_LESS_OR_EQUAL,
|
|
12581
12590
|
"<=": JUDGE_LESS_OR_EQUAL,
|
|
12582
12591
|
"less_or_equal": JUDGE_LESS_OR_EQUAL,
|
|
12583
|
-
"contains":
|
|
12592
|
+
"contains": JUDGE_INCLUDE,
|
|
12593
|
+
"include": JUDGE_INCLUDE,
|
|
12584
12594
|
"like": JUDGE_FUZZY_MATCH,
|
|
12585
12595
|
"fuzzy": JUDGE_FUZZY_MATCH,
|
|
12586
12596
|
"fuzzy_match": JUDGE_FUZZY_MATCH,
|
|
@@ -21152,6 +21162,20 @@ def _merge_view_summary_with_config(
|
|
|
21152
21162
|
question_entries_by_id=query_question_entries_by_id,
|
|
21153
21163
|
)
|
|
21154
21164
|
config_enriched = True
|
|
21165
|
+
if "viewgraphLimit" in config:
|
|
21166
|
+
filter_question_entries_by_id = dict(question_entries_by_id)
|
|
21167
|
+
for entry in canonical_question_entries:
|
|
21168
|
+
field_id = _coerce_nonnegative_int(entry.get("field_id"))
|
|
21169
|
+
if field_id is not None:
|
|
21170
|
+
filter_question_entries_by_id[field_id] = entry
|
|
21171
|
+
filter_groups = _public_view_filter_groups_from_match_rules(
|
|
21172
|
+
config.get("viewgraphLimit"),
|
|
21173
|
+
question_entries_by_id=filter_question_entries_by_id,
|
|
21174
|
+
)
|
|
21175
|
+
summary["filter_groups"] = filter_groups
|
|
21176
|
+
summary["filters"] = filter_groups[0] if len(filter_groups) == 1 else []
|
|
21177
|
+
summary["filter_logic"] = "or" if len(filter_groups) > 1 else ("and" if filter_groups else "none")
|
|
21178
|
+
config_enriched = True
|
|
21155
21179
|
if any(key in config for key in ("asosChartVisible", "asosChartConfig", "asosChartIdList", "limitType")):
|
|
21156
21180
|
summary["associated_resources_config"] = _extract_view_associated_resources_config(config)
|
|
21157
21181
|
config_enriched = True
|
|
@@ -22331,7 +22355,7 @@ def _translate_flow_condition_rule(*, field: dict[str, Any], rule: dict[str, Any
|
|
|
22331
22355
|
base["judgeType"] = JUDGE_INCLUDE_ANY if field_type in INCLUDE_ANY_FLOW_FIELD_TYPES else JUDGE_EQUAL_ANY
|
|
22332
22356
|
base["judgeValues"] = [_stringify_condition_value(value) for value in values]
|
|
22333
22357
|
elif operator == "contains":
|
|
22334
|
-
base["judgeType"] =
|
|
22358
|
+
base["judgeType"] = JUDGE_INCLUDE
|
|
22335
22359
|
base["judgeValues"] = [_stringify_condition_value(values[0])]
|
|
22336
22360
|
elif operator == "gte":
|
|
22337
22361
|
base["judgeType"] = JUDGE_GREATER_OR_EQUAL
|
|
@@ -24098,7 +24122,7 @@ def _translate_view_filter_rule(*, field: dict[str, Any], rule: dict[str, Any])
|
|
|
24098
24122
|
payload["judgeType"] = JUDGE_INCLUDE_ANY if field_type in INCLUDE_ANY_FLOW_FIELD_TYPES else JUDGE_EQUAL_ANY
|
|
24099
24123
|
payload["judgeValues"] = judge_values
|
|
24100
24124
|
elif operator == "contains":
|
|
24101
|
-
payload["judgeType"] =
|
|
24125
|
+
payload["judgeType"] = JUDGE_INCLUDE
|
|
24102
24126
|
payload["judgeValues"] = judge_values[:1] if judge_values else []
|
|
24103
24127
|
elif operator == "gte":
|
|
24104
24128
|
payload["judgeType"] = JUDGE_GREATER_OR_EQUAL
|
|
@@ -24122,7 +24146,8 @@ def _resolve_view_filter_values(
|
|
|
24122
24146
|
) -> tuple[list[str], list[dict[str, Any]], dict[str, Any] | None]:
|
|
24123
24147
|
field_type = str(field.get("type") or FieldType.text.value)
|
|
24124
24148
|
if field_type not in {FieldType.single_select.value, FieldType.multi_select.value, FieldType.boolean.value}:
|
|
24125
|
-
|
|
24149
|
+
string_values = [_stringify_condition_value(value) for value in values]
|
|
24150
|
+
return string_values, _build_view_filter_scalar_value_details(values), None
|
|
24126
24151
|
|
|
24127
24152
|
option_details = [
|
|
24128
24153
|
item
|
|
@@ -24202,6 +24227,23 @@ def _build_view_filter_value_details(values: list[Any]) -> list[dict[str, Any]]:
|
|
|
24202
24227
|
return details
|
|
24203
24228
|
|
|
24204
24229
|
|
|
24230
|
+
def _build_view_filter_scalar_value_details(values: list[Any]) -> list[dict[str, Any]]:
|
|
24231
|
+
details: list[dict[str, Any]] = []
|
|
24232
|
+
for value in values:
|
|
24233
|
+
if isinstance(value, dict):
|
|
24234
|
+
raw_value = value.get("value")
|
|
24235
|
+
if raw_value is None:
|
|
24236
|
+
raw_value = value.get("dataValue")
|
|
24237
|
+
if raw_value is None:
|
|
24238
|
+
raw_value = value.get("id")
|
|
24239
|
+
if raw_value is None:
|
|
24240
|
+
continue
|
|
24241
|
+
details.append(_coerce_view_filter_value_detail({"value": _stringify_condition_value(raw_value)}))
|
|
24242
|
+
continue
|
|
24243
|
+
details.append(_coerce_view_filter_value_detail({"value": _stringify_condition_value(value)}))
|
|
24244
|
+
return details
|
|
24245
|
+
|
|
24246
|
+
|
|
24205
24247
|
def _coerce_view_filter_value_detail(value: dict[str, Any]) -> dict[str, Any]:
|
|
24206
24248
|
item_id = value.get("id")
|
|
24207
24249
|
item_value = value.get("value")
|
|
@@ -24229,9 +24271,16 @@ def _normalize_view_filter_groups_for_compare(groups: Any) -> list[list[dict[str
|
|
|
24229
24271
|
continue
|
|
24230
24272
|
normalized_details = []
|
|
24231
24273
|
for detail in raw_rule.get("judgeValueDetails") or []:
|
|
24232
|
-
if not isinstance(detail, dict)
|
|
24274
|
+
if not isinstance(detail, dict):
|
|
24275
|
+
continue
|
|
24276
|
+
item_id = detail.get("id")
|
|
24277
|
+
item_value = detail.get("value")
|
|
24278
|
+
if item_id is None and item_value is None:
|
|
24233
24279
|
continue
|
|
24234
|
-
|
|
24280
|
+
normalized_detail = {"value": str(item_value if item_value is not None else item_id)}
|
|
24281
|
+
if item_id is not None:
|
|
24282
|
+
normalized_detail["id"] = item_id
|
|
24283
|
+
normalized_details.append(normalized_detail)
|
|
24235
24284
|
normalized_rules.append(
|
|
24236
24285
|
{
|
|
24237
24286
|
"queId": _coerce_positive_int(raw_rule.get("queId")) or 0,
|
|
@@ -24283,6 +24332,78 @@ def _view_filter_groups_equivalent(expected: Any, actual: Any) -> bool:
|
|
|
24283
24332
|
return _view_filter_groups_signature(expected) == _view_filter_groups_signature(actual)
|
|
24284
24333
|
|
|
24285
24334
|
|
|
24335
|
+
def _public_view_filter_groups_from_match_rules(
|
|
24336
|
+
groups: Any,
|
|
24337
|
+
*,
|
|
24338
|
+
question_entries_by_id: dict[int, dict[str, Any]] | None = None,
|
|
24339
|
+
) -> list[list[dict[str, Any]]]:
|
|
24340
|
+
question_entries_by_id = question_entries_by_id or {}
|
|
24341
|
+
public_groups: list[list[dict[str, Any]]] = []
|
|
24342
|
+
for raw_group in groups or []:
|
|
24343
|
+
if not isinstance(raw_group, list):
|
|
24344
|
+
continue
|
|
24345
|
+
public_group: list[dict[str, Any]] = []
|
|
24346
|
+
for raw_rule in raw_group:
|
|
24347
|
+
if not isinstance(raw_rule, dict):
|
|
24348
|
+
continue
|
|
24349
|
+
que_id = _coerce_positive_int(raw_rule.get("queId")) or 0
|
|
24350
|
+
field_name = str((question_entries_by_id.get(que_id) or {}).get("name") or raw_rule.get("queTitle") or "").strip()
|
|
24351
|
+
if not field_name:
|
|
24352
|
+
continue
|
|
24353
|
+
values = _public_view_filter_values_from_rule(raw_rule)
|
|
24354
|
+
operator = _public_view_filter_operator_from_judge_type(raw_rule.get("judgeType"), values=values)
|
|
24355
|
+
item: dict[str, Any] = {
|
|
24356
|
+
"field_name": field_name,
|
|
24357
|
+
"operator": operator,
|
|
24358
|
+
}
|
|
24359
|
+
if operator not in {"is_empty", "not_empty"}:
|
|
24360
|
+
if operator == "in":
|
|
24361
|
+
item["values"] = values
|
|
24362
|
+
elif len(values) == 1:
|
|
24363
|
+
item["value"] = values[0]
|
|
24364
|
+
elif values:
|
|
24365
|
+
item["values"] = values
|
|
24366
|
+
public_group.append(item)
|
|
24367
|
+
if public_group:
|
|
24368
|
+
public_groups.append(public_group)
|
|
24369
|
+
return public_groups
|
|
24370
|
+
|
|
24371
|
+
|
|
24372
|
+
def _public_view_filter_values_from_rule(rule: dict[str, Any]) -> list[str]:
|
|
24373
|
+
detail_values: list[str] = []
|
|
24374
|
+
for detail in rule.get("judgeValueDetails") or []:
|
|
24375
|
+
if not isinstance(detail, dict):
|
|
24376
|
+
continue
|
|
24377
|
+
value = detail.get("value")
|
|
24378
|
+
if value is None:
|
|
24379
|
+
value = detail.get("id")
|
|
24380
|
+
if value is None:
|
|
24381
|
+
continue
|
|
24382
|
+
text = str(value).strip()
|
|
24383
|
+
if text:
|
|
24384
|
+
detail_values.append(text)
|
|
24385
|
+
if detail_values:
|
|
24386
|
+
return detail_values
|
|
24387
|
+
return [str(value) for value in (rule.get("judgeValues") or []) if str(value).strip()]
|
|
24388
|
+
|
|
24389
|
+
|
|
24390
|
+
def _public_view_filter_operator_from_judge_type(judge_type: Any, *, values: list[str]) -> str:
|
|
24391
|
+
numeric = _coerce_positive_int(judge_type)
|
|
24392
|
+
if numeric == JUDGE_EQUAL:
|
|
24393
|
+
return "eq" if values else "is_empty"
|
|
24394
|
+
if numeric == JUDGE_UNEQUAL:
|
|
24395
|
+
return "neq" if values else "not_empty"
|
|
24396
|
+
if numeric in {JUDGE_INCLUDE, JUDGE_FUZZY_MATCH}:
|
|
24397
|
+
return "contains"
|
|
24398
|
+
if numeric == JUDGE_GREATER_OR_EQUAL:
|
|
24399
|
+
return "gte"
|
|
24400
|
+
if numeric == JUDGE_LESS_OR_EQUAL:
|
|
24401
|
+
return "lte"
|
|
24402
|
+
if numeric in {JUDGE_EQUAL_ANY, JUDGE_INCLUDE_ANY}:
|
|
24403
|
+
return "in"
|
|
24404
|
+
return f"raw:{numeric}" if numeric is not None else "raw"
|
|
24405
|
+
|
|
24406
|
+
|
|
24286
24407
|
def _infer_status_field_id(fields: list[dict[str, Any]]) -> str | None:
|
|
24287
24408
|
preferred_names = {"status", "状态", "订单状态", "审批状态", "流程状态"}
|
|
24288
24409
|
for field in fields:
|