Skip to content

Commit 3fc3f5d

Browse files
committed
feat: export chart
1 parent 0b12b79 commit 3fc3f5d

File tree

12 files changed

+250
-45
lines changed

12 files changed

+250
-45
lines changed

backend/apps/chat/api/chat.py

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
1+
import datetime
12
import traceback
23

4+
import numpy as np
5+
import pandas as pd
36
from fastapi import APIRouter, HTTPException
47
from fastapi.responses import StreamingResponse
58

69
from apps.chat.curd.chat import list_chats, get_chat_with_records, create_chat, rename_chat, \
710
delete_chat, get_chat_chart_data, get_chat_predict_data
8-
from apps.chat.models.chat_model import CreateChat, ChatRecord, RenameChat, ChatQuestion
11+
from apps.chat.models.chat_model import CreateChat, ChatRecord, RenameChat, ChatQuestion, ExcelData
912
from apps.chat.task.llm import LLMService
13+
from common.core.config import settings
1014
from common.core.deps import CurrentAssistant, SessionDep, CurrentUser
1115

1216
router = APIRouter(tags=["Data Q&A"], prefix="/chat")
@@ -183,3 +187,27 @@ async def analysis_or_predict(session: SessionDep, current_user: CurrentUser, ch
183187
)
184188

185189
return StreamingResponse(llm_service.await_result(), media_type="text/event-stream")
190+
191+
192+
@router.post("/excel/export")
193+
async def export_excel(excel_data: ExcelData):
194+
_fields_list = []
195+
data = []
196+
for _data in excel_data.data:
197+
_row = []
198+
for field in excel_data.axis:
199+
_row.append(_data.get(field.value))
200+
data.append(_row)
201+
for field in excel_data.axis:
202+
_fields_list.append(field.name)
203+
df = pd.DataFrame(np.array(data), columns=_fields_list)
204+
205+
file_name = f"{excel_data.name}-{datetime.datetime.now().strftime('%Y-%m-%d_%H-%M-%S')}.xlsx"
206+
207+
file_path = f'{(settings.EXCEL_PATH if settings.EXCEL_PATH[-1] == "/" else (settings.EXCEL_PATH + "/"))}{file_name}'
208+
209+
file_download_path = f'{(settings.SERVER_EXCEL_HOST if settings.SERVER_EXCEL_HOST[-1] == "/" else (settings.SERVER_EXCEL_HOST + "/"))}{file_name}'
210+
211+
df.to_excel(file_path, index=False)
212+
213+
return file_download_path

backend/apps/chat/models/chat_model.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,3 +180,15 @@ class McpQuestion(BaseModel):
180180
question: str = Body(description='用户提问')
181181
chat_id: int = Body(description='会话ID')
182182
token: str = Body(description='token')
183+
184+
185+
class AxisObj(BaseModel):
186+
name: str = ''
187+
value: str = ''
188+
type: str | None = None
189+
190+
191+
class ExcelData(BaseModel):
192+
axis: list[AxisObj] = []
193+
data: list[dict] = []
194+
name: str = 'Excel'

backend/apps/datasource/crud/datasource.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,9 @@
44

55
from fastapi import HTTPException
66
from sqlalchemy import and_, text, func
7-
87
from sqlmodel import select
98

10-
9+
from apps.datasource.crud.permission import get_column_permission_fields, get_row_permission_filters, is_normal_user
1110
from apps.datasource.utils.utils import aes_decrypt
1211
from apps.db.constant import DB
1312
from apps.db.db import get_engine, get_tables, get_fields, exec_sql
@@ -20,7 +19,7 @@
2019
from ..crud.table import delete_table_by_ds_id, update_table
2120
from ..models.datasource import CoreDatasource, CreateDatasource, CoreTable, CoreField, ColumnSchema, TableObj, \
2221
DatasourceConf, TableAndFields
23-
from apps.datasource.crud.permission import get_column_permission_fields, get_row_permission_filters, is_normal_user
22+
2423

