F: Implement Operation edit
This commit is contained in:
parent
d3213211b5
commit
0a7cfa1375
|
@ -7,10 +7,14 @@ from django.db.models import (
|
|||
FloatField,
|
||||
ForeignKey,
|
||||
Model,
|
||||
QuerySet,
|
||||
TextChoices,
|
||||
TextField
|
||||
)
|
||||
|
||||
from .Argument import Argument
|
||||
from .Substitution import Substitution
|
||||
|
||||
|
||||
class OperationType(TextChoices):
|
||||
''' Type of operation. '''
|
||||
|
@ -74,3 +78,11 @@ class Operation(Model):
|
|||
|
||||
def __str__(self) -> str:
|
||||
return f'Операция {self.alias}'
|
||||
|
||||
def getArguments(self) -> QuerySet[Argument]:
|
||||
''' Operation arguments. '''
|
||||
return Argument.objects.filter(operation=self)
|
||||
|
||||
def getSubstitutions(self) -> QuerySet[Substitution]:
|
||||
''' Operation substitutions. '''
|
||||
return Substitution.objects.filter(operation=self)
|
||||
|
|
|
@ -100,32 +100,50 @@ class OperationSchema:
|
|||
self.save()
|
||||
|
||||
@transaction.atomic
|
||||
def add_argument(self, operation: Operation, argument: Operation) -> Optional[Argument]:
|
||||
''' Add Argument to operation. '''
|
||||
if Argument.objects.filter(operation=operation, argument=argument).exists():
|
||||
return None
|
||||
result = Argument.objects.create(operation=operation, argument=argument)
|
||||
self.save()
|
||||
return result
|
||||
|
||||
@transaction.atomic
|
||||
def clear_arguments(self, target: Operation):
|
||||
''' Clear all arguments for operation. '''
|
||||
if not Argument.objects.filter(operation=target).exists():
|
||||
def set_arguments(self, operation: Operation, arguments: list[Operation]):
|
||||
''' Set arguments to operation. '''
|
||||
processed: list[Operation] = []
|
||||
changed = False
|
||||
for current in operation.getArguments():
|
||||
if current.argument not in arguments:
|
||||
changed = True
|
||||
current.delete()
|
||||
else:
|
||||
processed.append(current.argument)
|
||||
for arg in arguments:
|
||||
if arg not in processed:
|
||||
changed = True
|
||||
processed.append(arg)
|
||||
Argument.objects.create(operation=operation, argument=arg)
|
||||
if not changed:
|
||||
return
|
||||
|
||||
Argument.objects.filter(operation=target).delete()
|
||||
Substitution.objects.filter(operation=target).delete()
|
||||
|
||||
# trigger on_change effects
|
||||
|
||||
self.save()
|
||||
|
||||
@transaction.atomic
|
||||
def set_substitutions(self, target: Operation, substitutes: list[dict]):
|
||||
''' Clear all arguments for operation. '''
|
||||
Substitution.objects.filter(operation=target).delete()
|
||||
processed: list[dict] = []
|
||||
changed = False
|
||||
|
||||
for current in target.getSubstitutions():
|
||||
subs = [
|
||||
x for x in substitutes
|
||||
if x['original'] == current.original and x['substitution'] == current.substitution
|
||||
]
|
||||
if len(subs) == 0:
|
||||
changed = True
|
||||
current.delete()
|
||||
continue
|
||||
if current.transfer_term != subs[0]['transfer_term']:
|
||||
current.transfer_term = subs[0]['transfer_term']
|
||||
current.save()
|
||||
continue
|
||||
processed.append(subs[0])
|
||||
|
||||
for sub in substitutes:
|
||||
if sub not in processed:
|
||||
changed = True
|
||||
Substitution.objects.create(
|
||||
operation=target,
|
||||
original=sub['original'],
|
||||
|
@ -133,6 +151,8 @@ class OperationSchema:
|
|||
transfer_term=sub['transfer_term']
|
||||
)
|
||||
|
||||
if not changed:
|
||||
return
|
||||
# trigger on_change effects
|
||||
|
||||
self.save()
|
||||
|
|
|
@ -7,6 +7,7 @@ from .data_access import (
|
|||
OperationSchemaSerializer,
|
||||
OperationSerializer,
|
||||
OperationTargetSerializer,
|
||||
OperationUpdateSerializer,
|
||||
SetOperationInputSerializer
|
||||
)
|
||||
from .responses import NewOperationResponse, NewSchemaResponse
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
''' Serializers for persistent data manipulation. '''
|
||||
import re
|
||||
from typing import cast
|
||||
|
||||
from django.db.models import F
|
||||
|
@ -7,6 +8,8 @@ from rest_framework.serializers import PrimaryKeyRelatedField as PKField
|
|||
|
||||
from apps.library.models import LibraryItem, LibraryItemType
|
||||
from apps.library.serializers import LibraryItemDetailsSerializer
|
||||
from apps.rsform.models import Constituenta
|
||||
from apps.rsform.serializers import SubstitutionSerializerBase
|
||||
from shared import messages as msg
|
||||
|
||||
from ..models import Argument, Operation, OperationSchema, OperationType
|
||||
|
@ -47,12 +50,75 @@ class OperationCreateSerializer(serializers.Serializer):
|
|||
create_schema = serializers.BooleanField(default=False, required=False)
|
||||
item_data = OperationData()
|
||||
arguments = PKField(many=True, queryset=Operation.objects.all(), required=False)
|
||||
|
||||
positions = serializers.ListField(
|
||||
child=OperationPositionSerializer(),
|
||||
default=[]
|
||||
)
|
||||
|
||||
|
||||
class OperationUpdateSerializer(serializers.Serializer):
|
||||
''' Serializer: Operation creation. '''
|
||||
class OperationData(serializers.ModelSerializer):
|
||||
''' Serializer: Operation creation data. '''
|
||||
class Meta:
|
||||
''' serializer metadata. '''
|
||||
model = Operation
|
||||
fields = 'alias', 'title', 'sync_text', 'comment'
|
||||
|
||||
target = PKField(many=False, queryset=Operation.objects.all())
|
||||
item_data = OperationData()
|
||||
arguments = PKField(many=True, queryset=Operation.objects.all(), required=False)
|
||||
substitutions = serializers.ListField(
|
||||
child=SubstitutionSerializerBase(),
|
||||
required=False
|
||||
)
|
||||
|
||||
positions = serializers.ListField(
|
||||
child=OperationPositionSerializer(),
|
||||
default=[]
|
||||
)
|
||||
|
||||
def validate(self, attrs):
|
||||
if 'arguments' not in attrs:
|
||||
return attrs
|
||||
|
||||
oss = cast(LibraryItem, self.context['oss'])
|
||||
for operation in attrs['arguments']:
|
||||
if operation.oss != oss:
|
||||
raise serializers.ValidationError({
|
||||
'arguments': msg.operationNotInOSS(oss.title)
|
||||
})
|
||||
|
||||
if 'substitutions' not in attrs:
|
||||
return attrs
|
||||
schemas = [arg.result.pk for arg in attrs['arguments'] if arg.result is not None]
|
||||
deleted = set()
|
||||
for item in attrs['substitutions']:
|
||||
original_cst = cast(Constituenta, item['original'])
|
||||
substitution_cst = cast(Constituenta, item['substitution'])
|
||||
if original_cst.schema.pk not in schemas:
|
||||
raise serializers.ValidationError({
|
||||
f'{original_cst.id}': msg.constituentaNotFromOperation()
|
||||
})
|
||||
if substitution_cst.schema.pk not in schemas:
|
||||
raise serializers.ValidationError({
|
||||
f'{substitution_cst.id}': msg.constituentaNotFromOperation()
|
||||
})
|
||||
if original_cst.pk in deleted:
|
||||
raise serializers.ValidationError({
|
||||
f'{original_cst.id}': msg.substituteDouble(original_cst.alias)
|
||||
})
|
||||
if original_cst.schema == substitution_cst.schema:
|
||||
raise serializers.ValidationError({
|
||||
'alias': msg.substituteTrivial(original_cst.alias)
|
||||
})
|
||||
deleted.add(original_cst.pk)
|
||||
return attrs
|
||||
|
||||
|
||||
|
||||
|
||||
class OperationTargetSerializer(serializers.Serializer):
|
||||
''' Serializer: Delete operation. '''
|
||||
target = PKField(many=False, queryset=Operation.objects.all())
|
||||
|
|
|
@ -41,8 +41,7 @@ class TestOssViewset(EndpointTester):
|
|||
alias='3',
|
||||
operation_type=OperationType.SYNTHESIS
|
||||
)
|
||||
self.owned.add_argument(self.operation3, self.operation1)
|
||||
self.owned.add_argument(self.operation3, self.operation2)
|
||||
self.owned.set_arguments(self.operation3, [self.operation1, self.operation2])
|
||||
self.owned.set_substitutions(self.operation3, [{
|
||||
'original': self.ks1x1,
|
||||
'substitution': self.ks2x1,
|
||||
|
@ -344,3 +343,76 @@ class TestOssViewset(EndpointTester):
|
|||
self.operation2.refresh_from_db()
|
||||
self.assertEqual(self.operation2.sync_text, True)
|
||||
self.assertEqual(self.operation2.result, self.ks2.model)
|
||||
|
||||
@decl_endpoint('/api/oss/{item}/update-operation', method='patch')
|
||||
def test_update_operation(self):
|
||||
self.populateData()
|
||||
self.executeBadData(item=self.owned_id)
|
||||
|
||||
ks3 = RSForm.create(alias='KS3', title='Test3', owner=self.user)
|
||||
ks3x1 = ks3.insert_new('X1', term_resolved='X1_1')
|
||||
|
||||
data = {
|
||||
'target': self.operation3.pk,
|
||||
'item_data': {
|
||||
'alias': 'Test3 mod',
|
||||
'title': 'Test title mod',
|
||||
'comment': 'Comment mod',
|
||||
'sync_text': True
|
||||
},
|
||||
'positions': [],
|
||||
'arguments': [self.operation1.pk, self.operation2.pk],
|
||||
'substitutions': [
|
||||
{
|
||||
'original': self.ks1x1.pk,
|
||||
'substitution': ks3x1.pk,
|
||||
'transfer_term': False
|
||||
}
|
||||
]
|
||||
}
|
||||
self.executeBadData(data=data)
|
||||
|
||||
data['substitutions'][0]['substitution'] = self.ks2x1.pk
|
||||
self.toggle_admin(True)
|
||||
self.executeBadData(data=data, item=self.unowned_id)
|
||||
self.logout()
|
||||
self.executeForbidden(data=data, item=self.owned_id)
|
||||
|
||||
self.login()
|
||||
response = self.executeOK(data=data)
|
||||
self.operation3.refresh_from_db()
|
||||
self.assertEqual(self.operation3.sync_text, data['item_data']['sync_text'])
|
||||
self.assertEqual(self.operation3.alias, data['item_data']['alias'])
|
||||
self.assertEqual(self.operation3.title, data['item_data']['title'])
|
||||
self.assertEqual(self.operation3.comment, data['item_data']['comment'])
|
||||
self.assertEqual(set([argument.pk for argument in self.operation3.getArguments()]), set(data['arguments']))
|
||||
sub = self.operation3.getSubstitutions()[0]
|
||||
self.assertEqual(sub.original.pk, data['substitutions'][0]['original'])
|
||||
self.assertEqual(sub.substitution.pk, data['substitutions'][0]['substitution'])
|
||||
self.assertEqual(sub.transfer_term, data['substitutions'][0]['transfer_term'])
|
||||
|
||||
@decl_endpoint('/api/oss/{item}/update-operation', method='patch')
|
||||
def test_update_operation_sync(self):
|
||||
self.populateData()
|
||||
self.executeBadData(item=self.owned_id)
|
||||
|
||||
data = {
|
||||
'target': self.operation1.pk,
|
||||
'item_data': {
|
||||
'alias': 'Test3 mod',
|
||||
'title': 'Test title mod',
|
||||
'comment': 'Comment mod',
|
||||
'sync_text': True
|
||||
},
|
||||
'positions': [],
|
||||
}
|
||||
|
||||
response = self.executeOK(data=data)
|
||||
self.operation1.refresh_from_db()
|
||||
self.assertEqual(self.operation1.sync_text, data['item_data']['sync_text'])
|
||||
self.assertEqual(self.operation1.alias, data['item_data']['alias'])
|
||||
self.assertEqual(self.operation1.title, data['item_data']['title'])
|
||||
self.assertEqual(self.operation1.comment, data['item_data']['comment'])
|
||||
self.assertEqual(self.operation1.result.alias, data['item_data']['alias'])
|
||||
self.assertEqual(self.operation1.result.title, data['item_data']['title'])
|
||||
self.assertEqual(self.operation1.result.comment, data['item_data']['comment'])
|
||||
|
|
|
@ -36,7 +36,9 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
|
|||
'delete_operation',
|
||||
'update_positions',
|
||||
'create_input',
|
||||
'set_input'
|
||||
'set_input',
|
||||
'update_operation',
|
||||
'execute_operation',
|
||||
]:
|
||||
permission_list = [permissions.ItemEditor]
|
||||
elif self.action in ['details']:
|
||||
|
@ -117,8 +119,10 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
|
|||
data['result'] = schema
|
||||
new_operation = oss.create_operation(**data)
|
||||
if new_operation.operation_type != m.OperationType.INPUT and 'arguments' in serializer.validated_data:
|
||||
for argument in serializer.validated_data['arguments']:
|
||||
oss.add_argument(operation=new_operation, argument=argument)
|
||||
oss.set_arguments(
|
||||
operation=new_operation,
|
||||
arguments=serializer.validated_data['arguments']
|
||||
)
|
||||
|
||||
oss.refresh_from_db()
|
||||
return Response(
|
||||
|
@ -257,3 +261,96 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
|
|||
status=c.HTTP_200_OK,
|
||||
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):
|
||||
''' 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_positions(serializer.validated_data['positions'])
|
||||
operation.alias = serializer.validated_data['item_data']['alias']
|
||||
operation.title = serializer.validated_data['item_data']['title']
|
||||
operation.comment = serializer.validated_data['item_data']['comment']
|
||||
operation.sync_text = serializer.validated_data['item_data']['sync_text']
|
||||
operation.save()
|
||||
|
||||
if operation.sync_text and operation.result is not None:
|
||||
can_edit = permissions.can_edit_item(request.user, operation.result)
|
||||
if can_edit:
|
||||
operation.result.alias = operation.alias
|
||||
operation.result.title = operation.title
|
||||
operation.result.comment = operation.comment
|
||||
operation.result.save()
|
||||
if 'arguments' in serializer.validated_data:
|
||||
oss.set_arguments(operation, serializer.validated_data['arguments'])
|
||||
if 'substitutions' in serializer.validated_data:
|
||||
oss.set_substitutions(operation, serializer.validated_data['substitutions'])
|
||||
return Response(
|
||||
status=c.HTTP_200_OK,
|
||||
data=s.OperationSchemaSerializer(oss.model).data
|
||||
)
|
||||
|
||||
@extend_schema(
|
||||
summary='execute operation',
|
||||
tags=['OSS'],
|
||||
request=s.OperationTargetSerializer(),
|
||||
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')
|
||||
def execute_operation(self, request: Request, pk):
|
||||
''' Execute operation. '''
|
||||
serializer = s.OperationTargetSerializer(
|
||||
data=request.data,
|
||||
context={'oss': self.get_object()}
|
||||
)
|
||||
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)
|
||||
})
|
||||
|
||||
oss = m.OperationSchema(self.get_object())
|
||||
# with transaction.atomic():
|
||||
# oss.update_positions(serializer.validated_data['positions'])
|
||||
# operation.result.refresh_from_db()
|
||||
# operation.result.title = operation.title
|
||||
# operation.result.comment = operation.comment
|
||||
# operation.result.alias = operation.alias
|
||||
# operation.result.save()
|
||||
|
||||
# update arguments
|
||||
|
||||
oss.refresh_from_db()
|
||||
return Response(
|
||||
status=c.HTTP_200_OK,
|
||||
data=s.OperationSchemaSerializer(oss.model).data
|
||||
)
|
||||
|
|
|
@ -19,7 +19,8 @@ from .data_access import (
|
|||
CstTargetSerializer,
|
||||
InlineSynthesisSerializer,
|
||||
RSFormParseSerializer,
|
||||
RSFormSerializer
|
||||
RSFormSerializer,
|
||||
SubstitutionSerializerBase
|
||||
)
|
||||
from .io_files import FileSerializer, RSFormTRSSerializer, RSFormUploadSerializer
|
||||
from .io_pyconcept import PyConceptAdapter
|
||||
|
|
|
@ -270,7 +270,7 @@ class CstMoveSerializer(CstListSerializer):
|
|||
move_to = serializers.IntegerField()
|
||||
|
||||
|
||||
class CstSubstituteSerializerBase(serializers.Serializer):
|
||||
class SubstitutionSerializerBase(serializers.Serializer):
|
||||
''' Serializer: Basic substitution. '''
|
||||
original = PKField(many=False, queryset=Constituenta.objects.all())
|
||||
substitution = PKField(many=False, queryset=Constituenta.objects.all())
|
||||
|
@ -280,7 +280,7 @@ class CstSubstituteSerializerBase(serializers.Serializer):
|
|||
class CstSubstituteSerializer(serializers.Serializer):
|
||||
''' Serializer: Constituenta substitution. '''
|
||||
substitutions = serializers.ListField(
|
||||
child=CstSubstituteSerializerBase(),
|
||||
child=SubstitutionSerializerBase(),
|
||||
min_length=1
|
||||
)
|
||||
|
||||
|
@ -316,7 +316,7 @@ class InlineSynthesisSerializer(serializers.Serializer):
|
|||
source = PKField(many=False, queryset=LibraryItem.objects.all()) # type: ignore
|
||||
items = PKField(many=True, queryset=Constituenta.objects.all())
|
||||
substitutions = serializers.ListField(
|
||||
child=CstSubstituteSerializerBase()
|
||||
child=SubstitutionSerializerBase()
|
||||
)
|
||||
|
||||
def validate(self, attrs):
|
||||
|
|
|
@ -6,8 +6,12 @@ def constituentaNotInRSform(title: str):
|
|||
return f'Конституента не принадлежит схеме: {title}'
|
||||
|
||||
|
||||
def constituentaNotFromOperation():
|
||||
return f'Конституента не соответствую аргументам операции'
|
||||
|
||||
|
||||
def operationNotInOSS(title: str):
|
||||
return f'Операция не принадлежит схеме: {title}'
|
||||
return f'Операция не принадлежит ОСС: {title}'
|
||||
|
||||
|
||||
def substitutionNotInList():
|
||||
|
@ -22,6 +26,10 @@ def operationNotInput(title: str):
|
|||
return f'Операция не является Загрузкой: {title}'
|
||||
|
||||
|
||||
def operationNotSynthesis(title: str):
|
||||
return f'Операция не является Синтезом: {title}'
|
||||
|
||||
|
||||
def operationResultNotEmpty(title: str):
|
||||
return f'Результат операции не пуст: {title}'
|
||||
|
||||
|
|
|
@ -32,6 +32,24 @@ def _extract_item(obj: Any) -> LibraryItem:
|
|||
})
|
||||
|
||||
|
||||
def can_edit_item(user, obj: Any) -> bool:
|
||||
if user.is_anonymous:
|
||||
return False
|
||||
if hasattr(user, 'is_staff') and user.is_staff:
|
||||
return True
|
||||
|
||||
item = _extract_item(obj)
|
||||
if item.owner == user:
|
||||
return True
|
||||
|
||||
if Editor.objects.filter(
|
||||
item=item,
|
||||
editor=cast(User, user)
|
||||
).exists() and item.access_policy != AccessPolicy.PRIVATE:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
class GlobalAdmin(_Base):
|
||||
''' Item permission: Admin or higher. '''
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ import {
|
|||
IOperationCreatedResponse,
|
||||
IOperationSchemaData,
|
||||
IOperationSetInputData,
|
||||
IOperationUpdateData,
|
||||
IPositionsData,
|
||||
ITargetOperation
|
||||
} from '@/models/oss';
|
||||
|
@ -58,3 +59,17 @@ export function patchSetInput(oss: string, request: FrontExchange<IOperationSetI
|
|||
request: request
|
||||
});
|
||||
}
|
||||
|
||||
export function patchUpdateOperation(oss: string, request: FrontExchange<IOperationUpdateData, IOperationSchemaData>) {
|
||||
AxiosPatch({
|
||||
endpoint: `/api/oss/${oss}/update-operation`,
|
||||
request: request
|
||||
});
|
||||
}
|
||||
|
||||
export function postExecuteOperation(oss: string, request: FrontExchange<ITargetOperation, IOperationSchemaData>) {
|
||||
AxiosPost({
|
||||
endpoint: `/api/oss/${oss}/execute-operation`,
|
||||
request: request
|
||||
});
|
||||
}
|
||||
|
|
|
@ -16,8 +16,10 @@ import {
|
|||
patchCreateInput,
|
||||
patchDeleteOperation,
|
||||
patchSetInput,
|
||||
patchUpdateOperation,
|
||||
patchUpdatePositions,
|
||||
postCreateOperation
|
||||
postCreateOperation,
|
||||
postExecuteOperation
|
||||
} from '@/backend/oss';
|
||||
import { type ErrorData } from '@/components/info/InfoError';
|
||||
import { AccessPolicy, ILibraryItem } from '@/models/library';
|
||||
|
@ -28,6 +30,7 @@ import {
|
|||
IOperationSchema,
|
||||
IOperationSchemaData,
|
||||
IOperationSetInputData,
|
||||
IOperationUpdateData,
|
||||
IPositionsData,
|
||||
ITargetOperation
|
||||
} from '@/models/oss';
|
||||
|
@ -63,6 +66,8 @@ interface IOssContext {
|
|||
deleteOperation: (data: ITargetOperation, callback?: () => void) => void;
|
||||
createInput: (data: ITargetOperation, callback?: DataCallback<ILibraryItem>) => void;
|
||||
setInput: (data: IOperationSetInputData, callback?: () => void) => void;
|
||||
updateOperation: (data: IOperationUpdateData, callback?: () => void) => void;
|
||||
executeOperation: (data: ITargetOperation, callback?: () => void) => void;
|
||||
}
|
||||
|
||||
const OssContext = createContext<IOssContext | null>(null);
|
||||
|
@ -362,6 +367,50 @@ export const OssState = ({ itemID, children }: OssStateProps) => {
|
|||
[itemID, schema, library]
|
||||
);
|
||||
|
||||
const updateOperation = useCallback(
|
||||
(data: IOperationUpdateData, callback?: () => void) => {
|
||||
if (!schema) {
|
||||
return;
|
||||
}
|
||||
setProcessingError(undefined);
|
||||
patchUpdateOperation(itemID, {
|
||||
data: data,
|
||||
showError: true,
|
||||
setLoading: setProcessing,
|
||||
onError: setProcessingError,
|
||||
onSuccess: newData => {
|
||||
library.setGlobalOSS(newData);
|
||||
library.reloadItems(() => {
|
||||
if (callback) callback();
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
[itemID, schema, library]
|
||||
);
|
||||
|
||||
const executeOperation = useCallback(
|
||||
(data: ITargetOperation, callback?: () => void) => {
|
||||
if (!schema) {
|
||||
return;
|
||||
}
|
||||
setProcessingError(undefined);
|
||||
postExecuteOperation(itemID, {
|
||||
data: data,
|
||||
showError: true,
|
||||
setLoading: setProcessing,
|
||||
onError: setProcessingError,
|
||||
onSuccess: newData => {
|
||||
library.setGlobalOSS(newData);
|
||||
library.reloadItems(() => {
|
||||
if (callback) callback();
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
[itemID, schema, library]
|
||||
);
|
||||
|
||||
return (
|
||||
<OssContext.Provider
|
||||
value={{
|
||||
|
@ -386,7 +435,9 @@ export const OssState = ({ itemID, children }: OssStateProps) => {
|
|||
createOperation,
|
||||
deleteOperation,
|
||||
createInput,
|
||||
setInput
|
||||
setInput,
|
||||
updateOperation,
|
||||
executeOperation
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
|
|
@ -10,7 +10,14 @@ import Overlay from '@/components/ui/Overlay';
|
|||
import TabLabel from '@/components/ui/TabLabel';
|
||||
import useRSFormCache from '@/hooks/useRSFormCache';
|
||||
import { HelpTopic } from '@/models/miscellaneous';
|
||||
import { ICstSubstitute, IOperation, IOperationSchema, OperationID, OperationType } from '@/models/oss';
|
||||
import {
|
||||
ICstSubstitute,
|
||||
IOperation,
|
||||
IOperationSchema,
|
||||
IOperationUpdateData,
|
||||
OperationID,
|
||||
OperationType
|
||||
} from '@/models/oss';
|
||||
import { PARAMETER } from '@/utils/constants';
|
||||
|
||||
import TabArguments from './TabArguments';
|
||||
|
@ -21,8 +28,7 @@ interface DlgEditOperationProps {
|
|||
hideWindow: () => void;
|
||||
oss: IOperationSchema;
|
||||
target: IOperation;
|
||||
// onSubmit: (data: IOperationEditData) => void;
|
||||
onSubmit: () => void;
|
||||
onSubmit: (data: IOperationUpdateData) => void;
|
||||
}
|
||||
|
||||
export enum TabID {
|
||||
|
@ -56,22 +62,19 @@ function DlgEditOperation({ hideWindow, oss, target, onSubmit }: DlgEditOperatio
|
|||
}, [schemasIDs]);
|
||||
|
||||
const handleSubmit = () => {
|
||||
// const data: IOperationCreateData = {
|
||||
// item_data: {
|
||||
// position_x: insertPosition.x,
|
||||
// position_y: insertPosition.y,
|
||||
// alias: alias,
|
||||
// title: title,
|
||||
// comment: comment,
|
||||
// sync_text: activeTab === TabID.INPUT ? syncText : true,
|
||||
// operation_type: activeTab === TabID.INPUT ? OperationType.INPUT : OperationType.SYNTHESIS,
|
||||
// result: activeTab === TabID.INPUT ? attachedID ?? null : null
|
||||
// },
|
||||
// positions: positions,
|
||||
// arguments: activeTab === TabID.INPUT ? undefined : inputs.length > 0 ? inputs : undefined,
|
||||
// create_schema: createSchema
|
||||
// };
|
||||
onSubmit();
|
||||
const data: IOperationUpdateData = {
|
||||
target: target.id,
|
||||
item_data: {
|
||||
alias: alias,
|
||||
title: title,
|
||||
comment: comment,
|
||||
sync_text: syncText
|
||||
},
|
||||
positions: [],
|
||||
arguments: target.operation_type !== OperationType.SYNTHESIS ? undefined : inputs,
|
||||
substitutions: target.operation_type !== OperationType.SYNTHESIS ? undefined : substitutions
|
||||
};
|
||||
onSubmit(data);
|
||||
};
|
||||
|
||||
const cardPanel = useMemo(
|
||||
|
|
|
@ -69,6 +69,15 @@ export interface IOperationCreateData extends IPositionsData {
|
|||
create_schema: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents {@link IOperation} data, used in update process.
|
||||
*/
|
||||
export interface IOperationUpdateData extends ITargetOperation {
|
||||
item_data: Pick<IOperation, 'alias' | 'title' | 'comment' | 'sync_text'>;
|
||||
arguments: OperationID[] | undefined;
|
||||
substitutions: ICstSubstitute[] | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents {@link IOperation} data, used in setInput process.
|
||||
*/
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
'use client';
|
||||
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
import { IconConnect, IconDestroy, IconEdit2, IconExecute, IconNewItem, IconRSForm } from '@/components/Icons';
|
||||
import Dropdown from '@/components/ui/Dropdown';
|
||||
|
@ -25,6 +24,7 @@ interface NodeContextMenuProps extends ContextMenuData {
|
|||
onCreateInput: (target: OperationID) => void;
|
||||
onEditSchema: (target: OperationID) => void;
|
||||
onEditOperation: (target: OperationID) => void;
|
||||
onRunOperation: (target: OperationID) => void;
|
||||
}
|
||||
|
||||
function NodeContextMenu({
|
||||
|
@ -35,7 +35,8 @@ function NodeContextMenu({
|
|||
onDelete,
|
||||
onCreateInput,
|
||||
onEditSchema,
|
||||
onEditOperation
|
||||
onEditOperation,
|
||||
onRunOperation
|
||||
}: NodeContextMenuProps) {
|
||||
const controller = useOssEdit();
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
@ -95,8 +96,8 @@ function NodeContextMenu({
|
|||
};
|
||||
|
||||
const handleRunSynthesis = () => {
|
||||
toast.error('Not implemented');
|
||||
handleHide();
|
||||
onRunOperation(operation.id);
|
||||
};
|
||||
|
||||
return (
|
||||
|
|
|
@ -162,6 +162,13 @@ function OssFlow({ isModified, setIsModified }: OssFlowProps) {
|
|||
[controller, getPositions]
|
||||
);
|
||||
|
||||
const handleRunOperation = useCallback(
|
||||
(target: OperationID) => {
|
||||
controller.runOperation(target, getPositions());
|
||||
},
|
||||
[controller, getPositions]
|
||||
);
|
||||
|
||||
const handleFitView = useCallback(() => {
|
||||
flow.fitView({ duration: PARAMETER.zoomDuration });
|
||||
}, [flow]);
|
||||
|
@ -227,6 +234,17 @@ function OssFlow({ isModified, setIsModified }: OssFlowProps) {
|
|||
handleContextMenuHide();
|
||||
}, [handleContextMenuHide]);
|
||||
|
||||
const handleNodeClick = useCallback(
|
||||
(event: CProps.EventMouse, node: OssNode) => {
|
||||
if (event.ctrlKey || event.metaKey) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
handleEditOperation(Number(node.id));
|
||||
}
|
||||
},
|
||||
[handleEditOperation]
|
||||
);
|
||||
|
||||
function handleKeyDown(event: React.KeyboardEvent<HTMLDivElement>) {
|
||||
if (controller.isProcessing) {
|
||||
return;
|
||||
|
@ -266,6 +284,7 @@ function OssFlow({ isModified, setIsModified }: OssFlowProps) {
|
|||
edges={edges}
|
||||
onNodesChange={handleNodesChange}
|
||||
onEdgesChange={onEdgesChange}
|
||||
onNodeClick={handleNodeClick}
|
||||
fitView
|
||||
proOptions={{ hideAttribution: true }}
|
||||
nodeTypes={OssNodeTypes}
|
||||
|
@ -309,6 +328,7 @@ function OssFlow({ isModified, setIsModified }: OssFlowProps) {
|
|||
onCreateInput={handleCreateInput}
|
||||
onEditSchema={handleEditSchema}
|
||||
onEditOperation={handleEditOperation}
|
||||
onRunOperation={handleRunOperation}
|
||||
{...menuProps}
|
||||
/>
|
||||
) : null}
|
||||
|
|
|
@ -22,6 +22,7 @@ import {
|
|||
IOperationPosition,
|
||||
IOperationSchema,
|
||||
IOperationSetInputData,
|
||||
IOperationUpdateData,
|
||||
OperationID
|
||||
} from '@/models/oss';
|
||||
import { UserID, UserLevel } from '@/models/user';
|
||||
|
@ -55,6 +56,7 @@ export interface IOssEditContext {
|
|||
createInput: (target: OperationID, positions: IOperationPosition[]) => void;
|
||||
promptEditInput: (target: OperationID, positions: IOperationPosition[]) => void;
|
||||
promptEditOperation: (target: OperationID, positions: IOperationPosition[]) => void;
|
||||
runOperation: (target: OperationID, positions: IOperationPosition[]) => void;
|
||||
}
|
||||
|
||||
const OssEditContext = createContext<IOssEditContext | null>(null);
|
||||
|
@ -228,17 +230,13 @@ export const OssEditState = ({ selected, setSelected, children }: OssEditStatePr
|
|||
setShowEditOperation(true);
|
||||
}, []);
|
||||
|
||||
const handleEditOperation = useCallback(() => {
|
||||
// TODO: проверить наличие всех аргументов
|
||||
// TODO: проверить наличие синтеза
|
||||
// TODO: проверить полноту синтеза
|
||||
// TODO: проверить правильность синтеза
|
||||
// TODO: сохранить позиции
|
||||
// TODO: обновить схему
|
||||
// model.setInput(data, () => toast.success(information.changesSaved));
|
||||
|
||||
toast.error('Not implemented');
|
||||
}, []);
|
||||
const handleEditOperation = useCallback(
|
||||
(data: IOperationUpdateData) => {
|
||||
data.positions = positions;
|
||||
model.updateOperation(data, () => toast.success(information.changesSaved));
|
||||
},
|
||||
[model, positions]
|
||||
);
|
||||
|
||||
const deleteOperation = useCallback(
|
||||
(target: OperationID, positions: IOperationPosition[]) => {
|
||||
|
@ -281,6 +279,19 @@ export const OssEditState = ({ selected, setSelected, children }: OssEditStatePr
|
|||
[model, targetOperationID, positions]
|
||||
);
|
||||
|
||||
const runOperation = useCallback(
|
||||
(target: OperationID, positions: IOperationPosition[]) => {
|
||||
model.executeOperation(
|
||||
{
|
||||
target: target,
|
||||
positions: positions
|
||||
},
|
||||
() => toast.success(information.changesSaved)
|
||||
);
|
||||
},
|
||||
[model]
|
||||
);
|
||||
|
||||
return (
|
||||
<OssEditContext.Provider
|
||||
value={{
|
||||
|
@ -308,7 +319,8 @@ export const OssEditState = ({ selected, setSelected, children }: OssEditStatePr
|
|||
deleteOperation,
|
||||
createInput,
|
||||
promptEditInput,
|
||||
promptEditOperation
|
||||
promptEditOperation,
|
||||
runOperation
|
||||
}}
|
||||
>
|
||||
{model.schema ? (
|
||||
|
|
Loading…
Reference in New Issue
Block a user