@josephyan/qingflow-cli 1.1.8 → 1.1.9

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-cli@1.1.8
6
+ npm install @josephyan/qingflow-cli@1.1.9
7
7
  ```
8
8
 
9
9
  Run:
10
10
 
11
11
  ```bash
12
- npx -y -p @josephyan/qingflow-cli@1.1.8 qingflow
12
+ npx -y -p @josephyan/qingflow-cli@1.1.9 qingflow
13
13
  ```
14
14
 
15
15
  Environment:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@josephyan/qingflow-cli",
3
- "version": "1.1.8",
3
+ "version": "1.1.9",
4
4
  "description": "Human-friendly Qingflow command line interface for auth, record operations, import, tasks, and stable builder flows.",
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 = "1.1.8"
7
+ version = "1.1.9"
8
8
  description = "User-authenticated MCP server for Qingflow"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -5999,6 +5999,20 @@ class AiBuilderFacade:
5999
5999
  result["message"] = "read app chart config"
6000
6000
  return result
6001
6001
 
6002
+ def _chart_filter_field_names_by_id(
6003
+ self,
6004
+ *,
6005
+ profile: str,
6006
+ app_key: str,
6007
+ ) -> tuple[dict[str, str], dict[str, Any] | None]:
6008
+ try:
6009
+ state = self._load_base_schema_state(profile=profile, app_key=app_key)
6010
+ except (QingflowApiError, RuntimeError) as error:
6011
+ api_error = _coerce_api_error(error)
6012
+ return {}, _transport_error_payload(api_error)
6013
+ fields = cast(list[dict[str, Any]], state["parsed"]["fields"])
6014
+ return _chart_field_names_by_id_from_public_fields(app_key=app_key, fields=fields), None
6015
+
6002
6016
  def app_get_buttons(self, *, profile: str, app_key: str) -> JSONObject:
6003
6017
  self.apps._require_app_key(app_key)
6004
6018
  warnings: list[dict[str, Any]] = []
@@ -6525,6 +6539,7 @@ class AiBuilderFacade:
6525
6539
  charts = _summarize_charts(items)
6526
6540
  chart_visibility_read_errors: list[dict[str, Any]] = []
6527
6541
  chart_config_read_errors: list[dict[str, Any]] = []
6542
+ field_name_by_id, field_name_read_error = self._chart_filter_field_names_by_id(profile=profile, app_key=resolved_app_key)
6528
6543
  for chart in charts:
6529
6544
  chart_id = str(chart.get("chart_id") or "").strip()
6530
6545
  if not chart_id:
@@ -6564,7 +6579,7 @@ class AiBuilderFacade:
6564
6579
  if isinstance(config, dict):
6565
6580
  chart["group_by"] = _public_chart_group_by_from_qingbi_config(config)
6566
6581
  chart["metrics"] = _public_chart_metrics_from_qingbi_config(config)
6567
- chart["filters"] = _public_chart_filter_groups_from_qingbi_config(config)
6582
+ chart["filters"] = _public_chart_filter_groups_from_qingbi_config(config, field_name_by_id=field_name_by_id)
6568
6583
  response = AppChartsReadResponse(
6569
6584
  app_key=resolved_app_key,
6570
6585
  charts=charts,
@@ -6581,6 +6596,7 @@ class AiBuilderFacade:
6581
6596
  "details": {
6582
6597
  **({"chart_visibility_read_errors": chart_visibility_read_errors} if chart_visibility_read_errors else {}),
6583
6598
  **({"chart_config_read_errors": chart_config_read_errors} if chart_config_read_errors else {}),
6599
+ **({"chart_filter_field_name_read_error": field_name_read_error} if field_name_read_error else {}),
6584
6600
  },
6585
6601
  "request_id": None,
6586
6602
  "suggested_next_call": None,
@@ -6597,6 +6613,11 @@ class AiBuilderFacade:
6597
6613
  if chart_config_read_errors
6598
6614
  else []
6599
6615
  )
6616
+ + (
6617
+ [_warning("CHART_FILTER_FIELD_NAMES_UNRESOLVED", "chart configs were read, but form fields could not be loaded to resolve filter field names")]
6618
+ if field_name_read_error
6619
+ else []
6620
+ )
6600
6621
  ),
6601
6622
  "verification": {
6602
6623
  "app_exists": True,
@@ -6604,8 +6625,9 @@ class AiBuilderFacade:
6604
6625
  "chart_list_source": list_source,
6605
6626
  "chart_visibility_readback_complete": not chart_visibility_read_errors,
6606
6627
  "chart_config_readback_complete": not chart_config_read_errors,
6628
+ "chart_filter_field_names_resolved": not field_name_read_error,
6607
6629
  },
6608
- "verified": not chart_config_read_errors,
6630
+ "verified": not chart_config_read_errors and not field_name_read_error,
6609
6631
  **response.model_dump(mode="json"),
6610
6632
  }
6611
6633
 
@@ -7803,11 +7825,24 @@ class AiBuilderFacade:
7803
7825
  suggested_next_call={"tool_name": "chart_get", "arguments": {"profile": profile, "chart_id": chart_id}},
7804
7826
  )
7805
7827
 
7828
+ field_name_by_id: dict[str, str] = {}
7829
+ data_source = config.get("dataSource") if isinstance(config.get("dataSource"), dict) else {}
7830
+ data_source_app_key = str(data_source.get("dataSourceId") or config.get("dataSourceId") or "").strip()
7831
+ if data_source_app_key:
7832
+ field_name_by_id, field_name_error = self._chart_filter_field_names_by_id(profile=profile, app_key=data_source_app_key)
7833
+ if field_name_error:
7834
+ warnings.append(
7835
+ _warning(
7836
+ "CHART_FILTER_FIELD_NAMES_UNRESOLVED",
7837
+ "chart config was read, but form fields could not be loaded to resolve filter field names",
7838
+ **field_name_error,
7839
+ )
7840
+ )
7806
7841
  response = ChartGetResponse(
7807
7842
  chart_id=chart_id,
7808
7843
  base=deepcopy(base) if isinstance(base, dict) else {},
7809
7844
  visibility=_public_visibility_from_chart_visible_auth(base.get("visibleAuth")),
7810
- filters=_public_chart_filter_groups_from_qingbi_config(config) if isinstance(config, dict) else [],
7845
+ filters=_public_chart_filter_groups_from_qingbi_config(config, field_name_by_id=field_name_by_id) if isinstance(config, dict) else [],
7811
7846
  group_by=_public_chart_group_by_from_qingbi_config(config) if isinstance(config, dict) else [],
7812
7847
  metrics=_public_chart_metrics_from_qingbi_config(config) if isinstance(config, dict) else [],
7813
7848
  config=deepcopy(config) if isinstance(config, dict) else {},
@@ -16316,11 +16351,38 @@ def _qingbi_chart_filter_value_to_text(*, value: Any, form_field: dict[str, Any]
16316
16351
  reject(value)
16317
16352
 
16318
16353
 
16319
- def _public_chart_filter_groups_from_qingbi_config(config: dict[str, Any]) -> list[list[dict[str, Any]]]:
16354
+ def _chart_field_names_by_id_from_public_fields(*, app_key: str, fields: list[dict[str, Any]]) -> dict[str, str]:
16355
+ field_name_by_id: dict[str, str] = {}
16356
+ for field in fields:
16357
+ if not isinstance(field, dict):
16358
+ continue
16359
+ name = str(field.get("name") or "").strip()
16360
+ if not name:
16361
+ continue
16362
+ que_id = field.get("que_id")
16363
+ field_id = str(field.get("field_id") or "").strip()
16364
+ for raw_key in (
16365
+ que_id,
16366
+ field_id,
16367
+ f"{app_key}:{que_id}" if que_id is not None else None,
16368
+ f"{app_key}:{field_id}" if field_id else None,
16369
+ ):
16370
+ key = str(raw_key or "").strip()
16371
+ if key:
16372
+ field_name_by_id.setdefault(key, name)
16373
+ return field_name_by_id
16374
+
16375
+
16376
+ def _public_chart_filter_groups_from_qingbi_config(
16377
+ config: dict[str, Any],
16378
+ *,
16379
+ field_name_by_id: dict[str, str] | None = None,
16380
+ ) -> list[list[dict[str, Any]]]:
16320
16381
  groups: list[list[dict[str, Any]]] = []
16321
16382
  raw_groups = config.get("beforeAggregationFilterMatrix")
16322
16383
  if not isinstance(raw_groups, list):
16323
16384
  return groups
16385
+ resolved_field_name_by_id = field_name_by_id or {}
16324
16386
  for raw_group in raw_groups:
16325
16387
  if not isinstance(raw_group, list):
16326
16388
  continue
@@ -16330,19 +16392,26 @@ def _public_chart_filter_groups_from_qingbi_config(config: dict[str, Any]) -> li
16330
16392
  continue
16331
16393
  operator = _public_chart_filter_operator_from_judge_type(raw_rule.get("judgeType"))
16332
16394
  field_id = raw_rule.get("fieldId") or raw_rule.get("field_id")
16333
- field_name = (
16395
+ field_id_text = _stringify_condition_value(field_id).strip() if field_id is not None else ""
16396
+ raw_field_name = (
16334
16397
  raw_rule.get("fieldName")
16335
16398
  or raw_rule.get("field_name")
16336
16399
  or raw_rule.get("queTitle")
16337
16400
  or raw_rule.get("title")
16338
- or field_id
16401
+ )
16402
+ raw_field_name_text = _stringify_condition_value(raw_field_name).strip()
16403
+ field_name = (
16404
+ resolved_field_name_by_id.get(raw_field_name_text)
16405
+ or resolved_field_name_by_id.get(field_id_text)
16406
+ or raw_field_name_text
16407
+ or field_id_text
16339
16408
  )
16340
16409
  public_rule: dict[str, Any] = {
16341
- "field_name": _stringify_condition_value(field_name).strip(),
16410
+ "field_name": field_name,
16342
16411
  "operator": operator,
16343
16412
  }
16344
16413
  if field_id is not None:
16345
- public_rule["field_id"] = _stringify_condition_value(field_id).strip()
16414
+ public_rule["field_id"] = field_id_text
16346
16415
  values = _public_chart_filter_values_from_rule(raw_rule, operator=operator)
16347
16416
  if values:
16348
16417
  public_rule["values"] = values