F: Implementing backend for synthesis operation

This commit is contained in:
Ivan 2024-07-31 18:09:31 +03:00
parent 727c1b3ab6
commit 16a225a959
15 changed files with 221 additions and 133 deletions

View File

@ -432,7 +432,8 @@ disable=too-many-public-methods,
missing-function-docstring, missing-function-docstring,
attribute-defined-outside-init, attribute-defined-outside-init,
ungrouped-imports, ungrouped-imports,
abstract-method abstract-method,
fixme
# Enable the message, report, category or checker with the given id(s). You can # Enable the message, report, category or checker with the given id(s). You can
# either give multiple identifier separated by comma (,) or put this option # either give multiple identifier separated by comma (,) or put this option

View File

@ -5,10 +5,12 @@ from django.core.exceptions import ValidationError
from django.db import transaction from django.db import transaction
from django.db.models import QuerySet from django.db.models import QuerySet
from apps.library.models import LibraryItem, LibraryItemType from apps.library.models import Editor, LibraryItem, LibraryItemType
from apps.rsform.models import RSForm
from shared import messages as msg from shared import messages as msg
from .Argument import Argument from .Argument import Argument
from .Inheritance import Inheritance
from .Operation import Operation from .Operation import Operation
from .Substitution import Substitution from .Substitution import Substitution
@ -76,8 +78,8 @@ class OperationSchema:
''' Delete operation. ''' ''' Delete operation. '''
operation.delete() operation.delete()
# deal with attached schema # TODO: deal with attached schema
# trigger on_change effects # TODO: trigger on_change effects
self.save() self.save()
@ -86,16 +88,15 @@ class OperationSchema:
''' Set input schema for operation. ''' ''' Set input schema for operation. '''
if schema == target.result: if schema == target.result:
return return
if schema: target.result = schema
if schema is not None:
target.result = schema target.result = schema
target.alias = schema.alias target.alias = schema.alias
target.title = schema.title target.title = schema.title
target.comment = schema.comment target.comment = schema.comment
else:
target.result = None
target.save() target.save()
# trigger on_change effects # TODO: trigger on_change effects
self.save() self.save()
@ -117,7 +118,7 @@ class OperationSchema:
Argument.objects.create(operation=operation, argument=arg) Argument.objects.create(operation=operation, argument=arg)
if not changed: if not changed:
return return
# trigger on_change effects # TODO: trigger on_change effects
self.save() self.save()
@transaction.atomic @transaction.atomic
@ -148,6 +149,63 @@ class OperationSchema:
if not changed: if not changed:
return return
# trigger on_change effects # TODO: trigger on_change effects
self.save() self.save()
@transaction.atomic
def create_input(self, operation: Operation) -> RSForm:
''' Create input RSForm. '''
schema = RSForm.create(
owner=self.model.owner,
alias=operation.alias,
title=operation.title,
comment=operation.comment,
visible=False,
access_policy=self.model.access_policy,
location=self.model.location
)
Editor.set(schema.model, self.model.editors())
operation.result = schema.model
operation.save()
self.save()
return schema
@transaction.atomic
def execute_operation(self, operation: Operation) -> bool:
''' Execute target operation. '''
schemas: list[LibraryItem] = [arg.argument.result for arg in operation.getArguments()]
if None in schemas:
return False
substitutions = operation.getSubstitutions()
receiver = self.create_input(operation)
parents: dict = {}
children: dict = {}
for operand in schemas:
schema = RSForm(operand)
items = list(schema.constituents())
new_items = receiver.insert_copy(items)
for (i, cst) in enumerate(new_items):
parents[cst.pk] = items[i]
children[items[i].pk] = cst
for sub in substitutions:
original = children[sub.original.pk]
replacement = children[sub.substitution.pk]
receiver.substitute(original, replacement)
# TODO: remove duplicates from diamond
for cst in receiver.constituents():
parent = parents.get(cst.id)
assert parent is not None
Inheritance.objects.create(
child=cst,
parent=parent
)
receiver.restore_order()
receiver.reset_aliases()
self.save()
return True

View File

