F: Implementing backend for synthesis operation
This commit is contained in:
parent
6a21125e87
commit
2a30661355
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
})
|
})
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,10 @@ def operationNotInOSS(title: str):
|
||||||
return f'Операция не принадлежит ОСС: {title}'
|
return f'Операция не принадлежит ОСС: {title}'
|
||||||
|
|
||||||
|
|
||||||
|
def previousResultMissing():
|
||||||
|
return 'Отсутствует результат предыдущей операции'
|
||||||
|
|
||||||
|
|
||||||
def substitutionNotInList():
|
def substitutionNotInList():
|
||||||
return 'Отождествляемая конституента отсутствует в списке'
|
return 'Отождествляемая конституента отсутствует в списке'
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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}
|
||||||
|
|
|
@ -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 });
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 ? (
|
||||||
|
|
|
@ -951,7 +951,8 @@ export const information = {
|
||||||
export const errors = {
|
export const errors = {
|
||||||
astFailed: 'Невозможно построить дерево разбора',
|
astFailed: 'Невозможно построить дерево разбора',
|
||||||
passwordsMismatch: 'Пароли не совпадают',
|
passwordsMismatch: 'Пароли не совпадают',
|
||||||
imageFailed: 'Ошибка при создании изображения'
|
imageFailed: 'Ошибка при создании изображения',
|
||||||
|
reuseOriginal: 'Повторное использование удаляемой конституенты при отождествлении'
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in New Issue
Block a user