AdvModelApi 的使用

AdvModelApi 的使用

数据库数据获取

class AdvModelApi:

    def __init__(self,
                 org_id: str,
                 adv_model_stub: RpcAdvModelApiStub,
                 input_stub: RpcAdvModelInputApiStub,
                 output_stub: RpcAdvModelOutputApiStub,
                 file_api: FileApi,
                 rpc_ts_query_api: TsQueryApi):
        self.__adv_model_stub = adv_model_stub
        self.__input_stub = input_stub
        self.__output_stub = output_stub
        self.__file_api = file_api
        self.__rpc_ts_query_api = rpc_ts_query_api
        self.org_id = org_id

    def query_model_config_file(self, model_id: str,
                                config_name: str = 'default',
                                write_to: Union[str, IO, IOBase] = None) -> RpcAdvModelConfig:
        """
        查询对应配置文件的信息
        :param model_id: 模型id
        :param config_name: 配置名
        :param write_to:如果该参数不为空,会将内容写入到对应的位置。如果为str,写入到对应文件。如果是IO对象,会写入到对应对象中。
        :return: RpcAdvModelConfig对象,包含了

            string model_id;
            string config_name;
            string path;
            string create_time;
            string modify_time;
        """
        request = RpcQueryModelConfigRequest(model_id=model_id, config_name=config_name, org_id=self.org_id)
        resp = self.__adv_model_stub.QueryModelConfig(request)

        # 同时写入到对应文件
        if write_to is not None:
            self.__file_api.get_and_write_file(resp.path, write_to)

        return resp

    def add_or_update_input(self,
                            input_name: str,
                            model_id: str,
                            input_metric_id: str,
                            desc: str = None) -> str:
        """
        添加或更新输入信息

        :param input_name: 输入名称
        :param model_id: 模型id
        :param input_metric_id: 输入关联的scada 指标id
        :param desc: 描述
        :return: 成功后的id
        """
        request = RpcAddInputRequest(input_name=input_name, model_id=model_id, input_metric_id=input_metric_id,
                                     desc=desc, org_id=self.org_id)

        resp = self.__input_stub.AddInput(request)
        return resp.value

    def delete_input(self,
                     input_name: str,
                     model_id: str):
        """
        添加或更新输入信息

        :param input_name: 输入名称
        :param model_id: 模型id
        :return: 成功不会返回任何信息,失败会抛出异常
        """
        request = RpcDeleteInputRequest(input_name=input_name, model_id=model_id, org_id=self.org_id)
        self.__input_stub.DeleteInput(request)

    def query_inputs(self, model_id: str) -> list[dict]:
        """
        查询模型的相关输入信息
        :param model_id:
        :return: 输入信息的dict列表
        """
        request = RpcQueryInputsRequest(model_id=model_id, org_id=self.org_id)
        resp = self.__input_stub.QueryInputs(request)
        return json.loads(resp.inputs_json_list)

    def add_output(self,
                   output_name: str,
                   model_id: str,
                   output_type: OutputType,
                   desc: str = "") -> outputApi.RpcAddOutputResp:
        """
        :param output_name: 输出指标名称,同一模型下不能重复
        :param model_id: 模型id
        :param output_type: 输出数据类型
        :param desc: 描述、备注
        :return: 返回响应对象   {
                output_name: 输出指标名称,同一模型下不能重复
                model_id: 模型id
                output_ts_name: 如果类型为时序数据,这里是其序列名
                output_type: 输出数据类型
                desc: 描述、备注
        }
        """
        output_type_v = output_type.value

        request = outputApi.RpcAddOutputRequest(output_name=output_name,
                                                model_id=model_id,
                                                output_type=output_type_v,
                                                desc=desc,
                                                org_id=self.org_id)

        resp = self.__output_stub.AddOutput(request)
        return resp

    # 模型输出查询
    def query_output(self,
                     model_id: str,
                     model_types: Union[str, list[str]] = None,
                     model_name: str = None,
                     output_name: str = None,
                     output_names: list[str] = None,
                     sort: list[str] = None) -> list[dict]:
        """
        查询模型的输出

        :param model_id: 模型id,必传
        :param model_types: 模型类型
        :param model_name: 模型名称,模糊查询
        :param output_name: 输出的名称,模糊查询
        :param output_names: 输出的名称,根据列表精确匹配,可以为空
        :param sort: 排序规则,可以不传
        :return: 输出信息列表的dict
        """
        request = outputApi.RpcQueryAdvModelOutputsRequest(model_ids=to_list(model_id),
                                                           model_types=to_list(model_types),
                                                           model_name=model_name,
                                                           output_name=output_name,
                                                           output_names=to_list(output_names),
                                                           sort=sort,
                                                           org_id=self.org_id)

        resp = self.__output_stub.QueryModelOutputs(request)
        return json.loads(resp.output_list_json)

    # 会将数据类型做转换
    # def query_latest_output_detail_content(self,
    #                                model_id: str,
    #                                output_name: str,
    #                                output_detail_name: str = None) -> Union[dict, None]:
    #     self.query_latest_output_detail(model_id, output_name, output_detail_name)
    #     if len(content) != 0:
    #         return content[0]
    #     else:
    #         return None

    # 时序输出数据查询
    def query_ts_output(self,
                        model_id: str,
                        output_name: str,
                        from_time: datetime,
                        to_time: datetime = datetime.now(),
                        down_sampling: RpcDownSampling = None,
                        result_tz: Union[str, pytz.timezone, dateutil.tz.tzfile] = None) -> Union[TsResp, None]:

        output_list = self.query_output(model_id=model_id, output_names=[output_name])
        if len(output_list) == 0:
            raise Exception(f'output:{output_name} not found in model:{model_id}')

        output = next((x for x in output_list if x['outputName'] == output_name), None)
        if output is None:
            raise Exception(f'output:{output_name} not found in model:{model_id}')
        # output = output_list[0]
        if output['outputType'] != OutputType.timeSeries.value:
            raise Exception(f'outputType of output:{output_name} is not timeSeries,can not query timeSeries data')

        ts_name = output['outputTsName']

        ts_list = list(self.__rpc_ts_query_api.query_ts_data(from_time, to_time, [ts_name], down_sampling, result_tz))
        if len(ts_list) == 0:
            return None
        else:
            return ts_list[0]

    # 删除某条输出的详情
    def delete_output_detail(self,
                             model_id: str,
                             output_name: str,
                             output_detail_name: str):
        """
        删除某条输出的详情

        :param model_id:  模型id
        :param output_name:  输出名称
        :param output_detail_name:  输出详情名称
        :return: 没有返回信息,失败会抛出异常
        """
        request = outputApi.RpcDeleteOutputDetailRequest(model_id=model_id, output_name=output_name,
                                                         output_detail_name=output_detail_name, org_id=self.org_id)
        self.__output_stub.DeleteOutputDetail(request)

    # 最新的输出信息
    def query_latest_output_detail(self,
                                   model_id: str,
                                   output_name: str,
                                   output_detail_name: str = None) -> Union[dict, None]:
        """
        如果outputType类型为table,那么可以通过table来访问其数据的dataFrame
        :param model_id:
        :param output_name:
        :param output_detail_name:
        :return:
        """
        page = self.query_output_detail_history(model_id=model_id, output_names=[output_name],
                                                output_detail_names=[output_detail_name],
                                                sort=['createTime;desc'], page=0, size=1)
        content = page['content']
        if len(content) != 0:
            return content[0]
        else:
            return None

    # 查询输出详情
    def query_output_detail_history(self,
                                    model_id: str,
                                    output_names: Union[str, list[str]] = None,
                                    output_detail_name_like: str = None,
                                    output_detail_names: list[str] = [],
                                    output_types: list[OutputType] = [],
                                    sort: list[str] = None,
                                    page: int = 0,
                                    size: int = 10) -> dict:
        """
        查询输出详情。

        如果outputType类型为table,那么content list中,可以通过table来访问其数据的dataFrame
        :param model_id: 模型id,必传
        :param output_names: 输出名称,可以为str或str的列表
        :param output_detail_name_like: 根据输出详情名称模糊查询
        :param output_detail_names: 输出详情名称,list
        :param output_types: 输出类型
        :param sort: 排序,默认为空
        :param page: 页码
        :param size: 每页大小
        :return: {
            content:[]
            size:
            page:
            totalPage
        }
        """
        request = outputApi.RpcQueryAdvModelOutputsDetailRequest(model_ids=[model_id],
                                                                 output_names=to_list(output_names),
                                                                 output_detail_name=output_detail_name_like,
                                                                 output_detail_names=to_list(output_detail_names),
                                                                 output_types=to_list(output_types),
                                                                 sort=sort,
                                                                 page=page, size=size,
                                                                 org_id=self.org_id)
        resp = self.__output_stub.QueryModelOutputDetails(request)
        output_list: dict = json.loads(resp.output_detail_list_json)
        for output in output_list['content']:
            if output['outputType'] == OutputType.table.value:
                output['table'] = pd.read_json(path_or_buf=output['content'], orient='table')
            if output['outputType'] == OutputType.timeSeries.value:
                # 时序数据暂时不通过此处查询
                continue
            elif output['outputType'] == OutputType.text.value:
                output['data'] = output['content']

        return output_list

    # 上传数据
    def upload_output_detail(self,
                             output_name: str,
                             model_id: str,
                             output_type: OutputType,
                             data: Union[pd.DataFrame, pd.Series, bytes, str] = None,
                             file_path: str = None,
                             output_detail_name: str = None,
                             ext: str = "",
                             desc: str = "") -> str:
        """
        如果上传的类型是时序数据,那么data必须为pandas的Series的数据。Series的数据支持int、float、str和bool

        :param output_name: 输出指标名称,同一模型下不能重复
        :param model_id: 模型id
        :param output_type: 输出数据类型
        :param data: 数据内容、备注
        :param file_path: 描述、备注
        :param output_detail_name:
                该详情的名称,如果留空,服务端会自动使用当前时间作为名称。
                建议根据业务自行命名,比如2021-09-01排水控制
        :param ext: 描述、备注
        :param desc: 描述、备注
        :return: 改调消息的 output_detail_name

        :raise
           如果类型为table,但data类型不是DataFrame
        """
        request = outputApi.RpcUploadOutputDetailRequest(
            output_name=output_name,
            model_id=model_id,
            output_type=output_type.value,
            output_detail_name=output_detail_name,
            org_id=self.org_id
        )

        if ext is not None:
            request.ext = ext
        if desc is not None:
            request.desc = desc

        # 处理table类型
        if output_type == OutputType.table:
            self.__handle_table_output_upload(request, data)
        # 处理时序数据
        elif output_type == OutputType.timeSeries:
            self.__handle_ts_output_upload(request, output_type, data)
        # 处理其它类型
        elif output_type == OutputType.text or output_type == OutputType.html or \
                output_type == OutputType.csv or output_type == OutputType.image or \
                output_type == OutputType.file:
            self.__handle_file_or_bytes_output_upload(request, output_type, data, file_path, output_detail_name)
        else:
            raise Exception('not supported data type:' + output_type.value)
        resp = self.__output_stub.UploadOutputDetail(request)
        return resp.detail_id

    # 处理table类型的数据
    @staticmethod
    def __handle_table_output_upload(request: outputApi.RpcUploadOutputDetailRequest,
                                     data: Union[pd.DataFrame, pd.Series, bytes, str]):
        if data is None:
            raise Exception("data is None")

        if not isinstance(data, pd.DataFrame):
            raise Exception('OutputType.table only support data type is DataFrame')

        df: pd.DataFrame = data
        json = df.to_json(date_format='iso', date_unit='ms', orient="table")
        request.content = bytes(json, 'utf-8')

    # 处理时序数据上次
    @staticmethod
    def __handle_ts_output_upload(request: outputApi.RpcUploadOutputDetailRequest,
                                  output_type: OutputType,
                                  data: Union[pd.DataFrame, pd.Series, bytes, str]):
        if not isinstance(data, pd.Series):
            raise Exception('OutputType.timeSeries only support data type is pandas.Series')

        series: pd.Series = data
        if isinstance(series.index.dtype, pd.PeriodIndex):
            series = series.to_timestamp()

        # grpc的时序格式
        rpc_s = outputApi.RpcUploadOutputDetailRequest.TimeSeries()
        # 转换格式
        for (time, value) in series.items():
            t: pd.Timestamp = time
            # point = outputApi.RpcUploadOutputDetailRequest.Point()
            point = rpc_s.points.add()
            point.time = int(t.timestamp() * 1000)
            if value is None:
                point.null = grpc_struct.NULL_VALUE
            elif series.dtype in FLOAT_TYPES:
                point.double = value
            elif series.dtype in INT_TYPES:
                point.int = value
            elif 'boolean' == series.dtype:
                point.bool = value
            elif 'string' == series.dtype:
                point.str = value
            else:
                # 无法识别,通通转为double
                point.double = float(value)
        # grpc python就是这么奇怪……
        # https://stackoverflow.com/questions/18376190/attributeerror-assignment-not-allowed-to-composite-field-task-in-protocol-mes
        request.time_series.CopyFrom(rpc_s)

    # 处理其它类型的数据
    @staticmethod
    def __handle_file_or_bytes_output_upload(request: outputApi.RpcUploadOutputDetailRequest,
                                             output_type: OutputType,
                                             data: Union[pd.DataFrame, pd.Series, bytes, str],
                                             file_path: str,
                                             output_detail_name: str = None):
        if data is not None:
            if isinstance(data, bytes):
                request.content = data
            if isinstance(data, str):
                request.content = bytes(data, 'utf-8')
            if isinstance(data, pd.DataFrame):
                raise Exception('OutputType ' + output_type.value + ' not support data type:DataFrame')
        # 处理文件
        elif file_path is not None:
            with open(file_path, 'rb') as file:
                request.file_name = os.path.basename(file.name)
                request.content = file.read()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值