@qingflow-tech/qingflow-app-builder-mcp 1.0.36 → 1.0.37
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 +110 -3
package/README.md
CHANGED
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
Install:
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
|
-
npm install @qingflow-tech/qingflow-app-builder-mcp@1.0.
|
|
6
|
+
npm install @qingflow-tech/qingflow-app-builder-mcp@1.0.37
|
|
7
7
|
```
|
|
8
8
|
|
|
9
9
|
Run:
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
|
-
npx -y -p @qingflow-tech/qingflow-app-builder-mcp@1.0.
|
|
12
|
+
npx -y -p @qingflow-tech/qingflow-app-builder-mcp@1.0.37 qingflow-app-builder-mcp
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
Environment:
|
package/package.json
CHANGED
package/pyproject.toml
CHANGED
|
@@ -9,7 +9,7 @@ import random
|
|
|
9
9
|
import re
|
|
10
10
|
import string
|
|
11
11
|
import tempfile
|
|
12
|
-
from typing import Any, cast
|
|
12
|
+
from typing import Any, NoReturn, cast
|
|
13
13
|
from urllib.parse import quote_plus, unquote_plus
|
|
14
14
|
from uuid import uuid4
|
|
15
15
|
|
|
@@ -14615,7 +14615,18 @@ def _build_qingbi_chart_field_lookup(
|
|
|
14615
14615
|
field_lookup: dict[str, dict[str, Any]],
|
|
14616
14616
|
) -> dict[str, Any]:
|
|
14617
14617
|
by_selector: dict[str, list[dict[str, Any]]] = {}
|
|
14618
|
-
form_by_que_id = field_lookup.get("by_que_id") or {}
|
|
14618
|
+
form_by_que_id = dict(field_lookup.get("by_que_id") or {})
|
|
14619
|
+
if not form_by_que_id:
|
|
14620
|
+
for bucket_name in ("by_name", "by_field_id"):
|
|
14621
|
+
bucket = field_lookup.get(bucket_name) or {}
|
|
14622
|
+
if not isinstance(bucket, dict):
|
|
14623
|
+
continue
|
|
14624
|
+
for form_field in bucket.values():
|
|
14625
|
+
if not isinstance(form_field, dict):
|
|
14626
|
+
continue
|
|
14627
|
+
que_id = _coerce_any_int(form_field.get("que_id"))
|
|
14628
|
+
if que_id is not None:
|
|
14629
|
+
form_by_que_id.setdefault(que_id, form_field)
|
|
14619
14630
|
|
|
14620
14631
|
def add_selector(key: Any, field: dict[str, Any]) -> None:
|
|
14621
14632
|
normalized = str(key or "").strip()
|
|
@@ -15095,13 +15106,109 @@ def _build_public_chart_filter_matrix(
|
|
|
15095
15106
|
"fieldName": qingbi_field.get("fieldName") or form_field.get("name") or field_id,
|
|
15096
15107
|
"fieldType": qingbi_field.get("fieldType") or _qingbi_field_type_from_public_field(str(form_field.get("type") or "")),
|
|
15097
15108
|
"judgeType": judge_map.get(operator, "equal"),
|
|
15098
|
-
"
|
|
15109
|
+
"judgeValue": _build_qingbi_chart_filter_judge_value(
|
|
15110
|
+
operator=operator,
|
|
15111
|
+
values=values,
|
|
15112
|
+
form_field=form_field,
|
|
15113
|
+
chart_type=chart_type,
|
|
15114
|
+
),
|
|
15115
|
+
"judgeValueDetailList": [],
|
|
15099
15116
|
"matchType": 1,
|
|
15100
15117
|
}
|
|
15101
15118
|
)
|
|
15102
15119
|
return [group] if group else []
|
|
15103
15120
|
|
|
15104
15121
|
|
|
15122
|
+
def _build_qingbi_chart_filter_judge_value(
|
|
15123
|
+
*,
|
|
15124
|
+
operator: str,
|
|
15125
|
+
values: list[Any],
|
|
15126
|
+
form_field: dict[str, Any],
|
|
15127
|
+
chart_type: str,
|
|
15128
|
+
) -> str | None:
|
|
15129
|
+
if operator in {ViewFilterOperator.is_empty.value, ViewFilterOperator.not_empty.value}:
|
|
15130
|
+
return None
|
|
15131
|
+
if operator == ViewFilterOperator.in_.value:
|
|
15132
|
+
return "<&&>".join(
|
|
15133
|
+
_qingbi_chart_filter_value_to_text(value=value, form_field=form_field, chart_type=chart_type) for value in values
|
|
15134
|
+
)
|
|
15135
|
+
if not values:
|
|
15136
|
+
return ""
|
|
15137
|
+
return _qingbi_chart_filter_value_to_text(value=values[0], form_field=form_field, chart_type=chart_type)
|
|
15138
|
+
|
|
15139
|
+
|
|
15140
|
+
def _qingbi_chart_filter_value_to_text(*, value: Any, form_field: dict[str, Any], chart_type: str) -> str:
|
|
15141
|
+
option_details = [
|
|
15142
|
+
item
|
|
15143
|
+
for item in (form_field.get("option_details") or [])
|
|
15144
|
+
if isinstance(item, dict) and item.get("id") is not None and item.get("value") is not None
|
|
15145
|
+
]
|
|
15146
|
+
if not option_details:
|
|
15147
|
+
if isinstance(value, dict):
|
|
15148
|
+
for key in ("value", "label", "name", "title"):
|
|
15149
|
+
raw = str(value.get(key) or "").strip()
|
|
15150
|
+
if raw:
|
|
15151
|
+
return raw
|
|
15152
|
+
if value.get("id") is not None:
|
|
15153
|
+
return str(value.get("id"))
|
|
15154
|
+
return _stringify_condition_value(value)
|
|
15155
|
+
|
|
15156
|
+
option_by_value = {str(item.get("value") or "").strip(): item for item in option_details if str(item.get("value") or "").strip()}
|
|
15157
|
+
option_by_id = {str(item.get("id")): item for item in option_details if item.get("id") is not None}
|
|
15158
|
+
|
|
15159
|
+
def resolve(raw: Any) -> str | None:
|
|
15160
|
+
text = _stringify_condition_value(raw).strip()
|
|
15161
|
+
if not text:
|
|
15162
|
+
return None
|
|
15163
|
+
matched = option_by_value.get(text) or option_by_id.get(text)
|
|
15164
|
+
if not matched:
|
|
15165
|
+
return None
|
|
15166
|
+
return str(matched.get("value") or "").strip()
|
|
15167
|
+
|
|
15168
|
+
def reject(raw: Any) -> NoReturn:
|
|
15169
|
+
allowed = [
|
|
15170
|
+
{"id": str(item.get("id")), "value": str(item.get("value"))}
|
|
15171
|
+
for item in option_details
|
|
15172
|
+
]
|
|
15173
|
+
_raise_chart_rule(
|
|
15174
|
+
rule_code="CHART_FILTER_OPTION_VALUE_UNSUPPORTED",
|
|
15175
|
+
chart_type=chart_type,
|
|
15176
|
+
message="chart filter value is not in the field option list",
|
|
15177
|
+
expected="Use an existing option label or option id from the target field schema.",
|
|
15178
|
+
actual={
|
|
15179
|
+
"field_name": form_field.get("name"),
|
|
15180
|
+
"field_id": form_field.get("que_id"),
|
|
15181
|
+
"value": raw,
|
|
15182
|
+
"allowed_values": allowed,
|
|
15183
|
+
},
|
|
15184
|
+
offending_fields=[{"field_name": form_field.get("name"), "field_id": form_field.get("que_id"), "options": allowed}],
|
|
15185
|
+
next_action="Read schema first, then pass one of the allowed option labels or option ids.",
|
|
15186
|
+
)
|
|
15187
|
+
|
|
15188
|
+
if isinstance(value, dict):
|
|
15189
|
+
for key in ("value", "label", "name", "title"):
|
|
15190
|
+
if value.get(key) is not None:
|
|
15191
|
+
resolved = resolve(value.get(key))
|
|
15192
|
+
if resolved is not None:
|
|
15193
|
+
return resolved
|
|
15194
|
+
reject(value.get(key))
|
|
15195
|
+
if value.get("id") is not None:
|
|
15196
|
+
resolved = resolve(value.get("id"))
|
|
15197
|
+
if resolved is not None:
|
|
15198
|
+
return resolved
|
|
15199
|
+
reject(value.get("id"))
|
|
15200
|
+
reject(value)
|
|
15201
|
+
if isinstance(value, int):
|
|
15202
|
+
resolved = resolve(value)
|
|
15203
|
+
if resolved is not None:
|
|
15204
|
+
return resolved
|
|
15205
|
+
reject(value)
|
|
15206
|
+
resolved = resolve(value)
|
|
15207
|
+
if resolved is not None:
|
|
15208
|
+
return resolved
|
|
15209
|
+
reject(value)
|
|
15210
|
+
|
|
15211
|
+
|
|
15105
15212
|
def _build_public_chart_config_payload(
|
|
15106
15213
|
*,
|
|
15107
15214
|
patch: ChartUpsertPatch,
|