Skip to content

Commit a95a4d2

Browse files
committed
feat: Batch delete and move applications
1 parent 4c69884 commit a95a4d2

File tree

10 files changed

+232
-7
lines changed

10 files changed

+232
-7
lines changed

apps/application/api/application_api.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
ApplicationImportRequest, ApplicationEditSerializer, TextToSpeechRequest, SpeechToTextRequest, PlayDemoTextRequest
1616
from common.mixins.api_mixin import APIMixin
1717
from common.result import ResultSerializer, ResultPageSerializer, DefaultResultSerializer
18+
from knowledge.serializers.common import BatchSerializer, BatchMoveSerializer
1819

1920

2021
class ApplicationCreateRequest(ApplicationCreateSerializer.SimplateRequest):
@@ -160,6 +161,27 @@ def get_parameters():
160161
]
161162

162163

164+
class ApplicationBatchOperateAPI(APIMixin):
165+
@staticmethod
166+
def get_parameters():
167+
return [
168+
OpenApiParameter(
169+
name="workspace_id",
170+
description="工作空间id",
171+
type=OpenApiTypes.STR,
172+
location='path',
173+
required=True,
174+
)
175+
]
176+
@staticmethod
177+
def get_request():
178+
return BatchSerializer
179+
180+
@staticmethod
181+
def get_move_request():
182+
return BatchMoveSerializer
183+
184+
163185
class ApplicationExportAPI(APIMixin):
164186
@staticmethod
165187
def get_parameters():

apps/application/serializers/application.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
from common.utils.logger import maxkb_logger
4747
from common.utils.tool_code import ToolExecutor
4848
from knowledge.models import Knowledge, KnowledgeScope, File, FileSourceType
49+
from knowledge.serializers.common import BatchSerializer, BatchMoveSerializer
4950
from knowledge.serializers.knowledge import KnowledgeSerializer, KnowledgeModelSerializer
5051
from maxkb.conf import PROJECT_DIR
5152
from models_provider.models import Model
@@ -1314,3 +1315,52 @@ def play_demo_text(self, instance, with_valid=True):
13141315
tts_model_id = instance.pop('tts_model_id')
13151316
model = get_model_instance_by_model_workspace_id(tts_model_id, self.data.get('workspace_id'), **instance)
13161317
return model.text_to_speech(text)
1318+
1319+
1320+
class ApplicationBatchOperateSerializer(serializers.Serializer):
1321+
workspace_id = serializers.CharField(required=True, label=_("Workspace ID"))
1322+
1323+
def is_valid(self, *, raise_exception=False):
1324+
super().is_valid(raise_exception=True)
1325+
1326+
@transaction.atomic
1327+
def batch_delete(self, instance: Dict, with_valid=True):
1328+
from trigger.handler.simple_tools import deploy
1329+
from trigger.serializers.trigger import TriggerModelSerializer
1330+
1331+
if with_valid:
1332+
BatchSerializer(data=instance).is_valid(model=Application,raise_exception=True)
1333+
self.is_valid(raise_exception=True)
1334+
id_list = instance.get("id_list")
1335+
workspace_id = self.data.get('workspace_id')
1336+
1337+
QuerySet(ApplicationVersion).filter(application_id__in=id_list).delete()
1338+
QuerySet(ResourceMapping).filter(
1339+
Q(target_id__in=id_list) | Q(source_id__in=id_list)
1340+
).delete()
1341+
1342+
QuerySet(Application).filter(id__in=id_list, workspace_id=workspace_id).delete()
1343+
1344+
trigger_ids = list(
1345+
QuerySet(TriggerTask).filter(
1346+
source_type="APPLICATION", source_id__in=id_list
1347+
).values('trigger_id').distinct()
1348+
)
1349+
QuerySet(TriggerTask).filter(source_type="APPLICATION", source_id__in=id_list).delete()
1350+
1351+
for trigger_id in trigger_ids:
1352+
trigger = Trigger.objects.filter(id=trigger_id['trigger_id']).first()
1353+
if trigger and trigger.is_active:
1354+
deploy(TriggerModelSerializer(trigger).data, **{})
1355+
return True
1356+
1357+
def batch_move(self, instance: Dict, with_valid=True):
1358+
if with_valid:
1359+
BatchMoveSerializer(data=instance).is_valid(model=Application, raise_exception=True)
1360+
self.is_valid(raise_exception=True)
1361+
id_list = instance.get("id_list")
1362+
folder_id = instance.get("folder_id")
1363+
workspace_id = self.data.get('workspace_id')
1364+
1365+
QuerySet(Application).filter(id__in=id_list, workspace_id=workspace_id).update(folder_id=folder_id)
1366+
return True

apps/application/urls.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
path('workspace/<str:workspace_id>/application', views.ApplicationAPI.as_view(), name='application'),
1010
path('workspace/<str:workspace_id>/application/folder/<str:folder_id>/import', views.ApplicationAPI.Import.as_view()),
1111
path('workspace/<str:workspace_id>/application/<int:current_page>/<int:page_size>', views.ApplicationAPI.Page.as_view(), name='application_page'),
12+
path('workspace/<str:workspace_id>/application/batch_delete', views.ApplicationAPI.BatchDelete.as_view()),
13+
path('workspace/<str:workspace_id>/application/batch_move', views.ApplicationAPI.BatchMove.as_view()),
1214
path('workspace/<str:workspace_id>/application/<str:application_id>', views.ApplicationAPI.Operate.as_view()),
1315
path('workspace/<str:workspace_id>/application/<str:application_id>/publish', views.ApplicationAPI.Publish.as_view()),
1416
path('workspace/<str:workspace_id>/application/<str:application_id>/move/<str:folder_id>', views.ApplicationAPI.Move.as_view()),

apps/application/views/application.py

Lines changed: 90 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,14 @@
1515
from rest_framework.views import APIView
1616

1717
from application.api.application_api import ApplicationCreateAPI, ApplicationQueryAPI, ApplicationImportAPI, \
18-
ApplicationExportAPI, ApplicationOperateAPI, ApplicationEditAPI, TextToSpeechAPI, SpeechToTextAPI, PlayDemoTextAPI
18+
ApplicationExportAPI, ApplicationOperateAPI, ApplicationEditAPI, TextToSpeechAPI, SpeechToTextAPI, PlayDemoTextAPI, \
19+
ApplicationBatchOperateAPI
1920
from application.models import Application
20-
from application.serializers.application import ApplicationSerializer, Query, ApplicationOperateSerializer
21+
from application.serializers.application import ApplicationSerializer, Query, ApplicationOperateSerializer, \
22+
ApplicationBatchOperateSerializer
2123
from common import result
2224
from common.auth import TokenAuth
23-
from common.auth.authentication import has_permissions, get_is_permissions
25+
from common.auth.authentication import has_permissions, get_is_permissions, check_batch_permissions
2426
from common.constants.permission_constants import PermissionConstants, RoleConstants, ViewPermission, CompareConstants
2527
from common.log.log import log
2628
from tools.api.tool import GetInternalToolAPI
@@ -35,6 +37,16 @@ def get_application_operation_object(application_id):
3537
return {}
3638

3739

40+
def get_application_operation_object_batch(application_id_list):
41+
application_model_list = QuerySet(model=Application).filter(id__in=application_id_list)
42+
if application_model_list is not None:
43+
return {
44+
"name": f'[{",".join([app.name for app in application_model_list])}]',
45+
'application_list': [{'name': app.name} for app in application_model_list]
46+
}
47+
return {}
48+
49+
3850
class ApplicationAPI(APIView):
3951
authentication_classes = [TokenAuth]
4052

@@ -296,6 +308,81 @@ def get(self, request: Request):
296308
'name': request.query_params.get('name', ''),
297309
}).get_appstore_templates())
298310

311+
class BatchDelete(APIView):
312+
authentication_classes = [TokenAuth]
313+
314+
@extend_schema(
315+
methods=['PUT'],
316+
description=_("Batch delete applications"),
317+
summary=_("Batch delete applications"),
318+
operation_id=_("Batch delete applications"),
319+
parameters=ApplicationBatchOperateAPI.get_parameters(),
320+
request=ApplicationBatchOperateAPI.get_request(),
321+
responses=result.DefaultResultSerializer,
322+
tags=[_('Application')]
323+
)
324+
@has_permissions(PermissionConstants.APPLICATION_BATCH_DELETE.get_workspace_permission(),
325+
RoleConstants.USER.get_workspace_role(),
326+
RoleConstants.WORKSPACE_MANAGE.get_workspace_role()
327+
)
328+
def put(self, request: Request, workspace_id: str):
329+
id_list = request.data.get('id_list', [])
330+
permitted_ids = check_batch_permissions(
331+
request, id_list, 'application_id',
332+
(PermissionConstants.APPLICATION_DELETE.get_workspace_application_permission(),
333+
PermissionConstants.APPLICATION_DELETE.get_workspace_permission_workspace_manage_role(),
334+
ViewPermission([RoleConstants.USER.get_workspace_role()],
335+
[PermissionConstants.APPLICATION.get_workspace_application_permission()],
336+
CompareConstants.AND),
337+
RoleConstants.WORKSPACE_MANAGE.get_workspace_role()), workspace_id=workspace_id
338+
)
339+
@log(menu='Application', operate='Batch delete applications',
340+
get_operation_object=lambda r, k: get_application_operation_object_batch(permitted_ids))
341+
def inner(view,r, **kwargs):
342+
return ApplicationBatchOperateSerializer(
343+
data={'workspace_id': workspace_id, 'user_id': request.user.id}
344+
).batch_delete({'id_list': permitted_ids})
345+
346+
return result.success(inner(self,request, workspace_id=workspace_id))
347+
348+
class BatchMove(APIView):
349+
authentication_classes = [TokenAuth]
350+
351+
@extend_schema(
352+
methods=['PUT'],
353+
description=_("Batch move applications"),
354+
summary=_("Batch move applications"),
355+
operation_id=_("Batch move applications"),
356+
parameters=ApplicationBatchOperateAPI.get_parameters(),
357+
request=ApplicationBatchOperateAPI.get_move_request(),
358+
responses=result.DefaultResultSerializer,
359+
tags=[_('Application')]
360+
)
361+
@has_permissions(PermissionConstants.APPLICATION_BATCH_MOVE.get_workspace_permission(),
362+
RoleConstants.USER.get_workspace_role(),
363+
RoleConstants.WORKSPACE_MANAGE.get_workspace_role()
364+
)
365+
def put(self, request: Request, workspace_id: str):
366+
id_list = request.data.get('id_list', [])
367+
permitted_ids = check_batch_permissions(
368+
request, id_list, 'application_id',
369+
(PermissionConstants.APPLICATION_EDIT.get_workspace_application_permission(),
370+
PermissionConstants.APPLICATION_EDIT.get_workspace_permission_workspace_manage_role(),
371+
ViewPermission([RoleConstants.USER.get_workspace_role()],
372+
[PermissionConstants.APPLICATION.get_workspace_application_permission()],
373+
CompareConstants.AND),
374+
RoleConstants.WORKSPACE_MANAGE.get_workspace_role()),
375+
workspace_id=workspace_id
376+
)
377+
378+
@log(menu='Application', operate='Batch move applications',
379+
get_operation_object=lambda r, k: get_application_operation_object_batch(permitted_ids))
380+
def inner(view,r, **kwargs):
381+
return ApplicationBatchOperateSerializer(
382+
data={'workspace_id': workspace_id, 'user_id': request.user.id}
383+
).batch_move({'id_list': permitted_ids, 'folder_id': request.data.get('folder_id')})
384+
385+
return result.success(inner(self,request, workspace_id=workspace_id))
299386

300387
class McpServers(APIView):
301388
authentication_classes = [TokenAuth]

apps/common/auth/authentication.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from typing import List
1010

1111
from django.utils.translation import gettext_lazy as _
12+
from rest_framework.request import Request
1213

