Skip to content

Commit 8e018a1

Browse files
authored
feat: 支持应用对话日志导出 (#28)
1 parent 16ab1f0 commit 8e018a1

File tree

7 files changed

+107
-9
lines changed

7 files changed

+107
-9
lines changed

apps/application/serializers/chat_serializers.py

Lines changed: 58 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,12 @@
1414
from functools import reduce
1515
from typing import Dict
1616

17+
import xlwt
1718
from django.core import validators
18-
from django.core.cache import cache, caches
19+
from django.core.cache import caches
1920
from django.db import transaction, models
2021
from django.db.models import QuerySet, Q
22+
from django.http import HttpResponse
2123
from rest_framework import serializers
2224

2325
from application.models import Chat, Application, ApplicationDatasetMapping, VoteChoices, ChatRecord
@@ -73,16 +75,17 @@ def get_end_time(self):
7375
def get_query_set(self):
7476
end_time = self.get_end_time()
7577
query_set = QuerySet(model=get_dynamics_model(
76-
{'application_id': models.CharField(),
77-
'abstract': models.CharField(),
78+
{'application_chat.application_id': models.CharField(),
79+
'application_chat.abstract': models.CharField(),
7880
"star_num": models.IntegerField(),
7981
'trample_num': models.IntegerField(),
8082
'comparer': models.CharField(),
81-
'create_time': models.DateTimeField()}))
83+
'application_chat.create_time': models.DateTimeField()}))
8284

83-
base_query_dict = {'application_id': self.data.get("application_id"), 'create_time__gte': end_time}
85+
base_query_dict = {'application_chat.application_id': self.data.get("application_id"),
86+
'application_chat.create_time__gte': end_time}
8487
if 'abstract' in self.data and self.data.get('abstract') is not None:
85-
base_query_dict['abstract__contains'] = self.data.get('abstract')
88+
base_query_dict['application_chat.abstract__contains'] = self.data.get('abstract')
8689
base_condition = Q(**base_query_dict)
8790
min_star_query = None
8891
min_trample_query = None
@@ -102,7 +105,7 @@ def get_query_set(self):
102105
condition = base_condition & min_trample_query
103106
else:
104107
condition = base_condition
105-
return query_set.filter(condition).order_by("-create_time")
108+
return query_set.filter(condition).order_by("-application_chat.create_time")
106109

107110
def list(self, with_valid=True):
108111
if with_valid:
@@ -111,6 +114,54 @@ def list(self, with_valid=True):
111114
os.path.join(PROJECT_DIR, "apps", "application", 'sql', 'list_application_chat.sql')),
112115
with_table_name=False)
113116

117+
@staticmethod
118+
def to_row(row: Dict):
119+
details = row.get('details')
120+
padding_problem_text = details.get('problem_padding').get(
121+
'padding_problem_text') if 'problem_padding' in details and 'padding_problem_text' in details.get(
122+
'problem_padding') else ""
123+
paragraph_list = details.get('search_step').get(
124+
'paragraph_list') if 'search_step' in details and 'paragraph_list' in details.get('search_step') else []
125+
improve_paragraph_list = row.get('improve_paragraph_list')
126+
vote_status_map = {'-1': '未投票', '0': '赞同', '1': '反对'}
127+
return [str(row.get('chat_id')), row.get('abstract'), row.get('problem_text'), padding_problem_text,
128+
row.get('answer_text'), vote_status_map.get(row.get('vote_status')), len(paragraph_list), "\n".join(
129+
[f"{index}{paragraph_list[index].get('title')}\n{paragraph_list[index].get('content')}" for index
130+
in
131+
range(len(paragraph_list))]),
132+
"\n".join([
133+
f"{improve_paragraph_list[index].get('title')}\n{improve_paragraph_list[index].get('content')}"
134+
for index in range(len(improve_paragraph_list))]),
135+
row.get('message_tokens') + row.get('answer_tokens'), row.get('run_time'),
136+
str(row.get('create_time'))]
137+
138+
def export(self, with_valid=True):
139+
if with_valid:
140+
self.is_valid(raise_exception=True)
141+
data_list = native_search(self.get_query_set(), select_string=get_file_content(
142+
os.path.join(PROJECT_DIR, "apps", "application", 'sql', 'export_application_chat.sql')),
143+
with_table_name=False)
144+
145+
# 创建工作簿对象
146+
workbook = xlwt.Workbook(encoding='utf-8')
147+
# 添加工作表
148+
worksheet = workbook.add_sheet('Sheet1')
149+
data = [
150+
['会话ID', '摘要', '用户问题', '优化后问题', '回答', '用户反馈', '引用分段数', '分段标题+内容',
151+
'标注', '消耗tokens', '耗时(s)', '提问时间'],
152+
*[self.to_row(row) for row in data_list]
153+
]
154+
# 写入数据到工作表
155+
for row_idx, row in enumerate(data):
156+
for col_idx, col in enumerate(row):
157+
worksheet.write(row_idx, col_idx, col)
158+
# 创建HttpResponse对象返回Excel文件
159+
response = HttpResponse(content_type='application/vnd.ms-excel')
160+
response['Content-Disposition'] = 'attachment; filename="data.xls"'
161+
162+
workbook.save(response)
163+
return response
164+
114165
def page(self, current_page: int, page_size: int, with_valid=True):
115166
if with_valid:
116167
self.is_valid(raise_exception=True)

