F: Implement delete-block and update-block backend

This commit is contained in:
Ivan 2025-04-16 11:19:29 +03:00
parent 227b0dd72c
commit 3b26457b83
11 changed files with 337 additions and 117 deletions

View File

@ -92,26 +92,26 @@ class OperationSchema:
)
def update_layout(self, data: dict) -> None:
''' Update positions. '''
''' Update graphical layout. '''
layout = self.layout()
layout.data = data
layout.save()
def create_operation(self, **kwargs) -> Operation:
''' Insert new operation. '''
''' Create Operation. '''
result = Operation.objects.create(oss=self.model, **kwargs)
self.cache.insert_operation(result)
self.save(update_fields=['time_update'])
return result
def create_block(self, **kwargs) -> Block:
''' Insert new block. '''
''' Create Block. '''
result = Block.objects.create(oss=self.model, **kwargs)
self.save(update_fields=['time_update'])
return result
def delete_operation(self, target: int, keep_constituents: bool = False):
''' Delete operation. '''
''' Delete Operation. '''
self.cache.ensure_loaded()
operation = self.cache.operation_by_id[target]
schema = self.cache.get_schema(operation)
@ -139,6 +139,20 @@ class OperationSchema:
operation.delete()
self.save(update_fields=['time_update'])
def delete_block(self, target: Block):
''' Delete Block. '''
new_parent = target.parent
if new_parent is not None:
for block in Block.objects.filter(parent=target):
if block != new_parent:
block.parent = new_parent
block.save(update_fields=['parent'])
for operation in Operation.objects.filter(parent=target):
operation.parent = new_parent
operation.save(update_fields=['parent'])
target.delete()
self.save(update_fields=['time_update'])
def set_input(self, target: int, schema: Optional[LibraryItem]) -> None:
''' Set input schema for operation. '''
operation = self.cache.operation_by_id[target]
@ -165,7 +179,7 @@ class OperationSchema:
self.save(update_fields=['time_update'])
def set_arguments(self, target: int, arguments: list[Operation]) -> None:
''' Set arguments to operation. '''
''' Set arguments of target Operation. '''
self.cache.ensure_loaded()
operation = self.cache.operation_by_id[target]
processed: list[Operation] = []
@ -198,7 +212,7 @@ class OperationSchema:
self.save(update_fields=['time_update'])
def set_substitutions(self, target: int, substitutes: list[dict]) -> None:
''' Clear all arguments for operation. '''
''' Clear all arguments for target Operation. '''
self.cache.ensure_loaded()
operation = self.cache.operation_by_id[target]
schema = self.cache.get_schema(operation)
@ -237,7 +251,7 @@ class OperationSchema:
self.save(update_fields=['time_update'])
def create_input(self, operation: Operation) -> RSForm:
''' Create input RSForm. '''
''' Create input RSForm for given Operation. '''
schema = RSForm.create(
owner=self.model.owner,
alias=operation.alias,
@ -254,7 +268,7 @@ class OperationSchema:
return schema
def execute_operation(self, operation: Operation) -> bool:
''' Execute target operation. '''
''' Execute target Operation. '''
schemas = [
arg.argument.result
for arg in operation.getQ_arguments().order_by('order')
@ -301,7 +315,7 @@ class OperationSchema:
return True
def relocate_down(self, source: RSForm, destination: RSForm, items: list[Constituenta]):
''' Move list of constituents to specific schema inheritor. '''
''' Move list of Constituents to destination Schema inheritor. '''
self.cache.ensure_loaded()
self.cache.insert_schema(source)
self.cache.insert_schema(destination)
@ -315,7 +329,7 @@ class OperationSchema:
Inheritance.objects.filter(operation_id=operation.pk, parent__in=items).delete()
def relocate_up(self, source: RSForm, destination: RSForm, items: list[Constituenta]) -> list[Constituenta]:
''' Move list of constituents to specific schema upstream. '''
''' Move list of Constituents upstream to destination Schema. '''
self.cache.ensure_loaded()
self.cache.insert_schema(source)
self.cache.insert_schema(destination)
@ -345,7 +359,7 @@ class OperationSchema:
cst_list: list[Constituenta],
exclude: Optional[list[int]] = None
) -> None:
''' Trigger cascade resolutions when new constituent is created. '''
''' Trigger cascade resolutions when new Constituenta is created. '''
self.cache.insert_schema(source)
inserted_aliases = [cst.alias for cst in cst_list]
depend_aliases: set[str] = set()
@ -361,13 +375,13 @@ class OperationSchema:
self._cascade_inherit_cst(operation.pk, source, cst_list, alias_mapping, exclude)
def after_change_cst_type(self, source: RSForm, target: Constituenta) -> None:
''' Trigger cascade resolutions when constituenta type is changed. '''
''' Trigger cascade resolutions when Constituenta type is changed. '''
self.cache.insert_schema(source)
operation = self.cache.get_operation(source.model.pk)
self._cascade_change_cst_type(operation.pk, target.pk, cast(CstType, target.cst_type))
def after_update_cst(self, source: RSForm, target: Constituenta, data: dict, old_data: dict) -> None:
''' Trigger cascade resolutions when constituenta data is changed. '''
''' Trigger cascade resolutions when Constituenta data is changed. '''
self.cache.insert_schema(source)
operation = self.cache.get_operation(source.model.pk)
depend_aliases = self._extract_data_references(data, old_data)
@ -385,13 +399,13 @@ class OperationSchema:
)
def before_delete_cst(self, source: RSForm, target: list[Constituenta]) -> None:
''' Trigger cascade resolutions before constituents are deleted. '''
''' Trigger cascade resolutions before Constituents are deleted. '''
self.cache.insert_schema(source)
operation = self.cache.get_operation(source.model.pk)
self._cascade_delete_inherited(operation.pk, target)
def before_substitute(self, source: RSForm, substitutions: CstSubstitution) -> None:
''' Trigger cascade resolutions before constituents are substituted. '''
''' Trigger cascade resolutions before Constituents are substituted. '''
self.cache.insert_schema(source)
operation = self.cache.get_operation(source.model.pk)
self._cascade_before_substitute(substitutions, operation)

View File

@ -17,7 +17,7 @@ class PropagationFacade:
@staticmethod
def after_create_cst(source: RSForm, new_cst: list[Constituenta], exclude: Optional[list[int]] = None) -> None:
''' Trigger cascade resolutions when new constituent is created. '''
''' Trigger cascade resolutions when new constituenta is created. '''
hosts = _get_oss_hosts(source.model)
for host in hosts:
if exclude is None or host.pk not in exclude:

View File

@ -3,16 +3,18 @@
from .basics import LayoutSerializer, SubstitutionExSerializer
from .data_access import (
ArgumentSerializer,
BlockCreateSerializer,
BlockSerializer,
OperationCreateSerializer,
CreateBlockSerializer,
CreateOperationSerializer,
DeleteBlockSerializer,
OperationDeleteSerializer,
OperationSchemaSerializer,
OperationSerializer,
OperationTargetSerializer,
OperationUpdateSerializer,
RelocateConstituentsSerializer,
SetOperationInputSerializer
SetOperationInputSerializer,
UpdateBlockSerializer,
UpdateOperationSerializer
)
from .responses import (
ConstituentaReferenceResponse,

View File

@ -41,7 +41,7 @@ class ArgumentSerializer(serializers.ModelSerializer):
fields = ('operation', 'argument')
class BlockCreateSerializer(serializers.Serializer):
class CreateBlockSerializer(serializers.Serializer):
''' Serializer: Block creation. '''
class BlockCreateData(serializers.ModelSerializer):
''' Serializer: Block creation data. '''
@ -52,7 +52,6 @@ class BlockCreateSerializer(serializers.Serializer):
fields = 'title', 'description', 'parent'
layout = LayoutSerializer()
item_data = BlockCreateData()
width = serializers.FloatField()
height = serializers.FloatField()
@ -84,9 +83,9 @@ class BlockCreateSerializer(serializers.Serializer):
return attrs
class OperationCreateSerializer(serializers.Serializer):
class CreateOperationSerializer(serializers.Serializer):
''' Serializer: Operation creation. '''
class OperationCreateData(serializers.ModelSerializer):
class CreateOperationData(serializers.ModelSerializer):
''' Serializer: Operation creation data. '''
alias = serializers.CharField()
operation_type = serializers.ChoiceField(OperationType.choices)
@ -99,8 +98,7 @@ class OperationCreateSerializer(serializers.Serializer):
'description', 'result', 'parent'
layout = LayoutSerializer()
item_data = OperationCreateData()
item_data = CreateOperationData()
position_x = serializers.FloatField()
position_y = serializers.FloatField()
create_schema = serializers.BooleanField(default=False, required=False)
@ -120,23 +118,23 @@ class OperationCreateSerializer(serializers.Serializer):
for operation in attrs['arguments']:
if operation.oss_id != oss.pk:
raise serializers.ValidationError({
'arguments': msg.operationNotInOSS(oss.title)
'arguments': msg.operationNotInOSS()
})
return attrs
class OperationUpdateSerializer(serializers.Serializer):
class UpdateOperationSerializer(serializers.Serializer):
''' Serializer: Operation update. '''
class OperationUpdateData(serializers.ModelSerializer):
class UpdateOperationData(serializers.ModelSerializer):
''' Serializer: Operation update data. '''
class Meta:
''' serializer metadata. '''
model = Operation
fields = 'alias', 'title', 'description'
fields = 'alias', 'title', 'description', 'parent'
layout = LayoutSerializer()
layout = LayoutSerializer(required=False)
target = PKField(many=False, queryset=Operation.objects.all())
item_data = OperationUpdateData()
item_data = UpdateOperationData()
arguments = PKField(many=True, queryset=Operation.objects.all().only('oss_id', 'result_id'), required=False)
substitutions = serializers.ListField(
child=SubstitutionSerializerBase(),
@ -145,6 +143,12 @@ class OperationUpdateSerializer(serializers.Serializer):
def validate(self, attrs):
oss = cast(LibraryItem, self.context['oss'])
target = cast(Block, attrs['target'])
if target.oss_id != oss.pk:
raise serializers.ValidationError({
'target': msg.operationNotInOSS()
})
if 'parent' in attrs['item_data'] and attrs['item_data']['parent'].oss_id != oss.pk:
raise serializers.ValidationError({
'parent': msg.parentNotInOSS()
@ -160,7 +164,7 @@ class OperationUpdateSerializer(serializers.Serializer):
for operation in attrs['arguments']:
if operation.oss_id != oss.pk:
raise serializers.ValidationError({
'arguments': msg.operationNotInOSS(oss.title)
'arguments': msg.operationNotInOSS()
})
if 'substitutions' not in attrs:
@ -192,17 +196,51 @@ class OperationUpdateSerializer(serializers.Serializer):
return attrs
class OperationTargetSerializer(serializers.Serializer):
''' Serializer: Target single operation. '''
layout = LayoutSerializer()
target = PKField(many=False, queryset=Operation.objects.all().only('oss_id', 'result_id'))
class UpdateBlockSerializer(serializers.Serializer):
''' Serializer: Block update. '''
class UpdateBlockData(serializers.ModelSerializer):
''' Serializer: Block update data. '''
class Meta:
''' serializer metadata. '''
model = Block
fields = 'title', 'description', 'parent'
layout = LayoutSerializer(required=False)
target = PKField(many=False, queryset=Block.objects.all())
item_data = UpdateBlockData()
def validate(self, attrs):
oss = cast(LibraryItem, self.context['oss'])
operation = cast(Operation, attrs['target'])
if oss and operation.oss_id != oss.pk:
block = cast(Block, attrs['target'])
if block.oss_id != oss.pk:
raise serializers.ValidationError({
'target': msg.operationNotInOSS(oss.title)
'target': msg.blockNotInOSS()
})
if 'parent' in attrs['item_data'] and \
attrs['item_data']['parent'] is not None:
if attrs['item_data']['parent'].oss_id != oss.pk:
raise serializers.ValidationError({
'parent': msg.parentNotInOSS()
})
if attrs['item_data']['parent'] == attrs['target']:
raise serializers.ValidationError({
'parent': msg.blockSelfParent()
})
return attrs
class DeleteBlockSerializer(serializers.Serializer):
''' Serializer: Delete block. '''
layout = LayoutSerializer()
target = PKField(many=False, queryset=Block.objects.all().only('oss_id'))
def validate(self, attrs):
oss = cast(LibraryItem, self.context['oss'])
block = cast(Block, attrs['target'])
if block.oss_id != oss.pk:
raise serializers.ValidationError({
'target': msg.blockNotInOSS()
})
return attrs
@ -217,9 +255,24 @@ class OperationDeleteSerializer(serializers.Serializer):
def validate(self, attrs):
oss = cast(LibraryItem, self.context['oss'])
operation = cast(Operation, attrs['target'])
if oss and operation.oss_id != oss.pk:
if operation.oss_id != oss.pk:
raise serializers.ValidationError({
'target': msg.operationNotInOSS(oss.title)
'target': msg.operationNotInOSS()
})
return attrs
class OperationTargetSerializer(serializers.Serializer):
''' Serializer: Target single operation. '''
layout = LayoutSerializer()
target = PKField(many=False, queryset=Operation.objects.all().only('oss_id', 'result_id'))
def validate(self, attrs):
oss = cast(LibraryItem, self.context['oss'])
operation = cast(Operation, attrs['target'])
if operation.oss_id != oss.pk:
raise serializers.ValidationError({
'target': msg.operationNotInOSS()
})
return attrs
@ -240,7 +293,7 @@ class SetOperationInputSerializer(serializers.Serializer):
operation = cast(Operation, attrs['target'])
if oss and operation.oss_id != oss.pk:
raise serializers.ValidationError({
'target': msg.operationNotInOSS(oss.title)
'target': msg.operationNotInOSS()
})
if operation.operation_type != OperationType.INPUT:
raise serializers.ValidationError({

View File

@ -27,6 +27,10 @@ class TestOssBlocks(EndpointTester):
self.block1 = self.owned.create_block(
title='1',
)
self.block2 = self.owned.create_block(
title='2',
parent=self.block1
)
self.operation1 = self.owned.create_operation(
alias='1',
operation_type=OperationType.INPUT,
@ -35,15 +39,12 @@ class TestOssBlocks(EndpointTester):
self.operation2 = self.owned.create_operation(
alias='2',
operation_type=OperationType.INPUT,
parent=self.block2,
)
self.operation3 = self.unowned.create_operation(
alias='3',
operation_type=OperationType.INPUT
)
self.block2 = self.owned.create_block(
title='2',
parent=self.block1
)
self.block3 = self.unowned.create_block(
title='3',
parent=self.block1
@ -165,3 +166,73 @@ class TestOssBlocks(EndpointTester):
self.block1.refresh_from_db()
self.assertEqual(self.operation1.parent.pk, new_block['id'])
self.assertEqual(self.block1.parent.pk, new_block['id'])
@decl_endpoint('/api/oss/{item}/delete-block', method='patch')
def test_delete_block(self):
self.populateData()
self.executeNotFound(item=self.invalid_id)
self.executeBadData(item=self.owned_id)
data = {
'layout': self.layout_data
}
self.executeBadData(data=data)
data['target'] = self.operation1.pk
self.executeBadData(data=data)
data['target'] = self.block3.pk
self.executeBadData(data=data)
data['target'] = self.block2.pk
self.logout()
self.executeForbidden(data=data)
self.login()
response = self.executeOK(data=data)
self.operation2.refresh_from_db()
self.assertEqual(len(response.data['blocks']), 1)
self.assertEqual(self.operation2.parent.pk, self.block1.pk)
data['target'] = self.block1.pk
response = self.executeOK(data=data)
self.operation1.refresh_from_db()
self.operation2.refresh_from_db()
self.assertEqual(len(response.data['blocks']), 0)
self.assertEqual(self.operation1.parent, None)
self.assertEqual(self.operation2.parent, None)
@decl_endpoint('/api/oss/{item}/update-block', method='patch')
def test_update_block(self):
self.populateData()
self.executeBadData(item=self.owned_id)
data = {
'target': self.invalid_id,
'item_data': {
'title': 'Test title mod',
'description': 'Comment mod',
'parent': None
},
}
self.executeBadData(data=data)
data['target'] = self.block3.pk
self.toggle_admin(True)
self.executeBadData(data=data)
data['target'] = self.block2.pk
self.logout()
self.executeForbidden(data=data)
self.login()
response = self.executeOK(data=data)
self.block2.refresh_from_db()
self.assertEqual(self.block2.title, data['item_data']['title'])
self.assertEqual(self.block2.description, data['item_data']['description'])
self.assertEqual(self.block2.parent, data['item_data']['parent'])
data['layout'] = self.layout_data
self.executeOK(data=data)

View File

@ -226,9 +226,8 @@ class TestOssOperations(EndpointTester):
@decl_endpoint('/api/oss/{item}/delete-operation', method='patch')
def test_delete_operation(self):
self.executeNotFound(item=self.invalid_id)
self.populateData()
self.executeNotFound(item=self.invalid_id)
self.executeBadData(item=self.owned_id)
data = {
@ -371,7 +370,6 @@ class TestOssOperations(EndpointTester):
'title': 'Test title mod',
'description': 'Comment mod'
},
'layout': self.layout_data,
'arguments': [self.operation2.pk, self.operation1.pk],
'substitutions': [
{
@ -403,6 +401,10 @@ class TestOssOperations(EndpointTester):
self.assertEqual(sub.original.pk, data['substitutions'][0]['original'])
self.assertEqual(sub.substitution.pk, data['substitutions'][0]['substitution'])
data['layout'] = self.layout_data
self.executeOK(data=data)
@decl_endpoint('/api/oss/{item}/update-operation', method='patch')
def test_update_operation_sync(self):
self.populateData()

View File

@ -40,9 +40,11 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
'create_operation',
'create_block',
'delete_operation',
'delete_block',
'update_operation',
'update_block',
'create_input',
'set_input',
'update_operation',
'execute_operation',
'relocate_constituents'
]:
@ -94,7 +96,7 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
@extend_schema(
summary='create operation',
tags=['OSS'],
request=s.OperationCreateSerializer(),
request=s.CreateOperationSerializer(),
responses={
c.HTTP_201_CREATED: s.NewOperationResponse,
c.HTTP_400_BAD_REQUEST: None,
@ -104,8 +106,8 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
)
@action(detail=True, methods=['post'], url_path='create-operation')
def create_operation(self, request: Request, pk) -> HttpResponse:
''' Create new operation. '''
serializer = s.OperationCreateSerializer(
''' Create Operation. '''
serializer = s.CreateOperationSerializer(
data=request.data,
context={'oss': self.get_object()}
)
@ -155,7 +157,7 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
@extend_schema(
summary='create block',
tags=['OSS'],
request=s.BlockCreateSerializer(),
request=s.CreateBlockSerializer(),
responses={
c.HTTP_201_CREATED: s.NewBlockResponse,
c.HTTP_400_BAD_REQUEST: None,
@ -165,8 +167,8 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
)
@action(detail=True, methods=['post'], url_path='create-block')
def create_block(self, request: Request, pk) -> HttpResponse:
''' Create new block. '''
serializer = s.BlockCreateSerializer(
''' Create Block. '''
serializer = s.CreateBlockSerializer(
data=request.data,
context={'oss': self.get_object()}
)
@ -216,7 +218,7 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
)
@action(detail=True, methods=['patch'], url_path='delete-operation')
def delete_operation(self, request: Request, pk) -> HttpResponse:
''' Endpoint: Delete operation. '''
''' Endpoint: Delete Operation. '''
serializer = s.OperationDeleteSerializer(
data=request.data,
context={'oss': self.get_object()}
@ -243,6 +245,119 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
data=s.OperationSchemaSerializer(oss.model).data
)
@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. '''
serializer = s.DeleteBlockSerializer(
data=request.data,
context={'oss': self.get_object()}
)
serializer.is_valid(raise_exception=True)
oss = m.OperationSchema(self.get_object())
block = cast(m.Block, serializer.validated_data['target'])
layout = serializer.validated_data['layout']
layout['blocks'] = [x for x in layout['blocks'] if x['id'] != block.pk]
with transaction.atomic():
oss.delete_block(block)
oss.update_layout(layout)
return Response(
status=c.HTTP_200_OK,
data=s.OperationSchemaSerializer(oss.model).data
)
@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. '''
serializer = s.UpdateOperationSerializer(
data=request.data,
context={'oss': self.get_object()}
)
serializer.is_valid(raise_exception=True)
operation: m.Operation = cast(m.Operation, serializer.validated_data['target'])
oss = m.OperationSchema(self.get_object())
with transaction.atomic():
if 'layout' in serializer.validated_data:
oss.update_layout(serializer.validated_data['layout'])
operation.alias = serializer.validated_data['item_data']['alias']
operation.title = serializer.validated_data['item_data']['title']
operation.description = serializer.validated_data['item_data']['description']
operation.save(update_fields=['alias', 'title', 'description'])
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'])
return Response(
status=c.HTTP_200_OK,
data=s.OperationSchemaSerializer(oss.model).data
)
@extend_schema(
summary='update block',
tags=['OSS'],
request=s.UpdateBlockSerializer(),
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-block')
def update_block(self, request: Request, pk) -> HttpResponse:
''' Update Block. '''
serializer = s.UpdateBlockSerializer(
data=request.data,
context={'oss': self.get_object()}
)
serializer.is_valid(raise_exception=True)
block: m.Block = cast(m.Block, serializer.validated_data['target'])
oss = m.OperationSchema(self.get_object())
with transaction.atomic():
if 'layout' in serializer.validated_data:
oss.update_layout(serializer.validated_data['layout'])
block.title = serializer.validated_data['item_data']['title']
block.description = serializer.validated_data['item_data']['description']
block.parent = serializer.validated_data['item_data']['parent']
block.save(update_fields=['title', 'description', 'parent'])
return Response(
status=c.HTTP_200_OK,
data=s.OperationSchemaSerializer(oss.model).data
)
@extend_schema(
summary='create input schema for target operation',
tags=['OSS'],
@ -256,7 +371,7 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
)
@action(detail=True, methods=['patch'], url_path='create-input')
def create_input(self, request: Request, pk) -> HttpResponse:
''' Create new input RSForm. '''
''' Create input RSForm. '''
serializer = s.OperationTargetSerializer(
data=request.data,
context={'oss': self.get_object()}
@ -333,51 +448,6 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
data=s.OperationSchemaSerializer(oss.model).data
)
@extend_schema(
summary='update operation',
tags=['OSS'],
request=s.OperationUpdateSerializer(),
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. '''
serializer = s.OperationUpdateSerializer(
data=request.data,
context={'oss': self.get_object()}
)
serializer.is_valid(raise_exception=True)
operation: m.Operation = cast(m.Operation, serializer.validated_data['target'])
oss = m.OperationSchema(self.get_object())
with transaction.atomic():
oss.update_layout(serializer.validated_data['layout'])
operation.alias = serializer.validated_data['item_data']['alias']
operation.title = serializer.validated_data['item_data']['title']
operation.description = serializer.validated_data['item_data']['description']
operation.save(update_fields=['alias', 'title', 'description'])
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'])
return Response(
status=c.HTTP_200_OK,
data=s.OperationSchemaSerializer(oss.model).data
)
@extend_schema(
summary='execute operation',
tags=['OSS'],

View File

@ -137,7 +137,7 @@ class RSForm:
return result
def create_cst(self, data: dict, insert_after: Optional[Constituenta] = None) -> Constituenta:
''' Create new cst from data. '''
''' Create constituenta from data. '''
if insert_after is None:
position = INSERT_LAST
else:

View File

@ -77,7 +77,7 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
)
@action(detail=True, methods=['post'], url_path='create-cst')
def create_cst(self, request: Request, pk) -> HttpResponse:
''' Create new constituenta. '''
''' Create Constituenta. '''
serializer = s.CstCreateSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
data = serializer.validated_data
@ -254,7 +254,7 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
)
@action(detail=True, methods=['patch'], url_path='delete-multiple-cst')
def delete_multiple_cst(self, request: Request, pk) -> HttpResponse:
''' Endpoint: Delete multiple constituents. '''
''' Endpoint: Delete multiple Constituents. '''
model = self._get_item()
serializer = s.CstListSerializer(
data=request.data,
@ -284,7 +284,7 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
)
@action(detail=True, methods=['patch'], url_path='move-cst')
def move_cst(self, request: Request, pk) -> HttpResponse:
''' Endpoint: Move multiple constituents. '''
''' Endpoint: Move multiple Constituents. '''
model = self._get_item()
serializer = s.CstMoveSerializer(
data=request.data,
@ -334,7 +334,7 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
)
@action(detail=True, methods=['patch'], url_path='restore-order')
def restore_order(self, request: Request, pk) -> HttpResponse:
''' Endpoint: Restore order based on types and term graph. '''
''' Endpoint: Restore order based on types and Term graph. '''
model = self._get_item()
m.RSForm(model).restore_order()
return Response(
@ -449,7 +449,7 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
)
@action(detail=True, methods=['post'], url_path='check-constituenta')
def check_constituenta(self, request: Request, pk) -> HttpResponse:
''' Endpoint: Check RSLang expression against schema context. '''
''' Endpoint: Check RSLang expression against Schema context. '''
serializer = s.ConstituentaCheckSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
expression = serializer.validated_data['definition_formal']
@ -474,7 +474,7 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
)
@action(detail=True, methods=['post'], url_path='resolve')
def resolve(self, request: Request, pk) -> HttpResponse:
''' Endpoint: Resolve references in text against schema terms context. '''
''' Endpoint: Resolve references in text against Schema terms context. '''
serializer = s.TextSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
text = serializer.validated_data['text']
@ -543,7 +543,7 @@ class TrsImportView(views.APIView):
@extend_schema(
summary='create new RSForm empty or from file',
summary='create RSForm empty or from file',
tags=['RSForm'],
request=LibraryItemSerializer,
responses={

View File

@ -7,15 +7,23 @@ def constituentaNotInRSform(title: str):
def constituentaNotFromOperation():
return f'Конституента не соответствую аргументам операции'
return 'Конституента не соответствую аргументам операции'
def operationNotInOSS(title: str):
return f'Операция не принадлежит ОСС: {title}'
def operationNotInOSS():
return 'Операция не принадлежит ОСС'
def blockNotInOSS():
return 'Блок не принадлежит ОСС'
def parentNotInOSS():
return f'Родительский блок не принадлежит ОСС'
return 'Родительский блок не принадлежит ОСС'
def blockSelfParent():
return 'Попытка создания циклического вложения'
def childNotInOSS():

View File

@ -9,7 +9,7 @@ export type IUserProfile = z.infer<typeof schemaUserProfile>;
/** Represents user reference information. */
export type IUserInfo = z.infer<typeof schemaUserInfo>;
/** Represents signup data, used to create new users. */
/** Represents signup data, used to create users. */
export type IUserSignupDTO = z.infer<typeof schemaUserSignup>;
/** Represents user data, intended to update user profile in persistent storage. */