feat: add Mingdao HAP tool, implemented read and maintain HAP application worksheet data. (#6257)
Co-authored-by: takatost <takatost@gmail.com>pull/6562/head
parent
f30a51e673
commit
5fcc2caeed
|
After Width: | Height: | Size: 1.6 KiB |
@ -0,0 +1,8 @@
|
||||
from typing import Any
|
||||
|
||||
from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
|
||||
|
||||
|
||||
class HapProvider(BuiltinToolProviderController):
|
||||
def _validate_credentials(self, credentials: dict[str, Any]) -> None:
|
||||
pass
|
||||
@ -0,0 +1,15 @@
|
||||
identity:
|
||||
author: Mingdao
|
||||
name: hap
|
||||
label:
|
||||
en_US: HAP
|
||||
zh_Hans: HAP
|
||||
pt_BR: HAP
|
||||
description:
|
||||
en_US: "Hyper application platform that is particularly friendly to AI"
|
||||
zh_Hans: "对 AI 特别友好的超级应用平台"
|
||||
pt_BR: "Plataforma de aplicação hiper que é particularmente amigável à IA"
|
||||
icon: icon.svg
|
||||
tags:
|
||||
- productivity
|
||||
credentials_for_provider:
|
||||
@ -0,0 +1,53 @@
|
||||
import json
|
||||
from typing import Any, Union
|
||||
|
||||
import httpx
|
||||
|
||||
from core.tools.entities.tool_entities import ToolInvokeMessage
|
||||
from core.tools.tool.builtin_tool import BuiltinTool
|
||||
|
||||
|
||||
class AddWorksheetRecordTool(BuiltinTool):
|
||||
|
||||
def _invoke(self, user_id: str, tool_parameters: dict[str, Any]
|
||||
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
|
||||
|
||||
appkey = tool_parameters.get('appkey', '')
|
||||
if not appkey:
|
||||
return self.create_text_message('Invalid parameter App Key')
|
||||
sign = tool_parameters.get('sign', '')
|
||||
if not sign:
|
||||
return self.create_text_message('Invalid parameter Sign')
|
||||
worksheet_id = tool_parameters.get('worksheet_id', '')
|
||||
if not worksheet_id:
|
||||
return self.create_text_message('Invalid parameter Worksheet ID')
|
||||
record_data = tool_parameters.get('record_data', '')
|
||||
if not record_data:
|
||||
return self.create_text_message('Invalid parameter Record Row Data')
|
||||
|
||||
host = tool_parameters.get('host', '')
|
||||
if not host:
|
||||
host = 'https://api.mingdao.com'
|
||||
elif not host.startswith(("http://", "https://")):
|
||||
return self.create_text_message('Invalid parameter Host Address')
|
||||
else:
|
||||
host = f"{host[:-1] if host.endswith('/') else host}/api"
|
||||
|
||||
url = f"{host}/v2/open/worksheet/addRow"
|
||||
headers = {'Content-Type': 'application/json'}
|
||||
payload = {"appKey": appkey, "sign": sign, "worksheetId": worksheet_id}
|
||||
|
||||
try:
|
||||
payload['controls'] = json.loads(record_data)
|
||||
res = httpx.post(url, headers=headers, json=payload, timeout=60)
|
||||
res.raise_for_status()
|
||||
res_json = res.json()
|
||||
if res_json.get('error_code') != 1:
|
||||
return self.create_text_message(f"Failed to add the new record. {res_json['error_msg']}")
|
||||
return self.create_text_message(f"New record added successfully. The record ID is {res_json['data']}.")
|
||||
except httpx.RequestError as e:
|
||||
return self.create_text_message(f"Failed to add the new record, request error: {e}")
|
||||
except json.JSONDecodeError as e:
|
||||
return self.create_text_message(f"Failed to parse JSON response: {e}")
|
||||
except Exception as e:
|
||||
return self.create_text_message(f"Failed to add the new record, unexpected error: {e}")
|
||||
@ -0,0 +1,49 @@
|
||||
from typing import Any, Union
|
||||
|
||||
import httpx
|
||||
|
||||
from core.tools.entities.tool_entities import ToolInvokeMessage
|
||||
from core.tools.tool.builtin_tool import BuiltinTool
|
||||
|
||||
|
||||
class DeleteWorksheetRecordTool(BuiltinTool):
|
||||
|
||||
def _invoke(self, user_id: str, tool_parameters: dict[str, Any]
|
||||
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
|
||||
|
||||
appkey = tool_parameters.get('appkey', '')
|
||||
if not appkey:
|
||||
return self.create_text_message('Invalid parameter App Key')
|
||||
sign = tool_parameters.get('sign', '')
|
||||
if not sign:
|
||||
return self.create_text_message('Invalid parameter Sign')
|
||||
worksheet_id = tool_parameters.get('worksheet_id', '')
|
||||
if not worksheet_id:
|
||||
return self.create_text_message('Invalid parameter Worksheet ID')
|
||||
row_id = tool_parameters.get('row_id', '')
|
||||
if not row_id:
|
||||
return self.create_text_message('Invalid parameter Record Row ID')
|
||||
|
||||
host = tool_parameters.get('host', '')
|
||||
if not host:
|
||||
host = 'https://api.mingdao.com'
|
||||
elif not host.startswith(("http://", "https://")):
|
||||
return self.create_text_message('Invalid parameter Host Address')
|
||||
else:
|
||||
host = f"{host[:-1] if host.endswith('/') else host}/api"
|
||||
|
||||
url = f"{host}/v2/open/worksheet/deleteRow"
|
||||
headers = {'Content-Type': 'application/json'}
|
||||
payload = {"appKey": appkey, "sign": sign, "worksheetId": worksheet_id, "rowId": row_id}
|
||||
|
||||
try:
|
||||
res = httpx.post(url, headers=headers, json=payload, timeout=30)
|
||||
res.raise_for_status()
|
||||
res_json = res.json()
|
||||
if res_json.get('error_code') != 1:
|
||||
return self.create_text_message(f"Failed to delete the record. {res_json['error_msg']}")
|
||||
return self.create_text_message("Successfully deleted the record.")
|
||||
except httpx.RequestError as e:
|
||||
return self.create_text_message(f"Failed to delete the record, request error: {e}")
|
||||
except Exception as e:
|
||||
return self.create_text_message(f"Failed to delete the record, unexpected error: {e}")
|
||||
@ -0,0 +1,71 @@
|
||||
identity:
|
||||
name: delete_worksheet_record
|
||||
author: Ryan Tian
|
||||
label:
|
||||
en_US: Delete Worksheet Record
|
||||
zh_Hans: 删除指定的一条工作表记录
|
||||
description:
|
||||
human:
|
||||
en_US: Deletes a single record from a worksheet based on the specified record row ID
|
||||
zh_Hans: 根据指定的记录ID删除一条工作表记录数据
|
||||
llm: A tool to remove a particular record from a worksheet by specifying its unique record identifier.
|
||||
parameters:
|
||||
- name: appkey
|
||||
type: secret-input
|
||||
required: true
|
||||
label:
|
||||
en_US: App Key
|
||||
zh_Hans: App Key
|
||||
human_description:
|
||||
en_US: The AppKey parameter for the HAP application, typically found in the application's API documentation.
|
||||
zh_Hans: HAP 应用的 AppKey 参数,可以从应用 API 文档中查找到
|
||||
llm_description: the AppKey parameter for the HAP application
|
||||
form: form
|
||||
|
||||
- name: sign
|
||||
type: secret-input
|
||||
required: true
|
||||
label:
|
||||
en_US: Sign
|
||||
zh_Hans: Sign
|
||||
human_description:
|
||||
en_US: The Sign parameter for the HAP application
|
||||
zh_Hans: HAP 应用的 Sign 参数
|
||||
llm_description: the Sign parameter for the HAP application
|
||||
form: form
|
||||
|
||||
- name: worksheet_id
|
||||
type: string
|
||||
required: true
|
||||
label:
|
||||
en_US: Worksheet ID
|
||||
zh_Hans: 工作表 ID
|
||||
human_description:
|
||||
en_US: The ID of the specified worksheet
|
||||
zh_Hans: 要获取字段信息的工作表 ID
|
||||
llm_description: The ID of the specified worksheet which to get the fields information.
|
||||
form: llm
|
||||
|
||||
- name: row_id
|
||||
type: string
|
||||
required: true
|
||||
label:
|
||||
en_US: Record Row ID
|
||||
zh_Hans: 记录 ID
|
||||
human_description:
|
||||
en_US: The row ID of the specified record
|
||||
zh_Hans: 要删除的记录 ID
|
||||
llm_description: The row ID of the specified record which to be deleted.
|
||||
form: llm
|
||||
|
||||
- name: host
|
||||
type: string
|
||||
required: false
|
||||
label:
|
||||
en_US: Host Address
|
||||
zh_Hans: 服务器地址
|
||||
human_description:
|
||||
en_US: The address for the privately deployed HAP server.
|
||||
zh_Hans: 私有部署 HAP 服务器地址,公有云无需填写
|
||||
llm_description: the address for the privately deployed HAP server.
|
||||
form: form
|
||||
@ -0,0 +1,148 @@
|
||||
import json
|
||||
from typing import Any, Union
|
||||
|
||||
import httpx
|
||||
|
||||
from core.tools.entities.tool_entities import ToolInvokeMessage
|
||||
from core.tools.tool.builtin_tool import BuiltinTool
|
||||
|
||||
|
||||
class GetWorksheetFieldsTool(BuiltinTool):
|
||||
|
||||
def _invoke(self, user_id: str, tool_parameters: dict[str, Any]
|
||||
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
|
||||
|
||||
appkey = tool_parameters.get('appkey', '')
|
||||
if not appkey:
|
||||
return self.create_text_message('Invalid parameter App Key')
|
||||
sign = tool_parameters.get('sign', '')
|
||||
if not sign:
|
||||
return self.create_text_message('Invalid parameter Sign')
|
||||
worksheet_id = tool_parameters.get('worksheet_id', '')
|
||||
if not worksheet_id:
|
||||
return self.create_text_message('Invalid parameter Worksheet ID')
|
||||
|
||||
host = tool_parameters.get('host', '')
|
||||
if not host:
|
||||
host = 'https://api.mingdao.com'
|
||||
elif not host.startswith(("http://", "https://")):
|
||||
return self.create_text_message('Invalid parameter Host Address')
|
||||
else:
|
||||
host = f"{host[:-1] if host.endswith('/') else host}/api"
|
||||
|
||||
url = f"{host}/v2/open/worksheet/getWorksheetInfo"
|
||||
headers = {'Content-Type': 'application/json'}
|
||||
payload = {"appKey": appkey, "sign": sign, "worksheetId": worksheet_id}
|
||||
|
||||
try:
|
||||
res = httpx.post(url, headers=headers, json=payload, timeout=60)
|
||||
res.raise_for_status()
|
||||
res_json = res.json()
|
||||
if res_json.get('error_code') != 1:
|
||||
return self.create_text_message(f"Failed to get the worksheet information. {res_json['error_msg']}")
|
||||
|
||||
fields_json, fields_table = self.get_controls(res_json['data']['controls'])
|
||||
result_type = tool_parameters.get('result_type', 'table')
|
||||
return self.create_text_message(
|
||||
text=json.dumps(fields_json, ensure_ascii=False) if result_type == 'json' else fields_table
|
||||
)
|
||||
except httpx.RequestError as e:
|
||||
return self.create_text_message(f"Failed to get the worksheet information, request error: {e}")
|
||||
except json.JSONDecodeError as e:
|
||||
return self.create_text_message(f"Failed to parse JSON response: {e}")
|
||||
except Exception as e:
|
||||
return self.create_text_message(f"Failed to get the worksheet information, unexpected error: {e}")
|
||||
|
||||
def get_field_type_by_id(self, field_type_id: int) -> str:
|
||||
field_type_map = {
|
||||
2: "Text",
|
||||
3: "Text-Phone",
|
||||
4: "Text-Phone",
|
||||
5: "Text-Email",
|
||||
6: "Number",
|
||||
7: "Text",
|
||||
8: "Number",
|
||||
9: "Option-Single Choice",
|
||||
10: "Option-Multiple Choices",
|
||||
11: "Option-Single Choice",
|
||||
15: "Date",
|
||||
16: "Date",
|
||||
24: "Option-Region",
|
||||
25: "Text",
|
||||
26: "Option-Member",
|
||||
27: "Option-Department",
|
||||
28: "Number",
|
||||
29: "Option-Linked Record",
|
||||
30: "Unknown Type",
|
||||
31: "Number",
|
||||
32: "Text",
|
||||
33: "Text",
|
||||
35: "Option-Linked Record",
|
||||
36: "Number-Yes1/No0",
|
||||
37: "Number",
|
||||
38: "Date",
|
||||
40: "Location",
|
||||
41: "Text",
|
||||
46: "Time",
|
||||
48: "Option-Organizational Role",
|
||||
50: "Text",
|
||||
51: "Query Record",
|
||||
}
|
||||
return field_type_map.get(field_type_id, '')
|
||||
|
||||
def get_controls(self, controls: list) -> dict:
|
||||
fields = []
|
||||
fields_list = ['|fieldId|fieldName|fieldType|fieldTypeId|description|options|','|'+'---|'*6]
|
||||
for control in controls:
|
||||
if control['type'] in self._get_ignore_types():
|
||||
continue
|
||||
field_type_id = control['type']
|
||||
field_type = self.get_field_type_by_id(control['type'])
|
||||
if field_type_id == 30:
|
||||
source_type = control['sourceControl']['type']
|
||||
if source_type in self._get_ignore_types():
|
||||
continue
|
||||
else:
|
||||
field_type_id = source_type
|
||||
field_type = self.get_field_type_by_id(source_type)
|
||||
field = {
|
||||
'id': control['controlId'],
|
||||
'name': control['controlName'],
|
||||
'type': field_type,
|
||||
'typeId': field_type_id,
|
||||
'description': control['remark'].replace('\n', ' ').replace('\t', ' '),
|
||||
'options': self._extract_options(control),
|
||||
}
|
||||
fields.append(field)
|
||||
fields_list.append(f"|{field['id']}|{field['name']}|{field['type']}|{field['typeId']}|{field['description']}|{field['options'] if field['options'] else ''}|")
|
||||
|
||||
fields.append({
|
||||
'id': 'ctime',
|
||||
'name': 'Created Time',
|
||||
'type': self.get_field_type_by_id(16),
|
||||
'typeId': 16,
|
||||
'description': '',
|
||||
'options': []
|
||||
})
|
||||
fields_list.append("|ctime|Created Time|Date|16|||")
|
||||
return fields, '\n'.join(fields_list)
|
||||
|
||||
def _extract_options(self, control: dict) -> list:
|
||||
options = []
|
||||
if control['type'] in [9, 10, 11]:
|
||||
options.extend([{"key": opt['key'], "value": opt['value']} for opt in control.get('options', [])])
|
||||
elif control['type'] in [28, 36]:
|
||||
itemnames = control['advancedSetting'].get('itemnames')
|
||||
if itemnames and itemnames.startswith('[{'):
|
||||
try:
|
||||
options = json.loads(itemnames)
|
||||
except json.JSONDecodeError:
|
||||
pass
|
||||
elif control['type'] == 30:
|
||||
source_type = control['sourceControl']['type']
|
||||
if source_type not in self._get_ignore_types():
|
||||
options.extend([{"key": opt['key'], "value": opt['value']} for opt in control.get('options', [])])
|
||||
return options
|
||||
|
||||
def _get_ignore_types(self):
|
||||
return {14, 21, 22, 34, 42, 43, 45, 47, 49, 10010}
|
||||
@ -0,0 +1,130 @@
|
||||
import json
|
||||
from typing import Any, Union
|
||||
|
||||
import httpx
|
||||
|
||||
from core.tools.entities.tool_entities import ToolInvokeMessage
|
||||
from core.tools.tool.builtin_tool import BuiltinTool
|
||||
|
||||
|
||||
class GetWorksheetPivotDataTool(BuiltinTool):
|
||||
|
||||
def _invoke(self, user_id: str, tool_parameters: dict[str, Any]
|
||||
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
|
||||
|
||||
appkey = tool_parameters.get('appkey', '')
|
||||
if not appkey:
|
||||
return self.create_text_message('Invalid parameter App Key')
|
||||
sign = tool_parameters.get('sign', '')
|
||||
if not sign:
|
||||
return self.create_text_message('Invalid parameter Sign')
|
||||
worksheet_id = tool_parameters.get('worksheet_id', '')
|
||||
if not worksheet_id:
|
||||
return self.create_text_message('Invalid parameter Worksheet ID')
|
||||
x_column_fields = tool_parameters.get('x_column_fields', '')
|
||||
if not x_column_fields or not x_column_fields.startswith('['):
|
||||
return self.create_text_message('Invalid parameter Column Fields')
|
||||
y_row_fields = tool_parameters.get('y_row_fields', '')
|
||||
if y_row_fields and not y_row_fields.strip().startswith('['):
|
||||
return self.create_text_message('Invalid parameter Row Fields')
|
||||
elif not y_row_fields:
|
||||
y_row_fields = '[]'
|
||||
value_fields = tool_parameters.get('value_fields', '')
|
||||
if not value_fields or not value_fields.strip().startswith('['):
|
||||
return self.create_text_message('Invalid parameter Value Fields')
|
||||
|
||||
host = tool_parameters.get('host', '')
|
||||
if not host:
|
||||
host = 'https://api.mingdao.com'
|
||||
elif not host.startswith(("http://", "https://")):
|
||||
return self.create_text_message('Invalid parameter Host Address')
|
||||
else:
|
||||
host = f"{host[:-1] if host.endswith('/') else host}/api"
|
||||
|
||||
url = f"{host}/report/getPivotData"
|
||||
headers = {'Content-Type': 'application/json'}
|
||||
payload = {"appKey": appkey, "sign": sign, "worksheetId": worksheet_id, "options": {"showTotal": True}}
|
||||
|
||||
try:
|
||||
x_column_fields = json.loads(x_column_fields)
|
||||
payload['columns'] = x_column_fields
|
||||
y_row_fields = json.loads(y_row_fields)
|
||||
if y_row_fields: payload['rows'] = y_row_fields
|
||||
value_fields = json.loads(value_fields)
|
||||
payload['values'] = value_fields
|
||||
sort_fields = tool_parameters.get('sort_fields', '')
|
||||
if not sort_fields: sort_fields = '[]'
|
||||
sort_fields = json.loads(sort_fields)
|
||||
if sort_fields: payload['options']['sort'] = sort_fields
|
||||
res = httpx.post(url, headers=headers, json=payload, timeout=60)
|
||||
res.raise_for_status()
|
||||
res_json = res.json()
|
||||
if res_json.get('status') != 1:
|
||||
return self.create_text_message(f"Failed to get the worksheet pivot data. {res_json['msg']}")
|
||||
|
||||
pivot_json = self.generate_pivot_json(res_json['data'])
|
||||
pivot_table = self.generate_pivot_table(res_json['data'])
|
||||
result_type = tool_parameters.get('result_type', '')
|
||||
text = pivot_table if result_type == 'table' else json.dumps(pivot_json, ensure_ascii=False)
|
||||
return self.create_text_message(text)
|
||||
except httpx.RequestError as e:
|
||||
return self.create_text_message(f"Failed to get the worksheet pivot data, request error: {e}")
|
||||
except json.JSONDecodeError as e:
|
||||
return self.create_text_message(f"Failed to parse JSON response: {e}")
|
||||
except Exception as e:
|
||||
return self.create_text_message(f"Failed to get the worksheet pivot data, unexpected error: {e}")
|
||||
|
||||
def generate_pivot_table(self, data: dict[str, Any]) -> str:
|
||||
columns = data['metadata']['columns']
|
||||
rows = data['metadata']['rows']
|
||||
values = data['metadata']['values']
|
||||
|
||||
rows_data = data['data']
|
||||
|
||||
header = ([row['displayName'] for row in rows] if rows else []) + [column['displayName'] for column in columns] + [value['displayName'] for value in values]
|
||||
line = (['---'] * len(rows) if rows else []) + ['---'] * len(columns) + ['--:'] * len(values)
|
||||
|
||||
table = [header, line]
|
||||
for row in rows_data:
|
||||
row_data = [self.replace_pipe(row['rows'][r['controlId']]) for r in rows] if rows else []
|
||||
row_data.extend([self.replace_pipe(row['columns'][column['controlId']]) for column in columns])
|
||||
row_data.extend([self.replace_pipe(str(row['values'][value['controlId']])) for value in values])
|
||||
table.append(row_data)
|
||||
|
||||
return '\n'.join([('|'+'|'.join(row) +'|') for row in table])
|
||||
|
||||
def replace_pipe(self, text: str) -> str:
|
||||
return text.replace('|', '▏').replace('\n', ' ')
|
||||
|
||||
def generate_pivot_json(self, data: dict[str, Any]) -> dict:
|
||||
fields = {
|
||||
"x-axis": [
|
||||
{"fieldId": column["controlId"], "fieldName": column["displayName"]}
|
||||
for column in data["metadata"]["columns"]
|
||||
],
|
||||
"y-axis": [
|
||||
{"fieldId": row["controlId"], "fieldName": row["displayName"]}
|
||||
for row in data["metadata"]["rows"]
|
||||
] if data["metadata"]["rows"] else [],
|
||||
"values": [
|
||||
{"fieldId": value["controlId"], "fieldName": value["displayName"]}
|
||||
for value in data["metadata"]["values"]
|
||||
]
|
||||
}
|
||||
# fields = ([
|
||||
# {"fieldId": row["controlId"], "fieldName": row["displayName"]}
|
||||
# for row in data["metadata"]["rows"]
|
||||
# ] if data["metadata"]["rows"] else []) + [
|
||||
# {"fieldId": column["controlId"], "fieldName": column["displayName"]}
|
||||
# for column in data["metadata"]["columns"]
|
||||
# ] + [
|
||||
# {"fieldId": value["controlId"], "fieldName": value["displayName"]}
|
||||
# for value in data["metadata"]["values"]
|
||||
# ]
|
||||
rows = []
|
||||
for row in data["data"]:
|
||||
row_data = row["rows"] if row["rows"] else {}
|
||||
row_data.update(row["columns"])
|
||||
row_data.update(row["values"])
|
||||
rows.append(row_data)
|
||||
return {"fields": fields, "rows": rows, "summary": data["metadata"]["totalRow"]}
|
||||
@ -0,0 +1,209 @@
|
||||
import json
|
||||
import re
|
||||
from typing import Any, Union
|
||||
|
||||
import httpx
|
||||
|
||||
from core.tools.entities.tool_entities import ToolInvokeMessage
|
||||
from core.tools.tool.builtin_tool import BuiltinTool
|
||||
|
||||
|
||||
class ListWorksheetRecordsTool(BuiltinTool):
|
||||
def _invoke(self, user_id: str, tool_parameters: dict[str, Any]
|
||||
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
|
||||
|
||||
appkey = tool_parameters.get('appkey', '')
|
||||
if not appkey:
|
||||
return self.create_text_message('Invalid parameter App Key')
|
||||
|
||||
sign = tool_parameters.get('sign', '')
|
||||
if not sign:
|
||||
return self.create_text_message('Invalid parameter Sign')
|
||||
|
||||
worksheet_id = tool_parameters.get('worksheet_id', '')
|
||||
if not worksheet_id:
|
||||
return self.create_text_message('Invalid parameter Worksheet ID')
|
||||
|
||||
host = tool_parameters.get('host', '')
|
||||
if not host:
|
||||
host = 'https://api.mingdao.com'
|
||||
elif not (host.startswith("http://") or host.startswith("https://")):
|
||||
return self.create_text_message('Invalid parameter Host Address')
|
||||
else:
|
||||
host = f"{host[:-1] if host.endswith('/') else host}/api"
|
||||
|
||||
url_fields = f"{host}/v2/open/worksheet/getWorksheetInfo"
|
||||
headers = {'Content-Type': 'application/json'}
|
||||
payload = {"appKey": appkey, "sign": sign, "worksheetId": worksheet_id}
|
||||
|
||||
field_ids = tool_parameters.get('field_ids', '')
|
||||
|
||||
try:
|
||||
res = httpx.post(url_fields, headers=headers, json=payload, timeout=30)
|
||||
res_json = res.json()
|
||||
if res.is_success:
|
||||
if res_json['error_code'] != 1:
|
||||
return self.create_text_message("Failed to get the worksheet information. {}".format(res_json['error_msg']))
|
||||
else:
|
||||
worksheet_name = res_json['data']['name']
|
||||
fields, schema, table_header = self.get_schema(res_json['data']['controls'], field_ids)
|
||||
else:
|
||||
return self.create_text_message(
|
||||
f"Failed to get the worksheet information, status code: {res.status_code}, response: {res.text}")
|
||||
except Exception as e:
|
||||
return self.create_text_message("Failed to get the worksheet information, something went wrong: {}".format(e))
|
||||
|
||||
if field_ids:
|
||||
payload['controls'] = [v.strip() for v in field_ids.split(',')] if field_ids else []
|
||||
filters = tool_parameters.get('filters', '')
|
||||
if filters:
|
||||
payload['filters'] = json.loads(filters)
|
||||
sort_id = tool_parameters.get('sort_id', '')
|
||||
sort_is_asc = tool_parameters.get('sort_is_asc', False)
|
||||
if sort_id:
|
||||
payload['sortId'] = sort_id
|
||||
payload['isAsc'] = sort_is_asc
|
||||
limit = tool_parameters.get('limit', 50)
|
||||
payload['pageSize'] = limit
|
||||
page_index = tool_parameters.get('page_index', 1)
|
||||
payload['pageIndex'] = page_index
|
||||
payload['useControlId'] = True
|
||||
payload['listType'] = 1
|
||||
|
||||
url = f"{host}/v2/open/worksheet/getFilterRows"
|
||||
try:
|
||||
res = httpx.post(url, headers=headers, json=payload, timeout=90)
|
||||
res_json = res.json()
|
||||
if res.is_success:
|
||||
if res_json['error_code'] != 1:
|
||||
return self.create_text_message("Failed to get the records. {}".format(res_json['error_msg']))
|
||||
else:
|
||||
result = {
|
||||
"fields": fields,
|
||||
"rows": [],
|
||||
"total": res_json.get("data", {}).get("total"),
|
||||
"payload": {key: payload[key] for key in ['worksheetId', 'controls', 'filters', 'sortId', 'isAsc', 'pageSize', 'pageIndex'] if key in payload}
|
||||
}
|
||||
rows = res_json.get("data", {}).get("rows", [])
|
||||
result_type = tool_parameters.get('result_type', '')
|
||||
if not result_type: result_type = 'table'
|
||||
if result_type == 'json':
|
||||
for row in rows:
|
||||
result['rows'].append(self.get_row_field_value(row, schema))
|
||||
return self.create_text_message(json.dumps(result, ensure_ascii=False))
|
||||
else:
|
||||
result_text = f"Found {result['total']} rows in worksheet \"{worksheet_name}\"."
|
||||
if result['total'] > 0:
|
||||
result_text += f" The following are {result['total'] if result['total'] < limit else limit} pieces of data presented in a table format:\n\n{table_header}"
|
||||
for row in rows:
|
||||
result_values = []
|
||||
for f in fields:
|
||||
result_values.append(self.handle_value_type(row[f['fieldId']], schema[f['fieldId']]))
|
||||
result_text += '\n|'+'|'.join(result_values)+'|'
|
||||
return self.create_text_message(result_text)
|
||||
else:
|
||||
return self.create_text_message(
|
||||
f"Failed to get the records, status code: {res.status_code}, response: {res.text}")
|
||||
except Exception as e:
|
||||
return self.create_text_message("Failed to get the records, something went wrong: {}".format(e))
|
||||
|
||||
|
||||
def get_row_field_value(self, row: dict, schema: dict):
|
||||
row_value = {"rowid": row["rowid"]}
|
||||
for field in schema:
|
||||
row_value[field] = self.handle_value_type(row[field], schema[field])
|
||||
return row_value
|
||||
|
||||
|
||||
def get_schema(self, controls: list, fieldids: str):
|
||||
allow_fields = {v.strip() for v in fieldids.split(',')} if fieldids else set()
|
||||
fields = []
|
||||
schema = {}
|
||||
field_names = []
|
||||
for control in controls:
|
||||
control_type_id = self.get_real_type_id(control)
|
||||
if (control_type_id in self._get_ignore_types()) or (allow_fields and not control['controlId'] in allow_fields):
|
||||
continue
|
||||
else:
|
||||
fields.append({'fieldId': control['controlId'], 'fieldName': control['controlName']})
|
||||
schema[control['controlId']] = {'typeId': control_type_id, 'options': self.set_option(control)}
|
||||
field_names.append(control['controlName'])
|
||||
if (not allow_fields or ('ctime' in allow_fields)):
|
||||
fields.append({'fieldId': 'ctime', 'fieldName': 'Created Time'})
|
||||
schema['ctime'] = {'typeId': 16, 'options': {}}
|
||||
field_names.append("Created Time")
|
||||
fields.append({'fieldId':'rowid', 'fieldName': 'Record Row ID'})
|
||||
schema['rowid'] = {'typeId': 2, 'options': {}}
|
||||
field_names.append("Record Row ID")
|
||||
return fields, schema, '|'+'|'.join(field_names)+'|\n|'+'---|'*len(field_names)
|
||||
|
||||
def get_real_type_id(self, control: dict) -> int:
|
||||
return control['sourceControlType'] if control['type'] == 30 else control['type']
|
||||
|
||||
def set_option(self, control: dict) -> dict:
|
||||
options = {}
|
||||
if control.get('options'):
|
||||
options = {option['key']: option['value'] for option in control['options']}
|
||||
elif control.get('advancedSetting', {}).get('itemnames'):
|
||||
try:
|
||||
itemnames = json.loads(control['advancedSetting']['itemnames'])
|
||||
options = {item['key']: item['value'] for item in itemnames}
|
||||
except json.JSONDecodeError:
|
||||
pass
|
||||
return options
|
||||
|
||||
def _get_ignore_types(self):
|
||||
return {14, 21, 22, 34, 42, 43, 45, 47, 49, 10010}
|
||||
|
||||
def handle_value_type(self, value, field):
|
||||
type_id = field.get("typeId")
|
||||
if type_id == 10:
|
||||
value = value if isinstance(value, str) else "、".join(value)
|
||||
elif type_id in [28, 36]:
|
||||
value = field.get("options", {}).get(value, value)
|
||||
elif type_id in [26, 27, 48, 14]:
|
||||
value = self.process_value(value)
|
||||
elif type_id in [35, 29]:
|
||||
value = self.parse_cascade_or_associated(field, value)
|
||||
elif type_id == 40:
|
||||
value = self.parse_location(value)
|
||||
return self.rich_text_to_plain_text(value) if value else ''
|
||||
|
||||
def process_value(self, value):
|
||||
if isinstance(value, str):
|
||||
if value.startswith("[{\"accountId\""):
|
||||
value = json.loads(value)
|
||||
value = ', '.join([item['fullname'] for item in value])
|
||||
elif value.startswith("[{\"departmentId\""):
|
||||
value = json.loads(value)
|
||||
value = '、'.join([item['departmentName'] for item in value])
|
||||
elif value.startswith("[{\"organizeId\""):
|
||||
value = json.loads(value)
|
||||
value = '、'.join([item['organizeName'] for item in value])
|
||||
elif value.startswith("[{\"file_id\""):
|
||||
value = ''
|
||||
elif value == '[]':
|
||||
value = ''
|
||||
elif hasattr(value, 'accountId'):
|
||||
value = value['fullname']
|
||||
return value
|
||||
|
||||
def parse_cascade_or_associated(self, field, value):
|
||||
if (field['typeId'] == 35 and value.startswith('[')) or (field['typeId'] == 29 and value.startswith('[{')):
|
||||
value = json.loads(value)
|
||||
value = value[0]['name'] if len(value) > 0 else ''
|
||||
else:
|
||||
value = ''
|
||||
return value
|
||||
|
||||
def parse_location(self, value):
|
||||
if len(value) > 10:
|
||||
parsed_value = json.loads(value)
|
||||
value = parsed_value.get("address", "")
|
||||
else:
|
||||
value = ""
|
||||
return value
|
||||
|
||||
def rich_text_to_plain_text(self, rich_text):
|
||||
text = re.sub(r'<[^>]+>', '', rich_text) if '<' in rich_text else rich_text
|
||||
return text.replace("|", "▏").replace("\n", " ")
|
||||
@ -0,0 +1,82 @@
|
||||
import json
|
||||
from typing import Any, Union
|
||||
|
||||
import httpx
|
||||
|
||||
from core.tools.entities.tool_entities import ToolInvokeMessage
|
||||
from core.tools.tool.builtin_tool import BuiltinTool
|
||||
|
||||
|
||||
class ListWorksheetsTool(BuiltinTool):
|
||||
|
||||
def _invoke(self, user_id: str, tool_parameters: dict[str, Any]
|
||||
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
|
||||
|
||||
appkey = tool_parameters.get('appkey', '')
|
||||
if not appkey:
|
||||
return self.create_text_message('Invalid parameter App Key')
|
||||
sign = tool_parameters.get('sign', '')
|
||||
if not sign:
|
||||
return self.create_text_message('Invalid parameter Sign')
|
||||
|
||||
host = tool_parameters.get('host', '')
|
||||
if not host:
|
||||
host = 'https://api.mingdao.com'
|
||||
elif not (host.startswith("http://") or host.startswith("https://")):
|
||||
return self.create_text_message('Invalid parameter Host Address')
|
||||
else:
|
||||
host = f"{host[:-1] if host.endswith('/') else host}/api"
|
||||
url = f"{host}/v1/open/app/get"
|
||||
|
||||
result_type = tool_parameters.get('result_type', '')
|
||||
if not result_type:
|
||||
result_type = 'table'
|
||||
|
||||
headers = { 'Content-Type': 'application/json' }
|
||||
params = { "appKey": appkey, "sign": sign, }
|
||||
try:
|
||||
res = httpx.get(url, headers=headers, params=params, timeout=30)
|
||||
res_json = res.json()
|
||||
if res.is_success:
|
||||
if res_json['error_code'] != 1:
|
||||
return self.create_text_message("Failed to access the application. {}".format(res_json['error_msg']))
|
||||
else:
|
||||
if result_type == 'json':
|
||||
worksheets = []
|
||||
for section in res_json['data']['sections']:
|
||||
worksheets.extend(self._extract_worksheets(section, result_type))
|
||||
return self.create_text_message(text=json.dumps(worksheets, ensure_ascii=False))
|
||||
else:
|
||||
worksheets = '|worksheetId|worksheetName|description|\n|---|---|---|'
|
||||
for section in res_json['data']['sections']:
|
||||
worksheets += self._extract_worksheets(section, result_type)
|
||||
return self.create_text_message(worksheets)
|
||||
|
||||
else:
|
||||
return self.create_text_message(
|
||||
f"Failed to list worksheets, status code: {res.status_code}, response: {res.text}")
|
||||
except Exception as e:
|
||||
return self.create_text_message("Failed to list worksheets, something went wrong: {}".format(e))
|
||||
|
||||
def _extract_worksheets(self, section, type):
|
||||
items = []
|
||||
tables = ''
|
||||
for item in section.get('items', []):
|
||||
if item.get('type') == 0 and (not 'notes' in item or item.get('notes') != 'NO'):
|
||||
if type == 'json':
|
||||
filtered_item = {
|
||||
'id': item['id'],
|
||||
'name': item['name'],
|
||||
'notes': item.get('notes', '')
|
||||
}
|
||||
items.append(filtered_item)
|
||||
else:
|
||||
tables += f"\n|{item['id']}|{item['name']}|{item.get('notes', '')}|"
|
||||
|
||||
for child_section in section.get('childSections', []):
|
||||
if type == 'json':
|
||||
items.extend(self._extract_worksheets(child_section, 'json'))
|
||||
else:
|
||||
tables += self._extract_worksheets(child_section, 'table')
|
||||
|
||||
return items if type == 'json' else tables
|
||||
@ -0,0 +1,56 @@
|
||||
import json
|
||||
from typing import Any, Union
|
||||
|
||||
import httpx
|
||||
|
||||
from core.tools.entities.tool_entities import ToolInvokeMessage
|
||||
from core.tools.tool.builtin_tool import BuiltinTool
|
||||
|
||||
|
||||
class UpdateWorksheetRecordTool(BuiltinTool):
|
||||
|
||||
def _invoke(self, user_id: str, tool_parameters: dict[str, Any]
|
||||
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
|
||||
|
||||
appkey = tool_parameters.get('appkey', '')
|
||||
if not appkey:
|
||||
return self.create_text_message('Invalid parameter App Key')
|
||||
sign = tool_parameters.get('sign', '')
|
||||
if not sign:
|
||||
return self.create_text_message('Invalid parameter Sign')
|
||||
worksheet_id = tool_parameters.get('worksheet_id', '')
|
||||
if not worksheet_id:
|
||||
return self.create_text_message('Invalid parameter Worksheet ID')
|
||||
row_id = tool_parameters.get('row_id', '')
|
||||
if not row_id:
|
||||
return self.create_text_message('Invalid parameter Record Row ID')
|
||||
record_data = tool_parameters.get('record_data', '')
|
||||
if not record_data:
|
||||
return self.create_text_message('Invalid parameter Record Row Data')
|
||||
|
||||
host = tool_parameters.get('host', '')
|
||||
if not host:
|
||||
host = 'https://api.mingdao.com'
|
||||
elif not host.startswith(("http://", "https://")):
|
||||
return self.create_text_message('Invalid parameter Host Address')
|
||||
else:
|
||||
host = f"{host[:-1] if host.endswith('/') else host}/api"
|
||||
|
||||
url = f"{host}/v2/open/worksheet/editRow"
|
||||
headers = {'Content-Type': 'application/json'}
|
||||
payload = {"appKey": appkey, "sign": sign, "worksheetId": worksheet_id, "rowId": row_id}
|
||||
|
||||
try:
|
||||
payload['controls'] = json.loads(record_data)
|
||||
res = httpx.post(url, headers=headers, json=payload, timeout=60)
|
||||
res.raise_for_status()
|
||||
res_json = res.json()
|
||||
if res_json.get('error_code') != 1:
|
||||
return self.create_text_message(f"Failed to update the record. {res_json['error_msg']}")
|
||||
return self.create_text_message("Record updated successfully.")
|
||||
except httpx.RequestError as e:
|
||||
return self.create_text_message(f"Failed to update the record, request error: {e}")
|
||||
except json.JSONDecodeError as e:
|
||||
return self.create_text_message(f"Failed to parse JSON response: {e}")
|
||||
except Exception as e:
|
||||
return self.create_text_message(f"Failed to update the record, unexpected error: {e}")
|
||||
Loading…
Reference in New Issue