From 8e018a1ee81f1e96fbd4fb3fca45ee8c6e187d6e Mon Sep 17 00:00:00 2001 From: shaohuzhang1 <80892890+shaohuzhang1@users.noreply.github.com> Date: Tue, 2 Apr 2024 16:19:36 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E5=BA=94=E7=94=A8?= =?UTF-8?q?=E5=AF=B9=E8=AF=9D=E6=97=A5=E5=BF=97=E5=AF=BC=E5=87=BA=20(#28)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../serializers/chat_serializers.py | 65 +++++++++++++++++-- apps/application/urls.py | 1 + apps/application/views/chat_views.py | 19 ++++++ pyproject.toml | 1 + ui/src/api/log.ts | 13 +++- ui/src/styles/app.scss | 4 ++ ui/src/views/log/index.vue | 13 ++++ 7 files changed, 107 insertions(+), 9 deletions(-) diff --git a/apps/application/serializers/chat_serializers.py b/apps/application/serializers/chat_serializers.py index 0d7f4a103..d60f543bb 100644 --- a/apps/application/serializers/chat_serializers.py +++ b/apps/application/serializers/chat_serializers.py @@ -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) diff --git a/apps/application/urls.py b/apps/application/urls.py index 648583198..507759286 100644 --- a/apps/application/urls.py +++ b/apps/application/urls.py @@ -29,6 +29,7 @@ urlpatterns = [ path('application//', views.Application.Page.as_view(), name='application_page'), path('application//chat/open', views.ChatView.Open.as_view()), path("application/chat/open", views.ChatView.OpenTemp.as_view()), + path('application//chat/export', views.ChatView.Export.as_view(), name='export'), path('application//chat', views.ChatView.as_view(), name='chats'), path('application//chat//', views.ChatView.Page.as_view()), path('application//chat/', views.ChatView.Operate.as_view()), diff --git a/apps/application/views/chat_views.py b/apps/application/views/chat_views.py index 5f7102246..7e968038f 100644 --- a/apps/application/views/chat_views.py +++ b/apps/application/views/chat_views.py @@ -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] diff --git a/pyproject.toml b/pyproject.toml index b2152f4c9..3ca6513ec 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -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"] diff --git a/ui/src/api/log.ts b/ui/src/api/log.ts index 26f9245fe..c610af2c4 100644 --- a/ui/src/api/log.ts +++ b/ui/src/api/log.ts @@ -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 +) => Promise = (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 } diff --git a/ui/src/styles/app.scss b/ui/src/styles/app.scss index 0d49981d9..ccd671552 100644 --- a/ui/src/styles/app.scss +++ b/ui/src/styles/app.scss @@ -189,6 +189,10 @@ h4 { padding-bottom: 0; } +.float-right{ + float:right; +} + .flex { display: flex; } diff --git a/ui/src/views/log/index.vue b/ui/src/views/log/index.vue index 880efcb11..8bdee9c8c 100644 --- a/ui/src/views/log/index.vue +++ b/ui/src/views/log/index.vue @@ -17,6 +17,7 @@ prefix-icon="Search" class="w-240" /> + 导出 { + 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() }