hap-cli 0.8.25__tar.gz → 0.8.26__tar.gz

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.
Files changed (286) hide show
  1. {hap_cli-0.8.25 → hap_cli-0.8.26}/PKG-INFO +1 -1
  2. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/__init__.py +1 -1
  3. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/commands/chart_cmd.py +5 -2
  4. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/commands/record_cmd.py +14 -2
  5. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/chart.py +30 -0
  6. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/record.py +7 -0
  7. hap_cli-0.8.26/hap_cli/guide/en/chart.md +150 -0
  8. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/guide/index.json +8 -0
  9. hap_cli-0.8.26/hap_cli/guide/zh-Hans/chart.md +133 -0
  10. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/locale/messages.json +9 -5
  11. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/tests/test_chart.py +33 -0
  12. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/tests/test_core.py +24 -0
  13. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli.egg-info/PKG-INFO +1 -1
  14. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli.egg-info/SOURCES.txt +2 -0
  15. {hap_cli-0.8.25 → hap_cli-0.8.26}/MANIFEST.in +0 -0
  16. {hap_cli-0.8.25 → hap_cli-0.8.26}/README.md +0 -0
  17. {hap_cli-0.8.25 → hap_cli-0.8.26}/app_live_tests/__init__.py +0 -0
  18. {hap_cli-0.8.25 → hap_cli-0.8.26}/app_live_tests/__main__.py +0 -0
  19. {hap_cli-0.8.25 → hap_cli-0.8.26}/app_live_tests/config.py +0 -0
  20. {hap_cli-0.8.25 → hap_cli-0.8.26}/app_live_tests/harness.py +0 -0
  21. {hap_cli-0.8.25 → hap_cli-0.8.26}/app_live_tests/smoke.py +0 -0
  22. {hap_cli-0.8.25 → hap_cli-0.8.26}/app_live_tests/state.py +0 -0
  23. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/README.md +0 -0
  24. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/README_CN.md +0 -0
  25. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/__main__.py +0 -0
  26. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/commands/__init__.py +0 -0
  27. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/commands/app_cmd.py +0 -0
  28. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/commands/app_creator_cmd.py +0 -0
  29. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/commands/app_editor_cmd.py +0 -0
  30. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/commands/attachment_cmd.py +0 -0
  31. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/commands/auth_cmd.py +0 -0
  32. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/commands/calendar_cmd.py +0 -0
  33. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/commands/chat_cmd.py +0 -0
  34. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/commands/chatbot_cmd.py +0 -0
  35. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/commands/config_cmd.py +0 -0
  36. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/commands/contact_cmd.py +0 -0
  37. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/commands/department_cmd.py +0 -0
  38. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/commands/group_cmd.py +0 -0
  39. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/commands/guide_cmd.py +0 -0
  40. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/commands/icon_cmd.py +0 -0
  41. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/commands/instance_cmd.py +0 -0
  42. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/commands/node_cmd.py +0 -0
  43. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/commands/optionset_cmd.py +0 -0
  44. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/commands/page_cmd.py +0 -0
  45. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/commands/post_cmd.py +0 -0
  46. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/commands/region_cmd.py +0 -0
  47. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/commands/role_cmd.py +0 -0
  48. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/commands/update_cmd.py +0 -0
  49. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/commands/upload_cmd.py +0 -0
  50. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/commands/v3_registry.py +0 -0
  51. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/commands/workflow_cmd.py +0 -0
  52. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/commands/worksheet_cmd.py +0 -0
  53. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/context.py +0 -0
  54. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/__init__.py +0 -0
  55. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/action_spec_adapter.py +0 -0
  56. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/app.py +0 -0
  57. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/app_creator/__init__.py +0 -0
  58. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/app_creator/_hapmeta/__init__.py +0 -0
  59. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/app_creator/_hapmeta/control_type_codes.py +0 -0
  60. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/app_creator/_hapmeta/worksheet_templates.py +0 -0
  61. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/app_creator/charts.py +0 -0
  62. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/app_creator/cleanup.py +0 -0
  63. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/app_creator/compiler.py +0 -0
  64. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/app_creator/config.py +0 -0
  65. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/app_creator/design/design.schema.json +0 -0
  66. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/app_creator/errors.py +0 -0
  67. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/app_creator/examples/minimal.design.json +0 -0
  68. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/app_creator/executor.py +0 -0
  69. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/app_creator/fields.py +0 -0
  70. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/app_creator/hap.py +0 -0
  71. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/app_creator/merge.py +0 -0
  72. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/app_creator/recording/__init__.py +0 -0
  73. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/app_creator/recording/console.py +0 -0
  74. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/app_creator/recording/jsonl.py +0 -0
  75. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/app_creator/recording/mirror.py +0 -0
  76. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/app_creator/recording/report.py +0 -0
  77. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/app_creator/schema.py +0 -0
  78. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/app_creator/seed/INSTRUCTIONS.md +0 -0
  79. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/app_creator/seed/__init__.py +0 -0
  80. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/app_creator/seed/cli.py +0 -0
  81. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/app_creator/seed/executor.py +0 -0
  82. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/app_creator/seed/resources/attachments.json +0 -0
  83. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/app_creator/seed/resources/sample.docx +0 -0
  84. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/app_creator/seed/resources/sample.pdf +0 -0
  85. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/app_creator/seed/resources/sample_images.json +0 -0
  86. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/app_creator/seed/template.py +0 -0
  87. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/app_creator/selftest.py +0 -0
  88. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/app_creator/smoke.py +0 -0
  89. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/app_creator/steps.py +0 -0
  90. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/app_creator/store.py +0 -0
  91. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/app_creator/validate.py +0 -0
  92. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/app_creator/workflow_dsl.py +0 -0
  93. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/attachment.py +0 -0
  94. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/auth.py +0 -0
  95. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/calendar_mod.py +0 -0
  96. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/chat.py +0 -0
  97. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/chatbot.py +0 -0
  98. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/cli_access.py +0 -0
  99. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/contact.py +0 -0
  100. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/control_type_codes.py +0 -0
  101. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/department.py +0 -0
  102. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/editor/__init__.py +0 -0
  103. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/editor/_hapmeta/__init__.py +0 -0
  104. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/editor/_hapmeta/control_type_codes.py +0 -0
  105. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/editor/apply.py +0 -0
  106. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/editor/componentlower.py +0 -0
  107. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/editor/editspec/component.schema.json +0 -0
  108. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/editor/editspec/custom-action.schema.json +0 -0
  109. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/editor/editspec/envelope.schema.json +0 -0
  110. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/editor/editspec/field.schema.json +0 -0
  111. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/editor/editspec.py +0 -0
  112. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/editor/errors.py +0 -0
  113. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/editor/fieldlower.py +0 -0
  114. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/editor/jsonschema_mini.py +0 -0
  115. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/editor/models.py +0 -0
  116. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/editor/ops.py +0 -0
  117. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/editor/planner.py +0 -0
  118. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/editor/reader.py +0 -0
  119. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/field_normalizer.py +0 -0
  120. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/field_spec_adapter.py +0 -0
  121. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/filter_translator.py +0 -0
  122. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/flow_node.py +0 -0
  123. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/global_meta.py +0 -0
  124. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/group.py +0 -0
  125. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/guide.py +0 -0
  126. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/icon_index.py +0 -0
  127. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/instance.py +0 -0
  128. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/logger.py +0 -0
  129. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/optionset.py +0 -0
  130. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/page.py +0 -0
  131. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/post.py +0 -0
  132. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/response_crypto.py +0 -0
  133. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/role.py +0 -0
  134. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/role_perm_builder.py +0 -0
  135. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/session.py +0 -0
  136. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/token_crypto.py +0 -0
  137. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/update.py +0 -0
  138. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/upload.py +0 -0
  139. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/v3/__init__.py +0 -0
  140. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/v3/dispatcher.py +0 -0
  141. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/v3/render.py +0 -0
  142. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/v3/schema.py +0 -0
  143. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/view_spec_adapter.py +0 -0
  144. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/workflow.py +0 -0
  145. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/workflow_node_dsl.py +0 -0
  146. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/worksheet.py +0 -0
  147. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/core/worksheet_templates.py +0 -0
  148. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/guide/en/messages.md +0 -0
  149. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/guide/en/overview.md +0 -0
  150. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/guide/en/record.md +0 -0
  151. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/guide/en/setup.md +0 -0
  152. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/guide/en/skills.md +0 -0
  153. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/guide/zh-Hans/messages.md +0 -0
  154. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/guide/zh-Hans/overview.md +0 -0
  155. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/guide/zh-Hans/record.md +0 -0
  156. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/guide/zh-Hans/setup.md +0 -0
  157. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/guide/zh-Hans/skills.md +0 -0
  158. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/hap_cli.py +0 -0
  159. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/i18n.py +0 -0
  160. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/locale/__init__.py +0 -0
  161. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/locale/messages.schema.json +0 -0
  162. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/tests/__init__.py +0 -0
  163. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/tests/conftest.py +0 -0
  164. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/tests/test_app_creator.py +0 -0
  165. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/tests/test_app_editor.py +0 -0
  166. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/tests/test_app_export_usage.py +0 -0
  167. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/tests/test_attachment_share.py +0 -0
  168. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/tests/test_auth_prerelease.py +0 -0
  169. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/tests/test_bug_036430.py +0 -0
  170. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/tests/test_bug_036750.py +0 -0
  171. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/tests/test_chat_attachment_send.py +0 -0
  172. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/tests/test_cli_access.py +0 -0
  173. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/tests/test_config_cmd.py +0 -0
  174. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/tests/test_content_language.py +0 -0
  175. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/tests/test_control_lookup_guard.py +0 -0
  176. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/tests/test_destructive_confirm_cli.py +0 -0
  177. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/tests/test_full_e2e.py +0 -0
  178. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/tests/test_global_meta.py +0 -0
  179. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/tests/test_guide.py +0 -0
  180. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/tests/test_i18n.py +0 -0
  181. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/tests/test_instance_history_cli.py +0 -0
  182. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/tests/test_integration.py +0 -0
  183. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/tests/test_integration_approval.py +0 -0
  184. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/tests/test_integration_approval_actions.py +0 -0
  185. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/tests/test_integration_calendar.py +0 -0
  186. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/tests/test_integration_destructive.py +0 -0
  187. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/tests/test_integration_misc.py +0 -0
  188. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/tests/test_integration_post.py +0 -0
  189. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/tests/test_integration_social.py +0 -0
  190. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/tests/test_integration_v3.py +0 -0
  191. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/tests/test_integration_workflow.py +0 -0
  192. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/tests/test_integration_worksheet_extra.py +0 -0
  193. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/tests/test_member_relation_array_wrap.py +0 -0
  194. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/tests/test_multi_profile.py +0 -0
  195. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/tests/test_no_workflow_unsupported.py +0 -0
  196. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/tests/test_org_approval_v3_semantic_cli.py +0 -0
  197. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/tests/test_org_id_cli.py +0 -0
  198. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/tests/test_org_id_docs.py +0 -0
  199. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/tests/test_parameter_conventions.py +0 -0
  200. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/tests/test_parameter_mapping_registry.py +0 -0
  201. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/tests/test_record_batch_v3_semantic_cli.py +0 -0
  202. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/tests/test_record_field_alias.py +0 -0
  203. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/tests/test_record_v3_semantic_cli.py +0 -0
  204. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/tests/test_record_write_fixes.py +0 -0
  205. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/tests/test_select_add_option.py +0 -0
  206. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/tests/test_select_rejects_relation_value.py +0 -0
  207. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/tests/test_semantic_types_registry.py +0 -0
  208. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/tests/test_update.py +0 -0
  209. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/tests/test_update_mode.py +0 -0
  210. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/tests/test_upload_host_resolution.py +0 -0
  211. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/tests/test_v3_api_schema_loader.py +0 -0
  212. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/tests/test_v3_dispatcher.py +0 -0
  213. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/tests/test_v3_registry_coverage.py +0 -0
  214. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/tests/test_v3_session.py +0 -0
  215. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/tests/test_v3_yaml_translation.py +0 -0
  216. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/tests/test_workflow_envelope_errors.py +0 -0
  217. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/tests/test_workflow_list_cli.py +0 -0
  218. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/tests/test_workflow_read_write_keys.py +0 -0
  219. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/tests/test_workflow_v3_semantic_cli.py +0 -0
  220. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/tests/test_worksheet_create_section.py +0 -0
  221. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/tests/test_worksheet_crud_cli.py +0 -0
  222. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/tests/test_worksheet_v3_semantic_cli.py +0 -0
  223. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/utils/__init__.py +0 -0
  224. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/utils/formatting.py +0 -0
  225. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/utils/options.py +0 -0
  226. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli/utils/parameter_mapping.py +0 -0
  227. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli.egg-info/dependency_links.txt +0 -0
  228. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli.egg-info/entry_points.txt +0 -0
  229. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli.egg-info/requires.txt +0 -0
  230. {hap_cli-0.8.25 → hap_cli-0.8.26}/hap_cli.egg-info/top_level.txt +0 -0
  231. {hap_cli-0.8.25 → hap_cli-0.8.26}/live_tests/__init__.py +0 -0
  232. {hap_cli-0.8.25 → hap_cli-0.8.26}/live_tests/__main__.py +0 -0
  233. {hap_cli-0.8.25 → hap_cli-0.8.26}/live_tests/_wftest.py +0 -0
  234. {hap_cli-0.8.25 → hap_cli-0.8.26}/live_tests/charts.py +0 -0
  235. {hap_cli-0.8.25 → hap_cli-0.8.26}/live_tests/cleanup.py +0 -0
  236. {hap_cli-0.8.25 → hap_cli-0.8.26}/live_tests/compiler.py +0 -0
  237. {hap_cli-0.8.25 → hap_cli-0.8.26}/live_tests/config.py +0 -0
  238. {hap_cli-0.8.25 → hap_cli-0.8.26}/live_tests/errors.py +0 -0
  239. {hap_cli-0.8.25 → hap_cli-0.8.26}/live_tests/executor.py +0 -0
  240. {hap_cli-0.8.25 → hap_cli-0.8.26}/live_tests/fields.py +0 -0
  241. {hap_cli-0.8.25 → hap_cli-0.8.26}/live_tests/hap.py +0 -0
  242. {hap_cli-0.8.25 → hap_cli-0.8.26}/live_tests/recording/__init__.py +0 -0
  243. {hap_cli-0.8.25 → hap_cli-0.8.26}/live_tests/recording/console.py +0 -0
  244. {hap_cli-0.8.25 → hap_cli-0.8.26}/live_tests/recording/jsonl.py +0 -0
  245. {hap_cli-0.8.25 → hap_cli-0.8.26}/live_tests/recording/mirror.py +0 -0
  246. {hap_cli-0.8.25 → hap_cli-0.8.26}/live_tests/recording/report.py +0 -0
  247. {hap_cli-0.8.25 → hap_cli-0.8.26}/live_tests/schema.py +0 -0
  248. {hap_cli-0.8.25 → hap_cli-0.8.26}/live_tests/seed/__init__.py +0 -0
  249. {hap_cli-0.8.25 → hap_cli-0.8.26}/live_tests/seed/cli.py +0 -0
  250. {hap_cli-0.8.25 → hap_cli-0.8.26}/live_tests/seed/executor.py +0 -0
  251. {hap_cli-0.8.25 → hap_cli-0.8.26}/live_tests/seed/template.py +0 -0
  252. {hap_cli-0.8.25 → hap_cli-0.8.26}/live_tests/selftest.py +0 -0
  253. {hap_cli-0.8.25 → hap_cli-0.8.26}/live_tests/smoke.py +0 -0
  254. {hap_cli-0.8.25 → hap_cli-0.8.26}/live_tests/step.py +0 -0
  255. {hap_cli-0.8.25 → hap_cli-0.8.26}/live_tests/steps.py +0 -0
  256. {hap_cli-0.8.25 → hap_cli-0.8.26}/live_tests/store.py +0 -0
  257. {hap_cli-0.8.25 → hap_cli-0.8.26}/live_tests/workflow_dsl.py +0 -0
  258. {hap_cli-0.8.25 → hap_cli-0.8.26}/setup.cfg +0 -0
  259. {hap_cli-0.8.25 → hap_cli-0.8.26}/setup.py +0 -0
  260. {hap_cli-0.8.25 → hap_cli-0.8.26}/sources/v3-api-schema/INDEX.json +0 -0
  261. {hap_cli-0.8.25 → hap_cli-0.8.26}/sources/v3-api-schema/add_member_to_role.yaml +0 -0
  262. {hap_cli-0.8.25 → hap_cli-0.8.26}/sources/v3-api-schema/batch_delete_records.yaml +0 -0
  263. {hap_cli-0.8.25 → hap_cli-0.8.26}/sources/v3-api-schema/create_optionset.yaml +0 -0
  264. {hap_cli-0.8.25 → hap_cli-0.8.26}/sources/v3-api-schema/create_role.yaml +0 -0
  265. {hap_cli-0.8.25 → hap_cli-0.8.26}/sources/v3-api-schema/delete_optionset.yaml +0 -0
  266. {hap_cli-0.8.25 → hap_cli-0.8.26}/sources/v3-api-schema/delete_record.yaml +0 -0
  267. {hap_cli-0.8.25 → hap_cli-0.8.26}/sources/v3-api-schema/delete_role.yaml +0 -0
  268. {hap_cli-0.8.25 → hap_cli-0.8.26}/sources/v3-api-schema/delete_worksheet.yaml +0 -0
  269. {hap_cli-0.8.25 → hap_cli-0.8.26}/sources/v3-api-schema/get_app_info.yaml +0 -0
  270. {hap_cli-0.8.25 → hap_cli-0.8.26}/sources/v3-api-schema/get_app_knowledge_list.yaml +0 -0
  271. {hap_cli-0.8.25 → hap_cli-0.8.26}/sources/v3-api-schema/get_app_worksheets_list.yaml +0 -0
  272. {hap_cli-0.8.25 → hap_cli-0.8.26}/sources/v3-api-schema/get_optionset_list.yaml +0 -0
  273. {hap_cli-0.8.25 → hap_cli-0.8.26}/sources/v3-api-schema/get_record_details.yaml +0 -0
  274. {hap_cli-0.8.25 → hap_cli-0.8.26}/sources/v3-api-schema/get_record_discussions.yaml +0 -0
  275. {hap_cli-0.8.25 → hap_cli-0.8.26}/sources/v3-api-schema/get_record_list.yaml +0 -0
  276. {hap_cli-0.8.25 → hap_cli-0.8.26}/sources/v3-api-schema/get_record_logs.yaml +0 -0
  277. {hap_cli-0.8.25 → hap_cli-0.8.26}/sources/v3-api-schema/get_record_pivot_data.yaml +0 -0
  278. {hap_cli-0.8.25 → hap_cli-0.8.26}/sources/v3-api-schema/get_record_relations.yaml +0 -0
  279. {hap_cli-0.8.25 → hap_cli-0.8.26}/sources/v3-api-schema/get_record_share_link.yaml +0 -0
  280. {hap_cli-0.8.25 → hap_cli-0.8.26}/sources/v3-api-schema/get_regions.yaml +0 -0
  281. {hap_cli-0.8.25 → hap_cli-0.8.26}/sources/v3-api-schema/get_role_details.yaml +0 -0
  282. {hap_cli-0.8.25 → hap_cli-0.8.26}/sources/v3-api-schema/get_role_list.yaml +0 -0
  283. {hap_cli-0.8.25 → hap_cli-0.8.26}/sources/v3-api-schema/knowledge_search.yaml +0 -0
  284. {hap_cli-0.8.25 → hap_cli-0.8.26}/sources/v3-api-schema/leave_all_roles.yaml +0 -0
  285. {hap_cli-0.8.25 → hap_cli-0.8.26}/sources/v3-api-schema/remove_member_from_role.yaml +0 -0
  286. {hap_cli-0.8.25 → hap_cli-0.8.26}/sources/v3-api-schema/update_optionset.yaml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hap-cli
3
- Version: 0.8.25
3
+ Version: 0.8.26
4
4
  Summary: CLI harness for Mingdao HAP - Enterprise no-code platform
5
5
  Author: hap-cli
6
6
  License: Apache-2.0
@@ -1,3 +1,3 @@
1
1
  """CLI harness for Mingdao HAP (hap-cli)."""
2
2
 
3
- __version__ = "0.8.25"
3
+ __version__ = "0.8.26"
@@ -123,7 +123,8 @@ def _resolve_app_id_for_filter(session, worksheet_id: str, extra: dict) -> str:
123
123
  return (data or {}).get("appId", "") if isinstance(data, dict) else ""
124
124
 
125
125
 
126
- @chart.command("create")
126
+ @chart.command("create", help="Create a new chart on a worksheet. "
127
+ "Chart spec shapes are in `hap guide chart`.")
127
128
  @click.argument("worksheet_id")
128
129
  @click.option("--name", required=True, help="Chart display name.")
129
130
  @click.option("--report-type", "report_type", type=int, required=True,
@@ -163,7 +164,9 @@ def chart_create(ctx, worksheet_id, name, report_type, app_type, app_id,
163
164
  ctx.handle_error(e)
164
165
 
165
166
 
166
- @chart.command("update")
167
+ @chart.command("update", help="Update an existing chart's configuration. "
168
+ "Read the config with `chart get`, edit it, submit "
169
+ "it back — see `hap guide chart`.")
167
170
  @click.argument("report_id")
168
171
  @click.option("--app-id", "-a", required=True, help="Worksheet ID the chart attaches to.")
169
172
  @click.option("--name", required=True, help="Chart display name.")
@@ -374,16 +374,28 @@ def record_discussions(ctx, worksheet_id, row_id, page_size, page):
374
374
  @click.option("--app-id", default="", help="Application ID (auto-detected from the worksheet if omitted)")
375
375
  @click.option("--view-id", default="", help="View ID the discussion originates from")
376
376
  @click.option("--reply-id", default="", help="ID of the discussion being replied to")
377
+ @click.option("--file", "-f", "files", multiple=True,
378
+ help="Attach a file to the comment (repeatable). Accepts a local "
379
+ "path or an http(s) URL.")
377
380
  @pass_context
378
- def record_add_discussion(ctx, worksheet_id, row_id, message, app_id, view_id, reply_id):
381
+ def record_add_discussion(ctx, worksheet_id, row_id, message, app_id, view_id, reply_id, files):
379
382
  """Add a discussion/comment to a record."""
380
383
  try:
381
384
  session = ctx.get_session()
385
+ attachments = None
386
+ if files:
387
+ from hap_cli.core import upload as up_mod
388
+ descriptors = up_mod.source_descriptors(list(files))
389
+ attachments = up_mod.upload_files(
390
+ session, descriptors,
391
+ worksheet_id=worksheet_id, app_id=app_id,
392
+ )
382
393
  data = rec_mod.add_discussion(
383
394
  session, worksheet_id, row_id, message,
384
395
  app_id=app_id, view_id=view_id, reply_id=reply_id,
396
+ attachments=attachments,
385
397
  )
386
- ctx.output(data, lambda d: click.echo(f"Discussion added."))
398
+ ctx.output(data, lambda d: click.echo("Discussion added."))
387
399
  except Exception as e:
388
400
  ctx.handle_error(e)
389
401
 
@@ -44,6 +44,30 @@ from hap_cli.core.session import Session
44
44
 
45
45
  logger = logging.getLogger("hap_cli.chart")
46
46
 
47
+ # A chart that is saved without a ``filter`` block saves successfully but
48
+ # never renders: the statistics backend cannot scope the query and
49
+ # ``report/getData`` answers status -1/0, which the UI paints as
50
+ # "无法形成图表 / 构成要素不存在或已删除". The chart looks fine in the editor
51
+ # (which supplies its own default scope) and only breaks after save — the
52
+ # single most common way a hand-written ``chart create`` spec silently
53
+ # produces a blank chart. The frontend's ``getNewReport`` always injects a
54
+ # default time filter for a new worksheet chart; we mirror that here, but
55
+ # default to ``rangeType: 0`` (全部时间 / all-time) instead of the UI's
56
+ # "last 365 days" so the chart is never blank merely because the default
57
+ # window excluded older data. Callers that supply their own ``filter``
58
+ # (any non-empty dict) keep it untouched.
59
+ _DEFAULT_CHART_FILTER: dict[str, Any] = {
60
+ "filterRangeId": "ctime",
61
+ "filterRangeName": "创建时间",
62
+ "rangeType": 0,
63
+ "rangeValue": None,
64
+ "today": False,
65
+ "dynamicFilter": {
66
+ "startType": 1, "startCount": 1, "startUnit": 1,
67
+ "endType": 1, "endCount": 1, "endUnit": 1,
68
+ },
69
+ }
70
+
47
71
 
48
72
  def list_charts(
49
73
  session: Session,
@@ -218,6 +242,12 @@ def save_chart(
218
242
  split_control_id = body["split"].get("controlId")
219
243
  if split_control_id:
220
244
  body["splitId"] = split_control_id
245
+ # A chart with no filter block saves but never renders (blank after
246
+ # save — see _DEFAULT_CHART_FILTER). Inject an all-time default when the
247
+ # caller omitted it; a caller-supplied filter (any non-empty value) is
248
+ # left untouched so an explicit scope always wins.
249
+ if not body.get("filter"):
250
+ body["filter"] = dict(_DEFAULT_CHART_FILTER)
221
251
  # Resolve the chart's filter block before sending saveReportConfig.
222
252
  if isinstance(body.get("filter"), dict):
223
253
  body["filter"] = _ensure_filter_id(
@@ -1089,6 +1089,7 @@ def add_discussion(
1089
1089
  app_id: str = "",
1090
1090
  view_id: str = "",
1091
1091
  reply_id: str = "",
1092
+ attachments: Optional[list[dict[str, Any]]] = None,
1092
1093
  ) -> dict[str, Any]:
1093
1094
  """Add a discussion/comment to a record (sourceType 8 / worksheet-row).
1094
1095
 
@@ -1117,6 +1118,12 @@ def add_discussion(
1117
1118
  }
1118
1119
  if reply_id:
1119
1120
  data["replyId"] = reply_id
1121
+ # ``attachments`` carries uploaded-file descriptors (from
1122
+ # hap_cli.core.upload). The frontend WorkSheetCommenter sends them as a
1123
+ # JSON-encoded string under ``attachments`` (see pd-openweb
1124
+ # components/comment/commenter.jsx), so match that shape.
1125
+ if attachments:
1126
+ data["attachments"] = json.dumps(attachments, ensure_ascii=False)
1120
1127
  result = session.api_call("Discussion", "AddDiscussion", data)
1121
1128
  _ensure_discussion_ok(result, "add discussion")
1122
1129
  return result
@@ -0,0 +1,150 @@
1
+ # Charts: create, update, place on a custom page
2
+
3
+ `hap worksheet chart` builds a chart (report) on a worksheet; `hap custom-page`
4
+ places charts into a custom page to form a dashboard. This topic covers how to
5
+ shape a chart spec, the right way to edit a chart, and the most common trap —
6
+ "saved fine, blank on the page".
7
+
8
+ > 🚨 Core risk of this topic: **an under-specified chart still saves
9
+ > successfully but renders blank**, showing "无法形成图表 / 构成要素不存在或已删除"
10
+ > (cannot draw chart / component missing or deleted). It looks right in the
11
+ > editor and only breaks after save. Root cause is in section 3 — the CLI now
12
+ > backfills the missing filter scope, but you still need the model.
13
+
14
+ ## 1. What a chart is made of
15
+
16
+ A chart hangs off **one worksheet** and needs at least three things:
17
+
18
+ - **Data source**: the worksheet ID (`WORKSHEET_ID` in the command, `appId` in
19
+ the spec). Note HAP's chart spec calls the worksheet `appId` — it is NOT the
20
+ application ID. When editing a chart, do not fill this with the app ID.
21
+ - **Dimension `xaxes`**: the grouping, e.g. by date (year) or by status. A
22
+ number chart (`report_type=10`) may have no dimension — just a value.
23
+ - **Value `yaxisList`**: what to aggregate, e.g. sum of amount, record count.
24
+
25
+ Plus a **required filter scope `filter`** (section 3).
26
+
27
+ ## 2. Chart types (reportType integer)
28
+
29
+ Pass an integer to `--report-type`. **There is no 4** — an old API doc labels 4
30
+ as the number chart, which is wrong; the number chart is **10**.
31
+
32
+ | # | type | # | type |
33
+ |---|------|---|------|
34
+ | 1 | column | 10 | number |
35
+ | 2 | line | 11 | bidirectional bar |
36
+ | 3 | pie | 12 | scatter |
37
+ | 5 | radar | 13 | word cloud |
38
+ | 6 | funnel | 14 | gauge |
39
+ | 7 | dual-axis | 15 | progress |
40
+ | 8 | pivot | 16 | ranking |
41
+ | 9 | region map | 17 | world map |
42
+
43
+ ## 3. Why "saved but blank": a missing filter
44
+
45
+ A chart **saves successfully even with no `filter` block** (`create` still
46
+ returns a `reportId`), but at render time the backend cannot scope the query and
47
+ `report/getData` returns status -1/0, so the page shows "cannot draw chart". This
48
+ is exactly the "fine in editor, blank after save" symptom: the editor supplies
49
+ its own default scope, the stored spec does not.
50
+
51
+ **The CLI now injects a default all-time filter when you omit `filter`**, so a
52
+ plain `chart create` no longer goes blank. You only need to write `filter`
53
+ yourself to **restrict the time range**:
54
+
55
+ ```jsonc
56
+ "filter": {
57
+ "filterRangeId": "ctime", // which date field to scope by; "ctime"=create time
58
+ "filterRangeName": "创建时间",
59
+ "rangeType": 0, // 0=all time; 8=this month; 18=last N days (with rangeValue)
60
+ "rangeValue": null,
61
+ "today": false
62
+ }
63
+ ```
64
+
65
+ Common `rangeType`: `0` all, `4` this week, `8` this month, `11` this quarter,
66
+ `15` this year, `16` last year, `18` last N days (`rangeValue` = day count, e.g. 365).
67
+
68
+ ## 4. Create: `hap worksheet chart create`
69
+
70
+ Pass the spec via `-j/--spec-json` (inline) or `-f/--spec-file` (file). Reference
71
+ fields by **controlId**; read fields with `hap worksheet fields WS_ID`.
72
+
73
+ **Column chart (amount by status):**
74
+
75
+ ```bash
76
+ hap worksheet chart create WS_ID --name "Amount by status" --report-type 1 -j '{
77
+ "xaxes": {"controlId": "<status field>", "controlType": 11},
78
+ "yaxisList": [{"controlId": "<amount field>", "controlType": 6, "normType": 1}]
79
+ }'
80
+ ```
81
+
82
+ **Number chart (no dimension, one value):** `--report-type 10`, spec with only `yaxisList`.
83
+
84
+ `yaxisList[].normType` aggregation: `1` sum, `2` max, `3` min, `4` avg, `5` count,
85
+ `6` distinct count. Numeric fields default to `1`, text/option fields use `5`.
86
+ Record count uses the special column
87
+ `{"controlId": "record_count", "controlName": "记录数量", "controlType": 10000000, "normType": 5}`.
88
+
89
+ **Date dimension**: add `particleSizeType` to `xaxes`: `5` year, `4` quarter,
90
+ `3` month, `2` week, `1` day.
91
+
92
+ **Condition filter** (not a time range but "status = done"): put a high-level
93
+ `items` array in `filter`; the CLI stores it as a saved filter and backfills the id:
94
+
95
+ ```jsonc
96
+ "filter": {"items": [{"controlId": "<status field>", "dataType": 11, "spliceType": 1,
97
+ "filterType": 2, "values": ["<option key>"]}]}
98
+ ```
99
+
100
+ When using `items`, pass `--app-id <application id>` (the app, not the worksheet)
101
+ so the CLI can store the filter.
102
+
103
+ ## 5. Update: read the config, change a few params, submit back
104
+
105
+ You don't rebuild a spec from scratch. **Read the current config, change what you
106
+ need, submit the whole thing back:**
107
+
108
+ ```bash
109
+ # 1. read the current config
110
+ hap --json worksheet chart get REPORT_ID --app-id WS_ID > chart.json
111
+ # 2. edit chart.json (name / normType / xaxes / add a filter, etc.)
112
+ # 3. submit it back (update needs --app-id=worksheetId, --name, --report-type)
113
+ hap worksheet chart update REPORT_ID --app-id WS_ID --name "New name" --report-type 1 -f chart.json
114
+ ```
115
+
116
+ Extra fields `get` returns (`controls`, `account`, `createdDate`) can be passed
117
+ back as-is; the server ignores them. `update` replaces the whole config, so edit
118
+ on top of the read config — don't send only the fields you changed.
119
+
120
+ ## 6. Place on a custom page (dashboard)
121
+
122
+ Once the chart exists and you have its `reportId`, add it as a component with
123
+ `custom-page save`.
124
+
125
+ ```bash
126
+ # page version: read it first with hap --json custom-page info PAGE_ID, pass the same value
127
+ hap custom-page save PAGE_ID --version <current version> --components '[
128
+ {"type": 1, "value": "<reportId>", "worksheetId": "<WS_ID>",
129
+ "name": "Amount by status", "reportType": 1,
130
+ "config": {"objectId": "<32 hex chars>"},
131
+ "web": {"title": "", "titleVisible": false, "visible": true,
132
+ "layout": {"x": 0, "y": 0, "w": 24, "h": 10, "minW": 2, "minH": 4}},
133
+ "mobile": {"title": "", "titleVisible": false, "visible": true, "layout": null}}
134
+ ]'
135
+ ```
136
+
137
+ Key points:
138
+
139
+ - A chart component has `type` **1**, `value` = `reportId`, plus `worksheetId`
140
+ and `reportType`.
141
+ - `config.objectId` is a unique 32-hex string per component (used by linked filters).
142
+ - Layout is a **48-column grid**: `w` max 48; two charts per row at `w:24` each.
143
+ - `--version` must equal the page's current version, else "自定义页面保存失败";
144
+ a successful save bumps version by 1.
145
+ - `save` replaces the whole page: to keep existing components, read them via
146
+ `custom-page info` first and append the new one before submitting.
147
+
148
+ For building a whole app with dashboards, prefer the `hap-cli-app-creator` skill:
149
+ it generates full chart specs and page layouts from a friendly config. Use the
150
+ commands above to hand-edit a single chart or add one chart to an existing page.
@@ -24,6 +24,14 @@
24
24
  "zh-Hans": "顺着通知或讨论找到它所属的那条应用记录。"
25
25
  }
26
26
  },
27
+ {
28
+ "name": "chart",
29
+ "title": {"en": "Charts and dashboards", "zh-Hans": "统计图与看板"},
30
+ "summary": {
31
+ "en": "Chart spec shape, editing a chart, placing it on a custom page. Miss a field and it saves fine but renders blank.",
32
+ "zh-Hans": "图表规格怎么写、怎么改图、怎么放进自定义页。写少了照样保存成功,页面却空白。"
33
+ }
34
+ },
27
35
  {
28
36
  "name": "skills",
29
37
  "title": {"en": "When to hand off", "zh-Hans": "什么时候转交专项 skill"},
@@ -0,0 +1,133 @@
1
+ # 统计图:创建、修改、放进自定义页
2
+
3
+ `hap worksheet chart` 在工作表上建统计图(报表),`hap custom-page` 把图放进自定义页
4
+ 组成看板。本主题讲清楚图表规格该怎么写、改图的正确姿势,以及最容易踩的「保存成功、
5
+ 页面空白」这个坑。
6
+
7
+ > 🚨 本主题核心风险:**图表规格写少了照样保存成功,但页面上一片空白**,提示
8
+ > 「无法形成图表 / 构成要素不存在或已删除」。编辑界面能正常显示,一保存就没了。
9
+ > 根因见下面第 3 节——现在 CLI 会自动补上缺失的筛选范围,但你仍要理解这个模型。
10
+
11
+ ## 1. 一张图由什么组成
12
+
13
+ 统计图挂在**一张工作表**上,最少需要三件东西:
14
+
15
+ - **数据源**:工作表 ID(就是命令里的 `WORKSHEET_ID` / 规格里的 `appId`)。
16
+ 注意 HAP 的图表规格里 `appId` 指的是**工作表**,不是应用;改图时不要填成应用 ID。
17
+ - **维度 `xaxes`**:分组依据,比如按「日期(年)」「状态」分。数值图(`report_type=10`)
18
+ 可以没有维度,只放数值。
19
+ - **数值 `yaxisList`**:要统计的量,比如「金额求和」「记录数量」。
20
+
21
+ 此外还有一个**必须存在的筛选范围 `filter`**(见第 3 节)。
22
+
23
+ ## 2. 图表类型(reportType 整数)
24
+
25
+ `--report-type` 传整数。**没有 4**——老接口文档把 4 标成数值图是错的,数值图是 **10**。
26
+
27
+ | 值 | 类型 | 值 | 类型 |
28
+ |----|------|----|------|
29
+ | 1 | 柱状图 | 10 | 数值图 |
30
+ | 2 | 折线图 | 11 | 双向条形图 |
31
+ | 3 | 饼图 | 12 | 散点图 |
32
+ | 5 | 雷达图 | 13 | 词云 |
33
+ | 6 | 漏斗图 | 14 | 仪表盘 |
34
+ | 7 | 双轴图 | 15 | 进度图 |
35
+ | 8 | 透视表 | 16 | 排行榜 |
36
+ | 9 | 行政区域地图 | 17 | 世界地图 |
37
+
38
+ ## 3. 「保存成功却空白」的根因:缺 filter
39
+
40
+ 一张图**没有 `filter` 块也能保存成功**(`create` 照样返回 `reportId`),但渲染时后端
41
+ 无法给查询定范围,`report/getData` 返回状态 -1/0,页面就显示「无法形成图表」。这正是
42
+ 「编辑正常、保存后空白」的原因:编辑器有自己的默认范围,存下来的规格却没有。
43
+
44
+ **现在 CLI 会在你没写 `filter` 时自动补一个「全部时间」的默认范围**,所以直接
45
+ `chart create` 已经不会空白。你只在想**限定时间范围**时才需要自己写 `filter`:
46
+
47
+ ```jsonc
48
+ "filter": {
49
+ "filterRangeId": "ctime", // 按哪个日期字段限定;"ctime"=创建时间
50
+ "filterRangeName": "创建时间",
51
+ "rangeType": 0, // 0=全部时间;8=本月;18=过去N天(配 rangeValue)
52
+ "rangeValue": null,
53
+ "today": false
54
+ }
55
+ ```
56
+
57
+ `rangeType` 常用值:`0` 全部、`4` 本周、`8` 本月、`11` 本季度、`15` 本年、
58
+ `16` 上一年、`18` 今天之前 N 天(`rangeValue` 给天数,如 365)。
59
+
60
+ ## 4. 创建:`hap worksheet chart create`
61
+
62
+ 规格用 `-j/--spec-json`(内联)或 `-f/--spec-file`(文件)。字段引用一律用 **controlId**,
63
+ 取字段用 `hap worksheet fields WS_ID`。
64
+
65
+ **柱图(按状态统计金额):**
66
+
67
+ ```bash
68
+ hap worksheet chart create WS_ID --name "各状态金额" --report-type 1 -j '{
69
+ "xaxes": {"controlId": "<状态字段>", "controlType": 11},
70
+ "yaxisList": [{"controlId": "<金额字段>", "controlType": 6, "normType": 1}]
71
+ }'
72
+ ```
73
+
74
+ **数值图(无维度,只放一个值):** `--report-type 10`,规格只给 `yaxisList`。
75
+
76
+ `yaxisList[].normType` 聚合方式:`1` 求和、`2` 最大、`3` 最小、`4` 平均、`5` 计数、
77
+ `6` 去重计数。数值字段默认 `1`,文本/选项字段用 `5`。记录数量用特殊列
78
+ `{"controlId": "record_count", "controlName": "记录数量", "controlType": 10000000, "normType": 5}`。
79
+
80
+ **日期维度**在 `xaxes` 里加 `particleSizeType`:`5` 年、`4` 季、`3` 月、`2` 周、`1` 日。
81
+
82
+ **带条件筛选**(不是时间范围,是「状态=已完成」这类):在 `filter` 里放 `items`
83
+ 高层条件数组,CLI 会自动存成筛选器并回填 id:
84
+
85
+ ```jsonc
86
+ "filter": {"items": [{"controlId": "<状态字段>", "dataType": 11, "spliceType": 1,
87
+ "filterType": 2, "values": ["<选项key>"]}]}
88
+ ```
89
+
90
+ 用 `items` 时给 `--app-id 应用ID`(应用,不是工作表),CLI 才能存筛选器。
91
+
92
+ ## 5. 修改:先取原配置,改几个参数,再提交
93
+
94
+ 改图不用从头拼规格,**把原图配置读出来、改动要改的、整体提交回去**:
95
+
96
+ ```bash
97
+ # 1. 取出当前配置
98
+ hap --json worksheet chart get REPORT_ID --app-id WS_ID > chart.json
99
+ # 2. 编辑 chart.json(改 name / normType / xaxes / 加 filter 等)
100
+ # 3. 提交回去(update 必须带 --app-id=工作表ID、--name、--report-type)
101
+ hap worksheet chart update REPORT_ID --app-id WS_ID --name "新名字" --report-type 1 -f chart.json
102
+ ```
103
+
104
+ `get` 返回的 `controls` / `account` / `createdDate` 等多余字段可以原样带回,服务端会忽略。
105
+ `update` 会整体替换这张图的配置,所以要在原配置基础上改,别只发你改的那几个字段。
106
+
107
+ ## 6. 放进自定义页(看板)
108
+
109
+ 图建好后拿到 `reportId`,再用 `custom-page save` 把它作为一个组件放到页面上。
110
+
111
+ ```bash
112
+ # 页面版本号:先 hap --json custom-page info PAGE_ID 看当前 version,save 时传相同值
113
+ hap custom-page save PAGE_ID --version <当前version> --components '[
114
+ {"type": 1, "value": "<reportId>", "worksheetId": "<WS_ID>",
115
+ "name": "各状态金额", "reportType": 1,
116
+ "config": {"objectId": "<32位十六进制随机串>"},
117
+ "web": {"title": "", "titleVisible": false, "visible": true,
118
+ "layout": {"x": 0, "y": 0, "w": 24, "h": 10, "minW": 2, "minH": 4}},
119
+ "mobile": {"title": "", "titleVisible": false, "visible": true, "layout": null}}
120
+ ]'
121
+ ```
122
+
123
+ 要点:
124
+
125
+ - 图表组件 `type` 是 **1**,`value` 放 `reportId`,还要带 `worksheetId` 和 `reportType`。
126
+ - `config.objectId` 每个组件一个唯一 32 位十六进制串(联动筛选靠它定位)。
127
+ - 布局是 **48 栅格**:`w` 最大 48,一行放两张图各 `w:24`。
128
+ - `--version` 必须等于页面当前 version,否则报「自定义页面保存失败」;存成功后 version +1。
129
+ - `save` 是**整页替换**:要保留页上已有组件,先 `custom-page info` 读出 `components`
130
+ 再把新组件追加进去一起提交。
131
+
132
+ 整体建应用带看板时优先用 `hap-cli-app-creator` skill,它按友好配置自动生成完整图表规格
133
+ 和页面布局,比手写规格省心。手改单张图、或往已有页加一张图时,用上面的命令即可。
@@ -3930,8 +3930,8 @@
3930
3930
  "zh-Hans": "工作表统计图管理"
3931
3931
  },
3932
3932
  "worksheet chart create": {
3933
- "en": "Create a new chart on a worksheet.",
3934
- "zh-Hans": "在工作表上创建新的统计图"
3933
+ "en": "Create a new chart on a worksheet. Chart spec shapes are in `hap guide chart`.",
3934
+ "zh-Hans": "在工作表上创建新的统计图。图表规格的写法见 `hap guide chart`。"
3935
3935
  },
3936
3936
  "worksheet chart create --app-id": {
3937
3937
  "en": "Application id the worksheet lives under. Required only when the chart spec carries inline filter conditions (filter.items) — the CLI then mints a stored filter on the user's behalf. Auto-resolved via the worksheet info when omitted.",
@@ -4006,8 +4006,8 @@
4006
4006
  "zh-Hans": "每页条数(可选)"
4007
4007
  },
4008
4008
  "worksheet chart update": {
4009
- "en": "Update an existing chart's configuration.",
4010
- "zh-Hans": "更新已有统计图的配置"
4009
+ "en": "Update an existing chart's configuration. Read the config with `chart get`, edit it, submit it back — see `hap guide chart`.",
4010
+ "zh-Hans": "更新已有统计图的配置。用 `chart get` 读出配置、修改后整体提交回去——见 `hap guide chart`。"
4011
4011
  },
4012
4012
  "worksheet chart update --app-id": {
4013
4013
  "en": "Worksheet ID the chart attaches to.",
@@ -4245,6 +4245,10 @@
4245
4245
  "en": "Application ID (auto-detected from the worksheet if omitted)",
4246
4246
  "zh-Hans": "应用 ID(省略时自动从工作表检测)"
4247
4247
  },
4248
+ "worksheet record add-discussion --file": {
4249
+ "en": "Attach a file to the comment (repeatable). Accepts a local path or an http(s) URL.",
4250
+ "zh-Hans": "为评论附加文件(可重复)。接受本地路径或 http(s) URL。"
4251
+ },
4248
4252
  "worksheet record add-discussion --message": {
4249
4253
  "en": "Discussion message",
4250
4254
  "zh-Hans": "讨论内容"
@@ -4814,4 +4818,4 @@
4814
4818
  "zh-Hans": "应用到现有视图的高层视图 JSON(格式与 'view create --view-spec' 相同)。规格中表达的内容全部更新;其他设置保持不变。字段引用可为名称、别名或 controlId。提供时忽略其他选项"
4815
4819
  }
4816
4820
  }
4817
- }
4821
+ }
@@ -199,6 +199,39 @@ class TestSaveChart:
199
199
 
200
200
  assert "splitId" not in mock_post.call_args[1]["json"]
201
201
 
202
+ @patch("hap_cli.core.session.requests.post")
203
+ def test_default_filter_block_injected_when_absent(self, mock_post):
204
+ # A chart saved without a `filter` block saves fine but never
205
+ # renders (blank after save). save_chart must inject an all-time
206
+ # default filter so a hand-written spec with only axes still works.
207
+ sess = Session(server_url="https://t.com", auth_token="tok")
208
+ mock_post.return_value = _resp({"reportId": "r1"})
209
+
210
+ chart_mod.save_chart(
211
+ sess, app_id="ws1", name="Bare", report_type=1,
212
+ extra={"yaxisList": [{"controlId": "m1", "normType": 1}]},
213
+ )
214
+
215
+ flt = mock_post.call_args[1]["json"]["filter"]
216
+ assert flt["filterRangeId"] == "ctime"
217
+ assert flt["rangeType"] == 0 # all-time, never blank by default
218
+ assert "dynamicFilter" in flt
219
+
220
+ @patch("hap_cli.core.session.requests.post")
221
+ def test_caller_supplied_filter_is_not_overwritten(self, mock_post):
222
+ # An explicit filter scope always wins over the injected default.
223
+ sess = Session(server_url="https://t.com", auth_token="tok")
224
+ mock_post.return_value = _resp({"reportId": "r1"})
225
+
226
+ chart_mod.save_chart(
227
+ sess, app_id="ws1", name="Scoped", report_type=1,
228
+ extra={"filter": {"filterRangeId": "6a1", "rangeType": 8}},
229
+ )
230
+
231
+ flt = mock_post.call_args[1]["json"]["filter"]
232
+ assert flt["filterRangeId"] == "6a1"
233
+ assert flt["rangeType"] == 8
234
+
202
235
  @patch("hap_cli.core.session.requests.post")
203
236
  def test_extra_dict_is_merged_under_required_fields(self, mock_post):
204
237
  sess = Session(server_url="https://t.com", auth_token="tok")
@@ -10042,6 +10042,30 @@ class TestRecordDiscussions:
10042
10042
  with pytest.raises(APIError):
10043
10043
  rec_mod.add_discussion(session, "ws1", "row1", "hi", app_id="app1")
10044
10044
 
10045
+ def test_add_discussion_encodes_attachments_as_json_string(self):
10046
+ # The frontend sends attachments as a JSON-encoded string under
10047
+ # `attachments`; a caller passing uploaded-file descriptors must
10048
+ # get that exact wire shape (and no key when none are supplied).
10049
+ session = MagicMock()
10050
+ session.get_global_meta.return_value = self._META
10051
+ session.api_call.return_value = {"code": 1, "data": {"discussionId": "d1"}}
10052
+ descriptor = {"fileID": "f1", "originalFileName": "shot", "fileExt": ".png"}
10053
+ rec_mod.add_discussion(
10054
+ session, "ws1", "row1", "see attached", app_id="app1",
10055
+ attachments=[descriptor],
10056
+ )
10057
+ _, _, payload = session.api_call.call_args[0][:3]
10058
+ assert isinstance(payload["attachments"], str)
10059
+ assert json.loads(payload["attachments"]) == [descriptor]
10060
+
10061
+ def test_add_discussion_omits_attachments_when_none(self):
10062
+ session = MagicMock()
10063
+ session.get_global_meta.return_value = self._META
10064
+ session.api_call.return_value = {"code": 1, "data": {"discussionId": "d1"}}
10065
+ rec_mod.add_discussion(session, "ws1", "row1", "no files", app_id="app1")
10066
+ _, _, payload = session.api_call.call_args[0][:3]
10067
+ assert "attachments" not in payload
10068
+
10045
10069
  def test_remove_discussion_defaults_to_row_source_type(self):
10046
10070
  session = MagicMock()
10047
10071
  session.api_call.return_value = {"code": 1}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hap-cli
3
- Version: 0.8.25
3
+ Version: 0.8.26
4
4
  Summary: CLI harness for Mingdao HAP - Enterprise no-code platform
5
5
  Author: hap-cli
6
6
  License: Apache-2.0
@@ -146,11 +146,13 @@ hap_cli/core/v3/dispatcher.py
146
146
  hap_cli/core/v3/render.py
147
147
  hap_cli/core/v3/schema.py
148
148
  hap_cli/guide/index.json
149
+ hap_cli/guide/en/chart.md
149
150
  hap_cli/guide/en/messages.md
150
151
  hap_cli/guide/en/overview.md
151
152
  hap_cli/guide/en/record.md
152
153
  hap_cli/guide/en/setup.md
153
154
  hap_cli/guide/en/skills.md
155
+ hap_cli/guide/zh-Hans/chart.md
154
156
  hap_cli/guide/zh-Hans/messages.md
155
157
  hap_cli/guide/zh-Hans/overview.md
156
158
  hap_cli/guide/zh-Hans/record.md
File without changes
File without changes
File without changes
File without changes
File without changes