@ -92,8 +92,10 @@ class OperationUpdateSerializer(serializers.Serializer):
if 'substitutions' not in attrs: if 'substitutions' not in attrs:
return attrs return attrs
schemas = [arg.result.pk for arg in attrs['arguments'] if arg.result is not None] schemas = [arg.result.pk for arg in attrs['arguments'] if arg.result is not None]
substitutions = attrs['substitutions']
to_delete = {x['original'].pk for x in substitutions}
deleted = set() deleted = set()
for item in attrs['substitutions']: for item in substitutions:
original_cst = cast(Constituenta, item['original']) original_cst = cast(Constituenta, item['original'])
substitution_cst = cast(Constituenta, item['substitution']) substitution_cst = cast(Constituenta, item['substitution'])
if original_cst.schema.pk not in schemas: if original_cst.schema.pk not in schemas:
@ -104,7 +106,7 @@ class OperationUpdateSerializer(serializers.Serializer):
raise serializers.ValidationError({ raise serializers.ValidationError({
f'{substitution_cst.id}': msg.constituentaNotFromOperation() f'{substitution_cst.id}': msg.constituentaNotFromOperation()
}) })
if original_cst.pk in deleted: if original_cst.pk in deleted or substitution_cst.pk in to_delete:
raise serializers.ValidationError({ raise serializers.ValidationError({
f'{original_cst.id}': msg.substituteDouble(original_cst.alias) f'{original_cst.id}': msg.substituteDouble(original_cst.alias)
}) })

View File

