Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions backend/alembic/versions/052_add_recommended_problem.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
"""empty message

Revision ID: e408f8766753
Revises: cb12c4238120
Create Date: 2025-11-24 17:34:04.436927

"""
from alembic import op
import sqlalchemy as sa
import sqlmodel.sql.sqltypes
from sqlalchemy.dialects import postgresql

# revision identifiers, used by Alembic.
revision = 'e408f8766753'
down_revision = 'cb12c4238120'
branch_labels = None
depends_on = None


def upgrade():
op.create_table('ds_recommended_problem',
sa.Column('id', sa.BIGINT(),
sa.Identity(always=True, start=1, increment=1, minvalue=1, maxvalue=9999999999,
cycle=False, cache=1), autoincrement=True, nullable=False),
sa.Column('datasource_id', sa.BIGINT(), autoincrement=False, nullable=True),
sa.Column('question', sa.TEXT(), autoincrement=False, nullable=True),
sa.Column('remark', sa.TEXT(), autoincrement=False, nullable=True),
sa.Column('sort', sa.BIGINT(), autoincrement=False, nullable=True),
sa.Column('create_time', postgresql.TIMESTAMP(precision=6), autoincrement=False, nullable=True),
sa.Column('create_by', sa.BIGINT(), autoincrement=False, nullable=True),
sa.PrimaryKeyConstraint('id', name=op.f('ds_recommended_problem_pkey'))
)
op.add_column('core_datasource', sa.Column('recommended_config', sa.BigInteger(),default=0, nullable=True))

def downgrade():
op.drop_table('ds_recommended_problem')
op.drop_column('core_datasource', 'recommended_config')

4 changes: 3 additions & 1 deletion backend/apps/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from apps.chat.api import chat
from apps.dashboard.api import dashboard_api
from apps.data_training.api import data_training
from apps.datasource.api import datasource, table_relation
from apps.datasource.api import datasource, table_relation, recommended_problem
from apps.mcp import mcp
from apps.system.api import login, user, aimodel, workspace, assistant
from apps.terminology.api import terminology
Expand All @@ -23,3 +23,5 @@
api_router.include_router(dashboard_api.router)
api_router.include_router(mcp.router)
api_router.include_router(table_relation.router)

api_router.include_router(recommended_problem.router)
12 changes: 12 additions & 0 deletions backend/apps/datasource/api/recommended_problem.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from fastapi import APIRouter

from apps.datasource.crud.recommended_problem import get_datasource_recommended
from common.core.deps import SessionDep

router = APIRouter(tags=["recommended_problem"], prefix="/recommended_problem")


@router.get("/get_datasource_recommended/{ds_id}")
async def datasource_recommended(session: SessionDep, ds_id: int):
return get_datasource_recommended(session, ds_id)

11 changes: 11 additions & 0 deletions backend/apps/datasource/crud/recommended_problem.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from sqlmodel import select

from common.core.deps import SessionDep
from ..models.datasource import DsRecommendedProblem


def get_datasource_recommended(session: SessionDep, ds_id: int):
statement = select(DsRecommendedProblem).where(DsRecommendedProblem.datasource_id == ds_id)
dsRecommendedProblem = session.exec(statement)
return dsRecommendedProblem

12 changes: 12 additions & 0 deletions backend/apps/datasource/models/datasource.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class CoreDatasource(SQLModel, table=True):
oid: int = Field(sa_column=Column(BigInteger()))
table_relation: List = Field(sa_column=Column(JSONB, nullable=True))
embedding: str = Field(sa_column=Column(Text, nullable=True))
recommended_config: int = Field(sa_column=Column(BigInteger()))


class CoreTable(SQLModel, table=True):
Expand All @@ -34,6 +35,16 @@ class CoreTable(SQLModel, table=True):
custom_comment: str = Field(sa_column=Column(Text))
embedding: str = Field(sa_column=Column(Text, nullable=True))

class DsRecommendedProblem(SQLModel, table=True):
__tablename__ = "ds_recommended_problem"
id: int = Field(sa_column=Column(BigInteger, Identity(always=True), nullable=False, primary_key=True))
datasource_id: int = Field(sa_column=Column(BigInteger()))
question: str = Field(sa_column=Column(Text))
remark: str = Field(sa_column=Column(Text))
sort: int = Field(sa_column=Column(BigInteger()))
create_time: datetime = Field(sa_column=Column(DateTime(timezone=False), nullable=True))
create_by: int = Field(sa_column=Column(BigInteger()))


