feat: 支持应用对话日志导出 (#28)

This commit is contained in:
shaohuzhang1 2024-04-02 16:19:36 +08:00 committed by GitHub
parent 16ab1f0eae
commit 8e018a1ee8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 107 additions and 9 deletions

View File

@ -14,10 +14,12 @@ import uuid
from functools import reduce
from typing import Dict
import xlwt
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.models import QuerySet, Q
from django.http import HttpResponse
from rest_framework import serializers
from application.models import Chat, Application, ApplicationDatasetMapping, VoteChoices, ChatRecord
@ -73,16 +75,17 @@ class ChatSerializers(serializers.Serializer):
def get_query_set(self):
end_time = self.get_end_time()
query_set = QuerySet(model=get_dynamics_model(
{'application_id': models.CharField(),
'abstract': models.CharField(),
{'application_chat.application_id': models.CharField(),
'application_chat.abstract': models.CharField(),
"star_num": models.IntegerField(),
'trample_num': models.IntegerField(),
'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:
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)
min_star_query = None
min_trample_query = None
@ -102,7 +105,7 @@ class ChatSerializers(serializers.Serializer):
condition = base_condition & min_trample_query
else:
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):
if with_valid:
@ -111,6 +114,54 @@ class ChatSerializers(serializers.Serializer):
os.path.join(PROJECT_DIR, "apps", "application", 'sql', 'list_application_chat.sql')),
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):
if with_valid:
self.is_valid(raise_exception=True)

View File

@ -29,6 +29,7 @@ urlpatterns = [
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/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/<int:current_page>/<int:page_size>', views.ChatView.Page.as_view()),
path('application/<str:application_id>/chat/<chat_id>', views.ChatView.Operate.as_view()),

View File

@ -25,6 +25,25 @@ from common.util.common import query_params_to_single_dict
class ChatView(APIView):
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):
authentication_classes = [TokenAuth]

View File

@ -33,6 +33,7 @@ django-apscheduler = "^0.6.2"
chardet2 = "^2.0.3"
pymupdf = "^1.24.0"
python-docx = "^1.1.0"
xlwt = "^1.3.0"
[build-system]
requires = ["poetry-core"]

View File

@ -1,5 +1,5 @@
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 Ref } from 'vue'
@ -29,6 +29,14 @@ const getChatLog: (
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,
getMarkRecord,
getRecordDetail,
delMarkRecord
delMarkRecord,
exportChatLog
}

View File

@ -189,6 +189,10 @@ h4 {
padding-bottom: 0;
}
.float-right{
float:right;
}
.flex {
display: flex;
}

View File

@ -17,6 +17,7 @@
prefix-icon="Search"
class="w-240"
/>
<el-button class="float-right" @click="exportLog">导出</el-button>
</div>
<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() {
getList()
}