@ -23,10 +23,27 @@ class TestOssViewset(EndpointTester):
def populateData(self): def populateData(self):
self.ks1 = RSForm.create(alias='KS1', title='Test1', owner=self.user) self.ks1 = RSForm.create(
self.ks1x1 = self.ks1.insert_new('X1', term_resolved='X1_1') alias='KS1',
self.ks2 = RSForm.create(alias='KS2', title='Test2', owner=self.user) title='Test1',
self.ks2x1 = self.ks2.insert_new('X2', term_resolved='X1_2') owner=self.user
)
self.ks1x1 = self.ks1.insert_new(
'X1',
term_raw='X1_1',
term_resolved='X1_1'
)
self.ks2 = RSForm.create(
alias='KS2',
title='Test2',
owner=self.user
)
self.ks2x1 = self.ks2.insert_new(
'X2',
term_raw='X1_2',
term_resolved='X1_2'
)
self.operation1 = self.owned.create_operation( self.operation1 = self.owned.create_operation(
alias='1', alias='1',
operation_type=OperationType.INPUT, operation_type=OperationType.INPUT,
@ -399,3 +416,61 @@ class TestOssViewset(EndpointTester):
self.assertEqual(self.operation1.result.alias, data['item_data']['alias']) 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.title, data['item_data']['title'])
self.assertEqual(self.operation1.result.comment, data['item_data']['comment']) self.assertEqual(self.operation1.result.comment, data['item_data']['comment'])
@decl_endpoint('/api/oss/{item}/update-operation', method='patch')
def test_update_operation_invalid_substitution(self):
self.populateData()
self.ks1x2 = self.ks1.insert_new('X2')
data = {
'target': self.operation3.pk,
'item_data': {
'alias': 'Test3 mod',
'title': 'Test title mod',
'comment': 'Comment mod'
},
'positions': [],
'arguments': [self.operation1.pk, self.operation2.pk],
'substitutions': [
{
'original': self.ks1x1.pk,
'substitution': self.ks2x1.pk
},
{
'original': self.ks2x1.pk,
'substitution': self.ks1x2.pk
}
]
}
self.executeBadData(data=data, item=self.owned_id)
@decl_endpoint('/api/oss/{item}/execute-operation', method='post')
def test_execute_operation(self):
self.populateData()
self.executeBadData(item=self.owned_id)
data = {
'positions': [],
'target': self.operation1.pk
}
self.executeBadData(data=data)
data['target'] = self.operation3.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()
self.executeOK(data=data)
self.operation3.refresh_from_db()
schema = self.operation3.result
self.assertEqual(schema.alias, self.operation3.alias)
self.assertEqual(schema.comment, self.operation3.comment)
self.assertEqual(schema.title, self.operation3.title)
self.assertEqual(schema.visible, False)
items = list(RSForm(schema).constituents())
self.assertEqual(len(items), 1)
self.assertEqual(items[0].alias, 'X1')
self.assertEqual(items[0].term_resolved, self.ks2x1.term_resolved)

View File

@ -10,7 +10,7 @@ from rest_framework.decorators import action
from rest_framework.request import Request from rest_framework.request import Request
from rest_framework.response import Response from rest_framework.response import Response
from apps.library.models import Editor, LibraryItem, LibraryItemType from apps.library.models import LibraryItem, LibraryItemType
from apps.library.serializers import LibraryItemSerializer from apps.library.serializers import LibraryItemSerializer
from shared import messages as msg from shared import messages as msg
from shared import permissions from shared import permissions
@ -38,7 +38,7 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
'create_input', 'create_input',
'set_input', 'set_input',
'update_operation', 'update_operation',
'execute_operation', 'execute_operation'
]: ]:
permission_list = [permissions.ItemEditor] permission_list = [permissions.ItemEditor]
elif self.action in ['details']: elif self.action in ['details']:
@ -103,28 +103,14 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
oss = m.OperationSchema(self.get_object()) oss = m.OperationSchema(self.get_object())
with transaction.atomic(): with transaction.atomic():
oss.update_positions(serializer.validated_data['positions']) oss.update_positions(serializer.validated_data['positions'])
data: dict = serializer.validated_data['item_data'] new_operation = oss.create_operation(**serializer.validated_data['item_data'])
if data['operation_type'] == m.OperationType.INPUT and serializer.validated_data['create_schema']: if new_operation.operation_type == m.OperationType.INPUT and serializer.validated_data['create_schema']:
schema = LibraryItem.objects.create( oss.create_input(new_operation)
item_type=LibraryItemType.RSFORM,
owner=oss.model.owner,
alias=data['alias'],
title=data['title'],
comment=data['comment'],
visible=False,
access_policy=oss.model.access_policy,
location=oss.model.location
)
Editor.set(schema, oss.model.editors())
data['result'] = schema
new_operation = oss.create_operation(**data)
if new_operation.operation_type != m.OperationType.INPUT and 'arguments' in serializer.validated_data: if new_operation.operation_type != m.OperationType.INPUT and 'arguments' in serializer.validated_data:
oss.set_arguments( oss.set_arguments(
operation=new_operation, operation=new_operation,
arguments=serializer.validated_data['arguments'] arguments=serializer.validated_data['arguments']
) )
oss.refresh_from_db()
return Response( return Response(
status=c.HTTP_201_CREATED, status=c.HTTP_201_CREATED,
data={ data={
@ -158,7 +144,6 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
oss.update_positions(serializer.validated_data['positions']) oss.update_positions(serializer.validated_data['positions'])
oss.delete_operation(serializer.validated_data['target']) oss.delete_operation(serializer.validated_data['target'])
oss.refresh_from_db()
return Response( return Response(
status=c.HTTP_200_OK, status=c.HTTP_200_OK,
data=s.OperationSchemaSerializer(oss.model).data data=s.OperationSchemaSerializer(oss.model).data
@ -197,25 +182,12 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
oss = m.OperationSchema(self.get_object()) oss = m.OperationSchema(self.get_object())
with transaction.atomic(): with transaction.atomic():
oss.update_positions(serializer.validated_data['positions']) oss.update_positions(serializer.validated_data['positions'])
schema = LibraryItem.objects.create( schema = oss.create_input(operation)
item_type=LibraryItemType.RSFORM,
owner=oss.model.owner,
alias=operation.alias,
title=operation.title,
comment=operation.comment,
visible=False,
access_policy=oss.model.access_policy,
location=oss.model.location
)
Editor.set(schema, oss.model.editors())
operation.result = schema
operation.save()
oss.refresh_from_db()
return Response( return Response(
status=c.HTTP_200_OK, status=c.HTTP_200_OK,
data={ data={
'new_schema': LibraryItemSerializer(schema).data, 'new_schema': LibraryItemSerializer(schema.model).data,
'oss': s.OperationSchemaSerializer(oss.model).data 'oss': s.OperationSchemaSerializer(oss.model).data
} }
) )
@ -241,20 +213,10 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
serializer.is_valid(raise_exception=True) serializer.is_valid(raise_exception=True)
operation: m.Operation = cast(m.Operation, serializer.validated_data['target']) operation: m.Operation = cast(m.Operation, serializer.validated_data['target'])
result = serializer.validated_data['input']
oss = m.OperationSchema(self.get_object()) oss = m.OperationSchema(self.get_object())
with transaction.atomic(): with transaction.atomic():
oss.update_positions(serializer.validated_data['positions']) oss.update_positions(serializer.validated_data['positions'])
operation.result = result oss.set_input(operation, serializer.validated_data['input'])
if result is not None:
operation.title = result.title
operation.comment = result.comment
operation.alias = result.alias
operation.save()
# update arguments
oss.refresh_from_db()
return Response( return Response(
status=c.HTTP_200_OK, status=c.HTTP_200_OK,
data=s.OperationSchemaSerializer(oss.model).data data=s.OperationSchemaSerializer(oss.model).data
@ -336,17 +298,10 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
}) })
oss = m.OperationSchema(self.get_object()) oss = m.OperationSchema(self.get_object())
# with transaction.atomic(): with transaction.atomic():
# oss.update_positions(serializer.validated_data['positions']) oss.update_positions(serializer.validated_data['positions'])
# operation.result.refresh_from_db() oss.execute_operation(operation)
# 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( return Response(
status=c.HTTP_200_OK, status=c.HTTP_200_OK,
data=s.OperationSchemaSerializer(oss.model).data data=s.OperationSchemaSerializer(oss.model).data

View File

@ -12,7 +12,6 @@ from django.db.models import (
TextChoices, TextChoices,
TextField TextField
) )
from django.urls import reverse
from ..utils import apply_pattern from ..utils import apply_pattern

View File

@ -119,7 +119,7 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
cst = m.Constituenta.objects.get(pk=request.data['id']) cst = m.Constituenta.objects.get(pk=request.data['id'])
if cst.schema != schema: if cst.schema != schema:
raise ValidationError({ raise ValidationError({
f'schema': msg.constituentaNotInRSform(schema.title) 'schema': msg.constituentaNotInRSform(schema.title)
}) })
serializer.update(instance=cst, validated_data=serializer.validated_data) serializer.update(instance=cst, validated_data=serializer.validated_data)

View File

@ -14,6 +14,10 @@ def operationNotInOSS(title: str):
return f'Операция не принадлежит ОСС: {title}' return f'Операция не принадлежит ОСС: {title}'
def previousResultMissing():
return 'Отсутствует результат предыдущей операции'
def substitutionNotInList(): def substitutionNotInList():
return 'Отождествляемая конституента отсутствует в списке' return 'Отождествляемая конституента отсутствует в списке'

View File

@ -73,10 +73,3 @@ export function postExecuteOperation(oss: string, request: FrontExchange<ITarget
request: request request: request
}); });
} }
export function postExecuteAll(oss: string, request: FrontExchange<IPositionsData, IOperationSchemaData>) {
AxiosPost({
endpoint: `/api/oss/${oss}/execute-all`,
request: request
});
}