class CoreField(SQLModel, table=True):
__tablename__ = "core_field"
Expand Down Expand Up @@ -61,6 +72,7 @@ class CreateDatasource(BaseModel):
num: str = ''
oid: int = 1
tables: List[CoreTable] = []
recommended_config: int = 1


# edit local saved table and fields
Expand Down
7 changes: 7 additions & 0 deletions frontend/src/api/recommendedApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { request } from '@/utils/request'

export const recommendedApi = {
get_recommended_problem: (dsId: any) =>
request.get(`/recommended_problem/get_datasource_recommended/${dsId}`),
save_recommended_problem: (data: any) => request.post(`/recommended_problem/save`, data),
}
1 change: 1 addition & 0 deletions frontend/src/assets/svg/icon_recommended_problem.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions frontend/src/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,12 @@
}
},
"datasource": {
"recommended_problem_configuration": "Recommended Problem Configuration",
"problem_generation_method": "Problem Generation Method",
"ai_automatic_generation": "AI Automatic Generation",
"user_defined": "User Defined",
"question_tips": "Please enter recommended questions",
"add_question": "Add Question",
"data_source_yet": "No data source yet",
"search_by_name": "Search by Name",
"search": "Search",
Expand Down
6 changes: 6 additions & 0 deletions frontend/src/i18n/ko-KR.json
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,12 @@
}
},
"datasource": {
"recommended_problem_configuration": "추천 문제 구성",
"problem_generation_method": "문제 생성 방식",
"ai_automatic_generation": "AI 자동 생성",
"user_defined": "사용자 정의",
"question_tips": "추천 문제를 입력해 주세요",
"add_question": "문제 추가",
"data_source_yet": "데이터 소스가 없습니다",
"search_by_name": "이름으로 검색",
"search": "검색",
Expand Down
6 changes: 6 additions & 0 deletions frontend/src/i18n/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,12 @@
}
},
"datasource": {
"recommended_problem_configuration": "推荐问题配置",
"problem_generation_method": "问题生成方式",
"ai_automatic_generation": "AI 自动生成",
"user_defined": "用户自定义",
"question_tips": "请输入推荐问题",
"add_question": "添加问题",
"data_source_yet": "暂无数据源",
"search_by_name": "通过名称搜索",
"search": "搜索",
Expand Down
20 changes: 19 additions & 1 deletion frontend/src/views/ds/Card.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { computed, ref, unref } from 'vue'
import { ClickOutside as vClickOutside } from 'element-plus-secondary'
import { dsTypeWithImg } from './js/ds-type'
import edit from '@/assets/svg/icon_edit_outlined.svg'
import icon_recommended_problem from '@/assets/svg/icon_recommended_problem.svg'
import { datasourceApi } from '@/api/datasource.ts'

const props = withDefaults(
Expand All @@ -27,14 +28,25 @@ const props = withDefaults(
}
)

const emits = defineEmits(['edit', 'del', 'question', 'dataTableDetail', 'showTable'])
const emits = defineEmits([
'edit',
'del',
'question',
'dataTableDetail',
'showTable',
'recommendation',
])
const icon = computed(() => {
return (dsTypeWithImg.find((ele) => props.type === ele.type) || {}).img
})
const handleEdit = () => {
emits('edit')
}

const handleRecommendation = () => {
emits('recommendation')
}

const handleDel = () => {
emits('del')
}
Expand Down Expand Up @@ -124,6 +136,12 @@ const onClickOutside = () => {
</el-icon>
{{ $t('datasource.edit') }}
</div>
<div class="item" style="display: none" @click.stop="handleRecommendation">
<el-icon size="16">
<icon_recommended_problem></icon_recommended_problem>
</el-icon>
{{ $t('datasource.recommended_problem_configuration') }}
</div>
<div class="item" @click.stop="handleDel">
<el-icon size="16">
<delIcon></delIcon>
Expand Down
15 changes: 13 additions & 2 deletions frontend/src/views/ds/Datasource.vue
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,19 @@ import { dsTypeWithImg } from './js/ds-type'
import { useI18n } from 'vue-i18n'
import { useUserStore } from '@/stores/user'
import { chatApi } from '@/api/chat'
import RecommendedProblemConfigDialog from '@/views/ds/RecommendedProblemConfigDialog.vue'
const userStore = useUserStore()
interface Datasource {
const recommendedProblemConfigRef = ref()

export interface Datasource {
name: string
num: string
type_name: string
type: string
img: string
description: string
id?: string
recommended_config?: string
}

const router = useRouter()
Expand Down Expand Up @@ -77,6 +81,10 @@ const handleEditDatasource = (res: any) => {
addDrawerRef.value.handleEditDatasource(res)
}

const handleRecommendation = (res: Datasource) => {
recommendedProblemConfigRef.value?.init(res)
}

const handleQuestion = async (id: string) => {
try {
await chatApi.checkLLMModel()
Expand Down Expand Up @@ -295,6 +303,7 @@ useEmitt({
:description="ele.description"
@question="handleQuestion"
@edit="handleEditDatasource(ele)"
@recommendation="handleRecommendation(ele)"
@del="deleteHandler(ele)"
@data-table-detail="dataTableDetail(ele)"
></Card>
Expand All @@ -317,7 +326,9 @@ useEmitt({
</el-button>
</div>
</template>

<RecommendedProblemConfigDialog
ref="recommendedProblemConfigRef"
></RecommendedProblemConfigDialog>
<AddDrawer ref="addDrawerRef" @search="search"></AddDrawer>
</div>
<DataTable
Expand Down
130 changes: 130 additions & 0 deletions frontend/src/views/ds/RecommendedProblemConfigDialog.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
<script setup lang="ts">
import { reactive, ref } from 'vue'
import { recommendedApi } from '@/api/recommendedApi.ts'

interface RecommendedProblem {
id?: number
question: string
datasourceId?: any | undefined
sort: number
}

const dialogShow = ref(false)

const state = reactive({
dsId: null,
recommended: {
recommendedConfig: 1,
recommendedProblemList: [] as RecommendedProblem[],
},
})

const init = (params: any) => {
dialogShow.value = true
state.recommended.recommendedConfig = params.recommendedConfig
state.dsId = params.id
recommendedApi.get_recommended_problem(state.dsId).then((res: any) => {
state.recommended.recommendedProblemList = res
})
}

const addRecommendedProblem = (): void => {
state.recommended.recommendedProblemList.push({
question: '',
datasourceId: state.dsId,
} as RecommendedProblem)
}

const closeDialog = () => {
dialogShow.value = false
state.recommended = {
recommendedConfig: 1,
recommendedProblemList: [] as RecommendedProblem[],
}
}
const save = () => {
recommendedApi.save_recommended_problem(state.recommended.recommendedProblemList)
closeDialog()
}

const form = ref<any>({
id: null,
question: '',
sort: null,
})

defineExpose({
init,
})
</script>

<template>
<el-dialog
v-model="dialogShow"
:title="$t('datasource.recommended_problem_configuration')"
width="600"
modal-class="add-question_dialog"
destroy-on-close
:close-on-click-modal="false"
@before-closed="closeDialog"
>
<el-form-item :label="$t('datasource.problem_generation_method')" prop="mode">
<el-radio-group v-model="form.mode">
<el-radio value="1">{{ $t('datasource.ai_automatic_generation') }}</el-radio>
<el-radio value="2">{{ $t('datasource.user_defined') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
v-for="(recommendedItem, index) in state.recommended.recommendedProblemList"
:key="index"
prop="mode"
>
<el-input
v-model="recommendedItem.question"
clearable
:placeholder="$t('datasource.question_tips')"
>
</el-input>
</el-form-item>
<div>
<el-button text @click="addRecommendedProblem">
{{ $t('datasource.add_question') }}</el-button
>
</div>
<div style="display: flex; justify-content: flex-end; margin-top: 20px">
<el-button secondary @click="closeDialog">{{ $t('common.cancel') }}</el-button>
<el-button type="primary" @click="save">{{ $t('model.add') }}</el-button>
</div>
</el-dialog>
</template>

<style scoped lang="less">
.add-question_dialog {
.ed-input-group__append {
background-color: #fff;
padding: 0 12px;
}

.value-input {
.ed-input-group__append {
color: #1f2329;
position: relative;
&:hover {
&::after {
content: '';
position: absolute;
left: -1px;
top: 0;
width: calc(100% - 1px);
height: calc(100% - 2px);
background: var(--ed-color-primary-1a, #1cba901a);
border: 1px solid var(--ed-color-primary);
border-bottom-right-radius: 6px;
border-top-right-radius: 6px;
pointer-events: none;
}
}
}
}
}
</style>