2524
def get_datasource_list(session: SessionDep, user: CurrentUser, oid: Optional[int] = None) -> List[CoreDatasource]:
2625
current_oid = user.oid if user.oid is not None else 1
@@ -249,11 +248,13 @@ def preview(session: SessionDep, current_user: CurrentUser, id: int, data: Table
249248
f_list = [f for f in data.fields if f.checked]
250249
if is_normal_user(current_user):
251250
# column is checked, and, column permission for data.fields
252-
f_list = get_column_permission_fields(session=session, current_user=current_user, table=data.table, fields=f_list) or f_list
251+
f_list = get_column_permission_fields(session=session, current_user=current_user, table=data.table,
252+
fields=f_list) or f_list
253253

254254
# row permission tree
255255
where_str = ''
256-
filter_mapping = get_row_permission_filters(session=session, current_user=current_user, ds=ds, tables=None, single_table=data.table)
256+
filter_mapping = get_row_permission_filters(session=session, current_user=current_user, ds=ds, tables=None,
257+
single_table=data.table)
257258
if filter_mapping:
258259
mapping_dict = filter_mapping[0]
259260
where_str = mapping_dict.get('filter')
@@ -325,13 +326,12 @@ def get_table_obj_by_ds(session: SessionDep, current_user: CurrentUser, ds: Core
325326
fields = session.query(CoreField).filter(and_(CoreField.table_id == table.id, CoreField.checked == True)).all()
326327

327328
# do column permissions, filter fields
328-
fields = get_column_permission_fields(session=session, current_user=current_user, table=table, fields=fields) or fields
329+
fields = get_column_permission_fields(session=session, current_user=current_user, table=table,
330+
fields=fields) or fields
329331
_list.append(TableAndFields(schema=schema, table=table, fields=fields))
330332
return _list
331333

332334

333-
334-
335335
def get_table_schema(session: SessionDep, current_user: CurrentUser, ds: CoreDatasource) -> str:
336336
schema_str = ""
337337
table_objs = get_table_obj_by_ds(session=session, current_user=current_user, ds=ds)
@@ -360,4 +360,5 @@ def get_table_schema(session: SessionDep, current_user: CurrentUser, ds: CoreDat
360360
field_list.append(f"({field.field_name}:{field.field_type}, {field_comment})")
361361
schema_str += ",\n".join(field_list)
362362
schema_str += '\n]\n'
363+
# todo 外键
363364
return schema_str

backend/common/core/config.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,10 @@ def SQLALCHEMY_DATABASE_URI(self) -> PostgresDsn | str:
7979
)
8080

8181
MCP_IMAGE_PATH: str = '/opt/sqlbot/images'
82+
EXCEL_PATH: str
8283
MCP_IMAGE_HOST: str = 'http://localhost:3000'
8384
SERVER_IMAGE_HOST: str
85+
SERVER_EXCEL_HOST: str
8486

8587
settings = Settings() # type: ignore
8688
print(settings)

backend/main.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ def custom_generate_unique_id(route: APIRoute) -> str:
4444
lifespan=lifespan
4545
)
4646

47+
app.mount("/excel", StaticFiles(directory=settings.EXCEL_PATH), name="excel")
48+
49+
4750
mcp_app = FastAPI()
4851
# mcp server, images path
4952
mcp_app.mount("/images", StaticFiles(directory=settings.MCP_IMAGE_PATH), name="images")

frontend/src/api/chat.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,4 +329,5 @@ export const chatApi = {
329329
return request.fetchStream(`/chat/recommend_questions/${record_id}`, {}, controller)
330330
},
331331
checkLLMModel: () => request.get('/system/aimodel/default', { requestOptions: { silent: true } }),
332+
export2Excel: (data: any) => request.post('/chat/excel/export', data),
332333
}
Lines changed: 5 additions & 0 deletions
Loading
Lines changed: 6 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)