feat(bi): expose dataset query workflow
This commit is contained in:
parent
5fb179eb9e
commit
4bf5f1e585
|
|
@ -18,10 +18,35 @@ class DatasetCreateUpdateSerializer(CustomModelSerializer):
|
||||||
|
|
||||||
|
|
||||||
class DatasetSerializer(CustomModelSerializer):
|
class DatasetSerializer(CustomModelSerializer):
|
||||||
|
description = serializers.CharField(
|
||||||
|
label="适用场景与统计口径",
|
||||||
|
help_text="说明该数据集适合回答的问题、指标口径、参数格式和返回字段含义",
|
||||||
|
required=False,
|
||||||
|
allow_blank=True,
|
||||||
|
)
|
||||||
|
default_param = serializers.JSONField(
|
||||||
|
label="默认查询参数",
|
||||||
|
help_text="执行时可覆盖的参数及默认值;内部 SQL 片段参数应保留默认值",
|
||||||
|
required=False,
|
||||||
|
)
|
||||||
|
test_param = serializers.JSONField(
|
||||||
|
label="测试查询参数",
|
||||||
|
help_text="数据集维护时使用的示例参数,普通查询优先参考 description",
|
||||||
|
required=False,
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Dataset
|
model = Dataset
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
|
|
||||||
|
|
||||||
|
class DatasetListResponseSerializer(serializers.Serializer):
|
||||||
|
count = serializers.IntegerField(label="数据集总数")
|
||||||
|
next = serializers.URLField(required=False, allow_null=True)
|
||||||
|
previous = serializers.URLField(required=False, allow_null=True)
|
||||||
|
results = DatasetSerializer(many=True)
|
||||||
|
|
||||||
|
|
||||||
class DatasetRecordSerializer(CustomModelSerializer):
|
class DatasetRecordSerializer(CustomModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = DatasetRecord
|
model = DatasetRecord
|
||||||
|
|
@ -36,6 +61,14 @@ class DatasetRecordSerializer(CustomModelSerializer):
|
||||||
|
|
||||||
class DataExecSerializer(serializers.Serializer):
|
class DataExecSerializer(serializers.Serializer):
|
||||||
query = serializers.JSONField(
|
query = serializers.JSONField(
|
||||||
label="查询字典参数", required=False, allow_null=True)
|
label="查询字典参数",
|
||||||
is_test = serializers.BooleanField(label='是否测试', default=False)
|
help_text="按所选数据集 description/default_param 声明的业务参数填写",
|
||||||
raise_exception = serializers.BooleanField(label='是否直接报错', default=False)
|
required=False,
|
||||||
|
allow_null=True,
|
||||||
|
)
|
||||||
|
is_test = serializers.BooleanField(
|
||||||
|
label='是否测试', help_text="普通业务查询固定为 false", default=False
|
||||||
|
)
|
||||||
|
raise_exception = serializers.BooleanField(
|
||||||
|
label='是否直接报错', help_text="建议为 true,便于修正缺失或非法参数", default=True
|
||||||
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,25 @@
|
||||||
from django.test import TestCase
|
from django.test import SimpleTestCase
|
||||||
|
|
||||||
# Create your tests here.
|
from apps.bi.serializers import (
|
||||||
|
DataExecSerializer,
|
||||||
|
DatasetListResponseSerializer,
|
||||||
|
DatasetSerializer,
|
||||||
|
)
|
||||||
|
from apps.bi.views import DatasetViewSet
|
||||||
|
|
||||||
|
|
||||||
|
class DatasetAgentDiscoveryTests(SimpleTestCase):
|
||||||
|
def test_dataset_catalog_searches_description(self):
|
||||||
|
self.assertEqual(
|
||||||
|
DatasetViewSet.search_fields,
|
||||||
|
["name", "code", "description"],
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_dataset_schema_explains_discovery_and_exec_parameters(self):
|
||||||
|
dataset = DatasetSerializer()
|
||||||
|
execute = DataExecSerializer()
|
||||||
|
|
||||||
|
self.assertIn("适合回答的问题", dataset.fields["description"].help_text)
|
||||||
|
self.assertIn("业务参数", execute.fields["query"].help_text)
|
||||||
|
self.assertTrue(execute.fields["raise_exception"].default)
|
||||||
|
self.assertIn("results", DatasetListResponseSerializer().fields)
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,13 @@ from apps.utils.viewsets import CustomModelViewSet, CustomGenericViewSet
|
||||||
from rest_framework.decorators import action
|
from rest_framework.decorators import action
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from apps.bi.models import Dataset, DatasetRecord
|
from apps.bi.models import Dataset, DatasetRecord
|
||||||
from apps.bi.serializers import DatasetSerializer, DatasetCreateUpdateSerializer, DataExecSerializer, DatasetRecordSerializer
|
from apps.bi.serializers import (
|
||||||
|
DataExecSerializer,
|
||||||
|
DatasetCreateUpdateSerializer,
|
||||||
|
DatasetListResponseSerializer,
|
||||||
|
DatasetRecordSerializer,
|
||||||
|
DatasetSerializer,
|
||||||
|
)
|
||||||
from django.apps import apps
|
from django.apps import apps
|
||||||
import concurrent.futures
|
import concurrent.futures
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
|
|
@ -13,6 +19,8 @@ from rest_framework.exceptions import ParseError
|
||||||
from rest_framework.generics import get_object_or_404
|
from rest_framework.generics import get_object_or_404
|
||||||
from apps.utils.mixins import ListModelMixin
|
from apps.utils.mixins import ListModelMixin
|
||||||
import logging
|
import logging
|
||||||
|
from drf_yasg import openapi
|
||||||
|
from drf_yasg.utils import swagger_auto_schema
|
||||||
myLogger = logging.getLogger('log')
|
myLogger = logging.getLogger('log')
|
||||||
# Create your views here.
|
# Create your views here.
|
||||||
|
|
||||||
|
|
@ -22,9 +30,54 @@ class DatasetViewSet(CustomModelViewSet):
|
||||||
serializer_class = DatasetSerializer
|
serializer_class = DatasetSerializer
|
||||||
create_serializer_class = DatasetCreateUpdateSerializer
|
create_serializer_class = DatasetCreateUpdateSerializer
|
||||||
update_serializer_class = DatasetCreateUpdateSerializer
|
update_serializer_class = DatasetCreateUpdateSerializer
|
||||||
search_fields = ['name', 'code']
|
search_fields = ['name', 'code', 'description']
|
||||||
ordering = ['name', 'code', 'id']
|
ordering = ['name', 'code', 'id']
|
||||||
|
|
||||||
|
@swagger_auto_schema(
|
||||||
|
operation_id="bi_dataset_list",
|
||||||
|
operation_summary="查询复杂统计报表的数据集目录",
|
||||||
|
operation_description=(
|
||||||
|
"产量、良率、缺陷、库存、绩效、趋势和按日/月汇总等统计聚合查询的统一入口。"
|
||||||
|
"先调用本接口,根据 name、description、default_param 和 test_param 选择数据集,"
|
||||||
|
"再调用 bi_dataset_exec。建议使用 query={id,name,code,description,default_param,"
|
||||||
|
"test_param,enabled} 裁剪字段,并设置 page_size=100 查看完整目录;"
|
||||||
|
"search 可按名称、code 或 description 检索。"
|
||||||
|
),
|
||||||
|
manual_parameters=[
|
||||||
|
openapi.Parameter(
|
||||||
|
"search",
|
||||||
|
openapi.IN_QUERY,
|
||||||
|
description="按数据集名称、code 或适用场景关键词检索",
|
||||||
|
type=openapi.TYPE_STRING,
|
||||||
|
),
|
||||||
|
openapi.Parameter(
|
||||||
|
"page",
|
||||||
|
openapi.IN_QUERY,
|
||||||
|
description="页码,从 1 开始",
|
||||||
|
type=openapi.TYPE_INTEGER,
|
||||||
|
),
|
||||||
|
openapi.Parameter(
|
||||||
|
"page_size",
|
||||||
|
openapi.IN_QUERY,
|
||||||
|
description="每页数量;当前目录建议传 100",
|
||||||
|
type=openapi.TYPE_INTEGER,
|
||||||
|
),
|
||||||
|
openapi.Parameter(
|
||||||
|
"query",
|
||||||
|
openapi.IN_QUERY,
|
||||||
|
description=(
|
||||||
|
"django-restql 字段裁剪表达式,例如 "
|
||||||
|
"{id,name,code,description,default_param,test_param,enabled}"
|
||||||
|
),
|
||||||
|
type=openapi.TYPE_STRING,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
responses={200: DatasetListResponseSerializer},
|
||||||
|
tags=["BI 数据集与报表"],
|
||||||
|
)
|
||||||
|
def list(self, request, *args, **kwargs):
|
||||||
|
return super().list(request, *args, **kwargs)
|
||||||
|
|
||||||
def get_object(self):
|
def get_object(self):
|
||||||
"""
|
"""
|
||||||
Returns the object the view is displaying.
|
Returns the object the view is displaying.
|
||||||
|
|
@ -57,6 +110,18 @@ class DatasetViewSet(CustomModelViewSet):
|
||||||
|
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
|
@swagger_auto_schema(
|
||||||
|
operation_id="bi_dataset_exec",
|
||||||
|
operation_summary="执行已配置的只读统计数据集",
|
||||||
|
operation_description=(
|
||||||
|
"使用 dataset list 返回的 id 或 code 执行数据集。body.query 只填写该数据集"
|
||||||
|
"description/default_param 声明的业务参数;正常查询设置 is_test=false。"
|
||||||
|
"统计聚合使用本接口,日志和业务明细列表用于逐条追溯。"
|
||||||
|
),
|
||||||
|
request_body=DataExecSerializer,
|
||||||
|
responses={200: DatasetSerializer},
|
||||||
|
tags=["BI 数据集与报表"],
|
||||||
|
)
|
||||||
@action(methods=['post'], detail=True, perms_map={'post': 'dataset.exec'}, serializer_class=DataExecSerializer, cache_seconds=0, logging_methods=[])
|
@action(methods=['post'], detail=True, perms_map={'post': 'dataset.exec'}, serializer_class=DataExecSerializer, cache_seconds=0, logging_methods=[])
|
||||||
def exec(self, request, pk=None):
|
def exec(self, request, pk=None):
|
||||||
"""执行sql查询
|
"""执行sql查询
|
||||||
|
|
|
||||||
|
|
@ -207,12 +207,28 @@ class CustomRetrieveModelMixin(RetrieveModelMixin):
|
||||||
|
|
||||||
class CustomListModelMixin(ListModelMixin):
|
class CustomListModelMixin(ListModelMixin):
|
||||||
|
|
||||||
@swagger_auto_schema(manual_parameters=[
|
@swagger_auto_schema(
|
||||||
openapi.Parameter(name="query", in_=openapi.IN_QUERY, description="定制返回数据",
|
operation_description=(
|
||||||
type=openapi.TYPE_STRING, required=False),
|
"通用列表接口用于记录或目录浏览以及逐条追溯。跨时间范围的产量、良率、缺陷、"
|
||||||
openapi.Parameter(name="with_children", in_=openapi.IN_QUERY, description="带有children(yes/no/count)",
|
"库存、绩效和趋势等统计聚合,优先查询 BI dataset 目录并执行匹配的数据集。"
|
||||||
type=openapi.TYPE_STRING, required=False),
|
),
|
||||||
])
|
manual_parameters=[
|
||||||
|
openapi.Parameter(
|
||||||
|
name="query",
|
||||||
|
in_=openapi.IN_QUERY,
|
||||||
|
description="django-restql 返回字段裁剪表达式",
|
||||||
|
type=openapi.TYPE_STRING,
|
||||||
|
required=False,
|
||||||
|
),
|
||||||
|
openapi.Parameter(
|
||||||
|
name="with_children",
|
||||||
|
in_=openapi.IN_QUERY,
|
||||||
|
description="带有children(yes/no/count)",
|
||||||
|
type=openapi.TYPE_STRING,
|
||||||
|
required=False,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
def list(self, request, *args, **kwargs):
|
def list(self, request, *args, **kwargs):
|
||||||
|
|
||||||
queryset = self.filter_queryset(self.get_queryset())
|
queryset = self.filter_queryset(self.get_queryset())
|
||||||
|
|
|
||||||
|
|
@ -354,6 +354,7 @@ class MlogViewSet(CustomModelViewSet):
|
||||||
return super().get_serializer_class()
|
return super().get_serializer_class()
|
||||||
|
|
||||||
@swagger_auto_schema(
|
@swagger_auto_schema(
|
||||||
|
operation_summary="查询生产日志明细(逐条追溯)",
|
||||||
manual_parameters=[
|
manual_parameters=[
|
||||||
openapi.Parameter(name="query", in_=openapi.IN_QUERY, description="定制返回数据", type=openapi.TYPE_STRING, required=False),
|
openapi.Parameter(name="query", in_=openapi.IN_QUERY, description="定制返回数据", type=openapi.TYPE_STRING, required=False),
|
||||||
openapi.Parameter(name="with_children", in_=openapi.IN_QUERY, description="带有children(yes/no/count)", type=openapi.TYPE_STRING, required=False),
|
openapi.Parameter(name="with_children", in_=openapi.IN_QUERY, description="带有children(yes/no/count)", type=openapi.TYPE_STRING, required=False),
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue