Portal/rsconcept/backend/apps/oss/views/oss.py

867 lines
34 KiB
Python
Raw Normal View History

''' Endpoints for OSS. '''
from copy import deepcopy
2024-08-14 23:36:52 +03:00
from typing import Optional, cast
2024-07-19 19:28:55 +03:00
from django.db import transaction
2024-08-07 21:54:14 +03:00
from django.http import HttpResponse
from drf_spectacular.utils import extend_schema, extend_schema_view
from rest_framework import generics, serializers
from rest_framework import status as c
from rest_framework import viewsets
from rest_framework.decorators import action
from rest_framework.request import Request
from rest_framework.response import Response
from apps.library.models import LibraryItem, LibraryItemType
from apps.library.serializers import LibraryItemSerializer
from apps.rsform.models import Attribution, Constituenta, RSFormCached
from apps.rsform.serializers import CstTargetSerializer
from shared import messages as msg
from shared import permissions
from .. import models as m
from .. import serializers as s
def _create_clone(prototype: LibraryItem, operation: m.Operation, oss: LibraryItem) -> LibraryItem:
''' Create clone of prototype schema for operation. '''
clone = deepcopy(prototype)
clone.pk = None
clone.owner = oss.owner
clone.title = operation.title
clone.alias = operation.alias
clone.description = operation.description
clone.visible = False
clone.read_only = False
clone.access_policy = oss.access_policy
clone.location = oss.location
clone.save()
cst_map: dict[int, int] = {}
cst_list: list[int] = []
2025-08-01 10:43:51 +03:00
for cst in Constituenta.objects.filter(schema_id=prototype.pk):
old_pk = cst.pk
cst_copy = deepcopy(cst)
cst_copy.pk = None
cst_copy.schema = clone
cst_copy.save()
cst_map[old_pk] = cst_copy.pk
cst_list.append(old_pk)
for attr in Attribution.objects.filter(container__in=cst_list, attribute__in=cst_list):
attr.pk = None
attr.container_id = cst_map[attr.container_id]
attr.attribute_id = cst_map[attr.attribute_id]
attr.save()
return clone
@extend_schema(tags=['OSS'])
@extend_schema_view()
class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.RetrieveAPIView):
''' Endpoint: OperationSchema. '''
queryset = LibraryItem.objects.filter(item_type=LibraryItemType.OPERATION_SCHEMA)
serializer_class = LibraryItemSerializer
def _get_item(self) -> LibraryItem:
return cast(LibraryItem, self.get_object())
def get_permissions(self):
''' Determine permission class. '''
if self.action in [
2025-04-06 13:28:00 +03:00
'update_layout',
2025-04-14 23:02:35 +03:00
'create_block',
2025-04-20 18:06:18 +03:00
'update_block',
'delete_block',
2025-04-23 23:18:44 +03:00
'move_items',
'create_schema',
2025-07-30 21:48:28 +03:00
'clone_schema',
'import_schema',
'create_replica',
'create_synthesis',
'update_operation',
2025-04-20 18:06:18 +03:00
'delete_operation',
'delete_replica',
2024-07-28 21:29:46 +03:00
'create_input',
2024-07-29 22:30:24 +03:00
'set_input',
2024-10-28 14:52:30 +03:00
'execute_operation',
'relocate_constituents'
]:
permission_list = [permissions.ItemEditor]
elif self.action in ['details']:
permission_list = [permissions.ItemAnyone]
elif self.action in ['get_predecessor']:
permission_list = [permissions.Anyone]
else:
permission_list = [permissions.Anyone]
return [permission() for permission in permission_list]
@extend_schema(
summary='get operations data',
tags=['OSS'],
request=None,
responses={
c.HTTP_200_OK: s.OperationSchemaSerializer,
c.HTTP_404_NOT_FOUND: None
}
)
@action(detail=True, methods=['get'], url_path='details')
2024-08-07 21:54:14 +03:00
def details(self, request: Request, pk) -> HttpResponse:
''' Endpoint: Detailed OSS data. '''
serializer = s.OperationSchemaSerializer(self._get_item())
return Response(
status=c.HTTP_200_OK,
data=serializer.data
)
2024-07-19 19:28:55 +03:00
@extend_schema(
2025-04-06 13:28:00 +03:00
summary='update layout',
2024-07-19 19:28:55 +03:00
tags=['OSS'],
2025-04-06 13:28:00 +03:00
request=s.LayoutSerializer,
2024-07-19 19:28:55 +03:00
responses={
2025-07-23 16:13:11 +03:00
c.HTTP_200_OK: s.OperationSchemaSerializer,
2024-07-19 19:28:55 +03:00
c.HTTP_403_FORBIDDEN: None,
c.HTTP_404_NOT_FOUND: None
}
)
2025-04-06 13:28:00 +03:00
@action(detail=True, methods=['patch'], url_path='update-layout')
def update_layout(self, request: Request, pk) -> HttpResponse:
''' Endpoint: Update schema layout. '''
serializer = s.LayoutSerializer(data=request.data)
2024-07-19 19:28:55 +03:00
serializer.is_valid(raise_exception=True)
2025-08-01 18:31:23 +03:00
item = self._get_item()
2025-08-03 15:28:16 +03:00
2025-07-23 16:13:11 +03:00
with transaction.atomic():
2025-08-01 18:31:23 +03:00
m.Layout.update_data(pk, serializer.validated_data['data'])
item.save(update_fields=['time_update'])
2025-08-03 15:28:16 +03:00
2025-08-01 18:31:23 +03:00
return Response(status=c.HTTP_200_OK, data=s.OperationSchemaSerializer(item).data)
2024-07-19 19:28:55 +03:00
2025-04-14 23:02:35 +03:00
@extend_schema(
summary='create block',
tags=['OSS'],
request=s.CreateBlockSerializer(),
2025-04-14 23:02:35 +03:00
responses={
2025-04-20 18:06:18 +03:00
c.HTTP_201_CREATED: s.BlockCreatedResponse,
2025-04-14 23:02:35 +03:00
c.HTTP_400_BAD_REQUEST: None,
c.HTTP_403_FORBIDDEN: None,
c.HTTP_404_NOT_FOUND: None
}
)
@action(detail=True, methods=['post'], url_path='create-block')
def create_block(self, request: Request, pk) -> HttpResponse:
''' Create Block. '''
2025-08-01 18:31:23 +03:00
item = self._get_item()
2025-08-10 14:08:36 +03:00
serializer = s.CreateBlockSerializer(data=request.data, context={'oss': item})
2025-04-14 23:02:35 +03:00
serializer.is_valid(raise_exception=True)
layout = serializer.validated_data['layout']
position = serializer.validated_data['position']
2025-04-14 23:02:35 +03:00
children_blocks: list[m.Block] = serializer.validated_data['children_blocks']
children_operations: list[m.Operation] = serializer.validated_data['children_operations']
2025-08-03 15:28:16 +03:00
2025-04-14 23:02:35 +03:00
with transaction.atomic():
2025-08-03 15:28:16 +03:00
oss = m.OperationSchema(item)
2025-04-14 23:02:35 +03:00
new_block = oss.create_block(**serializer.validated_data['item_data'])
2025-06-11 16:38:56 +03:00
layout.append({
'nodeID': 'b' + str(new_block.pk),
'x': position['x'],
'y': position['y'],
'width': position['width'],
'height': position['height'],
2025-04-14 23:02:35 +03:00
})
2025-08-01 18:31:23 +03:00
m.Layout.update_data(pk, layout)
2025-08-10 12:40:23 +03:00
if children_blocks:
2025-04-14 23:02:35 +03:00
for block in children_blocks:
block.parent = new_block
m.Block.objects.bulk_update(children_blocks, ['parent'])
2025-08-10 12:40:23 +03:00
if children_operations:
2025-04-14 23:02:35 +03:00
for operation in children_operations:
operation.parent = new_block
m.Operation.objects.bulk_update(children_operations, ['parent'])
2025-08-01 18:31:23 +03:00
item.save(update_fields=['time_update'])
2025-04-14 23:02:35 +03:00
return Response(
status=c.HTTP_201_CREATED,
data={
'new_block': new_block.pk,
2025-08-01 18:31:23 +03:00
'oss': s.OperationSchemaSerializer(item).data
2025-04-14 23:02:35 +03:00
}
)
2024-07-19 19:28:55 +03:00
@extend_schema(
2025-04-20 18:06:18 +03:00
summary='update block',
2024-07-19 19:28:55 +03:00
tags=['OSS'],
2025-04-20 18:06:18 +03:00
request=s.UpdateBlockSerializer(),
2024-07-19 19:28:55 +03:00
responses={
c.HTTP_200_OK: s.OperationSchemaSerializer,
c.HTTP_400_BAD_REQUEST: None,
c.HTTP_403_FORBIDDEN: None,
c.HTTP_404_NOT_FOUND: None
}
)
2025-04-20 18:06:18 +03:00
@action(detail=True, methods=['patch'], url_path='update-block')
def update_block(self, request: Request, pk) -> HttpResponse:
''' Update Block. '''
2025-08-01 18:31:23 +03:00
item = self._get_item()
2025-08-10 14:08:36 +03:00
serializer = s.UpdateBlockSerializer(data=request.data, context={'oss': item})
2024-07-19 19:28:55 +03:00
serializer.is_valid(raise_exception=True)
2025-04-20 18:06:18 +03:00
block: m.Block = cast(m.Block, serializer.validated_data['target'])
2025-08-03 15:28:16 +03:00
2024-07-19 19:28:55 +03:00
with transaction.atomic():
2025-04-22 14:23:26 +03:00
if 'title' in serializer.validated_data['item_data']:
block.title = serializer.validated_data['item_data']['title']
if 'description' in serializer.validated_data['item_data']:
block.description = serializer.validated_data['item_data']['description']
if 'parent' in serializer.validated_data['item_data']:
block.parent = serializer.validated_data['item_data']['parent']
2025-04-20 18:06:18 +03:00
block.save(update_fields=['title', 'description', 'parent'])
2025-08-01 18:31:23 +03:00
if 'layout' in serializer.validated_data:
layout = serializer.validated_data['layout']
m.Layout.update_data(pk, layout)
item.save(update_fields=['time_update'])
2025-08-03 15:28:16 +03:00
2024-07-19 19:28:55 +03:00
return Response(
status=c.HTTP_200_OK,
2025-08-01 18:31:23 +03:00
data=s.OperationSchemaSerializer(item).data
2024-07-19 19:28:55 +03:00
)
@extend_schema(
summary='delete block',
tags=['OSS'],
request=s.DeleteBlockSerializer,
responses={
c.HTTP_200_OK: s.OperationSchemaSerializer,
c.HTTP_400_BAD_REQUEST: None,
c.HTTP_403_FORBIDDEN: None,
c.HTTP_404_NOT_FOUND: None
}
)
@action(detail=True, methods=['patch'], url_path='delete-block')
def delete_block(self, request: Request, pk) -> HttpResponse:
''' Endpoint: Delete Block. '''
2025-08-01 18:31:23 +03:00
item = self._get_item()
2025-08-10 14:08:36 +03:00
serializer = s.DeleteBlockSerializer(data=request.data, context={'oss': item})
serializer.is_valid(raise_exception=True)
block = cast(m.Block, serializer.validated_data['target'])
layout = serializer.validated_data['layout']
2025-06-11 16:38:56 +03:00
layout = [x for x in layout if x['nodeID'] != 'b' + str(block.pk)]
2025-08-03 15:28:16 +03:00
with transaction.atomic():
2025-08-03 15:28:16 +03:00
oss = m.OperationSchema(item)
oss.delete_block(block)
2025-08-01 18:31:23 +03:00
m.Layout.update_data(pk, layout)
item.save(update_fields=['time_update'])
return Response(
status=c.HTTP_200_OK,
2025-08-01 18:31:23 +03:00
data=s.OperationSchemaSerializer(item).data
)
2025-04-23 23:18:44 +03:00
@extend_schema(
summary='move items',
tags=['OSS'],
request=s.MoveItemsSerializer(),
responses={
c.HTTP_200_OK: s.OperationSchemaSerializer,
c.HTTP_400_BAD_REQUEST: None,
c.HTTP_403_FORBIDDEN: None,
c.HTTP_404_NOT_FOUND: None
}
)
@action(detail=True, methods=['patch'], url_path='move-items')
def move_items(self, request: Request, pk) -> HttpResponse:
''' Move items to another parent. '''
2025-08-01 18:31:23 +03:00
item = self._get_item()
2025-08-10 14:08:36 +03:00
serializer = s.MoveItemsSerializer(data=request.data, context={'oss': item})
2025-04-23 23:18:44 +03:00
serializer.is_valid(raise_exception=True)
2025-08-01 18:31:23 +03:00
layout = serializer.validated_data['layout']
2025-08-03 15:28:16 +03:00
2025-04-23 23:18:44 +03:00
with transaction.atomic():
2025-08-01 18:31:23 +03:00
m.Layout.update_data(pk, layout)
2025-04-23 23:18:44 +03:00
for operation in serializer.validated_data['operations']:
operation.parent = serializer.validated_data['destination']
operation.save(update_fields=['parent'])
for block in serializer.validated_data['blocks']:
block.parent = serializer.validated_data['destination']
block.save(update_fields=['parent'])
2025-08-01 18:31:23 +03:00
item.save(update_fields=['time_update'])
2025-04-23 23:18:44 +03:00
return Response(
status=c.HTTP_200_OK,
2025-08-01 18:31:23 +03:00
data=s.OperationSchemaSerializer(item).data
2025-04-23 23:18:44 +03:00
)
2025-04-20 18:06:18 +03:00
@extend_schema(
summary='create empty conceptual schema',
tags=['OSS'],
request=s.CreateSchemaSerializer(),
responses={
c.HTTP_201_CREATED: s.OperationCreatedResponse,
c.HTTP_400_BAD_REQUEST: None,
c.HTTP_403_FORBIDDEN: None,
c.HTTP_404_NOT_FOUND: None
}
)
@action(detail=True, methods=['post'], url_path='create-schema')
def create_schema(self, request: Request, pk) -> HttpResponse:
''' Create schema. '''
2025-08-01 18:31:23 +03:00
item = self._get_item()
2025-08-10 14:08:36 +03:00
serializer = s.CreateSchemaSerializer(data=request.data, context={'oss': item})
serializer.is_valid(raise_exception=True)
layout = serializer.validated_data['layout']
position = serializer.validated_data['position']
data = serializer.validated_data['item_data']
data['operation_type'] = m.OperationType.INPUT
2025-08-03 15:28:16 +03:00
with transaction.atomic():
2025-08-03 15:28:16 +03:00
oss = m.OperationSchema(item)
new_operation = oss.create_operation(**serializer.validated_data['item_data'])
layout.append({
'nodeID': 'o' + str(new_operation.pk),
'x': position['x'],
'y': position['y'],
'width': position['width'],
'height': position['height']
})
2025-08-01 18:31:23 +03:00
m.Layout.update_data(pk, layout)
2025-08-05 14:01:43 +03:00
m.OperationSchema.create_input(item, new_operation)
2025-08-01 18:31:23 +03:00
item.save(update_fields=['time_update'])
return Response(
status=c.HTTP_201_CREATED,
data={
'new_operation': new_operation.pk,
2025-08-01 18:31:23 +03:00
'oss': s.OperationSchemaSerializer(item).data
}
)
2025-07-30 21:48:28 +03:00
@extend_schema(
summary='clone conceptual schema - result of a target operation',
tags=['OSS'],
request=s.CloneSchemaSerializer(),
responses={
c.HTTP_201_CREATED: s.OperationCreatedResponse,
c.HTTP_400_BAD_REQUEST: None,
c.HTTP_403_FORBIDDEN: None,
c.HTTP_404_NOT_FOUND: None
}
)
@action(detail=True, methods=['post'], url_path='clone-schema')
def clone_schema(self, request: Request, pk) -> HttpResponse:
''' Clone schema. '''
2025-08-01 18:31:23 +03:00
item = self._get_item()
2025-08-10 14:08:36 +03:00
serializer = s.CloneSchemaSerializer(data=request.data, context={'oss': item})
2025-07-30 21:48:28 +03:00
serializer.is_valid(raise_exception=True)
layout = serializer.validated_data['layout']
position = serializer.validated_data['position']
2025-08-03 15:28:16 +03:00
2025-07-30 21:48:28 +03:00
with transaction.atomic():
source = cast(m.Operation, serializer.validated_data['source_operation'])
alias = '+' + source.alias
title = '+' + source.title
2025-08-01 10:43:51 +03:00
source_schema = cast(LibraryItem, source.result)
constituents = Constituenta.objects.filter(schema_id=source_schema.pk)
2025-07-30 21:48:28 +03:00
2025-08-01 10:43:51 +03:00
new_schema = source_schema
2025-07-30 21:48:28 +03:00
new_schema.pk = None
2025-08-01 18:31:23 +03:00
new_schema.owner = item.owner
2025-07-30 21:48:28 +03:00
new_schema.title = title
new_schema.alias = alias
new_schema.save()
2025-08-01 10:43:51 +03:00
for cst in constituents:
2025-07-30 21:48:28 +03:00
cst.pk = None
cst.schema = new_schema
cst.save()
new_operation = source
new_operation.pk = None
new_operation.alias = alias
new_operation.title = title
new_operation.operation_type = m.OperationType.INPUT
new_operation.result = None
new_operation.save()
2025-08-01 18:31:23 +03:00
new_operation.setQ_result(new_schema)
2025-07-30 21:48:28 +03:00
layout.append({
'nodeID': 'o' + str(new_operation.pk),
'x': position['x'],
'y': position['y'],
'width': position['width'],
'height': position['height']
})
2025-08-01 18:31:23 +03:00
m.Layout.update_data(pk, layout)
item.save(update_fields=['time_update'])
2025-07-30 21:48:28 +03:00
return Response(
status=c.HTTP_201_CREATED,
data={
'new_operation': new_operation.pk,
2025-08-01 18:31:23 +03:00
'oss': s.OperationSchemaSerializer(item).data
2025-07-30 21:48:28 +03:00
}
)
@extend_schema(
summary='import conceptual schema to new OSS operation',
tags=['OSS'],
request=s.ImportSchemaSerializer(),
responses={
c.HTTP_201_CREATED: s.OperationCreatedResponse,
c.HTTP_400_BAD_REQUEST: None,
c.HTTP_403_FORBIDDEN: None,
c.HTTP_404_NOT_FOUND: None
}
)
@action(detail=True, methods=['post'], url_path='import-schema')
def import_schema(self, request: Request, pk) -> HttpResponse:
''' Create operation with existing schema. '''
2025-08-01 18:31:23 +03:00
item = self._get_item()
2025-08-10 14:08:36 +03:00
serializer = s.ImportSchemaSerializer(data=request.data, context={'oss': item})
serializer.is_valid(raise_exception=True)
layout = serializer.validated_data['layout']
position = serializer.validated_data['position']
data = serializer.validated_data['item_data']
data['operation_type'] = m.OperationType.INPUT
if not serializer.validated_data['clone_source']:
data['result'] = serializer.validated_data['source']
2025-08-03 15:28:16 +03:00
with transaction.atomic():
2025-08-03 15:28:16 +03:00
oss = m.OperationSchema(item)
new_operation = oss.create_operation(**serializer.validated_data['item_data'])
layout.append({
'nodeID': 'o' + str(new_operation.pk),
'x': position['x'],
'y': position['y'],
'width': position['width'],
'height': position['height']
})
2025-08-01 18:31:23 +03:00
m.Layout.update_data(pk, layout)
if serializer.validated_data['clone_source']:
prototype: LibraryItem = serializer.validated_data['source']
2025-08-01 18:31:23 +03:00
new_operation.result = _create_clone(prototype, new_operation, item)
new_operation.save(update_fields=["result"])
2025-08-01 18:31:23 +03:00
item.save(update_fields=['time_update'])
2025-07-23 16:13:11 +03:00
return Response(
status=c.HTTP_201_CREATED,
data={
'new_operation': new_operation.pk,
2025-08-01 18:31:23 +03:00
'oss': s.OperationSchemaSerializer(item).data
}
)
@extend_schema(
summary='create replica for operation',
tags=['OSS'],
request=s.CreateReplicaSerializer(),
responses={
c.HTTP_201_CREATED: s.OperationCreatedResponse,
c.HTTP_400_BAD_REQUEST: None,
c.HTTP_403_FORBIDDEN: None,
c.HTTP_404_NOT_FOUND: None
}
)
@action(detail=True, methods=['post'], url_path='create-replica')
def create_replica(self, request: Request, pk) -> HttpResponse:
''' Replicate schema. '''
2025-08-01 18:31:23 +03:00
item = self._get_item()
2025-08-10 14:08:36 +03:00
serializer = s.CreateReplicaSerializer(data=request.data, context={'oss': item})
serializer.is_valid(raise_exception=True)
layout = serializer.validated_data['layout']
position = serializer.validated_data['position']
2025-08-03 15:28:16 +03:00
with transaction.atomic():
2025-08-03 15:28:16 +03:00
oss = m.OperationSchema(item)
target = cast(m.Operation, serializer.validated_data['target'])
new_operation = oss.create_replica(target)
layout.append({
'nodeID': 'o' + str(new_operation.pk),
'x': position['x'],
'y': position['y'],
'width': position['width'],
'height': position['height']
})
2025-08-01 18:31:23 +03:00
m.Layout.update_data(pk, layout)
item.save(update_fields=['time_update'])
return Response(
status=c.HTTP_201_CREATED,
data={
'new_operation': new_operation.pk,
2025-08-01 18:31:23 +03:00
'oss': s.OperationSchemaSerializer(item).data
}
)
@extend_schema(
summary='create synthesis operation',
2025-04-20 18:06:18 +03:00
tags=['OSS'],
request=s.CreateSynthesisSerializer(),
2025-04-20 18:06:18 +03:00
responses={
c.HTTP_201_CREATED: s.OperationCreatedResponse,
c.HTTP_400_BAD_REQUEST: None,
c.HTTP_403_FORBIDDEN: None,
c.HTTP_404_NOT_FOUND: None
}
)
@action(detail=True, methods=['post'], url_path='create-synthesis')
def create_synthesis(self, request: Request, pk) -> HttpResponse:
''' Create Synthesis operation from arguments. '''
2025-08-01 18:31:23 +03:00
item = self._get_item()
2025-08-10 14:08:36 +03:00
serializer = s.CreateSynthesisSerializer(data=request.data, context={'oss': item})
2025-04-20 18:06:18 +03:00
serializer.is_valid(raise_exception=True)
layout = serializer.validated_data['layout']
position = serializer.validated_data['position']
data = serializer.validated_data['item_data']
data['operation_type'] = m.OperationType.SYNTHESIS
2025-08-03 15:28:16 +03:00
2025-04-20 18:06:18 +03:00
with transaction.atomic():
2025-08-03 15:28:16 +03:00
oss = m.OperationSchema(item)
2025-04-20 18:06:18 +03:00
new_operation = oss.create_operation(**serializer.validated_data['item_data'])
2025-06-11 16:38:56 +03:00
layout.append({
'nodeID': 'o' + str(new_operation.pk),
'x': position['x'],
'y': position['y'],
'width': position['width'],
'height': position['height']
2025-04-20 18:06:18 +03:00
})
oss.set_arguments(new_operation.pk, serializer.validated_data['arguments'])
oss.set_substitutions(new_operation.pk, serializer.validated_data['substitutions'])
oss.execute_operation(new_operation)
2025-08-01 18:31:23 +03:00
m.Layout.update_data(pk, layout)
item.save(update_fields=['time_update'])
2025-04-20 18:06:18 +03:00
return Response(
status=c.HTTP_201_CREATED,
data={
'new_operation': new_operation.pk,
2025-08-01 18:31:23 +03:00
'oss': s.OperationSchemaSerializer(item).data
2025-04-20 18:06:18 +03:00
}
)
@extend_schema(
summary='update operation',
tags=['OSS'],
request=s.UpdateOperationSerializer(),
responses={
c.HTTP_200_OK: s.OperationSchemaSerializer,
c.HTTP_400_BAD_REQUEST: None,
c.HTTP_403_FORBIDDEN: None,
c.HTTP_404_NOT_FOUND: None
}
)
@action(detail=True, methods=['patch'], url_path='update-operation')
def update_operation(self, request: Request, pk) -> HttpResponse:
''' Update Operation arguments and parameters. '''
2025-08-01 18:31:23 +03:00
item = self._get_item()
2025-08-10 14:08:36 +03:00
serializer = s.UpdateOperationSerializer(data=request.data, context={'oss': item})
serializer.is_valid(raise_exception=True)
operation: m.Operation = cast(m.Operation, serializer.validated_data['target'])
2025-08-03 15:28:16 +03:00
with transaction.atomic():
2025-08-03 15:28:16 +03:00
oss = m.OperationSchemaCached(item)
if 'layout' in serializer.validated_data:
2025-08-01 18:31:23 +03:00
layout = serializer.validated_data['layout']
m.Layout.update_data(pk, layout)
2025-04-22 14:23:26 +03:00
if 'alias' in serializer.validated_data['item_data']:
operation.alias = serializer.validated_data['item_data']['alias']
if 'title' in serializer.validated_data['item_data']:
operation.title = serializer.validated_data['item_data']['title']
if 'description' in serializer.validated_data['item_data']:
operation.description = serializer.validated_data['item_data']['description']
if 'parent' in serializer.validated_data['item_data']:
operation.parent = serializer.validated_data['item_data']['parent']
operation.save(update_fields=['alias', 'title', 'description', 'parent'])
if operation.result is not None:
can_edit = permissions.can_edit_item(request.user, operation.result)
if can_edit or operation.operation_type == m.OperationType.SYNTHESIS:
operation.result.alias = operation.alias
operation.result.title = operation.title
operation.result.description = operation.description
operation.result.save()
if 'arguments' in serializer.validated_data:
oss.set_arguments(operation.pk, serializer.validated_data['arguments'])
if 'substitutions' in serializer.validated_data:
oss.set_substitutions(operation.pk, serializer.validated_data['substitutions'])
2025-08-01 18:31:23 +03:00
item.save(update_fields=['time_update'])
2025-07-23 16:13:11 +03:00
return Response(
status=c.HTTP_200_OK,
2025-08-01 18:31:23 +03:00
data=s.OperationSchemaSerializer(item).data
)
@extend_schema(
2025-04-20 18:06:18 +03:00
summary='delete operation',
tags=['OSS'],
2025-04-20 18:06:18 +03:00
request=s.DeleteOperationSerializer,
responses={
c.HTTP_200_OK: s.OperationSchemaSerializer,
c.HTTP_400_BAD_REQUEST: None,
c.HTTP_403_FORBIDDEN: None,
c.HTTP_404_NOT_FOUND: None
}
)
2025-04-20 18:06:18 +03:00
@action(detail=True, methods=['patch'], url_path='delete-operation')
def delete_operation(self, request: Request, pk) -> HttpResponse:
''' Endpoint: Delete Operation. '''
2025-08-01 18:31:23 +03:00
item = self._get_item()
2025-08-10 14:08:36 +03:00
serializer = s.DeleteOperationSerializer(data=request.data, context={'oss': item})
serializer.is_valid(raise_exception=True)
2025-04-20 18:06:18 +03:00
operation = cast(m.Operation, serializer.validated_data['target'])
old_schema = operation.result
layout = serializer.validated_data['layout']
2025-06-11 16:38:56 +03:00
layout = [x for x in layout if x['nodeID'] != 'o' + str(operation.pk)]
2025-08-03 15:28:16 +03:00
with transaction.atomic():
2025-08-03 15:28:16 +03:00
oss = m.OperationSchemaCached(item)
2025-04-20 18:06:18 +03:00
oss.delete_operation(operation.pk, serializer.validated_data['keep_constituents'])
2025-08-01 18:31:23 +03:00
m.Layout.update_data(pk, layout)
2025-04-20 18:06:18 +03:00
if old_schema is not None:
if serializer.validated_data['delete_schema']:
m.PropagationFacade.before_delete_schema(old_schema)
old_schema.delete()
2025-08-01 18:31:23 +03:00
elif old_schema.is_synced(item):
2025-04-20 18:06:18 +03:00
old_schema.visible = True
old_schema.save(update_fields=['visible'])
2025-08-01 18:31:23 +03:00
item.save(update_fields=['time_update'])
2025-07-23 16:13:11 +03:00
return Response(
status=c.HTTP_200_OK,
2025-08-01 18:31:23 +03:00
data=s.OperationSchemaSerializer(item).data
)
@extend_schema(
summary='delete replica',
tags=['OSS'],
request=s.DeleteReplicaSerializer(),
responses={
c.HTTP_200_OK: s.OperationSchemaSerializer,
c.HTTP_400_BAD_REQUEST: None,
c.HTTP_403_FORBIDDEN: None,
c.HTTP_404_NOT_FOUND: None
}
)
@action(detail=True, methods=['patch'], url_path='delete-replica')
def delete_replica(self, request: Request, pk) -> HttpResponse:
''' Endpoint: Delete Replica Operation. '''
2025-08-01 18:31:23 +03:00
item = self._get_item()
2025-08-10 14:08:36 +03:00
serializer = s.DeleteReplicaSerializer(data=request.data, context={'oss': item})
serializer.is_valid(raise_exception=True)
operation = cast(m.Operation, serializer.validated_data['target'])
2025-08-05 19:11:13 +03:00
keep_connections = serializer.validated_data['keep_connections']
keep_constituents = serializer.validated_data['keep_constituents']
layout = serializer.validated_data['layout']
layout = [x for x in layout if x['nodeID'] != 'o' + str(operation.pk)]
2025-08-03 15:28:16 +03:00
with transaction.atomic():
2025-08-03 15:28:16 +03:00
oss = m.OperationSchemaCached(item)
2025-08-01 18:31:23 +03:00
m.Layout.update_data(pk, layout)
oss.delete_replica(operation.pk, keep_connections, keep_constituents)
2025-08-01 18:31:23 +03:00
item.save(update_fields=['time_update'])
return Response(
status=c.HTTP_200_OK,
2025-08-01 18:31:23 +03:00
data=s.OperationSchemaSerializer(item).data
)
@extend_schema(
summary='create input schema for target operation',
tags=['OSS'],
2025-04-20 18:06:18 +03:00
request=s.TargetOperationSerializer(),
responses={
2025-04-20 18:06:18 +03:00
c.HTTP_200_OK: s.SchemaCreatedResponse,
c.HTTP_400_BAD_REQUEST: None,
c.HTTP_403_FORBIDDEN: None,
c.HTTP_404_NOT_FOUND: None
}
)
@action(detail=True, methods=['patch'], url_path='create-input')
2024-08-07 21:54:14 +03:00
def create_input(self, request: Request, pk) -> HttpResponse:
''' Create input RSForm. '''
2025-08-01 18:31:23 +03:00
item = self._get_item()
2025-08-10 14:08:36 +03:00
serializer = s.TargetOperationSerializer(data=request.data, context={'oss': item})
serializer.is_valid(raise_exception=True)
operation: m.Operation = cast(m.Operation, serializer.validated_data['target'])
2025-03-19 13:23:32 +03:00
if len(operation.getQ_arguments()) > 0:
raise serializers.ValidationError({
2025-03-19 13:23:32 +03:00
'target': msg.operationHasArguments(operation.alias)
})
if operation.result is not None:
raise serializers.ValidationError({
'target': msg.operationResultNotEmpty(operation.alias)
})
2025-08-01 18:31:23 +03:00
layout = serializer.validated_data['layout']
2025-08-03 15:28:16 +03:00
with transaction.atomic():
2025-08-01 18:31:23 +03:00
m.Layout.update_data(pk, layout)
2025-08-05 14:01:43 +03:00
schema = m.OperationSchema.create_input(item, operation)
2025-08-01 18:31:23 +03:00
item.save(update_fields=['time_update'])
return Response(
status=c.HTTP_200_OK,
data={
'new_schema': LibraryItemSerializer(schema.model).data,
2025-08-01 18:31:23 +03:00
'oss': s.OperationSchemaSerializer(item).data
}
)
2024-07-28 21:29:46 +03:00
@extend_schema(
summary='set input schema for target operation',
tags=['OSS'],
request=s.SetOperationInputSerializer(),
responses={
c.HTTP_200_OK: s.OperationSchemaSerializer,
c.HTTP_400_BAD_REQUEST: None,
c.HTTP_403_FORBIDDEN: None,
c.HTTP_404_NOT_FOUND: None
}
)
@action(detail=True, methods=['patch'], url_path='set-input')
2024-08-07 21:54:14 +03:00
def set_input(self, request: Request, pk) -> HttpResponse:
2024-07-28 21:29:46 +03:00
''' Set input schema for target operation. '''
2025-08-01 18:31:23 +03:00
item = self._get_item()
2025-08-10 14:08:36 +03:00
serializer = s.SetOperationInputSerializer(data=request.data, context={'oss': item})
2024-07-28 21:29:46 +03:00
serializer.is_valid(raise_exception=True)
2025-08-01 18:31:23 +03:00
layout = serializer.validated_data['layout']
2024-08-14 23:36:52 +03:00
target_operation: m.Operation = cast(m.Operation, serializer.validated_data['target'])
schema: Optional[LibraryItem] = serializer.validated_data['input']
if schema is not None:
connected_operations = m.Operation.objects.filter(result=schema).only('operation_type', 'oss_id')
for operation in connected_operations:
if operation.operation_type != m.OperationType.INPUT:
raise serializers.ValidationError({
'input': msg.operationResultFromAnotherOSS()
})
if operation != target_operation and operation.oss_id == target_operation.oss_id:
raise serializers.ValidationError({
'input': msg.operationInputAlreadyConnected()
})
old_schema = target_operation.result
2025-08-03 15:28:16 +03:00
2024-07-28 21:29:46 +03:00
with transaction.atomic():
2025-08-03 15:28:16 +03:00
oss = m.OperationSchemaCached(item)
if old_schema is not None:
2025-08-01 18:31:23 +03:00
if old_schema.is_synced(item):
old_schema.visible = True
old_schema.save(update_fields=['visible'])
2025-08-01 18:31:23 +03:00
m.Layout.update_data(pk, layout)
2024-08-14 23:36:52 +03:00
oss.set_input(target_operation.pk, schema)
2025-08-01 18:31:23 +03:00
item.save(update_fields=['time_update'])
2025-07-23 16:13:11 +03:00
2024-07-28 21:29:46 +03:00
return Response(
status=c.HTTP_200_OK,
2025-08-01 18:31:23 +03:00
data=s.OperationSchemaSerializer(item).data
2024-07-28 21:29:46 +03:00
)
2024-07-29 22:30:24 +03:00
@extend_schema(
summary='execute operation',
tags=['OSS'],
2025-04-20 18:06:18 +03:00
request=s.TargetOperationSerializer(),
2024-07-29 22:30:24 +03:00
responses={
c.HTTP_200_OK: s.OperationSchemaSerializer,
c.HTTP_400_BAD_REQUEST: None,
c.HTTP_403_FORBIDDEN: None,
c.HTTP_404_NOT_FOUND: None
}
)
@action(detail=True, methods=['post'], url_path='execute-operation')
2024-08-07 21:54:14 +03:00
def execute_operation(self, request: Request, pk) -> HttpResponse:
2024-07-29 22:30:24 +03:00
''' Execute operation. '''
2025-08-01 18:31:23 +03:00
item = self._get_item()
2025-08-10 14:08:36 +03:00
serializer = s.TargetOperationSerializer(data=request.data, context={'oss': item})
2024-07-29 22:30:24 +03:00
serializer.is_valid(raise_exception=True)
operation: m.Operation = cast(m.Operation, serializer.validated_data['target'])
if operation.operation_type != m.OperationType.SYNTHESIS:
raise serializers.ValidationError({
'target': msg.operationNotSynthesis(operation.alias)
})
if operation.result is not None:
raise serializers.ValidationError({
'target': msg.operationResultNotEmpty(operation.alias)
})
2025-08-01 18:31:23 +03:00
layout = serializer.validated_data['layout']
2025-08-03 15:28:16 +03:00
with transaction.atomic():
2025-08-03 15:28:16 +03:00
oss = m.OperationSchemaCached(item)
oss.execute_operation(operation)
2025-08-01 18:31:23 +03:00
m.Layout.update_data(pk, layout)
item.save(update_fields=['time_update'])
2024-07-29 22:30:24 +03:00
return Response(
status=c.HTTP_200_OK,
2025-08-01 18:31:23 +03:00
data=s.OperationSchemaSerializer(item).data
2024-07-29 22:30:24 +03:00
)
@extend_schema(
summary='get predecessor for target constituenta',
tags=['OSS'],
request=CstTargetSerializer(),
responses={
c.HTTP_200_OK: s.ConstituentaReferenceResponse,
c.HTTP_400_BAD_REQUEST: None,
c.HTTP_403_FORBIDDEN: None,
c.HTTP_404_NOT_FOUND: None
}
)
@action(detail=False, methods=['post'], url_path='get-predecessor')
2024-08-07 21:54:14 +03:00
def get_predecessor(self, request: Request) -> HttpResponse:
''' Get predecessor. '''
serializer = CstTargetSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
cst = cast(Constituenta, serializer.validated_data['target'])
inheritance_query = m.Inheritance.objects.filter(child=cst)
while inheritance_query.exists():
inheritance = inheritance_query.first()
if inheritance is None:
break
cst = inheritance.parent
inheritance_query = m.Inheritance.objects.filter(child=cst)
return Response(
status=c.HTTP_200_OK,
data={
'id': cst.pk,
'schema': cst.schema_id
}
)
2024-10-28 14:52:30 +03:00
@extend_schema(
summary='relocate constituents from one schema to another',
tags=['OSS'],
request=s.RelocateConstituentsSerializer(),
responses={
c.HTTP_200_OK: s.OperationSchemaSerializer,
c.HTTP_400_BAD_REQUEST: None,
c.HTTP_403_FORBIDDEN: None,
c.HTTP_404_NOT_FOUND: None
}
)
@action(detail=False, methods=['post'], url_path='relocate-constituents')
def relocate_constituents(self, request: Request) -> Response:
''' Relocate constituents from one schema to another. '''
serializer = s.RelocateConstituentsSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
data = serializer.validated_data
2025-08-03 15:28:16 +03:00
ids = [cst.pk for cst in data['items']]
2024-10-28 14:52:30 +03:00
with transaction.atomic():
2025-08-03 15:28:16 +03:00
oss = m.OperationSchemaCached(LibraryItem.objects.get(pk=data['oss']))
source = RSFormCached(LibraryItem.objects.get(pk=data['source']))
destination = RSFormCached(LibraryItem.objects.get(pk=data['destination']))
2024-10-28 14:52:30 +03:00
if data['move_down']:
2025-08-03 15:28:16 +03:00
oss.relocate_down(source, destination, ids)
m.PropagationFacade.before_delete_cst(data['source'], ids)
source.delete_cst(ids)
2024-10-28 14:52:30 +03:00
else:
new_items = oss.relocate_up(source, destination, data['items'])
m.PropagationFacade.after_create_cst(destination, new_items, exclude=[oss.model.pk])
return Response(status=c.HTTP_200_OK)