mirror of
https://github.com/FunnyWolf/agentic-soc-platform.git
synced 2026-08-22 13:12:56 +02:00
553 lines
21 KiB
Python
553 lines
21 KiB
Python
from __future__ import annotations
|
|
|
|
import time
|
|
from typing import Dict
|
|
from typing import List, Any
|
|
|
|
import requests
|
|
from requests.adapters import HTTPAdapter
|
|
|
|
from Lib.log import logger
|
|
from Lib.xcache import Xcache
|
|
from PLUGINS.SIRP.CONFIG import SIRP_URL, SIRP_APPKEY, SIRP_SIGN
|
|
from PLUGINS.SIRP.nocolymodel import FieldType, OptionType
|
|
|
|
# SESSION
|
|
HEADERS = {"HAP-Appkey": SIRP_APPKEY,
|
|
"HAP-Sign": SIRP_SIGN}
|
|
|
|
SIRP_REQUEST_TIMEOUT = 10 # seconds
|
|
|
|
HTTP_SESSION = requests.Session()
|
|
HTTP_SESSION.headers.update(HEADERS)
|
|
HTTP_SESSION.verify = False
|
|
adapter = HTTPAdapter(
|
|
pool_connections=10,
|
|
pool_maxsize=10
|
|
)
|
|
HTTP_SESSION.mount('http://', adapter)
|
|
HTTP_SESSION.mount('https://', adapter)
|
|
|
|
|
|
def _request_with_timing(method: str, url: str, **kwargs):
|
|
started_at = time.perf_counter()
|
|
try:
|
|
response = HTTP_SESSION.request(method=method, url=url, **kwargs)
|
|
elapsed_ms = (time.perf_counter() - started_at) * 1000
|
|
logger.debug(
|
|
f"[SIRP] request method={method} url={url} status={response.status_code} elapsed_ms={elapsed_ms:.2f}"
|
|
)
|
|
return response
|
|
except Exception as exc:
|
|
elapsed_ms = (time.perf_counter() - started_at) * 1000
|
|
logger.debug(
|
|
f"[SIRP] request failed method={method} url={url} elapsed_ms={elapsed_ms:.2f} error={exc}"
|
|
)
|
|
raise
|
|
|
|
|
|
SYSTEM_FIELDS = ['_initiatedBy', '_owner', '_updatedAt', '_createdAt', '_remainingTime', '_createdBy', '_updatedBy', '_processName', '_nodeAssignees',
|
|
'_initiatedAt', '_nodeStartedAt', '_approvalCompletedAt', '_dueAt', '_processStatus']
|
|
|
|
SYSTEM_FIELDS_ROW = ['row_initiatedBy', 'row_owner', 'row_updatedAt', 'row_createdAt', 'row_remainingTime', 'row_createdBy', 'row_updatedBy', 'row_processName',
|
|
'row_nodeAssignees',
|
|
'row_initiatedAt', 'row_nodeStartedAt', 'row_approvalCompletedAt', 'row_dueAt', 'row_processStatus']
|
|
|
|
|
|
class Worksheet(object):
|
|
def __init__(self):
|
|
pass
|
|
|
|
@staticmethod
|
|
def get_fields(worksheet_id: str) -> Dict[str, FieldType]:
|
|
cached_fields = Xcache.get_sirp_fields(worksheet_id)
|
|
if cached_fields is not None:
|
|
return cached_fields
|
|
|
|
url = f"{SIRP_URL}/api/v3/app/worksheets/{worksheet_id}"
|
|
|
|
response = _request_with_timing(
|
|
"GET",
|
|
url
|
|
)
|
|
response.raise_for_status()
|
|
|
|
response_data = response.json()
|
|
if response_data.get("success"):
|
|
fields_list: List[FieldType] = response_data.get("data").get("fields")
|
|
fields_dict = {}
|
|
for field in fields_list:
|
|
fields_dict[field["alias"]] = field
|
|
Xcache.set_sirp_fields(worksheet_id, fields_dict)
|
|
return fields_dict
|
|
else:
|
|
raise Exception(f"error_code: {response_data.get('error_code')} error_msg: {response_data.get('error_msg')}")
|
|
|
|
|
|
class WorksheetRow(object):
|
|
def __init__(self):
|
|
pass
|
|
|
|
@staticmethod
|
|
def _format_input_row(row, fields_config, include_system_fields=True) -> dict:
|
|
data_new = {}
|
|
for alias_key_in_row in row:
|
|
if alias_key_in_row == "rowId": # rowId 特殊处理
|
|
data_new["row_id"] = row[alias_key_in_row]
|
|
elif alias_key_in_row in SYSTEM_FIELDS:
|
|
if include_system_fields:
|
|
data_new[f"row{alias_key_in_row}"] = row[alias_key_in_row]
|
|
else:
|
|
continue
|
|
else:
|
|
field_config = fields_config.get(alias_key_in_row)
|
|
|
|
if field_config is None:
|
|
logger.warning(f"field {alias_key_in_row} not found in fields")
|
|
for alias_key_in_field_config in fields_config:
|
|
if fields_config[alias_key_in_field_config]['id'] == alias_key_in_row:
|
|
logger.warning(f"error field is '{fields_config[alias_key_in_field_config]['name']}'")
|
|
logger.warning(f"row data : {row}")
|
|
continue
|
|
data_new[alias_key_in_row] = WorksheetRow._format_input_value(field_config, row[alias_key_in_row])
|
|
return data_new
|
|
|
|
@staticmethod
|
|
def _format_input_value(field, value):
|
|
field_type = field.get("type")
|
|
sub_type = field.get("subType")
|
|
if field_type in ["MultipleSelect"]:
|
|
value_list = []
|
|
for option in value:
|
|
value_list.append(option.get("value"))
|
|
return value_list
|
|
elif field_type in ['SingleSelect', "Dropdown"]:
|
|
if len(value) > 0:
|
|
return value[0].get("value")
|
|
else:
|
|
return None
|
|
elif field_type in ['Relation']:
|
|
if sub_type == 1:
|
|
value_list = []
|
|
for option in value:
|
|
value_list.append(option.get("sid"))
|
|
return value_list
|
|
else:
|
|
return value
|
|
elif field_type in ['Checkbox']:
|
|
return bool(int(value))
|
|
else:
|
|
return value
|
|
|
|
@staticmethod
|
|
def _format_output_value(fields_config, fields):
|
|
fields_new = []
|
|
for field in fields:
|
|
if field.get("id") == "row_id":
|
|
field["id"] = "rowId"
|
|
elif field.get("id") in SYSTEM_FIELDS_ROW:
|
|
field["id"] = field["id"][3:]
|
|
field_config = fields_config.get(field.get("id"))
|
|
|
|
if field_config is None:
|
|
raise Exception(f"field {field.get("id")} not found in fields_config")
|
|
|
|
field_type = field_config.get("type")
|
|
sub_type = field_config.get("subType")
|
|
value = field.get("value")
|
|
|
|
if field_type in ['Checkbox']:
|
|
field["value"] = 1 if value else 0
|
|
fields_new.append(field)
|
|
return fields_new
|
|
|
|
@staticmethod
|
|
def _translate_filter_names_to_ids(filter_data, fields_config):
|
|
if filter_data.get("type") == "group":
|
|
for child in filter_data.get("children", []):
|
|
WorksheetRow._translate_filter_names_to_ids(child, fields_config)
|
|
|
|
elif filter_data.get("type") == "condition":
|
|
|
|
if filter_data["field"] == "row_id":
|
|
filter_data["field"] = "rowId"
|
|
|
|
if filter_data.get("operator") == "in" and isinstance(filter_data.get("value"), list):
|
|
field_key = filter_data.get("field")
|
|
|
|
target_field_config = fields_config.get(field_key)
|
|
if not target_field_config:
|
|
for f in fields_config.values():
|
|
if f.get("id") == field_key:
|
|
target_field_config = f
|
|
break
|
|
|
|
if target_field_config and target_field_config.get("options"):
|
|
value_to_key = {opt["value"]: opt["key"] for opt in target_field_config["options"]}
|
|
filter_data["value"] = [value_to_key.get(v, v) for v in filter_data["value"]]
|
|
|
|
@staticmethod
|
|
def get(worksheet_id: str, row_id: str, include_system_fields=True) -> dict:
|
|
url = f"{SIRP_URL}/api/v3/app/worksheets/{worksheet_id}/rows/{row_id}"
|
|
fields = Worksheet.get_fields(worksheet_id)
|
|
response = _request_with_timing(
|
|
"GET",
|
|
url,
|
|
timeout=SIRP_REQUEST_TIMEOUT,
|
|
params={"includeSystemFields": include_system_fields}
|
|
)
|
|
response.raise_for_status()
|
|
|
|
response_data = response.json()
|
|
if response_data.get("success"):
|
|
row = response_data.get("data")
|
|
data_new = WorksheetRow._format_input_row(row, fields, include_system_fields)
|
|
return data_new
|
|
else:
|
|
raise Exception(f"error_code: {response_data.get('error_code')} error_msg: {response_data.get('error_msg')}")
|
|
|
|
@staticmethod
|
|
def list(worksheet_id: str, filter: dict, fields=[], include_system_fields=True) -> List:
|
|
url = f"{SIRP_URL}/api/v3/app/worksheets/{worksheet_id}/rows/list"
|
|
all_rows = []
|
|
page_index = 1
|
|
page_size = 1000
|
|
fields_config = Worksheet.get_fields(worksheet_id)
|
|
|
|
WorksheetRow._translate_filter_names_to_ids(filter, fields_config)
|
|
fields = [t.replace("row_id", "rowId") for t in fields]
|
|
|
|
while True:
|
|
data = {
|
|
"filter": filter,
|
|
"fields": fields,
|
|
"sorts": [
|
|
{
|
|
"field": "utime",
|
|
"isAsc": False
|
|
}
|
|
],
|
|
"includeTotalCount": True,
|
|
"pageSize": page_size,
|
|
"pageIndex": page_index
|
|
}
|
|
|
|
response = _request_with_timing("POST",
|
|
url,
|
|
timeout=SIRP_REQUEST_TIMEOUT,
|
|
headers=HEADERS,
|
|
json=data)
|
|
response.raise_for_status()
|
|
response_data = response.json()
|
|
|
|
if not response_data.get("success"):
|
|
raise Exception(f"error_code: {response_data.get('error_code')} error_msg: {response_data.get('error_msg')}")
|
|
|
|
result_data = response_data.get("data")
|
|
rows = result_data.get("rows")
|
|
total_count = result_data.get("total", 0)
|
|
|
|
if not rows:
|
|
break
|
|
|
|
for row in rows:
|
|
formatted_row = WorksheetRow._format_input_row(row, fields_config, include_system_fields)
|
|
all_rows.append(formatted_row)
|
|
|
|
if len(all_rows) >= total_count:
|
|
break
|
|
|
|
page_index += 1
|
|
|
|
return all_rows
|
|
|
|
@staticmethod
|
|
def create(worksheet_id: str, fields: List, trigger_workflow: bool = True):
|
|
|
|
url = f"{SIRP_URL}/api/v3/app/worksheets/{worksheet_id}/rows"
|
|
fields_config = Worksheet.get_fields(worksheet_id)
|
|
fields = WorksheetRow._format_output_value(fields_config, fields)
|
|
data = {
|
|
"triggerWorkflow": trigger_workflow,
|
|
"fields": fields
|
|
}
|
|
|
|
try:
|
|
response = _request_with_timing("POST",
|
|
url,
|
|
timeout=SIRP_REQUEST_TIMEOUT,
|
|
json=data)
|
|
response.raise_for_status()
|
|
|
|
response_data = response.json()
|
|
if response_data.get("success"):
|
|
return response_data.get("data").get("id")
|
|
else:
|
|
raise Exception(f"error_code: {response_data.get('error_code')} error_msg: {response_data.get('error_msg')} data: {response_data.get('data')}")
|
|
except Exception as e:
|
|
raise e
|
|
|
|
@staticmethod
|
|
def update(worksheet_id: str, row_id: str, fields: List, trigger_workflow: bool = True):
|
|
|
|
fields_config = Worksheet.get_fields(worksheet_id)
|
|
fields = WorksheetRow._format_output_value(fields_config, fields)
|
|
url = f"{SIRP_URL}/api/v3/app/worksheets/{worksheet_id}/rows/{row_id}"
|
|
|
|
data = {
|
|
"triggerWorkflow": trigger_workflow,
|
|
"fields": fields
|
|
}
|
|
response = _request_with_timing("PATCH",
|
|
url,
|
|
timeout=SIRP_REQUEST_TIMEOUT,
|
|
json=data)
|
|
response.raise_for_status()
|
|
|
|
response_data = response.json()
|
|
if response_data.get("success"):
|
|
return response_data.get("data")
|
|
else:
|
|
raise Exception(f"error_code: {response_data.get('error_code')} error_msg: {response_data.get('error_msg')}")
|
|
|
|
@staticmethod
|
|
def batch_create(worksheet_id: str,
|
|
rows: List[List[Dict]],
|
|
trigger_workflow: bool = True) -> Dict:
|
|
|
|
url = f"{SIRP_URL}/api/v3/app/worksheets/{worksheet_id}/rows/batch"
|
|
fields_config = Worksheet.get_fields(worksheet_id)
|
|
|
|
formatted_rows = []
|
|
for row_fields in rows:
|
|
formatted_fields = WorksheetRow._format_output_value(fields_config, row_fields)
|
|
formatted_rows.append({"fields": formatted_fields})
|
|
|
|
data = {
|
|
"rows": formatted_rows,
|
|
"triggerWorkflow": trigger_workflow
|
|
}
|
|
|
|
try:
|
|
response = _request_with_timing("POST",
|
|
url,
|
|
timeout=SIRP_REQUEST_TIMEOUT,
|
|
json=data)
|
|
response.raise_for_status()
|
|
|
|
response_data = response.json()
|
|
if response_data.get("success"):
|
|
return response_data.get("data") # {"rowIds":[row_id1,row_id2]}
|
|
else:
|
|
raise Exception(f"error_code: {response_data.get('error_code')} error_msg: {response_data.get('error_msg')} data: {response_data.get('data')}")
|
|
except Exception as e:
|
|
raise e
|
|
|
|
@staticmethod
|
|
def batch_update(worksheet_id: str,
|
|
row_ids: List[str],
|
|
fields: List[Dict],
|
|
trigger_workflow: bool = True) -> Dict:
|
|
|
|
url = f"{SIRP_URL}/api/v3/app/worksheets/{worksheet_id}/rows/batch"
|
|
fields_config = Worksheet.get_fields(worksheet_id)
|
|
|
|
fields = WorksheetRow._format_output_value(fields_config, fields)
|
|
|
|
data = {
|
|
"rowIds": row_ids,
|
|
"fields": fields,
|
|
"triggerWorkflow": trigger_workflow
|
|
}
|
|
|
|
try:
|
|
response = _request_with_timing("PATCH",
|
|
url,
|
|
timeout=SIRP_REQUEST_TIMEOUT,
|
|
json=data)
|
|
response.raise_for_status()
|
|
|
|
response_data = response.json()
|
|
if response_data.get("success"):
|
|
return response_data.get("data") # {"failedRowIds":[row_id1,row_id2], "successfulRowIds":[row_id3,row_id4]}
|
|
else:
|
|
raise Exception(f"error_code: {response_data.get('error_code')} error_msg: {response_data.get('error_msg')} data: {response_data.get('data')}")
|
|
except Exception as e:
|
|
raise e
|
|
|
|
@staticmethod
|
|
def delete(worksheet_id: str, row_id: List, trigger_workflow: bool = True):
|
|
|
|
url = f"{SIRP_URL}/api/v3/app/worksheets/{worksheet_id}/rows/{row_id}"
|
|
|
|
data = {
|
|
"triggerWorkflow": trigger_workflow,
|
|
}
|
|
|
|
response = _request_with_timing("DELETE",
|
|
url,
|
|
timeout=SIRP_REQUEST_TIMEOUT,
|
|
json=data)
|
|
response.raise_for_status()
|
|
|
|
response_data = response.json()
|
|
if response_data.get("success"):
|
|
return response_data.get("data")
|
|
else:
|
|
raise Exception(f"error_code: {response_data.get('error_code')} error_msg: {response_data.get('error_msg')}")
|
|
|
|
@staticmethod
|
|
def batch_delete(worksheet_id: str, row_ids: List, trigger_workflow: bool = True):
|
|
|
|
url = f"{SIRP_URL}/api/v3/app/worksheets/{worksheet_id}/rows/batch"
|
|
|
|
data = {
|
|
"rowIds": row_ids,
|
|
"triggerWorkflow": trigger_workflow,
|
|
}
|
|
|
|
response = _request_with_timing("DELETE",
|
|
url,
|
|
timeout=SIRP_REQUEST_TIMEOUT,
|
|
json=data)
|
|
response.raise_for_status()
|
|
|
|
response_data = response.json()
|
|
if response_data.get("success"):
|
|
return response_data.get("data")
|
|
elif response_data.get("error_code") == 0:
|
|
return True
|
|
else:
|
|
raise Exception(f"error_code: {response_data.get('error_code')} error_msg: {response_data.get('error_msg')}")
|
|
|
|
@staticmethod
|
|
def get_discussions(worksheet_id: str, row_id: str) -> List[Dict]:
|
|
url = f"{SIRP_URL}/api/v3/app/worksheets/{worksheet_id}/rows/{row_id}/discussions"
|
|
|
|
response = _request_with_timing("GET",
|
|
url,
|
|
timeout=SIRP_REQUEST_TIMEOUT)
|
|
response.raise_for_status()
|
|
|
|
response_data = response.json()
|
|
if response_data.get("success"):
|
|
# [
|
|
# {
|
|
# "id": "讨论ID",
|
|
# "message": "讨论内容",
|
|
# "_createdAt": "创建时间",
|
|
# "_createdBy": {"id", "name", "avatar", "isPortal", "status"},
|
|
# "replyToAuthor": {"id", "name", "avatar", "isPortal", "status"},
|
|
# "replyId": "回复ID",
|
|
# "projectId": "项目ID",
|
|
# "replyToId": [],
|
|
# "mentions": [],
|
|
# "attachments": [
|
|
# {"originalFilename", "ext", "filesize", "downloadUrl"}
|
|
# ]
|
|
# }
|
|
# ]
|
|
discussions = response_data.get("data", {}).get("discussions", [])
|
|
return discussions
|
|
else:
|
|
raise Exception(f"error_code: {response_data.get('error_code')} error_msg: {response_data.get('error_msg')}")
|
|
|
|
@staticmethod
|
|
def relations(worksheet_id: str, row_id: str, field: str, relation_worksheet_id: str, include_system_fields: bool = True, page_size: int = 1000,
|
|
page_index: int = 1):
|
|
fields = Worksheet.get_fields(relation_worksheet_id)
|
|
|
|
url = f"{SIRP_URL}/api/v3/app/worksheets/{worksheet_id}/rows/{row_id}/relations/{field}"
|
|
|
|
params = {}
|
|
if page_size is not None:
|
|
params["pageSize"] = page_size
|
|
if page_index is not None:
|
|
params["pageIndex"] = page_index
|
|
if include_system_fields is not None:
|
|
params["isReturnSystemFields"] = include_system_fields
|
|
|
|
response = _request_with_timing("GET",
|
|
url,
|
|
timeout=SIRP_REQUEST_TIMEOUT,
|
|
params=params)
|
|
response.raise_for_status()
|
|
|
|
response_data = response.json()
|
|
if response_data.get("success"):
|
|
rows = response_data.get("data").get("rows")
|
|
rows_new = []
|
|
for row in rows:
|
|
data_new = WorksheetRow._format_input_row(row, fields, include_system_fields)
|
|
rows_new.append(data_new)
|
|
return rows_new
|
|
else:
|
|
raise Exception(f"error_code: {response_data.get('error_code')} error_msg: {response_data.get('error_msg')}")
|
|
|
|
|
|
class OptionSet(object):
|
|
def __init__(self):
|
|
pass
|
|
|
|
@staticmethod
|
|
def list():
|
|
cached_optionsets = Xcache.get_sirp_optionset()
|
|
if cached_optionsets is not None:
|
|
return cached_optionsets
|
|
url = f"{SIRP_URL}/api/v3/app/optionsets"
|
|
|
|
response = _request_with_timing("GET",
|
|
url,
|
|
timeout=SIRP_REQUEST_TIMEOUT)
|
|
response.raise_for_status()
|
|
|
|
response_data = response.json()
|
|
if response_data.get("success"):
|
|
optionsets: List[Dict[str, Any]] = response_data.get("data").get("optionsets")
|
|
Xcache.set_sirp_optionset(optionsets)
|
|
return optionsets
|
|
else:
|
|
raise Exception(f"error_code: {response_data.get('error_code')} error_msg: {response_data.get('error_msg')}")
|
|
|
|
@staticmethod
|
|
def get(name):
|
|
optionsets = OptionSet.list()
|
|
for optionset in optionsets:
|
|
if optionset["name"] == name:
|
|
options = optionset.get("options", [])
|
|
return options
|
|
raise Exception(f"optionset {name} not found")
|
|
|
|
@staticmethod
|
|
def get_option_by_name_and_value(name, value) -> OptionType:
|
|
optionsets = OptionSet.list()
|
|
for optionset in optionsets:
|
|
if optionset["name"] == name:
|
|
options = optionset.get("options", [])
|
|
for option in options:
|
|
if option["value"] == value:
|
|
return option
|
|
raise Exception(f"optionset {name} {value} not found")
|
|
|
|
@staticmethod
|
|
def get_option_key_by_name_and_value(name, value):
|
|
optionsets = OptionSet.list()
|
|
for optionset in optionsets:
|
|
if optionset["name"] == name:
|
|
options = optionset.get("options", [])
|
|
for option in options:
|
|
if option["value"] == value:
|
|
return option["key"]
|
|
raise Exception(f"optionset {name} {value} not found")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
import os
|
|
|
|
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "ASP.settings")
|
|
import django
|
|
|
|
django.setup()
|
|
result = WorksheetRow.get_discussions("case", "55c0ac33-65c2-420d-8e90-3ac62ca46f85")
|
|
print(result)
|