Portal/rsconcept/backend/apps/oss/serializers/data_access.py

413 lines
15 KiB
Python
Raw Normal View History

''' Serializers for persistent data manipulation. '''
from typing import cast
2024-07-19 19:28:55 +03:00
from django.db.models import F
from rest_framework import serializers
2024-07-19 19:28:55 +03:00
from rest_framework.serializers import PrimaryKeyRelatedField as PKField
2024-07-28 21:29:46 +03:00
from apps.library.models import LibraryItem, LibraryItemType
from apps.library.serializers import LibraryItemDetailsSerializer
2024-07-29 22:30:24 +03:00
from apps.rsform.models import Constituenta
from apps.rsform.serializers import SubstitutionSerializerBase
from shared import messages as msg
2025-04-14 23:02:35 +03:00
from ..models import Argument, Block, Inheritance, Operation, OperationSchema, OperationType
2025-04-06 13:28:00 +03:00
from .basics import LayoutSerializer, SubstitutionExSerializer
class OperationSerializer(serializers.ModelSerializer):
''' Serializer: Operation data. '''
class Meta:
''' serializer metadata. '''
model = Operation
fields = '__all__'
read_only_fields = ('id', 'oss')
2025-04-14 23:02:35 +03:00
class BlockSerializer(serializers.ModelSerializer):
''' Serializer: Block data. '''
class Meta:
''' serializer metadata. '''
model = Block
fields = '__all__'
read_only_fields = ('id', 'oss')
class ArgumentSerializer(serializers.ModelSerializer):
''' Serializer: Operation data. '''
class Meta:
''' serializer metadata. '''
model = Argument
fields = ('operation', 'argument')
class CreateBlockSerializer(serializers.Serializer):
2025-04-14 23:02:35 +03:00
''' Serializer: Block creation. '''
class BlockCreateData(serializers.ModelSerializer):
''' Serializer: Block creation data. '''
class Meta:
''' serializer metadata. '''
model = Block
fields = 'title', 'description', 'parent'
layout = LayoutSerializer()
item_data = BlockCreateData()
width = serializers.FloatField()
height = serializers.FloatField()
position_x = serializers.FloatField()
position_y = serializers.FloatField()
children_operations = PKField(many=True, queryset=Operation.objects.all().only('oss_id'))
children_blocks = PKField(many=True, queryset=Block.objects.all().only('oss_id'))
def validate(self, attrs):
oss = cast(LibraryItem, self.context['oss'])
if 'parent' in attrs['item_data'] and \
attrs['item_data']['parent'] is not None and \
attrs['item_data']['parent'].oss_id != oss.pk:
raise serializers.ValidationError({
'parent': msg.parentNotInOSS()
})
for operation in attrs['children_operations']:
if operation.oss_id != oss.pk:
raise serializers.ValidationError({
'children_operations': msg.childNotInOSS()
})
for block in attrs['children_blocks']:
if block.oss_id != oss.pk:
raise serializers.ValidationError({
'children_blocks': msg.childNotInOSS()
})
return attrs
2025-04-20 18:06:18 +03:00
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'])
block = cast(Block, attrs['target'])
if block.oss_id != oss.pk:
raise serializers.ValidationError({
'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
class CreateOperationSerializer(serializers.Serializer):
2024-07-19 19:28:55 +03:00
''' Serializer: Operation creation. '''
class CreateOperationData(serializers.ModelSerializer):
2024-07-19 19:28:55 +03:00
''' Serializer: Operation creation data. '''
alias = serializers.CharField()
operation_type = serializers.ChoiceField(OperationType.choices)
2024-07-19 19:28:55 +03:00
class Meta:
''' serializer metadata. '''
model = Operation
fields = \
'alias', 'operation_type', 'title', \
2025-04-06 13:28:00 +03:00
'description', 'result', 'parent'
layout = LayoutSerializer()
item_data = CreateOperationData()
2025-04-14 23:02:35 +03:00
position_x = serializers.FloatField()
position_y = serializers.FloatField()
2025-04-06 13:28:00 +03:00
create_schema = serializers.BooleanField(default=False, required=False)
2024-08-06 23:13:57 +03:00
arguments = PKField(many=True, queryset=Operation.objects.all().only('pk'), required=False)
2024-07-29 22:30:24 +03:00
2025-04-14 23:02:35 +03:00
def validate(self, attrs):
oss = cast(LibraryItem, self.context['oss'])
if 'parent' in attrs['item_data'] and \
attrs['item_data']['parent'] is not None and \
attrs['item_data']['parent'].oss_id != oss.pk:
raise serializers.ValidationError({
'parent': msg.parentNotInOSS()
})
if 'arguments' not in attrs:
return attrs
for operation in attrs['arguments']:
if operation.oss_id != oss.pk:
raise serializers.ValidationError({
'arguments': msg.operationNotInOSS()
2025-04-14 23:02:35 +03:00
})
return attrs
class UpdateOperationSerializer(serializers.Serializer):
2024-08-10 11:41:28 +03:00
''' Serializer: Operation update. '''
class UpdateOperationData(serializers.ModelSerializer):
2024-08-10 11:41:28 +03:00
''' Serializer: Operation update data. '''
2024-07-29 22:30:24 +03:00
class Meta:
''' serializer metadata. '''
model = Operation
fields = 'alias', 'title', 'description', 'parent'
2024-07-29 22:30:24 +03:00
layout = LayoutSerializer(required=False)
2024-07-29 22:30:24 +03:00
target = PKField(many=False, queryset=Operation.objects.all())
item_data = UpdateOperationData()
2024-08-06 23:13:57 +03:00
arguments = PKField(many=True, queryset=Operation.objects.all().only('oss_id', 'result_id'), required=False)
2024-07-29 22:30:24 +03:00
substitutions = serializers.ListField(
child=SubstitutionSerializerBase(),
required=False
)
def validate(self, attrs):
2025-04-14 23:02:35 +03:00
oss = cast(LibraryItem, self.context['oss'])
target = cast(Block, attrs['target'])
if target.oss_id != oss.pk:
raise serializers.ValidationError({
'target': msg.operationNotInOSS()
})
2025-04-21 20:35:40 +03:00
if 'parent' in attrs['item_data'] and \
attrs['item_data']['parent'] is not None and \
attrs['item_data']['parent'].oss_id != oss.pk:
2025-04-14 23:02:35 +03:00
raise serializers.ValidationError({
'parent': msg.parentNotInOSS()
})
2024-07-29 22:30:24 +03:00
if 'arguments' not in attrs:
2025-04-14 23:02:35 +03:00
if 'substitutions' in attrs:
raise serializers.ValidationError({
'arguments': msg.missingArguments()
})
2024-07-29 22:30:24 +03:00
return attrs
for operation in attrs['arguments']:
2024-08-01 00:35:49 +03:00
if operation.oss_id != oss.pk:
2024-07-29 22:30:24 +03:00
raise serializers.ValidationError({
'arguments': msg.operationNotInOSS()
2024-07-29 22:30:24 +03:00
})
if 'substitutions' not in attrs:
return attrs
schemas = [arg.result_id for arg in attrs['arguments'] if arg.result is not None]
substitutions = attrs['substitutions']
to_delete = {x['original'].pk for x in substitutions}
2024-07-29 22:30:24 +03:00
deleted = set()
for item in substitutions:
2024-07-29 22:30:24 +03:00
original_cst = cast(Constituenta, item['original'])
substitution_cst = cast(Constituenta, item['substitution'])
if original_cst.schema_id not in schemas:
2024-07-29 22:30:24 +03:00
raise serializers.ValidationError({
f'{original_cst.pk}': msg.constituentaNotFromOperation()
2024-07-29 22:30:24 +03:00
})
if substitution_cst.schema_id not in schemas:
2024-07-29 22:30:24 +03:00
raise serializers.ValidationError({
f'{substitution_cst.pk}': msg.constituentaNotFromOperation()
2024-07-29 22:30:24 +03:00
})
if original_cst.pk in deleted or substitution_cst.pk in to_delete:
2024-07-29 22:30:24 +03:00
raise serializers.ValidationError({
f'{original_cst.pk}': msg.substituteDouble(original_cst.alias)
2024-07-29 22:30:24 +03:00
})
2024-08-01 00:35:49 +03:00
if original_cst.schema_id == substitution_cst.schema_id:
2024-07-29 22:30:24 +03:00
raise serializers.ValidationError({
'alias': msg.substituteTrivial(original_cst.alias)
})
deleted.add(original_cst.pk)
return attrs
2025-04-20 18:06:18 +03:00
class DeleteOperationSerializer(serializers.Serializer):
''' Serializer: Delete operation. '''
2025-04-06 13:28:00 +03:00
layout = LayoutSerializer()
target = PKField(many=False, queryset=Operation.objects.all().only('oss_id', 'result'))
keep_constituents = serializers.BooleanField(default=False, required=False)
delete_schema = serializers.BooleanField(default=False, required=False)
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
2025-04-20 18:06:18 +03:00
class TargetOperationSerializer(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
2024-07-28 21:29:46 +03:00
class SetOperationInputSerializer(serializers.Serializer):
''' Serializer: Set input schema for operation. '''
2025-04-06 13:28:00 +03:00
layout = LayoutSerializer()
2024-07-28 21:29:46 +03:00
target = PKField(many=False, queryset=Operation.objects.all())
input = PKField(
many=False,
queryset=LibraryItem.objects.filter(item_type=LibraryItemType.RSFORM),
allow_null=True,
default=None
)
def validate(self, attrs):
oss = cast(LibraryItem, self.context['oss'])
operation = cast(Operation, attrs['target'])
2024-08-01 00:35:49 +03:00
if oss and operation.oss_id != oss.pk:
2024-07-28 21:29:46 +03:00
raise serializers.ValidationError({
'target': msg.operationNotInOSS()
2024-07-28 21:29:46 +03:00
})
if operation.operation_type != OperationType.INPUT:
raise serializers.ValidationError({
'target': msg.operationNotInput(operation.alias)
2024-07-19 19:28:55 +03:00
})
return attrs
class OperationSchemaSerializer(serializers.ModelSerializer):
''' Serializer: Detailed data for OSS. '''
2025-04-06 15:47:40 +03:00
operations = serializers.ListField(
child=OperationSerializer()
)
2025-04-14 23:02:35 +03:00
blocks = serializers.ListField(
child=BlockSerializer()
)
2024-07-20 18:26:32 +03:00
arguments = serializers.ListField(
child=ArgumentSerializer()
)
2024-07-20 18:26:32 +03:00
substitutions = serializers.ListField(
child=SubstitutionExSerializer()
)
2025-04-06 13:28:00 +03:00
layout = LayoutSerializer()
class Meta:
''' serializer metadata. '''
model = LibraryItem
fields = '__all__'
def to_representation(self, instance: LibraryItem):
result = LibraryItemDetailsSerializer(instance).data
del result['versions']
oss = OperationSchema(instance)
2025-04-06 13:28:00 +03:00
result['layout'] = oss.layout().data
2025-04-06 15:47:40 +03:00
result['operations'] = []
2025-04-14 23:02:35 +03:00
result['blocks'] = []
result['arguments'] = []
result['substitutions'] = []
for operation in oss.operations().order_by('pk'):
2025-04-06 15:47:40 +03:00
result['operations'].append(OperationSerializer(operation).data)
2025-04-14 23:02:35 +03:00
for block in oss.blocks().order_by('pk'):
result['blocks'].append(BlockSerializer(block).data)
2024-09-04 14:35:03 +03:00
for argument in oss.arguments().order_by('order'):
2024-07-20 18:26:32 +03:00
result['arguments'].append(ArgumentSerializer(argument).data)
for substitution in oss.substitutions().values(
2024-07-19 19:28:55 +03:00
'operation',
'original',
'substitution',
2024-07-19 19:28:55 +03:00
original_alias=F('original__alias'),
original_term=F('original__term_resolved'),
substitution_alias=F('substitution__alias'),
substitution_term=F('substitution__term_resolved'),
).order_by('pk'):
result['substitutions'].append(substitution)
return result
2024-10-28 14:52:30 +03:00
class RelocateConstituentsSerializer(serializers.Serializer):
''' Serializer: Relocate constituents. '''
destination = PKField(
many=False,
queryset=LibraryItem.objects.all().only('id')
)
items = PKField(
many=True,
allow_empty=False,
queryset=Constituenta.objects.all()
)
def validate(self, attrs):
attrs['destination'] = attrs['destination'].id
attrs['source'] = attrs['items'][0].schema_id
# TODO: check permissions for editing source and destination
if attrs['source'] == attrs['destination']:
raise serializers.ValidationError({
'destination': msg.sourceEqualDestination()
})
for cst in attrs['items']:
if cst.schema_id != attrs['source']:
raise serializers.ValidationError({
f'{cst.pk}': msg.constituentaNotInRSform(attrs['items'][0].schema.title)
})
if Inheritance.objects.filter(child__in=attrs['items']).exists():
raise serializers.ValidationError({
'items': msg.RelocatingInherited()
})
oss = LibraryItem.objects \
.filter(operations__result_id=attrs['destination']) \
.filter(operations__result_id=attrs['source']).only('id')
if not oss.exists():
raise serializers.ValidationError({
'destination': msg.schemasNotConnected()
})
attrs['oss'] = oss[0].pk
if Argument.objects.filter(
operation__result_id=attrs['destination'],
argument__result_id=attrs['source']
).exists():
attrs['move_down'] = True
elif Argument.objects.filter(
operation__result_id=attrs['source'],
argument__result_id=attrs['destination']
).exists():
attrs['move_down'] = False
else:
raise serializers.ValidationError({
'destination': msg.schemasNotConnected()
})
return attrs