1314
from common.constants.permission_constants import PermissionConstants, RoleConstants, ViewPermission, CompareConstants, \
1415
Permission, Role
@@ -92,6 +93,18 @@ def is_permissions(*permission, compare=CompareConstants.OR):
9293

9394
return is_permissions
9495

96+
def check_batch_permissions(request: Request, id_list: List[str], id_key: str, permissions: tuple,
97+
compare=CompareConstants.OR, **kwargs) -> List[str]:
98+
result_list = []
99+
for resource_id in id_list:
100+
kwargs[id_key] = resource_id
101+
exit_list = list(
102+
map(lambda p: exist(request.auth.role_list, request.auth.permission_list, p, request, **kwargs),
103+
permissions)
104+
)
105+
if any(exit_list) if compare == CompareConstants.OR else all(exit_list):
106+
result_list.append(resource_id)
107+
return result_list
95108

96109
def has_permissions(*permission, compare=CompareConstants.OR):
97110
"""

apps/common/constants/permission_constants.py

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,8 @@ class Operate(Enum):
191191
TRIGGER_EDIT = "READ+TRIGGER_EDIT"
192192
TRIGGER_CREATE = "READ+TRIGGER_CREATE"
193193
TRIGGER_DELETE = "READ+TRIGGER_DELETE"
194+
BATCH_DELETE = "READ+BATCH_DELETE"
195+
BATCH_MOVE = "READ+BATCH_MOVE"
194196

195197

196198
class RoleGroup(Enum):
@@ -571,7 +573,16 @@ class PermissionConstants(Enum):
571573
parent_group=[WorkspaceGroup.TOOL, UserGroup.TOOL],
572574
resource_permission_group_list=[ResourcePermissionConst.TOOL_MANGE]
573575
)
574-
576+
TOOL_BATCH_MOVE = Permission(
577+
group=Group.TOOL, operate=Operate.BATCH_MOVE, role_list=[RoleConstants.ADMIN, RoleConstants.USER],
578+
parent_group=[WorkspaceGroup.TOOL, UserGroup.TOOL],
579+
resource_permission_group_list=[ResourcePermissionConst.TOOL_MANGE]
580+
)
581+
TOOL_BATCH_DELETE = Permission(
582+
group=Group.TOOL, operate=Operate.BATCH_DELETE, role_list=[RoleConstants.ADMIN, RoleConstants.USER],
583+
parent_group=[WorkspaceGroup.TOOL, UserGroup.TOOL],
584+
resource_permission_group_list=[ResourcePermissionConst.TOOL_MANGE]
585+
)
575586
TOOL_EDIT = Permission(
576587
group=Group.TOOL, operate=Operate.EDIT, role_list=[RoleConstants.ADMIN, RoleConstants.USER],
577588
parent_group=[WorkspaceGroup.TOOL, UserGroup.TOOL],
@@ -694,6 +705,16 @@ class PermissionConstants(Enum):
694705
resource_permission_group_list=[ResourcePermissionConst.KNOWLEDGE_MANGE],
695706
parent_group=[WorkspaceGroup.KNOWLEDGE, UserGroup.KNOWLEDGE]
696707
)
708+
KNOWLEDGE_BATCH_DELETE = Permission(group=Group.KNOWLEDGE, operate=Operate.BATCH_DELETE,
709+
role_list=[RoleConstants.ADMIN, RoleConstants.USER],
710+
resource_permission_group_list=[ResourcePermissionConst.KNOWLEDGE_MANGE],
711+
parent_group=[WorkspaceGroup.KNOWLEDGE, UserGroup.KNOWLEDGE],
712+
)
713+
KNOWLEDGE_BATCH_MOVE = Permission(group=Group.KNOWLEDGE, operate=Operate.BATCH_MOVE,
714+
role_list=[RoleConstants.ADMIN, RoleConstants.USER],
715+
resource_permission_group_list=[ResourcePermissionConst.KNOWLEDGE_MANGE],
716+
parent_group=[WorkspaceGroup.KNOWLEDGE, UserGroup.KNOWLEDGE],
717+
)
697718
KNOWLEDGE_RESOURCE_AUTHORIZATION = Permission(
698719
group=Group.KNOWLEDGE, operate=Operate.AUTH, role_list=[RoleConstants.ADMIN, RoleConstants.USER],
699720
resource_permission_group_list=[ResourcePermissionConst.KNOWLEDGE_MANGE],
@@ -1031,6 +1052,16 @@ class PermissionConstants(Enum):
10311052
resource_permission_group_list=[ResourcePermissionConst.APPLICATION_MANGE],
10321053
parent_group=[WorkspaceGroup.APPLICATION, UserGroup.APPLICATION],
10331054
)
1055+
APPLICATION_BATCH_DELETE = Permission(group=Group.APPLICATION, operate=Operate.BATCH_DELETE,
1056+
role_list=[RoleConstants.ADMIN, RoleConstants.USER],
1057+
resource_permission_group_list=[ResourcePermissionConst.APPLICATION_MANGE],
1058+
parent_group=[WorkspaceGroup.APPLICATION, UserGroup.APPLICATION],
1059+
)
1060+
APPLICATION_BATCH_MOVE = Permission(group=Group.APPLICATION, operate=Operate.BATCH_MOVE,
1061+
role_list=[RoleConstants.ADMIN, RoleConstants.USER],
1062+
resource_permission_group_list=[ResourcePermissionConst.APPLICATION_MANGE],
1063+
parent_group=[WorkspaceGroup.APPLICATION, UserGroup.APPLICATION],
1064+
)
10341065
APPLICATION_RESOURCE_AUTHORIZATION = Permission(group=Group.APPLICATION, operate=Operate.AUTH,
10351066
role_list=[RoleConstants.ADMIN, RoleConstants.USER],
10361067
parent_group=[WorkspaceGroup.APPLICATION, UserGroup.APPLICATION],

apps/knowledge/serializers/common.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ def is_valid(self, *, model=None, raise_exception=False):
6262
raise AppApiException(500, _('The following id does not exist: {error_id_list}').format(
6363
error_id_list=error_id_list))
6464

65+
class BatchMoveSerializer(BatchSerializer):
66+
folder_id = serializers.CharField(required=True, label=_('folder id'))
6567

6668
class ProblemParagraphObject:
6769
def __init__(self, knowledge_id: str, document_id: str, paragraph_id: str, problem_content: str):

apps/locales/en_US/LC_MESSAGES/django.po

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9168,4 +9168,10 @@ msgid "Document does not belong to current knowledge"
91689168
msgstr "Document does not belong to current knowledge"
91699169

91709170
msgid "Move an application"
9171-
msgstr "Move an application"
9171+
msgstr "Move an application"
9172+
9173+
msgid "Batch delete applications"
9174+
msgstr "Batch delete applications"
9175+
9176+
msgid "Batch move applications"
9177+
msgstr "Batch move applications"

apps/locales/zh_CN/LC_MESSAGES/django.po

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9291,4 +9291,10 @@ msgid "Document does not belong to current knowledge"
92919291
msgstr "文档不属于当前知识库"
92929292

92939293
msgid "Move an application"
9294-
msgstr "移动应用程序"
9294+
msgstr "移动应用程序"
9295+
9296+
msgid "Batch delete applications"
9297+
msgstr "批量删除应用"
9298+
9299+
msgid "Batch move applications"
9300+
msgstr "批量移动应用"

apps/locales/zh_Hant/LC_MESSAGES/django.po

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9288,4 +9288,10 @@ msgid "Document does not belong to current knowledge"
92889288
msgstr "文件不屬於當前知識庫"
92899289

92909290
msgid "Move an application"
9291-
msgstr "移動應用程序"
9291+
msgstr "移動應用程序"
9292+
9293+
msgid "Batch delete applications"
9294+
msgstr "批量刪除應用"
9295+
9296+
msgid "Batch move applications"
9297+
msgstr "批量移動應用"

0 commit comments

Comments
 (0)