View File

@ -1,6 +1,7 @@
'use client'; 'use client';
import { useCallback, useMemo, useState } from 'react'; import { useCallback, useMemo, useState } from 'react';
import { toast } from 'react-toastify';
import BadgeConstituenta from '@/components/info/BadgeConstituenta'; import BadgeConstituenta from '@/components/info/BadgeConstituenta';
import SelectConstituenta from '@/components/select/SelectConstituenta'; import SelectConstituenta from '@/components/select/SelectConstituenta';
@ -10,6 +11,7 @@ import { useConceptOptions } from '@/context/ConceptOptionsContext';
import { ILibraryItem } from '@/models/library'; import { ILibraryItem } from '@/models/library';
import { ICstSubstitute, IMultiSubstitution } from '@/models/oss'; import { ICstSubstitute, IMultiSubstitution } from '@/models/oss';
import { ConstituentaID, IConstituenta, IRSForm } from '@/models/rsform'; import { ConstituentaID, IConstituenta, IRSForm } from '@/models/rsform';
import { errors } from '@/utils/labels';
import { IconPageLeft, IconPageRight, IconRemove, IconReplace } from '../Icons'; import { IconPageLeft, IconPageRight, IconRemove, IconReplace } from '../Icons';
import NoData from '../ui/NoData'; import NoData from '../ui/NoData';
@ -98,6 +100,18 @@ function PickSubstitutions({
original: deleteRight ? rightCst.id : leftCst.id, original: deleteRight ? rightCst.id : leftCst.id,
substitution: deleteRight ? leftCst.id : rightCst.id substitution: deleteRight ? leftCst.id : rightCst.id
}; };
const toDelete = substitutions.map(item => item.original);
const replacements = substitutions.map(item => item.substitution);
console.log(toDelete, replacements);
console.log(newSubstitution);
if (
toDelete.includes(newSubstitution.original) ||
toDelete.includes(newSubstitution.substitution) ||
replacements.includes(newSubstitution.original)
) {
toast.error(errors.reuseOriginal);
return;
}
setSubstitutions(prev => [...prev, newSubstitution]); setSubstitutions(prev => [...prev, newSubstitution]);
setLeftCst(undefined); setLeftCst(undefined);
setRightCst(undefined); setRightCst(undefined);

