mirror of
https://github.com/1Panel-dev/MaxKB.git
synced 2025-12-29 16:12:55 +00:00
feat: application stats (#3225)
This commit is contained in:
parent
e8886d5c65
commit
6a0a57e785
|
|
@ -0,0 +1,55 @@
|
|||
# coding=utf-8
|
||||
"""
|
||||
@project: MaxKB
|
||||
@Author:虎虎
|
||||
@file: application_stats.py
|
||||
@date:2025/6/9 20:45
|
||||
@desc:
|
||||
"""
|
||||
from drf_spectacular.types import OpenApiTypes
|
||||
from drf_spectacular.utils import OpenApiParameter
|
||||
|
||||
from application.serializers.application_stats import ApplicationStatsSerializer
|
||||
from common.mixins.api_mixin import APIMixin
|
||||
from common.result import ResultSerializer
|
||||
|
||||
|
||||
class ApplicationStatsResult(ResultSerializer):
|
||||
def get_data(self):
|
||||
return ApplicationStatsSerializer(many=True)
|
||||
|
||||
|
||||
class ApplicationStatsAPI(APIMixin):
|
||||
@staticmethod
|
||||
def get_parameters():
|
||||
return [OpenApiParameter(
|
||||
name="workspace_id",
|
||||
description="工作空间id",
|
||||
type=OpenApiTypes.STR,
|
||||
location='path',
|
||||
required=True,
|
||||
),
|
||||
OpenApiParameter(
|
||||
name="application_id",
|
||||
description="application ID",
|
||||
type=OpenApiTypes.STR,
|
||||
location='path',
|
||||
required=True,
|
||||
),
|
||||
OpenApiParameter(
|
||||
name="start_time",
|
||||
description="start Time",
|
||||
type=OpenApiTypes.STR,
|
||||
required=True,
|
||||
),
|
||||
OpenApiParameter(
|
||||
name="end_time",
|
||||
description="end Time",
|
||||
type=OpenApiTypes.STR,
|
||||
required=True,
|
||||
),
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def get_response():
|
||||
return ApplicationStatsResult
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
# coding=utf-8
|
||||
"""
|
||||
@project: MaxKB
|
||||
@Author:虎虎
|
||||
@file: application_stats.py
|
||||
@date:2025/6/9 20:34
|
||||
@desc:
|
||||
"""
|
||||
import datetime
|
||||
import os
|
||||
from typing import Dict, List
|
||||
|
||||
from django.db import models
|
||||
from django.db.models import QuerySet
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from rest_framework import serializers
|
||||
|
||||
from application.models import ApplicationChatUserStats
|
||||
from common.db.search import native_search, get_dynamics_model
|
||||
from common.utils.common import get_file_content
|
||||
from maxkb.conf import PROJECT_DIR
|
||||
|
||||
|
||||
class ApplicationStatsSerializer(serializers.Serializer):
|
||||
chat_record_count = serializers.IntegerField(required=True, label=_("Number of conversations"))
|
||||
customer_added_count = serializers.IntegerField(required=True, label=_("Number of new users"))
|
||||
customer_num = serializers.IntegerField(required=True, label=_("Total number of users"))
|
||||
day = serializers.CharField(required=True, label=_("date"))
|
||||
star_num = serializers.IntegerField(required=True, label=_("Number of Likes"))
|
||||
tokens_num = serializers.IntegerField(required=True, label=_("Tokens consumption"))
|
||||
trample_num = serializers.IntegerField(required=True, label=_("Number of thumbs-downs"))
|
||||
|
||||
|
||||
class ApplicationStatisticsSerializer(serializers.Serializer):
|
||||
application_id = serializers.UUIDField(required=True, label=_("Application ID"))
|
||||
start_time = serializers.DateField(format='%Y-%m-%d', label=_("Start time"))
|
||||
end_time = serializers.DateField(format='%Y-%m-%d', label=_("End time"))
|
||||
|
||||
def get_end_time(self):
|
||||
return datetime.datetime.combine(
|
||||
datetime.datetime.strptime(self.data.get('end_time'), '%Y-%m-%d'),
|
||||
datetime.datetime.max.time())
|
||||
|
||||
def get_start_time(self):
|
||||
return self.data.get('start_time')
|
||||
|
||||
def get_customer_count_trend(self, with_valid=True):
|
||||
if with_valid:
|
||||
self.is_valid(raise_exception=True)
|
||||
start_time = self.get_start_time()
|
||||
end_time = self.get_end_time()
|
||||
return native_search(
|
||||
{'default_sql': QuerySet(ApplicationChatUserStats).filter(
|
||||
application_id=self.data.get('application_id'),
|
||||
create_time__gte=start_time,
|
||||
create_time__lte=end_time)},
|
||||
select_string=get_file_content(
|
||||
os.path.join(PROJECT_DIR, "apps", "application", 'sql', 'customer_count_trend.sql')))
|
||||
|
||||
def get_chat_record_aggregate_trend(self, with_valid=True):
|
||||
if with_valid:
|
||||
self.is_valid(raise_exception=True)
|
||||
start_time = self.get_start_time()
|
||||
end_time = self.get_end_time()
|
||||
chat_record_aggregate_trend = native_search(
|
||||
{'default_sql': QuerySet(model=get_dynamics_model(
|
||||
{'application_chat.application_id': models.UUIDField(),
|
||||
'application_chat_record.create_time': models.DateTimeField()})).filter(
|
||||
**{'application_chat.application_id': self.data.get('application_id'),
|
||||
'application_chat_record.create_time__gte': start_time,
|
||||
'application_chat_record.create_time__lte': end_time}
|
||||
)},
|
||||
select_string=get_file_content(
|
||||
os.path.join(PROJECT_DIR, "apps", "application", 'sql', 'chat_record_count_trend.sql')))
|
||||
customer_count_trend = self.get_customer_count_trend(with_valid=False)
|
||||
return self.merge_customer_chat_record(chat_record_aggregate_trend, customer_count_trend)
|
||||
|
||||
def merge_customer_chat_record(self, chat_record_aggregate_trend: List[Dict], customer_count_trend: List[Dict]):
|
||||
|
||||
return [{**self.find(chat_record_aggregate_trend, lambda c: c.get('day').strftime('%Y-%m-%d') == day,
|
||||
{'star_num': 0, 'trample_num': 0, 'tokens_num': 0, 'chat_record_count': 0,
|
||||
'customer_num': 0,
|
||||
'day': day}),
|
||||
**self.find(customer_count_trend, lambda c: c.get('day').strftime('%Y-%m-%d') == day,
|
||||
{'customer_added_count': 0})}
|
||||
for
|
||||
day in
|
||||
self.get_days_between_dates(self.data.get('start_time'), self.data.get('end_time'))]
|
||||
|
||||
@staticmethod
|
||||
def find(source_list, condition, default):
|
||||
value_list = [row for row in source_list if condition(row)]
|
||||
if len(value_list) > 0:
|
||||
return value_list[0]
|
||||
return default
|
||||
|
||||
@staticmethod
|
||||
def get_days_between_dates(start_date, end_date):
|
||||
start_date = datetime.datetime.strptime(start_date, '%Y-%m-%d')
|
||||
end_date = datetime.datetime.strptime(end_date, '%Y-%m-%d')
|
||||
days = []
|
||||
current_date = start_date
|
||||
while current_date <= end_date:
|
||||
days.append(current_date.strftime('%Y-%m-%d'))
|
||||
current_date += datetime.timedelta(days=1)
|
||||
return days
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
SELECT SUM
|
||||
( CASE WHEN application_chat_record.vote_status = '0' THEN 1 ELSE 0 END ) AS "star_num",
|
||||
SUM ( CASE WHEN application_chat_record.vote_status = '1' THEN 1 ELSE 0 END ) AS "trample_num",
|
||||
SUM ( application_chat_record.message_tokens + application_chat_record.answer_tokens ) as "tokens_num",
|
||||
"count"(application_chat_record."id") as chat_record_count,
|
||||
"count"(DISTINCT application_chat.chat_user_id) customer_num,
|
||||
application_chat_record.create_time :: DATE as "day"
|
||||
FROM
|
||||
application_chat_record application_chat_record
|
||||
LEFT JOIN application_chat application_chat ON application_chat."id" = application_chat_record.chat_id
|
||||
${default_sql}
|
||||
GROUP BY "day"
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
SELECT
|
||||
COUNT ( "application_chat_user_stats"."id" ) AS "customer_added_count",
|
||||
create_time :: DATE as "day"
|
||||
FROM
|
||||
"application_chat_user_stats"
|
||||
${default_sql}
|
||||
GROUP BY "day"
|
||||
|
|
@ -13,6 +13,8 @@ urlpatterns = [
|
|||
path('workspace/<str:workspace_id>/application/<str:application_id>', views.Application.Operate.as_view()),
|
||||
path('workspace/<str:workspace_id>/application/<str:application_id>/application_key',
|
||||
views.ApplicationKey.as_view()),
|
||||
path('workspace/<str:workspace_id>/application/<str:application_id>/application_stats',
|
||||
views.ApplicationStats.as_view()),
|
||||
path('workspace/<str:workspace_id>/application/<str:application_id>/application_key/<str:api_key_id>',
|
||||
views.ApplicationKey.Operate.as_view()),
|
||||
path('workspace/<str:workspace_id>/application/<str:application_id>/export', views.Application.Export.as_view()),
|
||||
|
|
|
|||
|
|
@ -10,3 +10,4 @@ from .application_api_key import *
|
|||
from .application import *
|
||||
from .application_version import *
|
||||
from .application_access_token import *
|
||||
from .application_stats import *
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
# coding=utf-8
|
||||
"""
|
||||
@project: MaxKB
|
||||
@Author:虎虎
|
||||
@file: application_stats.py
|
||||
@date:2025/6/9 20:30
|
||||
@desc:
|
||||
"""
|
||||
from drf_spectacular.utils import extend_schema
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from application.api.application_stats import ApplicationStatsAPI
|
||||
from application.serializers.application_stats import ApplicationStatisticsSerializer
|
||||
from common import result
|
||||
from common.auth import TokenAuth
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
||||
class ApplicationStats(APIView):
|
||||
authentication_classes = [TokenAuth]
|
||||
|
||||
@extend_schema(
|
||||
methods=['GET'],
|
||||
description=_('Dialogue-related statistical trends'),
|
||||
summary=_('Dialogue-related statistical trends'),
|
||||
operation_id=_('Dialogue-related statistical trends'), # type: ignore
|
||||
parameters=ApplicationStatsAPI.get_parameters(),
|
||||
responses=ApplicationStatsAPI.get_response(),
|
||||
tags=[_('Application')] # type: ignore
|
||||
)
|
||||
def get(self, request: Request, workspace_id: str, application_id: str):
|
||||
return result.success(
|
||||
ApplicationStatisticsSerializer(data={'application_id': application_id,
|
||||
'start_time': request.query_params.get(
|
||||
'start_time'),
|
||||
'end_time': request.query_params.get(
|
||||
'end_time')
|
||||
}).get_chat_record_aggregate_trend())
|
||||
Loading…
Reference in New Issue