cmdbox 0.6.4.2__py3-none-any.whl → 0.6.6__py3-none-any.whl

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.

Potentially problematic release.


This version of cmdbox might be problematic. Click here for more details.

Files changed (140) hide show
  1. cmdbox/app/app.py +7 -0
  2. cmdbox/app/client.py +384 -383
  3. cmdbox/app/common.py +85 -7
  4. cmdbox/app/commons/convert.py +3 -1
  5. cmdbox/app/edge.py +12 -12
  6. cmdbox/app/feature.py +1 -1
  7. cmdbox/app/features/cli/{cmdbox_vision_install.py → _cmdbox_vision_install.py} +2 -10
  8. cmdbox/app/features/cli/_cmdbox_vision_predict.py +487 -0
  9. cmdbox/app/features/cli/{cmdbox_vision_start.py → _cmdbox_vision_start.py} +5 -1
  10. cmdbox/app/features/cli/cmdbox_audit_createdb.py +1 -11
  11. cmdbox/app/features/cli/cmdbox_audit_delete.py +0 -9
  12. cmdbox/app/features/cli/cmdbox_audit_search.py +0 -9
  13. cmdbox/app/features/cli/cmdbox_audit_write.py +0 -9
  14. cmdbox/app/features/cli/cmdbox_cmd_list.py +3 -3
  15. cmdbox/app/features/cli/cmdbox_cmd_load.py +3 -3
  16. cmdbox/app/features/cli/cmdbox_excel_cell_details.py +436 -0
  17. cmdbox/app/features/cli/cmdbox_excel_cell_search.py +276 -0
  18. cmdbox/app/features/cli/cmdbox_excel_cell_values.py +258 -0
  19. cmdbox/app/features/cli/cmdbox_excel_sheet_list.py +159 -0
  20. cmdbox/app/features/cli/cmdbox_tts_install.py +4 -11
  21. cmdbox/app/features/cli/cmdbox_tts_say.py +2 -10
  22. cmdbox/app/features/cli/cmdbox_tts_start.py +0 -9
  23. cmdbox/app/features/cli/cmdbox_tts_stop.py +0 -9
  24. cmdbox/app/features/cli/cmdbox_web_apikey_add.py +3 -3
  25. cmdbox/app/features/cli/cmdbox_web_apikey_del.py +3 -3
  26. cmdbox/app/features/cli/cmdbox_web_group_add.py +3 -3
  27. cmdbox/app/features/cli/cmdbox_web_group_del.py +3 -3
  28. cmdbox/app/features/cli/cmdbox_web_group_edit.py +3 -3
  29. cmdbox/app/features/cli/cmdbox_web_group_list.py +3 -3
  30. cmdbox/app/features/cli/cmdbox_web_start.py +10 -10
  31. cmdbox/app/features/cli/cmdbox_web_user_add.py +3 -3
  32. cmdbox/app/features/cli/cmdbox_web_user_del.py +3 -3
  33. cmdbox/app/features/cli/cmdbox_web_user_edit.py +3 -3
  34. cmdbox/app/features/cli/cmdbox_web_user_list.py +3 -3
  35. cmdbox/app/features/cli/excel_base.py +301 -0
  36. cmdbox/app/features/web/cmdbox_web_exec_cmd.py +12 -14
  37. cmdbox/app/filer.py +5 -2
  38. cmdbox/app/mcp.py +4 -3
  39. cmdbox/app/options.py +8 -0
  40. cmdbox/app/web.py +58 -39
  41. cmdbox/extensions/features.yml +3 -0
  42. cmdbox/extensions/sample_project/sample/app/features/cli/sample_server_time.py +0 -9
  43. cmdbox/licenses/LICENSE_Mako_1_3_10_MIT_License.txt +19 -0
  44. cmdbox/licenses/LICENSE_alembic_1_16_5_UNKNOWN.txt +19 -0
  45. cmdbox/licenses/{LICENSE_cffi_1_17_1_MIT_License.txt → LICENSE_cffi_2_0_0_UNKNOWN.txt} +2 -5
  46. cmdbox/licenses/LICENSE_debugpy_1_8_17_MIT_License.txt +24 -0
  47. cmdbox/licenses/LICENSE_et_xmlfile_2_0_0_MIT_License.txt +298 -0
  48. cmdbox/licenses/LICENSE_fastuuid_0_13_5_BSD_License.txt +29 -0
  49. cmdbox/licenses/LICENSE_google-cloud-monitoring_2_27_2_Apache_Software_License.txt +202 -0
  50. cmdbox/licenses/LICENSE_google-cloud-spanner_3_58_0_Apache_Software_License.txt +202 -0
  51. cmdbox/licenses/LICENSE_google-genai_1_40_0_Apache_Software_License.txt +202 -0
  52. cmdbox/licenses/LICENSE_grpc-interceptor_0_15_4_MIT_License.txt +21 -0
  53. cmdbox/licenses/{LICENSE_lazy-object-proxy_1_11_0_BSD_License.txt → LICENSE_lazy-object-proxy_1_12_0_UNKNOWN.txt} +1 -1
  54. cmdbox/licenses/LICENSE_openpyxl_3_1_5_MIT_License.txt +23 -0
  55. cmdbox/licenses/LICENSE_opentelemetry-exporter-otlp-proto-common_1_37_0_UNKNOWN.txt +201 -0
  56. cmdbox/licenses/LICENSE_opentelemetry-exporter-otlp-proto-http_1_37_0_UNKNOWN.txt +201 -0
  57. cmdbox/licenses/LICENSE_opentelemetry-proto_1_37_0_UNKNOWN.txt +201 -0
  58. cmdbox/licenses/LICENSE_opentelemetry-sdk_1_37_0_UNKNOWN.txt +201 -0
  59. cmdbox/licenses/LICENSE_opentelemetry-semantic-conventions_0_58b0_UNKNOWN.txt +201 -0
  60. cmdbox/licenses/LICENSE_sqlalchemy-spanner_1_16_0_Apache_Software_License.txt +202 -0
  61. cmdbox/licenses/LICENSE_sqlparse_0_5_3_BSD_License.txt +25 -0
  62. cmdbox/licenses/{LICENSE_uvicorn_0_35_0_BSD_License.txt → LICENSE_uvicorn_0_37_0_BSD_License.txt} +2 -1
  63. cmdbox/licenses/files.txt +82 -71
  64. cmdbox/version.py +2 -2
  65. cmdbox/web/assets/cmdbox/svgicon.js +9 -0
  66. {cmdbox-0.6.4.2.dist-info → cmdbox-0.6.6.dist-info}/METADATA +29 -29
  67. {cmdbox-0.6.4.2.dist-info → cmdbox-0.6.6.dist-info}/RECORD +133 -117
  68. cmdbox/app/features/cli/cmdbox_vision_predict.py +0 -192
  69. cmdbox/licenses/LICENSE_APScheduler_3_11_0_MIT_License.txt +0 -19
  70. cmdbox/licenses/LICENSE_backoff_2_2_1_MIT_License.txt +0 -21
  71. cmdbox/licenses/LICENSE_fastapi-sso_0_18_0_MIT_License.txt +0 -21
  72. cmdbox/licenses/LICENSE_litellm-enterprise_0_1_19_UNKNOWN.txt +0 -37
  73. cmdbox/licenses/LICENSE_oauthlib_3_3_1_BSD-3-Clause.txt +0 -27
  74. cmdbox/licenses/LICENSE_orjson_3_11_1_Apache_Software_License-MIT_License.txt +0 -201
  75. /cmdbox/licenses/{LICENSE_Authlib_1_6_1_BSD_License.txt → LICENSE_Authlib_1_6_5_BSD_License.txt} +0 -0
  76. /cmdbox/licenses/{LICENSE_MarkupSafe_3_0_2_BSD_License.txt → LICENSE_MarkupSafe_3_0_3_UNKNOWN.txt} +0 -0
  77. /cmdbox/licenses/{LICENSE_PyYAML_6_0_2_MIT_License.txt → LICENSE_PyYAML_6_0_3_MIT_License.txt} +0 -0
  78. /cmdbox/licenses/{LICENSE_anyio_4_10_0_UNKNOWN.txt → LICENSE_anyio_4_11_0_UNKNOWN.txt} +0 -0
  79. /cmdbox/licenses/{LICENSE_cachetools_5_5_2_MIT_License.txt → LICENSE_cachetools_6_2_0_MIT_License.txt} +0 -0
  80. /cmdbox/licenses/{LICENSE_click_8_2_1_UNKNOWN.txt → LICENSE_click_8_3_0_UNKNOWN.txt} +0 -0
  81. /cmdbox/licenses/{LICENSE_cryptography_45_0_6_Apache-2_0_OR_BSD-3-Clause.txt → LICENSE_cryptography_46_0_2_UNKNOWN.txt} +0 -0
  82. /cmdbox/licenses/{LICENSE_cyclopts_3_22_5_Apache_Software_License.txt → LICENSE_cyclopts_3_24_0_Apache_Software_License.txt} +0 -0
  83. /cmdbox/licenses/{LICENSE_dnspython_2_7_0_ISC_License-ISCL.txt → LICENSE_dnspython_2_8_0_ISC_License-ISCL.txt} +0 -0
  84. /cmdbox/licenses/{LICENSE_email_validator_2_2_0_The_Unlicense-Unlicense.txt → LICENSE_email-validator_2_3_0_The_Unlicense-Unlicense.txt} +0 -0
  85. /cmdbox/licenses/{LICENSE_fastapi_0_116_1_MIT_License.txt → LICENSE_fastapi_0_118_0_MIT_License.txt} +0 -0
  86. /cmdbox/licenses/{LICENSE_fastmcp_2_11_3_Apache_Software_License.txt → LICENSE_fastmcp_2_12_4_Apache_Software_License.txt} +0 -0
  87. /cmdbox/licenses/{LICENSE_filelock_3_18_0_The_Unlicense-Unlicense.txt → LICENSE_filelock_3_19_1_The_Unlicense-Unlicense.txt} +0 -0
  88. /cmdbox/licenses/{LICENSE_fsspec_2025_7_0_BSD_License.txt → LICENSE_fsspec_2025_9_0_UNKNOWN.txt} +0 -0
  89. /cmdbox/licenses/{LICENSE_gevent_25_5_1_MIT.txt → LICENSE_gevent_25_9_1_MIT.txt} +0 -0
  90. /cmdbox/licenses/{LICENSE_google-adk_1_10_0_Apache_Software_License.txt → LICENSE_google-adk_1_15_1_Apache_Software_License.txt} +0 -0
  91. /cmdbox/licenses/{LICENSE_google-api-core_2_25_1_Apache_Software_License.txt → LICENSE_google-api-core_2_25_2_Apache_Software_License.txt} +0 -0
  92. /cmdbox/licenses/{LICENSE_google-api-python-client_2_178_0_Apache_Software_License.txt → LICENSE_google-api-python-client_2_184_0_Apache_Software_License.txt} +0 -0
  93. /cmdbox/licenses/{LICENSE_google-auth_2_40_3_Apache_Software_License.txt → LICENSE_google-auth_2_41_1_Apache_Software_License.txt} +0 -0
  94. /cmdbox/licenses/{LICENSE_google-cloud-aiplatform_1_108_0_Apache_2_0.txt → LICENSE_google-cloud-aiplatform_1_119_0_Apache_2_0.txt} +0 -0
  95. /cmdbox/licenses/{LICENSE_google-cloud-bigquery_3_35_1_Apache_Software_License.txt → LICENSE_google-cloud-bigquery_3_38_0_Apache_Software_License.txt} +0 -0
  96. /cmdbox/licenses/{LICENSE_google-genai_1_29_0_Apache_Software_License.txt → LICENSE_google-cloud-bigtable_2_32_0_Apache_Software_License.txt} +0 -0
  97. /cmdbox/licenses/{LICENSE_grpcio-status_1_74_0_Apache_Software_License.txt → LICENSE_grpcio-status_1_75_1_Apache_Software_License.txt} +0 -0
  98. /cmdbox/licenses/{LICENSE_grpcio_1_74_0_Apache_Software_License.txt → LICENSE_grpcio_1_75_1_Apache_Software_License.txt} +0 -0
  99. /cmdbox/licenses/{LICENSE_httplib2_0_22_0_MIT_License.txt → LICENSE_httplib2_0_31_0_MIT_License.txt} +0 -0
  100. /cmdbox/licenses/{LICENSE_huggingface-hub_0_34_4_Apache_Software_License.txt → LICENSE_huggingface-hub_0_35_3_Apache_Software_License.txt} +0 -0
  101. /cmdbox/licenses/{LICENSE_jaraco_functools_4_2_1_UNKNOWN.txt → LICENSE_jaraco_functools_4_3_0_UNKNOWN.txt} +0 -0
  102. /cmdbox/licenses/{LICENSE_jiter_0_10_0_MIT_License.txt → LICENSE_jiter_0_11_0_MIT_License.txt} +0 -0
  103. /cmdbox/licenses/{LICENSE_jsonschema-specifications_2025_4_1_UNKNOWN.txt → LICENSE_jsonschema-specifications_2025_9_1_UNKNOWN.txt} +0 -0
  104. /cmdbox/licenses/{LICENSE_jsonschema_4_25_0_UNKNOWN.txt → LICENSE_jsonschema_4_25_1_UNKNOWN.txt} +0 -0
  105. /cmdbox/licenses/{LICENSE_litellm_1_75_5_post1_MIT_License.txt → LICENSE_litellm_1_77_5_MIT_License.txt} +0 -0
  106. /cmdbox/licenses/{LICENSE_mcp_1_12_4_MIT_License.txt → LICENSE_mcp_1_16_0_MIT_License.txt} +0 -0
  107. /cmdbox/licenses/{LICENSE_more-itertools_10_7_0_MIT_License.txt → LICENSE_more-itertools_10_8_0_UNKNOWN.txt} +0 -0
  108. /cmdbox/licenses/{LICENSE_numpy_2_3_2_BSD_License.txt → LICENSE_numpy_2_3_3_BSD_License.txt} +0 -0
  109. /cmdbox/licenses/{LICENSE_openai_1_99_9_Apache_Software_License.txt → LICENSE_openai_2_1_0_Apache_Software_License.txt} +0 -0
  110. /cmdbox/licenses/{LICENSE_opentelemetry-api_1_36_0_UNKNOWN.txt → LICENSE_opentelemetry-api_1_37_0_UNKNOWN.txt} +0 -0
  111. /cmdbox/licenses/{LICENSE_opentelemetry-sdk_1_36_0_UNKNOWN.txt → LICENSE_opentelemetry-exporter-gcp-logging_1_9_0a0_Apache_Software_License.txt} +0 -0
  112. /cmdbox/licenses/{LICENSE_opentelemetry-semantic-conventions_0_57b0_UNKNOWN.txt → LICENSE_opentelemetry-exporter-gcp-monitoring_1_9_0a0_Apache_Software_License.txt} +0 -0
  113. /cmdbox/licenses/{LICENSE_prompt_toolkit_3_0_51_BSD_License.txt → LICENSE_prompt_toolkit_3_0_52_BSD_License.txt} +0 -0
  114. /cmdbox/licenses/{LICENSE_protobuf_6_31_1_3-Clause_BSD_License.txt → LICENSE_protobuf_6_32_1_3-Clause_BSD_License.txt} +0 -0
  115. /cmdbox/licenses/{LICENSE_psycopg-binary_3_2_9_GNU_Lesser_General_Public_License_v3-LGPLv3.txt → LICENSE_psycopg-binary_3_2_10_GNU_Lesser_General_Public_License_v3-LGPLv3.txt} +0 -0
  116. /cmdbox/licenses/{LICENSE_psycopg_3_2_9_GNU_Lesser_General_Public_License_v3-LGPLv3.txt → LICENSE_psycopg_3_2_10_GNU_Lesser_General_Public_License_v3-LGPLv3.txt} +0 -0
  117. /cmdbox/licenses/{LICENSE_pycparser_2_22_BSD_License.txt → LICENSE_pycparser_2_23_BSD_License.txt} +0 -0
  118. /cmdbox/licenses/{LICENSE_pydantic-settings_2_10_1_MIT_License.txt → LICENSE_pydantic-settings_2_11_0_MIT_License.txt} +0 -0
  119. /cmdbox/licenses/{LICENSE_pydantic_2_11_7_MIT_License.txt → LICENSE_pydantic_2_11_10_MIT_License.txt} +0 -0
  120. /cmdbox/licenses/{LICENSE_pyparsing_3_2_3_MIT_License.txt → LICENSE_pyparsing_3_2_5_UNKNOWN.txt} +0 -0
  121. /cmdbox/licenses/{LICENSE_pyperclip_1_9_0_BSD_License.txt → LICENSE_pyperclip_1_11_0_BSD_License.txt} +0 -0
  122. /cmdbox/licenses/{LICENSE_questionary_2_1_0_MIT_License.txt → LICENSE_questionary_2_1_1_MIT_License.txt} +0 -0
  123. /cmdbox/licenses/{LICENSE_regex_2025_7_34_UNKNOWN.txt → LICENSE_regex_2025_9_18_UNKNOWN.txt} +0 -0
  124. /cmdbox/licenses/{LICENSE_requests_2_32_4_Apache_Software_License.txt → LICENSE_requests_2_32_5_Apache_Software_License.txt} +0 -0
  125. /cmdbox/licenses/{LICENSE_rpds-py_0_27_0_UNKNOWN.txt → LICENSE_rpds-py_0_27_1_UNKNOWN.txt} +0 -0
  126. /cmdbox/licenses/{LICENSE_shapely_2_1_1_BSD_License.txt → LICENSE_shapely_2_1_2_BSD_License.txt} +0 -0
  127. /cmdbox/licenses/{LICENSE_sphinx-sitemap_2_7_2_UNKNOWN.txt → LICENSE_sphinx-sitemap_2_8_0_UNKNOWN.txt} +0 -0
  128. /cmdbox/licenses/{LICENSE_starlette_0_47_2_BSD_License.txt → LICENSE_starlette_0_48_0_BSD_License.txt} +0 -0
  129. /cmdbox/licenses/{LICENSE_tenacity_9_1_2_Apache_Software_License.txt → LICENSE_tenacity_8_5_0_Apache_Software_License.txt} +0 -0
  130. /cmdbox/licenses/{LICENSE_tokenizers_0_21_4_Apache_Software_License.txt → LICENSE_tokenizers_0_22_1_Apache_Software_License.txt} +0 -0
  131. /cmdbox/licenses/{LICENSE_twine_6_1_0_Apache_Software_License.txt → LICENSE_twine_6_2_0_UNKNOWN.txt} +0 -0
  132. /cmdbox/licenses/{LICENSE_typing-inspection_0_4_1_UNKNOWN.txt → LICENSE_typing-inspection_0_4_2_UNKNOWN.txt} +0 -0
  133. /cmdbox/licenses/{LICENSE_typing_extensions_4_14_1_UNKNOWN.txt → LICENSE_typing_extensions_4_15_0_UNKNOWN.txt} +0 -0
  134. /cmdbox/licenses/{LICENSE_wcwidth_0_2_13_MIT_License.txt → LICENSE_wcwidth_0_2_14_MIT_License.txt} +0 -0
  135. /cmdbox/licenses/{LICENSE_zope_event_5_1_1_Zope_Public_License.txt → LICENSE_zope_event_6_0_Zope_Public_License.txt} +0 -0
  136. /cmdbox/licenses/{LICENSE_zope_interface_7_2_Zope_Public_License.txt → LICENSE_zope_interface_8_0_1_Zope_Public_License.txt} +0 -0
  137. {cmdbox-0.6.4.2.dist-info → cmdbox-0.6.6.dist-info}/WHEEL +0 -0
  138. {cmdbox-0.6.4.2.dist-info → cmdbox-0.6.6.dist-info}/entry_points.txt +0 -0
  139. {cmdbox-0.6.4.2.dist-info → cmdbox-0.6.6.dist-info}/licenses/LICENSE +0 -0
  140. {cmdbox-0.6.4.2.dist-info → cmdbox-0.6.6.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,436 @@
1
+ from cmdbox.app import common, filer
2
+ from cmdbox.app.commons import convert, redis_client
3
+ from cmdbox.app.features.cli import excel_base
4
+ from cmdbox.app.options import Options
5
+ from datetime import datetime, timedelta, time
6
+ from pathlib import Path
7
+ from typing import Dict, Any, List, Tuple
8
+ import argparse
9
+ import logging
10
+ import json
11
+
12
+
13
+ class ExcelCellDetails(excel_base.ExcelBase):
14
+ def get_cmd(self):
15
+ """
16
+ この機能のコマンドを返します
17
+
18
+ Returns:
19
+ str: コマンド
20
+ """
21
+ return 'cell_details'
22
+
23
+ def get_option(self):
24
+ """
25
+ この機能のオプションを返します
26
+
27
+ Returns:
28
+ Dict[str, Any]: オプション
29
+ """
30
+ opt = super().get_option()
31
+ opt['description_ja'] = "データフォルダ配下のExcelファイルの指定したセルの詳細情報を取得します。"
32
+ opt['description_en'] = "Get the details of the specified cell in the Excel file under the data folder."
33
+ opt['choice'] += [
34
+ dict(opt="formula_data_only", type=Options.T_BOOL, default=False, required=True, multi=False, hide=False, choice=[True, False],
35
+ description_ja="数式データのみを参照するかどうかを指定します。このオプションはキャッシュされたデータが存在する場合に有効です。",
36
+ description_en="Specify whether to get only formula data. This option is valid if cached data exists."),
37
+ dict(opt="sheet_name", type=Options.T_STR, default=None, required=False, multi=False, hide=False, choice=None,
38
+ description_ja="セルの値を取得するシートの名前を指定します。省略した場合、最初のシートが使用されます。",
39
+ description_en="Specify the sheet name to get the cell value.If omitted, the first sheet will be used."),
40
+ dict(opt="cell_name", type=Options.T_STR, default=None, required=False, multi=True, hide=False, choice=None,
41
+ description_ja="セルの値を取得するセルの名前を指定します。例えば、`A1`、`B2`、`R5987`。",
42
+ description_en="Specify the cell name to get the cell value. For example, `A1`, `B2`, `R5987`."),
43
+ dict(opt="cell_top_left", type=Options.T_STR, default=None, required=False, multi=False, hide=False, choice=None,
44
+ description_ja="セルの値を取得する左上セルの名前を指定します。例えば、`A1`、`B2`、`R5987`。",
45
+ description_en="Specify the top-left cell name to get the cell value. For example, `A1`, `B2`, `R5987`."),
46
+ dict(opt="cell_bottom_right", type=Options.T_STR, default=None, required=False, multi=False, hide=False, choice=None,
47
+ description_ja="セルの値を取得する右下セルの名前を指定します。例えば、`A1`、`B2`、`R5987`。",
48
+ description_en="Specify the bottom-right cell name to get the cell value. For example, `A1`, `B2`, `R5987`."),
49
+ dict(opt="output_detail_format", type=Options.T_STR, default='json', required=False, multi=False, hide=False, choice=['json', 'text'],
50
+ description_ja="出力フォーマットを指定します。例えば、`json`、`text`。",
51
+ description_en="Specify the output format. For example, `json`, `text`."),
52
+ ]
53
+ return opt
54
+
55
+ def chk_args(self, args:argparse.Namespace, tm:float, pf:List[Dict[str, float]]=[]) -> Tuple[bool, str, Any]:
56
+ """
57
+ 引数のチェックを行います
58
+
59
+ Args:
60
+ args (argparse.Namespace): 引数
61
+
62
+ Returns:
63
+ Tuple[bool, str]: チェック結果, メッセージ
64
+ """
65
+ if args.svname is None:
66
+ msg = dict(warn=f"Please specify the --svname option.")
67
+ common.print_format(msg, args.format, tm, args.output_json, args.output_json_append, pf=pf)
68
+ return self.RESP_WARN, msg, None
69
+ if args.scope is None:
70
+ msg = dict(warn=f"Please specify the --scope option.")
71
+ common.print_format(msg, args.format, tm, args.output_json, args.output_json_append, pf=pf)
72
+ return self.RESP_WARN, msg, None
73
+ if args.svpath is None:
74
+ msg = dict(warn=f"Please specify the --svpath option.")
75
+ common.print_format(msg, args.format, tm, args.output_json, args.output_json_append, pf=pf)
76
+ return self.RESP_WARN, msg, None
77
+ return self.RESP_SUCCESS, None, None
78
+
79
+ def excel_proc(self, abspath:Path, args:argparse.Namespace, logger:logging.Logger, tm:float, pf:List[Dict[str, float]]=[]) -> Dict[str, Any]:
80
+ """
81
+ Excel処理のベース
82
+
83
+ Args:
84
+ abspath (Path): Excelファイルの絶対パス
85
+ args (argparse.Namespace): 引数
86
+ logger (logging.Logger): ロガー
87
+ tm (float): 処理時間
88
+ pf (List[Dict[str, float]]): パフォーマンス情報
89
+
90
+ Returns:
91
+ Dict[str, Any]: 結果
92
+ """
93
+ res = self.get_cell_details(abspath, args.formula_data_only, args.sheet_name,
94
+ args.cell_name, args.cell_top_left, args.cell_bottom_right,
95
+ args.output_detail_format, logger)
96
+ return res
97
+
98
+ def get_svparam(self, args:argparse.Namespace) -> List[str]:
99
+ """
100
+ サーバーに送信するパラメーターを返します
101
+
102
+ Args:
103
+ args (argparse.Namespace): 引数
104
+
105
+ Returns:
106
+ List[str]: サーバーに送信するパラメーター
107
+ """
108
+ cell_name = json.dumps(args.cell_name, default=common.default_json_enc) if args.cell_name is not None else '[]'
109
+ ret = [convert.str2b64str(str(args.svpath)), str(args.formula_data_only), convert.str2b64str(str(args.sheet_name)),
110
+ convert.str2b64str(cell_name), convert.str2b64str(args.cell_top_left), convert.str2b64str(args.cell_bottom_right),
111
+ convert.str2b64str(args.output_detail_format)]
112
+ return ret
113
+
114
+ def is_cluster_redirect(self):
115
+ """
116
+ クラスター宛のメッセージの場合、メッセージを転送するかどうかを返します
117
+
118
+ Returns:
119
+ bool: メッセージを転送する場合はTrue
120
+ """
121
+ return False
122
+
123
+ def svrun(self, data_dir:Path, logger:logging.Logger, redis_cli:redis_client.RedisClient, msg:List[str],
124
+ sessions:Dict[str, Dict[str, Any]]) -> int:
125
+ """
126
+ この機能のサーバー側の実行を行います
127
+
128
+ Args:
129
+ data_dir (Path): データディレクトリ
130
+ logger (logging.Logger): ロガー
131
+ redis_cli (redis_client.RedisClient): Redisクライアント
132
+ msg (List[str]): 受信メッセージ
133
+ sessions (Dict[str, Dict[str, Any]]): セッション情報
134
+
135
+ Returns:
136
+ int: 終了コード
137
+ """
138
+ svpath = convert.b64str2str(msg[2])
139
+ formula_data_only = msg[3]=='True'
140
+ sheet_name = convert.b64str2str(msg[4])
141
+ sheet_name = None if sheet_name=='None' else sheet_name
142
+ cell_name = json.loads(convert.b64str2str(msg[5]))
143
+ cell_top_left = convert.b64str2str(msg[6])
144
+ cell_bottom_right = convert.b64str2str(msg[7])
145
+ output_detail_format = convert.b64str2str(msg[8])
146
+
147
+ try:
148
+ f = filer.Filer(data_dir, logger)
149
+ chk, abspath, res = f._file_exists(svpath)
150
+ if not chk:
151
+ logger.warning(f"File not found. {svpath}")
152
+ redis_cli.rpush(msg[1], res)
153
+ return self.RESP_WARN
154
+ res = self.get_cell_details(abspath, formula_data_only, sheet_name, cell_name, cell_top_left, cell_bottom_right,
155
+ output_detail_format, logger)
156
+ redis_cli.rpush(msg[1], res)
157
+ except Exception as e:
158
+ logger.warning(f"Failed to cell details: {e}", exc_info=True)
159
+ redis_cli.rpush(msg[1], dict(warn=f"Failed to cell details: {e}"))
160
+ return self.RESP_WARN
161
+ return self.RESP_SUCCESS
162
+
163
+ def get_cell_details(self, filepath:str, formula_data_only:bool, sheet_name:str, cell_name:List[str], cell_top_left:str, cell_bottom_right:str,
164
+ output_detail_format:str, logger:logging.Logger) -> Dict[str, Any]:
165
+ """
166
+ 指定したワークブックの単一セルの値、データ型、スタイル、コメント、数式、ハイパーリンクなどの詳細を取得します。
167
+
168
+ Args:
169
+ filepath (str): ワークブックのパス
170
+ formula_data_only (bool): 数式データのみを参照するかどうか。このオプションはキャッシュされたデータが存在する場合に有効です。
171
+ sheet_name (str): 詳細情報を取得するシートの名前
172
+ cell_name (List[str]): 詳細情報を取得するセルの名前のリスト。例えば、`A1`、`B2`、`R5987`。
173
+ cell_top_left (str): 詳細情報を取得する範囲の左上セルの名前。例えば、`A1`、`B2`、`R5987`。
174
+ cell_bottom_right (str): 詳細情報を取得する範囲の右下セルの名前。例えば、`A1`、`B2`、`R5987`。
175
+ output_detail_format (str): 出力フォーマット。`json`または`text`。
176
+ logger (logging.Logger): ロガー
177
+ Returns:
178
+ dict: セルの詳細情報
179
+ """
180
+ wb:Workbook = None
181
+ try:
182
+ from openpyxl.cell import Cell
183
+ from openpyxl.workbook.workbook import Workbook
184
+ from openpyxl.worksheet.worksheet import Worksheet
185
+ from openpyxl.utils.datetime import from_excel
186
+ import openpyxl
187
+
188
+ wb:Workbook = openpyxl.load_workbook(filename=filepath, read_only=True, data_only=formula_data_only)
189
+ if sheet_name not in wb.sheetnames:
190
+ if len(wb.sheetnames) <= 0:
191
+ msg = dict(warn=f"There is no worksheet. filepath: {filepath}")
192
+ logger.warning(f"There is no worksheet. filepath: {filepath}")
193
+ return msg
194
+ sheet_name = wb.sheetnames[0]
195
+
196
+ def _proc(cellinfos:Dict[str,Any], sheet:Worksheet, cn:str):
197
+ cell:Cell = sheet[cn]
198
+ celltxt = f"Cell: {cn}\n" \
199
+ + f" Value: {cell.value}\n" \
200
+ + f" Data type: {self.OPENPYXL_TYPE_TO_STRING.get(cell.data_type, cell.data_type)}\n" \
201
+ + f" Number format: {cell.number_format}\n"
202
+ cellinfo = {
203
+ "Cell": cn,
204
+ "Value": cell.value,
205
+ "Data_type": self.OPENPYXL_TYPE_TO_STRING.get(cell.data_type, cell.data_type),
206
+ "Number_format": cell.number_format,
207
+ }
208
+ cellinfos[cn] = cellinfo
209
+
210
+ if hasattr(cell, "style"):
211
+ cellinfo["Style"] = cell.style
212
+ celltxt += f" Style: {cell.style}\n"
213
+
214
+ # 日時データ
215
+ if cell.is_date:
216
+ date_value: datetime | timedelta | time | None = from_excel(cell.value)
217
+ if isinstance(date_value, (datetime, time)):
218
+ cellinfo["Value_as_Date"] = date_value.isoformat()
219
+ celltxt += f" Value as Date: {date_value.isoformat()}\n"
220
+ elif isinstance(date_value, timedelta):
221
+ cellinfo["Value_as_Time_Interval"] = date_value.total_seconds()
222
+ celltxt += f" Value as Time Interval: {date_value.total_seconds()}\n"
223
+
224
+ # 数式
225
+ if cell.data_type == "f":
226
+ cellinfo["Formula"] = cell.value
227
+ celltxt += f" Formula: {cell.value}\n"
228
+
229
+ # ハイパーリンク
230
+ if hasattr(cell, "hyperlink") and cell.hyperlink:
231
+ cellinfo["Hyperlink_Text"] = cell.hyperlink
232
+ celltxt += f" Hyperlink: {cell.hyperlink}\n"
233
+ if hasattr(cell.hyperlink, "target"):
234
+ cellinfo["Hyperlink_Target"] = cell.hyperlink.target
235
+ celltxt += f" Hyperlink Target: {cell.hyperlink.target}\n"
236
+ if hasattr(cell.hyperlink, "tooltip"):
237
+ cellinfo["Hyperlink_Tooltip"] = cell.hyperlink.tooltip
238
+ celltxt += f" Hyperlink Tooltip: {cell.hyperlink.tooltip}\n"
239
+
240
+ # コメント
241
+ if hasattr(cell, "comment") and cell.comment:
242
+ cellinfo["Comment"] = cell.comment.text
243
+ cellinfo["Comment_Author"] = cell.comment.author
244
+ celltxt += f" Comment: {cell.comment.text}\n"
245
+ celltxt += f" Comment Author: {cell.comment.author}\n"
246
+
247
+ # フォント
248
+ font = cell.font
249
+ cellinfo["Font_Name"] = font.name
250
+ cellinfo["Font_Size"] = font.size
251
+ cellinfo["Font_Bold"] = font.bold
252
+ cellinfo["Font_Italic"] = font.italic
253
+ cellinfo["Font_Underline"] = font.underline
254
+ celltxt += f" Font Name: {font.name}\n"
255
+ celltxt += f" Font Size: {font.size}\n"
256
+ celltxt += f" Font Bold: {font.bold}\n"
257
+ celltxt += f" Font Italic: {font.italic}\n"
258
+ celltxt += f" Font Underline: {font.underline}\n"
259
+ if font.color:
260
+ if hasattr(font.color, "rgb") and font.color.rgb:
261
+ cellinfo["Font_Color_RGB"] = str(font.color.rgb)
262
+ celltxt += f" Font Color (RGB): {font.color.rgb}\n"
263
+ elif hasattr(font.color, "theme") and font.color.theme is not None:
264
+ cellinfo["Font_Color_Theme"] = str(font.color.theme)
265
+ celltxt += f" Font Color (Theme): {font.color.theme}\n"
266
+ else:
267
+ cellinfo["Font_Color"] = str(font.color)
268
+ celltxt += f" Font Color: {font.color}\n"
269
+ else:
270
+ cellinfo["Font_Color"] = "Default"
271
+ celltxt += f" Font Color: Default\n"
272
+
273
+ fill = cell.fill
274
+ if hasattr(fill, "patternType") and fill.patternType:
275
+ cellinfo["Fill_Pattern_Type"] = fill.patternType
276
+ celltxt += f" Fill Pattern Type: {fill.patternType}\n"
277
+
278
+ if hasattr(fill, "fgColor") and fill.fgColor:
279
+ if hasattr(fill.fgColor, "rgb") and fill.fgColor.rgb:
280
+ cellinfo["Fill_Foreground_Color_RGB"] = str(fill.fgColor.rgb)
281
+ celltxt += f" Fill Foreground Color (RGB): {fill.fgColor.rgb}\n"
282
+ elif hasattr(fill.fgColor, "theme") and fill.fgColor.theme is not None:
283
+ cellinfo["Fill_Foreground_Color_Theme"] = str(fill.fgColor.theme)
284
+ celltxt += f" Fill Foreground Color (Theme): {fill.fgColor.theme}\n"
285
+ else:
286
+ cellinfo["Fill_Foreground_Color"] = str(fill.fgColor)
287
+ celltxt += f" Fill Foreground Color: {fill.fgColor}\n"
288
+
289
+ if hasattr(fill, "bgColor") and fill.bgColor:
290
+ if hasattr(fill.bgColor, "rgb") and fill.bgColor.rgb:
291
+ cellinfo["Fill_Background_Color_RGB"] = str(fill.bgColor.rgb)
292
+ celltxt += f" Fill Background Color (RGB): {fill.bgColor.rgb}\n"
293
+ elif hasattr(fill.bgColor, "theme") and fill.bgColor.theme is not None:
294
+ cellinfo["Fill_Background_Color_Theme"] = str(fill.bgColor.theme)
295
+ celltxt += f" Fill Background Color (Theme): {fill.bgColor.theme}\n"
296
+ else:
297
+ cellinfo["Fill_Background_Color"] = str(fill.bgColor)
298
+ celltxt += f" Fill Background Color: {fill.bgColor}\n"
299
+ else:
300
+ cellinfo["Fill"] = "No fill pattern"
301
+ celltxt += f" Fill: No fill pattern\n"
302
+
303
+ # 配置
304
+ if hasattr(cell, "alignment") and cell.alignment:
305
+ alignment = cell.alignment
306
+ cellinfo["Horizontal_Alignment"] = alignment.horizontal
307
+ cellinfo["Vertical_Alignment"] = alignment.vertical
308
+ cellinfo["Text_Rotation"] = alignment.textRotation
309
+ cellinfo["Wrap_Text"] = alignment.wrapText
310
+ cellinfo["Indent"] = alignment.indent
311
+ cellinfo["Shrink_to_Fit"] = alignment.shrinkToFit
312
+ celltxt += f" Horizontal Alignment: {alignment.horizontal}\n"
313
+ celltxt += f" Vertical Alignment: {alignment.vertical}\n"
314
+ celltxt += f" Text Rotation: {alignment.textRotation}\n"
315
+ celltxt += f" Wrap Text: {alignment.wrapText}\n"
316
+ celltxt += f" Indent: {alignment.indent}\n"
317
+ celltxt += f" Shrink to Fit: {alignment.shrinkToFit}\n"
318
+
319
+ # 罫線
320
+ if hasattr(cell, "border") and cell.border:
321
+ if cell.border:
322
+ border_sides = {
323
+ "left": cell.border.left,
324
+ "right": cell.border.right,
325
+ "top": cell.border.top,
326
+ "bottom": cell.border.bottom,
327
+ "diagonal": cell.border.diagonal,
328
+ }
329
+
330
+ for side_name, side in border_sides.items():
331
+ b_name = f"Border {side_name.capitalize()}"
332
+ celltxt += f" {b_name}:\n"
333
+ if side and side.style:
334
+ cellinfo[f"{b_name}_Style"] = side.style
335
+ celltxt += f" Style: {side.style}\n"
336
+ if side.color:
337
+ if hasattr(side.color, "rgb") and side.color.rgb:
338
+ cellinfo[f"{b_name}_Color_RGB"] = str(side.color.rgb)
339
+ celltxt += f" Color (RGB): {side.color.rgb}\n"
340
+ elif hasattr(side.color, "theme") and side.color.theme is not None:
341
+ cellinfo[f"{b_name}_Color_Theme"] = str(side.color.theme)
342
+ celltxt += f" Color (Theme): {side.color.theme}\n"
343
+ else:
344
+ cellinfo[f"{b_name}_Color"] = str(side.color)
345
+ celltxt += f" Color: {side.color}\n"
346
+
347
+ # セルの保護
348
+ if hasattr(cell, "protection") and cell.protection:
349
+ if cell.protection:
350
+ cellinfo["Is_Cell_Locked"] = cell.protection.locked
351
+ cellinfo["Is_Cell_Hidden"] = cell.protection.hidden
352
+ celltxt += f" Is Cell Locked: {cell.protection.locked}\n"
353
+ celltxt += f" Is Cell Hidden: {cell.protection.hidden}\n"
354
+
355
+ # 条件付き書式
356
+ if hasattr(cell, "conditional_formatting") and cell.conditional_formatting:
357
+ cf_rules = []
358
+ for rule in sheet.conditional_formatting:
359
+ if f"P{cell.row}" in rule.cells.ranges:
360
+ cf_rules.append(rule)
361
+
362
+ if cf_rules:
363
+ cellinfo["Conditional_Formatting_Rules"] = []
364
+ for i, rule in enumerate(cf_rules):
365
+ rule_info = {"Rule_Index": i + 1, "SubRules": []}
366
+ cellinfo["Conditional_Formatting_Rules"].append(rule_info)
367
+ celltxt += f" Conditional Formatting Rule {i + 1}:\n"
368
+ for subrule in rule.rules:
369
+ subrule_info = {"Type": type(subrule).__name__}
370
+ rule_info["SubRules"].append(subrule_info)
371
+ celltxt += f" SubRule Type: {type(subrule).__name__}\n"
372
+ if hasattr(subrule, "formula"):
373
+ subrule_info["Formula"] = subrule.formula
374
+ celltxt += f" Formula: {subrule.formula}\n"
375
+ if hasattr(subrule, "operator"):
376
+ subrule_info["Operator"] = subrule.operator
377
+ celltxt += f" Operator: {subrule.operator}\n"
378
+ if hasattr(subrule, "dxf") and subrule.dxf:
379
+ subrule_info["Differential_Style"] = {}
380
+ celltxt += f" Differential Style:\n"
381
+ if hasattr(subrule.dxf, "font") and subrule.dxf.font:
382
+ subrule_info["Differential_Style"]["Font"] = subrule.dxf.font
383
+ celltxt += f" Font: {subrule.dxf.font}\n"
384
+ if hasattr(subrule.dxf, "fill") and subrule.dxf.fill:
385
+ subrule_info["Differential_Style"]["Fill"] = subrule.dxf.fill
386
+ celltxt += f" Fill: {subrule.dxf.fill}\n"
387
+ if hasattr(subrule.dxf, "border") and subrule.dxf.border:
388
+ subrule_info["Differential_Style"]["Border"] = subrule.dxf.border
389
+ celltxt += f" Border: {subrule.dxf.border}\n"
390
+
391
+ # merged cells
392
+ if hasattr(sheet, "merged_cells") and sheet.merged_cells:
393
+ celltxt += f" Merged Cells:\n"
394
+ for merged_range in sheet.merged_cells.ranges:
395
+ if cell.coordinate in merged_range:
396
+ cellinfo["Cell_is_part_of_merged_range"] = {
397
+ "Merge_starts_at": {"min_row": merged_range.min_row, "min_col": merged_range.min_col},
398
+ "Merge_ends_at": {"max_row": merged_range.max_row, "max_col": merged_range.max_col}
399
+ }
400
+ celltxt += f" Merge starts at: (Row: {merged_range.min_row}, Column: {merged_range.min_col})\n"
401
+ celltxt += f" Merge ends at: (Row: {merged_range.max_row}, Column: {merged_range.max_col})\n"
402
+ break
403
+
404
+ return celltxt
405
+
406
+ cellinfos = {}
407
+ celltxt = ""
408
+ sheet:Worksheet = wb[sheet_name]
409
+ if cell_name is not None and len(cell_name) > 0:
410
+ for cn in cell_name:
411
+ celltxt += _proc(cellinfos, sheet, cn)
412
+
413
+ if cell_top_left is not None and cell_bottom_right is not None:
414
+ range_str = ":".join(sorted([cell_top_left, cell_bottom_right]))
415
+ cell_range = sheet[range_str]
416
+ for row in cell_range:
417
+ for cell in row:
418
+ if hasattr(cell, 'coordinate'):
419
+ celltxt += _proc(cellinfos, sheet, cell.coordinate)
420
+
421
+ if (cell_name is None or len(cell_name) <= 0) \
422
+ and cell_top_left is None and cell_bottom_right is None:
423
+ for row in sheet.iter_rows():
424
+ for cell in row:
425
+ if hasattr(cell, 'coordinate'):
426
+ celltxt += _proc(cellinfos, sheet, cell.coordinate)
427
+
428
+ res = dict(success={sheet_name:cellinfos if output_detail_format=='json' else celltxt})
429
+ return res
430
+ except Exception as e:
431
+ msg = dict(warn=f"Failed to cell details: {e}")
432
+ logger.warning(f"Failed to cell details: {e}", exc_info=True)
433
+ return msg
434
+ finally:
435
+ if wb is not None:
436
+ wb.close()