View File

@ -19,7 +19,6 @@ import {
patchUpdateOperation, patchUpdateOperation,
patchUpdatePositions, patchUpdatePositions,
postCreateOperation, postCreateOperation,
postExecuteAll,
postExecuteOperation postExecuteOperation
} from '@/backend/oss'; } from '@/backend/oss';
import { type ErrorData } from '@/components/info/InfoError'; import { type ErrorData } from '@/components/info/InfoError';
@ -69,7 +68,6 @@ interface IOssContext {
setInput: (data: IOperationSetInputData, callback?: () => void) => void; setInput: (data: IOperationSetInputData, callback?: () => void) => void;
updateOperation: (data: IOperationUpdateData, callback?: () => void) => void; updateOperation: (data: IOperationUpdateData, callback?: () => void) => void;
executeOperation: (data: ITargetOperation, callback?: () => void) => void; executeOperation: (data: ITargetOperation, callback?: () => void) => void;
executeAll: (data: IPositionsData, callback?: () => void) => void;
} }
const OssContext = createContext<IOssContext | null>(null); const OssContext = createContext<IOssContext | null>(null);
@ -413,28 +411,6 @@ export const OssState = ({ itemID, children }: OssStateProps) => {
[itemID, schema, library] [itemID, schema, library]
); );
const executeAll = useCallback(
(data: IPositionsData, callback?: () => void) => {
if (!schema) {
return;
}
setProcessingError(undefined);
postExecuteAll(itemID, {
data: data,
showError: true,
setLoading: setProcessing,
onError: setProcessingError,
onSuccess: newData => {
library.setGlobalOSS(newData);
library.reloadItems(() => {
if (callback) callback();
});
}
});
},
[itemID, schema, library]
);
return ( return (
<OssContext.Provider <OssContext.Provider
value={{ value={{
@ -461,8 +437,7 @@ export const OssState = ({ itemID, children }: OssStateProps) => {
createInput, createInput,
setInput, setInput,
updateOperation, updateOperation,
executeOperation, executeOperation
executeAll
}} }}
> >
{children} {children}

View File

@ -170,12 +170,11 @@ function OssFlow({ isModified, setIsModified }: OssFlowProps) {
); );
const handleExecuteSelected = useCallback(() => { const handleExecuteSelected = useCallback(() => {
if (controller.selected.length === 1) { if (controller.selected.length !== 1) {
handleExecuteOperation(controller.selected[0]); return;
} else {
controller.executeAll(getPositions());
} }
}, [controller, handleExecuteOperation, getPositions]); handleExecuteOperation(controller.selected[0]);
}, [controller, handleExecuteOperation]);
const handleFitView = useCallback(() => { const handleFitView = useCallback(() => {
flow.fitView({ duration: PARAMETER.zoomDuration }); flow.fitView({ duration: PARAMETER.zoomDuration });

View File

@ -1,4 +1,7 @@
'use client';
import clsx from 'clsx'; import clsx from 'clsx';
import { useMemo } from 'react';
import { import {
IconAnimation, IconAnimation,
@ -18,6 +21,7 @@ import {
import BadgeHelp from '@/components/info/BadgeHelp'; import BadgeHelp from '@/components/info/BadgeHelp';
import MiniButton from '@/components/ui/MiniButton'; import MiniButton from '@/components/ui/MiniButton';
import { HelpTopic } from '@/models/miscellaneous'; import { HelpTopic } from '@/models/miscellaneous';
import { OperationType } from '@/models/oss';
import { PARAMETER } from '@/utils/constants'; import { PARAMETER } from '@/utils/constants';
import { prepareTooltip } from '@/utils/labels'; import { prepareTooltip } from '@/utils/labels';
@ -59,6 +63,30 @@ function ToolbarOssGraph({
toggleEdgeStraight toggleEdgeStraight
}: ToolbarOssGraphProps) { }: ToolbarOssGraphProps) {
const controller = useOssEdit(); const controller = useOssEdit();
const selectedOperation = useMemo(
() => controller.schema?.operationByID.get(controller.selected[0]),
[controller.selected, controller.schema]
);
const readyForSynthesis = useMemo(() => {
if (!selectedOperation || selectedOperation.operation_type !== OperationType.SYNTHESIS) {
return false;
}
if (!controller.schema || selectedOperation.result) {
return false;
}
const argumentIDs = controller.schema.graph.expandInputs([selectedOperation.id]);
if (!argumentIDs || argumentIDs.length < 2) {
return false;
}
const argumentOperations = argumentIDs.map(id => controller.schema!.operationByID.get(id)!);
if (argumentOperations.some(item => item.result === null)) {
return false;
}
return true;
}, [selectedOperation, controller.schema]);
return ( return (
<div className='flex flex-col items-center'> <div className='flex flex-col items-center'>
@ -120,7 +148,6 @@ function ToolbarOssGraph({
</div> </div>
{controller.isMutable ? ( {controller.isMutable ? (
<div className='cc-icons'> <div className='cc-icons'>
{' '}
<MiniButton <MiniButton
titleHtml={prepareTooltip('Сохранить изменения', 'Ctrl + S')} titleHtml={prepareTooltip('Сохранить изменения', 'Ctrl + S')}
icon={<IconSave size='1.25rem' className='icon-primary' />} icon={<IconSave size='1.25rem' className='icon-primary' />}
@ -134,14 +161,9 @@ function ToolbarOssGraph({
onClick={onCreate} onClick={onCreate}
/> />
<MiniButton <MiniButton
title='Выполнить выбранную / все операции' title='Выполнить операцию'
icon={ icon={<IconExecute size='1.25rem' className='icon-green' />}
<IconExecute disabled={controller.isProcessing || controller.selected.length !== 1 || !readyForSynthesis}
size='1.25rem'
className={controller.selected.length === 1 ? 'icon-primary' : 'icon-green'}
/>
}
disabled={controller.isProcessing}
onClick={onExecute} onClick={onExecute}
/> />
<MiniButton <MiniButton

View File

@ -57,7 +57,6 @@ export interface IOssEditContext {
promptEditInput: (target: OperationID, positions: IOperationPosition[]) => void; promptEditInput: (target: OperationID, positions: IOperationPosition[]) => void;
promptEditOperation: (target: OperationID, positions: IOperationPosition[]) => void; promptEditOperation: (target: OperationID, positions: IOperationPosition[]) => void;
executeOperation: (target: OperationID, positions: IOperationPosition[]) => void; executeOperation: (target: OperationID, positions: IOperationPosition[]) => void;
executeAll: (positions: IOperationPosition[]) => void;
} }
const OssEditContext = createContext<IOssEditContext | null>(null); const OssEditContext = createContext<IOssEditContext | null>(null);
@ -290,14 +289,6 @@ export const OssEditState = ({ selected, setSelected, children }: OssEditStatePr
[model] [model]
); );
const executeAll = useCallback(
(positions: IOperationPosition[]) => {
const data = { positions: positions };
model.executeAll(data, () => toast.success(information.allOperationExecuted));
},
[model]
);
return ( return (
<OssEditContext.Provider <OssEditContext.Provider
value={{ value={{
@ -326,8 +317,7 @@ export const OssEditState = ({ selected, setSelected, children }: OssEditStatePr
createInput, createInput,
promptEditInput, promptEditInput,
promptEditOperation, promptEditOperation,
executeOperation, executeOperation
executeAll
}} }}
> >
{model.schema ? ( {model.schema ? (

View File

@ -951,7 +951,8 @@ export const information = {
export const errors = { export const errors = {
astFailed: 'Невозможно построить дерево разбора', astFailed: 'Невозможно построить дерево разбора',
passwordsMismatch: 'Пароли не совпадают', passwordsMismatch: 'Пароли не совпадают',
imageFailed: 'Ошибка при создании изображения' imageFailed: 'Ошибка при создании изображения',
reuseOriginal: 'Повторное использование удаляемой конституенты при отождествлении'
}; };
/** /**