@josephyan/qingflow-app-user-mcp 1.1.28 → 1.1.30
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 +122 -3
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.30
|
|
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.30 qingflow-app-user-mcp
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
Environment:
|
package/package.json
CHANGED
package/pyproject.toml
CHANGED
|
@@ -6856,6 +6856,11 @@ class AiBuilderFacade:
|
|
|
6856
6856
|
if (field_id := _coerce_nonnegative_int(entry.get("field_id"))) is not None
|
|
6857
6857
|
}
|
|
6858
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 []
|
|
6859
6864
|
return {
|
|
6860
6865
|
"status": "success",
|
|
6861
6866
|
"error_code": None,
|
|
@@ -6871,6 +6876,9 @@ class AiBuilderFacade:
|
|
|
6871
6876
|
"warnings": warnings,
|
|
6872
6877
|
"verification": verification,
|
|
6873
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"),
|
|
6874
6882
|
"query_conditions": query_conditions,
|
|
6875
6883
|
"buttons_config": buttons_config,
|
|
6876
6884
|
"associated_resources_config": associated_resources_config,
|
|
@@ -21154,6 +21162,20 @@ def _merge_view_summary_with_config(
|
|
|
21154
21162
|
question_entries_by_id=query_question_entries_by_id,
|
|
21155
21163
|
)
|
|
21156
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
|
|
21157
21179
|
if any(key in config for key in ("asosChartVisible", "asosChartConfig", "asosChartIdList", "limitType")):
|
|
21158
21180
|
summary["associated_resources_config"] = _extract_view_associated_resources_config(config)
|
|
21159
21181
|
config_enriched = True
|
|
@@ -24124,7 +24146,8 @@ def _resolve_view_filter_values(
|
|
|
24124
24146
|
) -> tuple[list[str], list[dict[str, Any]], dict[str, Any] | None]:
|
|
24125
24147
|
field_type = str(field.get("type") or FieldType.text.value)
|
|
24126
24148
|
if field_type not in {FieldType.single_select.value, FieldType.multi_select.value, FieldType.boolean.value}:
|
|
24127
|
-
|
|
24149
|
+
string_values = [_stringify_condition_value(value) for value in values]
|
|
24150
|
+
return string_values, _build_view_filter_scalar_value_details(values), None
|
|
24128
24151
|
|
|
24129
24152
|
option_details = [
|
|
24130
24153
|
item
|
|
@@ -24204,6 +24227,23 @@ def _build_view_filter_value_details(values: list[Any]) -> list[dict[str, Any]]:
|
|
|
24204
24227
|
return details
|
|
24205
24228
|
|
|
24206
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
|
+
|
|
24207
24247
|
def _coerce_view_filter_value_detail(value: dict[str, Any]) -> dict[str, Any]:
|
|
24208
24248
|
item_id = value.get("id")
|
|
24209
24249
|
item_value = value.get("value")
|
|
@@ -24231,9 +24271,16 @@ def _normalize_view_filter_groups_for_compare(groups: Any) -> list[list[dict[str
|
|
|
24231
24271
|
continue
|
|
24232
24272
|
normalized_details = []
|
|
24233
24273
|
for detail in raw_rule.get("judgeValueDetails") or []:
|
|
24234
|
-
if not isinstance(detail, dict)
|
|
24274
|
+
if not isinstance(detail, dict):
|
|
24235
24275
|
continue
|
|
24236
|
-
|
|
24276
|
+
item_id = detail.get("id")
|
|
24277
|
+
item_value = detail.get("value")
|
|
24278
|
+
if item_id is None and item_value is None:
|
|
24279
|
+
continue
|
|
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)
|
|
24237
24284
|
normalized_rules.append(
|
|
24238
24285
|
{
|
|
24239
24286
|
"queId": _coerce_positive_int(raw_rule.get("queId")) or 0,
|
|
@@ -24285,6 +24332,78 @@ def _view_filter_groups_equivalent(expected: Any, actual: Any) -> bool:
|
|
|
24285
24332
|
return _view_filter_groups_signature(expected) == _view_filter_groups_signature(actual)
|
|
24286
24333
|
|
|
24287
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_nonnegative_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
|
+
|
|
24288
24407
|
def _infer_status_field_id(fields: list[dict[str, Any]]) -> str | None:
|
|
24289
24408
|
preferred_names = {"status", "状态", "订单状态", "审批状态", "流程状态"}
|
|
24290
24409
|
for field in fields:
|