apps/application/urls.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
path('application/<int:current_page>/<int:page_size>', views.Application.Page.as_view(), name='application_page'),
3030
path('application/<str:application_id>/chat/open', views.ChatView.Open.as_view()),
3131
path("application/chat/open", views.ChatView.OpenTemp.as_view()),
32+
path('application/<str:application_id>/chat/export', views.ChatView.Export.as_view(), name='export'),
3233
path('application/<str:application_id>/chat', views.ChatView.as_view(), name='chats'),
3334
path('application/<str:application_id>/chat/<int:current_page>/<int:page_size>', views.ChatView.Page.as_view()),
3435
path('application/<str:application_id>/chat/<chat_id>', views.ChatView.Operate.as_view()),

apps/application/views/chat_views.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,25 @@
2525
class ChatView(APIView):
2626
authentication_classes = [TokenAuth]
2727

28+
class Export(APIView):
29+
authentication_classes = [TokenAuth]
30+
31+
@action(methods=['GET'], detail=False)
32+
@swagger_auto_schema(operation_summary="导出对话",
33+
operation_id="导出对话",
34+
manual_parameters=ChatApi.get_request_params_api(),
35+
tags=["应用/对话日志"]
36+
)
37+
@has_permissions(
38+
ViewPermission([RoleConstants.ADMIN, RoleConstants.USER, RoleConstants.APPLICATION_KEY],
39+
[lambda r, keywords: Permission(group=Group.APPLICATION, operate=Operate.USE,
40+
dynamic_tag=keywords.get('application_id'))])
41+
)
42+
def get(self, request: Request, application_id: str):
43+
return ChatSerializers.Query(
44+
data={**query_params_to_single_dict(request.query_params), 'application_id': application_id,
45+
'user_id': request.user.id}).export()
46+
2847
class Open(APIView):
2948
authentication_classes = [TokenAuth]
3049

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ django-apscheduler = "^0.6.2"
3333
chardet2 = "^2.0.3"
3434
pymupdf = "^1.24.0"
3535
python-docx = "^1.1.0"
36+
xlwt = "^1.3.0"
3637

3738
[build-system]
3839
requires = ["poetry-core"]

ui/src/api/log.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Result } from '@/request/Result'
2-
import { get, post, del, put } from '@/request/index'
2+
import { get, post, del, put, exportExcel } from '@/request/index'
33
import type { pageRequest } from '@/api/type/common'
44
import { type Ref } from 'vue'
55

@@ -29,6 +29,14 @@ const getChatLog: (
2929
loading
3030
)
3131
}
32+
const exportChatLog: (
33+
applicaiton_id: string,
34+
applicantion_name: string,
35+
param: any,
36+
loading?: Ref<boolean>
37+
) => Promise<void> = (applicaiton_id, applicantion_name, param, loading) => {
38+
exportExcel(applicantion_name, `${prefix}/${applicaiton_id}/chat/export`, param, loading)
39+
}
3240

3341
/**
3442
* 删除日志
@@ -171,5 +179,6 @@ export default {
171179
putChatRecordLog,
172180
getMarkRecord,
173181
getRecordDetail,
174-
delMarkRecord
182+
delMarkRecord,
183+
exportChatLog
175184
}

ui/src/styles/app.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,10 @@ h4 {
189189
padding-bottom: 0;
190190
}
191191

192+
.float-right{
193+
float:right;
194+
}
195+
192196
.flex {
193197
display: flex;
194198
}

ui/src/views/log/index.vue

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
prefix-icon="Search"
1818
class="w-240"
1919
/>
20+
<el-button class="float-right" @click="exportLog">导出</el-button>
2021
</div>
2122

2223
<app-table
@@ -321,6 +322,18 @@ function getDetail() {
321322
})
322323
}
323324
325+
const exportLog = () => {
326+
if (detail.value) {
327+
let obj: any = {
328+
history_day: history_day.value,
329+
...filter.value
330+
}
331+
if (search.value) {
332+
obj = { ...obj, abstract: search.value }
333+
}
334+
logApi.exportChatLog(detail.value.id, detail.value.name, obj, loading)
335+
}
336+
}
324337
function refresh() {
325338
getList()
326339
}

0 commit comments

Comments
 (0)