feat: 支持应用对话日志导出 (#28)
This commit is contained in:
parent
16ab1f0eae
commit
8e018a1ee8
@ -14,10 +14,12 @@ import uuid
|
|||||||
from functools import reduce
|
from functools import reduce
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
|
|
||||||
|
import xlwt
|
||||||
from django.core import validators
|
from django.core import validators
|
||||||
from django.core.cache import cache, caches
|
from django.core.cache import caches
|
||||||
from django.db import transaction, models
|
from django.db import transaction, models
|
||||||
from django.db.models import QuerySet, Q
|
from django.db.models import QuerySet, Q
|
||||||
|
from django.http import HttpResponse
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
from application.models import Chat, Application, ApplicationDatasetMapping, VoteChoices, ChatRecord
|
from application.models import Chat, Application, ApplicationDatasetMapping, VoteChoices, ChatRecord
|
||||||
@ -73,16 +75,17 @@ class ChatSerializers(serializers.Serializer):
|
|||||||
def get_query_set(self):
|
def get_query_set(self):
|
||||||
end_time = self.get_end_time()
|
end_time = self.get_end_time()
|
||||||
query_set = QuerySet(model=get_dynamics_model(
|
query_set = QuerySet(model=get_dynamics_model(
|
||||||
{'application_id': models.CharField(),
|
{'application_chat.application_id': models.CharField(),
|
||||||
'abstract': models.CharField(),
|
'application_chat.abstract': models.CharField(),
|
||||||
"star_num": models.IntegerField(),
|
"star_num": models.IntegerField(),
|
||||||
'trample_num': models.IntegerField(),
|
'trample_num': models.IntegerField(),
|
||||||
'comparer': models.CharField(),
|
'comparer': models.CharField(),
|
||||||
'create_time': models.DateTimeField()}))
|
'application_chat.create_time': models.DateTimeField()}))
|
||||||
|
|
||||||
base_query_dict = {'application_id': self.data.get("application_id"), 'create_time__gte': end_time}
|
base_query_dict = {'application_chat.application_id': self.data.get("application_id"),
|
||||||
|
'application_chat.create_time__gte': end_time}
|
||||||
if 'abstract' in self.data and self.data.get('abstract') is not None:
|
if 'abstract' in self.data and self.data.get('abstract') is not None:
|
||||||
base_query_dict['abstract__contains'] = self.data.get('abstract')
|
base_query_dict['application_chat.abstract__contains'] = self.data.get('abstract')
|
||||||
base_condition = Q(**base_query_dict)
|
base_condition = Q(**base_query_dict)
|
||||||
min_star_query = None
|
min_star_query = None
|
||||||
min_trample_query = None
|
min_trample_query = None
|
||||||
@ -102,7 +105,7 @@ class ChatSerializers(serializers.Serializer):
|
|||||||
condition = base_condition & min_trample_query
|
condition = base_condition & min_trample_query
|
||||||
else:
|
else:
|
||||||
condition = base_condition
|
condition = base_condition
|
||||||
return query_set.filter(condition).order_by("-create_time")
|
return query_set.filter(condition).order_by("-application_chat.create_time")
|
||||||
|
|
||||||
def list(self, with_valid=True):
|
def list(self, with_valid=True):
|
||||||
if with_valid:
|
if with_valid:
|
||||||
@ -111,6 +114,54 @@ class ChatSerializers(serializers.Serializer):
|
|||||||
os.path.join(PROJECT_DIR, "apps", "application", 'sql', 'list_application_chat.sql')),
|
os.path.join(PROJECT_DIR, "apps", "application", 'sql', 'list_application_chat.sql')),
|
||||||
with_table_name=False)
|
with_table_name=False)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def to_row(row: Dict):
|
||||||
|
details = row.get('details')
|
||||||
|
padding_problem_text = details.get('problem_padding').get(
|
||||||
|
'padding_problem_text') if 'problem_padding' in details and 'padding_problem_text' in details.get(
|
||||||
|
'problem_padding') else ""
|
||||||
|
paragraph_list = details.get('search_step').get(
|
||||||
|
'paragraph_list') if 'search_step' in details and 'paragraph_list' in details.get('search_step') else []
|
||||||
|
improve_paragraph_list = row.get('improve_paragraph_list')
|
||||||
|
vote_status_map = {'-1': '未投票', '0': '赞同', '1': '反对'}
|
||||||
|
return [str(row.get('chat_id')), row.get('abstract'), row.get('problem_text'), padding_problem_text,
|
||||||
|
row.get('answer_text'), vote_status_map.get(row.get('vote_status')), len(paragraph_list), "\n".join(
|
||||||
|
[f"{index}、{paragraph_list[index].get('title')}\n{paragraph_list[index].get('content')}" for index
|
||||||
|
in
|
||||||
|
range(len(paragraph_list))]),
|
||||||
|
"\n".join([
|
||||||
|
f"{improve_paragraph_list[index].get('title')}\n{improve_paragraph_list[index].get('content')}"
|
||||||
|
for index in range(len(improve_paragraph_list))]),
|
||||||
|
row.get('message_tokens') + row.get('answer_tokens'), row.get('run_time'),
|
||||||
|
str(row.get('create_time'))]
|
||||||
|
|
||||||
|
def export(self, with_valid=True):
|
||||||
|
if with_valid:
|
||||||
|
self.is_valid(raise_exception=True)
|
||||||
|
data_list = native_search(self.get_query_set(), select_string=get_file_content(
|
||||||
|
os.path.join(PROJECT_DIR, "apps", "application", 'sql', 'export_application_chat.sql')),
|
||||||
|
with_table_name=False)
|
||||||
|
|
||||||
|
# 创建工作簿对象
|
||||||
|
workbook = xlwt.Workbook(encoding='utf-8')
|
||||||
|
# 添加工作表
|
||||||
|
worksheet = workbook.add_sheet('Sheet1')
|
||||||
|
data = [
|
||||||
|
['会话ID', '摘要', '用户问题', '优化后问题', '回答', '用户反馈', '引用分段数', '分段标题+内容',
|
||||||
|
'标注', '消耗tokens', '耗时(s)', '提问时间'],
|
||||||
|
*[self.to_row(row) for row in data_list]
|
||||||
|
]
|
||||||
|
# 写入数据到工作表
|
||||||
|
for row_idx, row in enumerate(data):
|
||||||
|
for col_idx, col in enumerate(row):
|
||||||
|
worksheet.write(row_idx, col_idx, col)
|
||||||
|
# 创建HttpResponse对象返回Excel文件
|
||||||
|
response = HttpResponse(content_type='application/vnd.ms-excel')
|
||||||
|
response['Content-Disposition'] = 'attachment; filename="data.xls"'
|
||||||
|
|
||||||
|
workbook.save(response)
|
||||||
|
return response
|
||||||
|
|
||||||
def page(self, current_page: int, page_size: int, with_valid=True):
|
def page(self, current_page: int, page_size: int, with_valid=True):
|
||||||
if with_valid:
|
if with_valid:
|
||||||
self.is_valid(raise_exception=True)
|
self.is_valid(raise_exception=True)
|
||||||
|
|||||||
@ -29,6 +29,7 @@ urlpatterns = [
|
|||||||
path('application/<int:current_page>/<int:page_size>', views.Application.Page.as_view(), name='application_page'),
|
path('application/<int:current_page>/<int:page_size>', views.Application.Page.as_view(), name='application_page'),
|
||||||
path('application/<str:application_id>/chat/open', views.ChatView.Open.as_view()),
|
path('application/<str:application_id>/chat/open', views.ChatView.Open.as_view()),
|
||||||
path("application/chat/open", views.ChatView.OpenTemp.as_view()),
|
path("application/chat/open", views.ChatView.OpenTemp.as_view()),
|
||||||
|
path('application/<str:application_id>/chat/export', views.ChatView.Export.as_view(), name='export'),
|
||||||
path('application/<str:application_id>/chat', views.ChatView.as_view(), name='chats'),
|
path('application/<str:application_id>/chat', views.ChatView.as_view(), name='chats'),
|
||||||
path('application/<str:application_id>/chat/<int:current_page>/<int:page_size>', views.ChatView.Page.as_view()),
|
path('application/<str:application_id>/chat/<int:current_page>/<int:page_size>', views.ChatView.Page.as_view()),
|
||||||
path('application/<str:application_id>/chat/<chat_id>', views.ChatView.Operate.as_view()),
|
path('application/<str:application_id>/chat/<chat_id>', views.ChatView.Operate.as_view()),
|
||||||
|
|||||||
@ -25,6 +25,25 @@ from common.util.common import query_params_to_single_dict
|
|||||||
class ChatView(APIView):
|
class ChatView(APIView):
|
||||||
authentication_classes = [TokenAuth]
|
authentication_classes = [TokenAuth]
|
||||||
|
|
||||||
|
class Export(APIView):
|
||||||
|
authentication_classes = [TokenAuth]
|
||||||
|
|
||||||
|
@action(methods=['GET'], detail=False)
|
||||||
|
@swagger_auto_schema(operation_summary="导出对话",
|
||||||
|
operation_id="导出对话",
|
||||||
|
manual_parameters=ChatApi.get_request_params_api(),
|
||||||
|
tags=["应用/对话日志"]
|
||||||
|
)
|
||||||
|
@has_permissions(
|
||||||
|
ViewPermission([RoleConstants.ADMIN, RoleConstants.USER, RoleConstants.APPLICATION_KEY],
|
||||||
|
[lambda r, keywords: Permission(group=Group.APPLICATION, operate=Operate.USE,
|
||||||
|
dynamic_tag=keywords.get('application_id'))])
|
||||||
|
)
|
||||||
|
def get(self, request: Request, application_id: str):
|
||||||
|
return ChatSerializers.Query(
|
||||||
|
data={**query_params_to_single_dict(request.query_params), 'application_id': application_id,
|
||||||
|
'user_id': request.user.id}).export()
|
||||||
|
|
||||||
class Open(APIView):
|
class Open(APIView):
|
||||||
authentication_classes = [TokenAuth]
|
authentication_classes = [TokenAuth]
|
||||||
|
|
||||||
|
|||||||
@ -33,6 +33,7 @@ django-apscheduler = "^0.6.2"
|
|||||||
chardet2 = "^2.0.3"
|
chardet2 = "^2.0.3"
|
||||||
pymupdf = "^1.24.0"
|
pymupdf = "^1.24.0"
|
||||||
python-docx = "^1.1.0"
|
python-docx = "^1.1.0"
|
||||||
|
xlwt = "^1.3.0"
|
||||||
|
|
||||||
[build-system]
|
[build-system]
|
||||||
requires = ["poetry-core"]
|
requires = ["poetry-core"]
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { Result } from '@/request/Result'
|
import { Result } from '@/request/Result'
|
||||||
import { get, post, del, put } from '@/request/index'
|
import { get, post, del, put, exportExcel } from '@/request/index'
|
||||||
import type { pageRequest } from '@/api/type/common'
|
import type { pageRequest } from '@/api/type/common'
|
||||||
import { type Ref } from 'vue'
|
import { type Ref } from 'vue'
|
||||||
|
|
||||||
@ -29,6 +29,14 @@ const getChatLog: (
|
|||||||
loading
|
loading
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
const exportChatLog: (
|
||||||
|
applicaiton_id: string,
|
||||||
|
applicantion_name: string,
|
||||||
|
param: any,
|
||||||
|
loading?: Ref<boolean>
|
||||||
|
) => Promise<void> = (applicaiton_id, applicantion_name, param, loading) => {
|
||||||
|
exportExcel(applicantion_name, `${prefix}/${applicaiton_id}/chat/export`, param, loading)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 删除日志
|
* 删除日志
|
||||||
@ -171,5 +179,6 @@ export default {
|
|||||||
putChatRecordLog,
|
putChatRecordLog,
|
||||||
getMarkRecord,
|
getMarkRecord,
|
||||||
getRecordDetail,
|
getRecordDetail,
|
||||||
delMarkRecord
|
delMarkRecord,
|
||||||
|
exportChatLog
|
||||||
}
|
}
|
||||||
|
|||||||
@ -189,6 +189,10 @@ h4 {
|
|||||||
padding-bottom: 0;
|
padding-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.float-right{
|
||||||
|
float:right;
|
||||||
|
}
|
||||||
|
|
||||||
.flex {
|
.flex {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,6 +17,7 @@
|
|||||||
prefix-icon="Search"
|
prefix-icon="Search"
|
||||||
class="w-240"
|
class="w-240"
|
||||||
/>
|
/>
|
||||||
|
<el-button class="float-right" @click="exportLog">导出</el-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<app-table
|
<app-table
|
||||||
@ -321,6 +322,18 @@ function getDetail() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const exportLog = () => {
|
||||||
|
if (detail.value) {
|
||||||
|
let obj: any = {
|
||||||
|
history_day: history_day.value,
|
||||||
|
...filter.value
|
||||||
|
}
|
||||||
|
if (search.value) {
|
||||||
|
obj = { ...obj, abstract: search.value }
|
||||||
|
}
|
||||||
|
logApi.exportChatLog(detail.value.id, detail.value.name, obj, loading)
|
||||||
|
}
|
||||||
|
}
|
||||||
function refresh() {
|
function refresh() {
|
||||||
getList()
|
getList()
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user