R: Simplify sync_text and transfer_term
This commit is contained in:
parent
afd3f5f7e4
commit
2e19c6fa69
|
@ -140,7 +140,7 @@ class LibraryItem(Model):
|
|||
def _update_connected_operations(self):
|
||||
# using method level import to prevent circular dependency
|
||||
from apps.oss.models import Operation # pylint: disable=import-outside-toplevel
|
||||
operations = Operation.objects.filter(result__pk=self.pk, sync_text=True)
|
||||
operations = Operation.objects.filter(result__pk=self.pk)
|
||||
if not operations.exists():
|
||||
return
|
||||
for operation in operations:
|
||||
|
|
|
@ -21,7 +21,7 @@ class ArgumentAdmin(admin.ModelAdmin):
|
|||
class SynthesisSubstitutionAdmin(admin.ModelAdmin):
|
||||
''' Admin model: Substitutions as part of Synthesis operation. '''
|
||||
ordering = ['operation']
|
||||
list_display = ['id', 'operation', 'original', 'substitution', 'transfer_term']
|
||||
list_display = ['id', 'operation', 'original', 'substitution']
|
||||
search_fields = ['id', 'operation', 'original', 'substitution']
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
# Generated by Django 5.0.7 on 2024-07-30 07:21
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('oss', '0002_inheritance'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='operation',
|
||||
name='sync_text',
|
||||
),
|
||||
]
|
|
@ -0,0 +1,17 @@
|
|||
# Generated by Django 5.0.7 on 2024-07-30 07:36
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('oss', '0003_remove_operation_sync_text'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='substitution',
|
||||
name='transfer_term',
|
||||
),
|
||||
]
|
|
@ -2,7 +2,6 @@
|
|||
from django.db.models import (
|
||||
CASCADE,
|
||||
SET_NULL,
|
||||
BooleanField,
|
||||
CharField,
|
||||
FloatField,
|
||||
ForeignKey,
|
||||
|
@ -43,10 +42,6 @@ class Operation(Model):
|
|||
on_delete=SET_NULL,
|
||||
related_name='producer'
|
||||
)
|
||||
sync_text: BooleanField = BooleanField(
|
||||
verbose_name='Синхронизация',
|
||||
default=True
|
||||
)
|
||||
|
||||
alias: CharField = CharField(
|
||||
verbose_name='Шифр',
|
||||
|
|
|
@ -134,12 +134,8 @@ class OperationSchema:
|
|||
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])
|
||||
else:
|
||||
processed.append(subs[0])
|
||||
|
||||
for sub in substitutes:
|
||||
if sub not in processed:
|
||||
|
@ -147,8 +143,7 @@ class OperationSchema:
|
|||
Substitution.objects.create(
|
||||
operation=target,
|
||||
original=sub['original'],
|
||||
substitution=sub['substitution'],
|
||||
transfer_term=sub['transfer_term']
|
||||
substitution=sub['substitution']
|
||||
)
|
||||
|
||||
if not changed:
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
''' Models: Synthesis Substitution. '''
|
||||
from django.db.models import CASCADE, BooleanField, ForeignKey, Model
|
||||
from django.db.models import CASCADE, ForeignKey, Model
|
||||
|
||||
|
||||
class Substitution(Model):
|
||||
|
@ -22,10 +22,6 @@ class Substitution(Model):
|
|||
on_delete=CASCADE,
|
||||
related_name='as_substitute'
|
||||
)
|
||||
transfer_term: BooleanField = BooleanField(
|
||||
verbose_name='Перенос термина',
|
||||
default=False
|
||||
)
|
||||
|
||||
class Meta:
|
||||
''' Model metadata. '''
|
||||
|
|
|
@ -21,7 +21,6 @@ class SubstitutionExSerializer(serializers.Serializer):
|
|||
operation = serializers.IntegerField()
|
||||
original = serializers.IntegerField()
|
||||
substitution = serializers.IntegerField()
|
||||
transfer_term = serializers.BooleanField()
|
||||
original_alias = serializers.CharField()
|
||||
original_term = serializers.CharField()
|
||||
substitution_alias = serializers.CharField()
|
||||
|
|
|
@ -34,7 +34,7 @@ class ArgumentSerializer(serializers.ModelSerializer):
|
|||
|
||||
class OperationCreateSerializer(serializers.Serializer):
|
||||
''' Serializer: Operation creation. '''
|
||||
class OperationData(serializers.ModelSerializer):
|
||||
class OperationCreateData(serializers.ModelSerializer):
|
||||
''' Serializer: Operation creation data. '''
|
||||
alias = serializers.CharField()
|
||||
operation_type = serializers.ChoiceField(OperationType.choices)
|
||||
|
@ -43,11 +43,11 @@ class OperationCreateSerializer(serializers.Serializer):
|
|||
''' serializer metadata. '''
|
||||
model = Operation
|
||||
fields = \
|
||||
'alias', 'operation_type', 'title', 'sync_text', \
|
||||
'alias', 'operation_type', 'title', \
|
||||
'comment', 'result', 'position_x', 'position_y'
|
||||
|
||||
create_schema = serializers.BooleanField(default=False, required=False)
|
||||
item_data = OperationData()
|
||||
item_data = OperationCreateData()
|
||||
arguments = PKField(many=True, queryset=Operation.objects.all(), required=False)
|
||||
|
||||
positions = serializers.ListField(
|
||||
|
@ -58,15 +58,15 @@ class OperationCreateSerializer(serializers.Serializer):
|
|||
|
||||
class OperationUpdateSerializer(serializers.Serializer):
|
||||
''' Serializer: Operation creation. '''
|
||||
class OperationData(serializers.ModelSerializer):
|
||||
class OperationUpdateData(serializers.ModelSerializer):
|
||||
''' Serializer: Operation creation data. '''
|
||||
class Meta:
|
||||
''' serializer metadata. '''
|
||||
model = Operation
|
||||
fields = 'alias', 'title', 'sync_text', 'comment'
|
||||
fields = 'alias', 'title', 'comment'
|
||||
|
||||
target = PKField(many=False, queryset=Operation.objects.all())
|
||||
item_data = OperationData()
|
||||
item_data = OperationUpdateData()
|
||||
arguments = PKField(many=True, queryset=Operation.objects.all(), required=False)
|
||||
substitutions = serializers.ListField(
|
||||
child=SubstitutionSerializerBase(),
|
||||
|
@ -145,7 +145,6 @@ class SetOperationInputSerializer(serializers.Serializer):
|
|||
allow_null=True,
|
||||
default=None
|
||||
)
|
||||
sync_text = serializers.BooleanField(default=False, required=False)
|
||||
positions = serializers.ListField(
|
||||
child=OperationPositionSerializer(),
|
||||
default=[]
|
||||
|
@ -196,7 +195,6 @@ class OperationSchemaSerializer(serializers.ModelSerializer):
|
|||
'operation',
|
||||
'original',
|
||||
'substitution',
|
||||
'transfer_term',
|
||||
original_alias=F('original__alias'),
|
||||
original_term=F('original__term_resolved'),
|
||||
substitution_alias=F('substitution__alias'),
|
||||
|
|
|
@ -29,7 +29,6 @@ class TestOperation(TestCase):
|
|||
self.assertEqual(self.operation.alias, 'KS1')
|
||||
self.assertEqual(self.operation.title, '')
|
||||
self.assertEqual(self.operation.comment, '')
|
||||
self.assertEqual(self.operation.sync_text, True)
|
||||
self.assertEqual(self.operation.position_x, 0)
|
||||
self.assertEqual(self.operation.position_y, 0)
|
||||
|
||||
|
@ -50,15 +49,6 @@ class TestOperation(TestCase):
|
|||
self.assertEqual(self.operation.title, schema.model.title)
|
||||
self.assertEqual(self.operation.comment, schema.model.comment)
|
||||
|
||||
self.operation.sync_text = False
|
||||
self.operation.save()
|
||||
|
||||
schema.model.alias = 'KS3'
|
||||
schema.save()
|
||||
self.operation.refresh_from_db()
|
||||
self.assertEqual(self.operation.result, schema.model)
|
||||
self.assertNotEqual(self.operation.alias, schema.model.alias)
|
||||
|
||||
def test_sync_from_library_item(self):
|
||||
schema = LibraryItem.objects.create(alias=self.operation.alias, item_type=LibraryItemType.RSFORM)
|
||||
self.operation.result = schema
|
||||
|
|
|
@ -47,8 +47,7 @@ class TestSynthesisSubstitution(TestCase):
|
|||
self.substitution = Substitution.objects.create(
|
||||
operation=self.operation3,
|
||||
original=self.ks1x1,
|
||||
substitution=self.ks2x1,
|
||||
transfer_term=False
|
||||
substitution=self.ks2x1
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -44,8 +44,7 @@ class TestOssViewset(EndpointTester):
|
|||
self.owned.set_arguments(self.operation3, [self.operation1, self.operation2])
|
||||
self.owned.set_substitutions(self.operation3, [{
|
||||
'original': self.ks1x1,
|
||||
'substitution': self.ks2x1,
|
||||
'transfer_term': False
|
||||
'substitution': self.ks2x1
|
||||
}])
|
||||
|
||||
@decl_endpoint('/api/oss/{item}/details', method='get')
|
||||
|
@ -71,7 +70,6 @@ class TestOssViewset(EndpointTester):
|
|||
self.assertEqual(sub['operation'], self.operation3.pk)
|
||||
self.assertEqual(sub['original'], self.ks1x1.pk)
|
||||
self.assertEqual(sub['substitution'], self.ks2x1.pk)
|
||||
self.assertEqual(sub['transfer_term'], False)
|
||||
self.assertEqual(sub['original_alias'], self.ks1x1.alias)
|
||||
self.assertEqual(sub['original_term'], self.ks1x1.term_resolved)
|
||||
self.assertEqual(sub['substitution_alias'], self.ks2x1.alias)
|
||||
|
@ -134,7 +132,6 @@ class TestOssViewset(EndpointTester):
|
|||
'alias': 'Test3',
|
||||
'title': 'Test title',
|
||||
'comment': 'Тест кириллицы',
|
||||
'sync_text': False,
|
||||
'position_x': 1,
|
||||
'position_y': 1,
|
||||
},
|
||||
|
@ -159,7 +156,6 @@ class TestOssViewset(EndpointTester):
|
|||
self.assertEqual(new_operation['comment'], data['item_data']['comment'])
|
||||
self.assertEqual(new_operation['position_x'], data['item_data']['position_x'])
|
||||
self.assertEqual(new_operation['position_y'], data['item_data']['position_y'])
|
||||
self.assertEqual(new_operation['sync_text'], data['item_data']['sync_text'])
|
||||
self.assertEqual(new_operation['result'], None)
|
||||
self.operation1.refresh_from_db()
|
||||
self.assertEqual(self.operation1.position_x, data['positions'][0]['position_x'])
|
||||
|
@ -274,13 +270,11 @@ class TestOssViewset(EndpointTester):
|
|||
self.operation1.result = None
|
||||
self.operation1.comment = 'TestComment'
|
||||
self.operation1.title = 'TestTitle'
|
||||
self.operation1.sync_text = False
|
||||
self.operation1.save()
|
||||
response = self.executeOK(data=data)
|
||||
self.operation1.refresh_from_db()
|
||||
|
||||
new_schema = response.data['new_schema']
|
||||
self.assertEqual(self.operation1.sync_text, True)
|
||||
self.assertEqual(new_schema['id'], self.operation1.result.pk)
|
||||
self.assertEqual(new_schema['alias'], self.operation1.alias)
|
||||
self.assertEqual(new_schema['title'], self.operation1.title)
|
||||
|
@ -295,7 +289,6 @@ class TestOssViewset(EndpointTester):
|
|||
self.executeBadData(item=self.owned_id)
|
||||
|
||||
data = {
|
||||
'sync_text': True,
|
||||
'positions': []
|
||||
}
|
||||
self.executeBadData(data=data)
|
||||
|
@ -312,7 +305,6 @@ class TestOssViewset(EndpointTester):
|
|||
self.login()
|
||||
response = self.executeOK(data=data)
|
||||
self.operation1.refresh_from_db()
|
||||
self.assertEqual(self.operation1.sync_text, True)
|
||||
self.assertEqual(self.operation1.result, None)
|
||||
|
||||
data['input'] = self.ks1.model.pk
|
||||
|
@ -322,7 +314,6 @@ class TestOssViewset(EndpointTester):
|
|||
self.ks1.save()
|
||||
response = self.executeOK(data=data)
|
||||
self.operation1.refresh_from_db()
|
||||
self.assertEqual(self.operation1.sync_text, True)
|
||||
self.assertEqual(self.operation1.result, self.ks1.model)
|
||||
self.assertEqual(self.operation1.alias, self.ks1.model.alias)
|
||||
self.assertEqual(self.operation1.title, self.ks1.model.title)
|
||||
|
@ -334,14 +325,12 @@ class TestOssViewset(EndpointTester):
|
|||
self.operation2.result = None
|
||||
|
||||
data = {
|
||||
'sync_text': True,
|
||||
'positions': [],
|
||||
'target': self.operation1.pk,
|
||||
'input': self.ks2.model.pk
|
||||
}
|
||||
response = self.executeOK(data=data, item=self.owned_id)
|
||||
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')
|
||||
|
@ -357,16 +346,14 @@ class TestOssViewset(EndpointTester):
|
|||
'item_data': {
|
||||
'alias': 'Test3 mod',
|
||||
'title': 'Test title mod',
|
||||
'comment': 'Comment mod',
|
||||
'sync_text': True
|
||||
'comment': 'Comment mod'
|
||||
},
|
||||
'positions': [],
|
||||
'arguments': [self.operation1.pk, self.operation2.pk],
|
||||
'substitutions': [
|
||||
{
|
||||
'original': self.ks1x1.pk,
|
||||
'substitution': ks3x1.pk,
|
||||
'transfer_term': False
|
||||
'substitution': ks3x1.pk
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -381,7 +368,6 @@ class TestOssViewset(EndpointTester):
|
|||
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'])
|
||||
|
@ -389,7 +375,6 @@ class TestOssViewset(EndpointTester):
|
|||
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):
|
||||
|
@ -401,15 +386,13 @@ class TestOssViewset(EndpointTester):
|
|||
'item_data': {
|
||||
'alias': 'Test3 mod',
|
||||
'title': 'Test title mod',
|
||||
'comment': 'Comment mod',
|
||||
'sync_text': True
|
||||
'comment': 'Comment mod'
|
||||
},
|
||||
'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'])
|
||||
|
|
|
@ -209,7 +209,6 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
|
|||
)
|
||||
Editor.set(schema, oss.model.editors())
|
||||
operation.result = schema
|
||||
operation.sync_text = True
|
||||
operation.save()
|
||||
|
||||
oss.refresh_from_db()
|
||||
|
@ -247,8 +246,7 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
|
|||
with transaction.atomic():
|
||||
oss.update_positions(serializer.validated_data['positions'])
|
||||
operation.result = result
|
||||
operation.sync_text = serializer.validated_data['sync_text']
|
||||
if result is not None and operation.sync_text:
|
||||
if result is not None:
|
||||
operation.title = result.title
|
||||
operation.comment = result.comment
|
||||
operation.alias = result.alias
|
||||
|
@ -289,10 +287,9 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
|
|||
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:
|
||||
if operation.result is not None:
|
||||
can_edit = permissions.can_edit_item(request.user, operation.result)
|
||||
if can_edit:
|
||||
operation.result.alias = operation.alias
|
||||
|
|
|
@ -241,18 +241,12 @@ class RSForm:
|
|||
def substitute(
|
||||
self,
|
||||
original: Constituenta,
|
||||
substitution: Constituenta,
|
||||
transfer_term: bool
|
||||
substitution: Constituenta
|
||||
):
|
||||
''' Execute constituenta substitution. '''
|
||||
assert original.pk != substitution.pk
|
||||
mapping = {original.alias: substitution.alias}
|
||||
self.apply_mapping(mapping)
|
||||
if transfer_term:
|
||||
substitution.term_raw = original.term_raw
|
||||
substitution.term_forms = original.term_forms
|
||||
substitution.term_resolved = original.term_resolved
|
||||
substitution.save()
|
||||
original.delete()
|
||||
self.on_term_change([substitution.id])
|
||||
|
||||
|
|
|
@ -274,7 +274,6 @@ class SubstitutionSerializerBase(serializers.Serializer):
|
|||
''' Serializer: Basic substitution. '''
|
||||
original = PKField(many=False, queryset=Constituenta.objects.all())
|
||||
substitution = PKField(many=False, queryset=Constituenta.objects.all())
|
||||
transfer_term = serializers.BooleanField(required=False, default=False)
|
||||
|
||||
|
||||
class CstSubstituteSerializer(serializers.Serializer):
|
||||
|
|
|
@ -57,13 +57,11 @@ class TestInlineSynthesis(EndpointTester):
|
|||
'substitutions': [
|
||||
{
|
||||
'original': ks1_x1.pk,
|
||||
'substitution': ks2_s1.pk,
|
||||
'transfer_term': False
|
||||
'substitution': ks2_s1.pk
|
||||
},
|
||||
{
|
||||
'original': ks2_x1.pk,
|
||||
'substitution': ks1_s1.pk,
|
||||
'transfer_term': True
|
||||
'substitution': ks1_s1.pk
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -272,43 +272,6 @@ class TestRSFormViewset(EndpointTester):
|
|||
self.assertEqual(x1.cst_type, CstType.TERM)
|
||||
|
||||
|
||||
@decl_endpoint('/api/rsforms/{item}/substitute', method='patch')
|
||||
def test_substitute_single(self):
|
||||
x1 = self.owned.insert_new(
|
||||
alias='X1',
|
||||
term_raw='Test1',
|
||||
term_resolved='Test1',
|
||||
term_forms=[{'text': 'form1', 'tags': 'sing,datv'}]
|
||||
)
|
||||
x2 = self.owned.insert_new(
|
||||
alias='X2',
|
||||
term_raw='Test2'
|
||||
)
|
||||
unowned = self.unowned.insert_new('X2')
|
||||
|
||||
data = {'substitutions': [{'original': x1.pk, 'substitution': unowned.pk, 'transfer_term': True}]}
|
||||
self.executeForbidden(data=data, item=self.unowned_id)
|
||||
self.executeBadData(data=data, item=self.owned_id)
|
||||
|
||||
data = {'substitutions': [{'original': unowned.pk, 'substitution': x1.pk, 'transfer_term': True}]}
|
||||
self.executeBadData(data=data, item=self.owned_id)
|
||||
|
||||
data = {'substitutions': [{'original': x1.pk, 'substitution': x1.pk, 'transfer_term': True}]}
|
||||
self.executeBadData(data=data, item=self.owned_id)
|
||||
|
||||
d1 = self.owned.insert_new(
|
||||
alias='D1',
|
||||
term_raw='@{X2|sing,datv}',
|
||||
definition_formal='X1'
|
||||
)
|
||||
data = {'substitutions': [{'original': x1.pk, 'substitution': x2.pk, 'transfer_term': True}]}
|
||||
response = self.executeOK(data=data, item=self.owned_id)
|
||||
d1.refresh_from_db()
|
||||
x2.refresh_from_db()
|
||||
self.assertEqual(x2.term_raw, 'Test1')
|
||||
self.assertEqual(d1.term_resolved, 'form1')
|
||||
self.assertEqual(d1.definition_formal, 'X2')
|
||||
|
||||
@decl_endpoint('/api/rsforms/{item}/substitute', method='patch')
|
||||
def test_substitute_multiple(self):
|
||||
self.set_params(item=self.owned_id)
|
||||
|
@ -327,13 +290,11 @@ class TestRSFormViewset(EndpointTester):
|
|||
data = {'substitutions': [
|
||||
{
|
||||
'original': x1.pk,
|
||||
'substitution': d1.pk,
|
||||
'transfer_term': True
|
||||
'substitution': d1.pk
|
||||
},
|
||||
{
|
||||
'original': x1.pk,
|
||||
'substitution': d2.pk,
|
||||
'transfer_term': True
|
||||
'substitution': d2.pk
|
||||
}
|
||||
]}
|
||||
self.executeBadData(data=data)
|
||||
|
@ -341,13 +302,11 @@ class TestRSFormViewset(EndpointTester):
|
|||
data = {'substitutions': [
|
||||
{
|
||||
'original': x1.pk,
|
||||
'substitution': d1.pk,
|
||||
'transfer_term': True
|
||||
'substitution': d1.pk
|
||||
},
|
||||
{
|
||||
'original': x2.pk,
|
||||
'substitution': d2.pk,
|
||||
'transfer_term': True
|
||||
'substitution': d2.pk
|
||||
}
|
||||
]}
|
||||
response = self.executeOK(data=data, item=self.owned_id)
|
||||
|
|
|
@ -41,7 +41,7 @@ def inline_synthesis(request: Request):
|
|||
else:
|
||||
index = next(i for (i, cst) in enumerate(items) if cst == replacement)
|
||||
replacement = new_items[index]
|
||||
receiver.substitute(original, replacement, substitution['transfer_term'])
|
||||
receiver.substitute(original, replacement)
|
||||
receiver.restore_order()
|
||||
|
||||
return Response(
|
||||
|
|
|
@ -174,7 +174,7 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
|
|||
)
|
||||
|
||||
@extend_schema(
|
||||
summary='substitute constituenta',
|
||||
summary='execute substitutions',
|
||||
tags=['RSForm'],
|
||||
request=s.CstSubstituteSerializer,
|
||||
responses={
|
||||
|
@ -198,7 +198,7 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
|
|||
for substitution in serializer.validated_data['substitutions']:
|
||||
original = cast(m.Constituenta, substitution['original'])
|
||||
replacement = cast(m.Constituenta, substitution['substitution'])
|
||||
m.RSForm(schema).substitute(original, replacement, substitution['transfer_term'])
|
||||
m.RSForm(schema).substitute(original, replacement)
|
||||
|
||||
schema.refresh_from_db()
|
||||
return Response(
|
||||
|
|
|
@ -89,8 +89,6 @@ export { BiHelpCircle as IconStatusUnknown } from 'react-icons/bi';
|
|||
export { BiPauseCircle as IconStatusIncalculable } from 'react-icons/bi';
|
||||
export { LuPower as IconKeepAliasOn } from 'react-icons/lu';
|
||||
export { LuPowerOff as IconKeepAliasOff } from 'react-icons/lu';
|
||||
export { LuFlag as IconKeepTermOn } from 'react-icons/lu';
|
||||
export { LuFlagOff as IconKeepTermOff } from 'react-icons/lu';
|
||||
|
||||
// ===== Domain actions =====
|
||||
export { BiUpvote as IconMoveUp } from 'react-icons/bi';
|
||||
|
|
|
@ -1,273 +0,0 @@
|
|||
'use client';
|
||||
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
|
||||
import BadgeConstituenta from '@/components/info/BadgeConstituenta';
|
||||
import SelectConstituenta from '@/components/select/SelectConstituenta';
|
||||
import DataTable, { createColumnHelper } from '@/components/ui/DataTable';
|
||||
import Label from '@/components/ui/Label';
|
||||
import MiniButton from '@/components/ui/MiniButton';
|
||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
||||
import { IBinarySubstitution, IConstituenta, IRSForm } from '@/models/rsform';
|
||||
import { describeConstituenta } from '@/utils/labels';
|
||||
|
||||
import {
|
||||
IconKeepAliasOff,
|
||||
IconKeepAliasOn,
|
||||
IconKeepTermOff,
|
||||
IconKeepTermOn,
|
||||
IconPageFirst,
|
||||
IconPageLast,
|
||||
IconPageLeft,
|
||||
IconPageRight,
|
||||
IconRemove,
|
||||
IconReplace
|
||||
} from '../Icons';
|
||||
import NoData from '../ui/NoData';
|
||||
|
||||
interface PickInlineSubstitutionsProps {
|
||||
prefixID: string;
|
||||
rows?: number;
|
||||
|
||||
schema1?: IRSForm;
|
||||
schema2?: IRSForm;
|
||||
filter1?: (cst: IConstituenta) => boolean;
|
||||
filter2?: (cst: IConstituenta) => boolean;
|
||||
|
||||
items: IBinarySubstitution[];
|
||||
setItems: React.Dispatch<React.SetStateAction<IBinarySubstitution[]>>;
|
||||
}
|
||||
|
||||
function SubstitutionIcon({ item }: { item: IBinarySubstitution }) {
|
||||
if (item.deleteRight) {
|
||||
if (item.takeLeftTerm) {
|
||||
return <IconPageRight size='1.2rem' />;
|
||||
} else {
|
||||
return <IconPageLast size='1.2rem' />;
|
||||
}
|
||||
} else {
|
||||
if (item.takeLeftTerm) {
|
||||
return <IconPageFirst size='1.2rem' />;
|
||||
} else {
|
||||
return <IconPageLeft size='1.2rem' />;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const columnHelper = createColumnHelper<IBinarySubstitution>();
|
||||
|
||||
function PickInlineSubstitutions({
|
||||
items,
|
||||
schema1,
|
||||
schema2,
|
||||
filter1,
|
||||
filter2,
|
||||
rows,
|
||||
setItems,
|
||||
prefixID
|
||||
}: PickInlineSubstitutionsProps) {
|
||||
const { colors } = useConceptOptions();
|
||||
|
||||
const [leftCst, setLeftCst] = useState<IConstituenta | undefined>(undefined);
|
||||
const [rightCst, setRightCst] = useState<IConstituenta | undefined>(undefined);
|
||||
const [deleteRight, setDeleteRight] = useState(true);
|
||||
const [takeLeftTerm, setTakeLeftTerm] = useState(true);
|
||||
|
||||
const toggleDelete = () => setDeleteRight(prev => !prev);
|
||||
const toggleTerm = () => setTakeLeftTerm(prev => !prev);
|
||||
|
||||
function addSubstitution() {
|
||||
if (!leftCst || !rightCst) {
|
||||
return;
|
||||
}
|
||||
const newSubstitution: IBinarySubstitution = {
|
||||
leftCst: leftCst,
|
||||
rightCst: rightCst,
|
||||
deleteRight: deleteRight,
|
||||
takeLeftTerm: takeLeftTerm
|
||||
};
|
||||
setItems([
|
||||
newSubstitution,
|
||||
...items.filter(
|
||||
item =>
|
||||
(!item.deleteRight && item.leftCst.id !== leftCst.id) ||
|
||||
(item.deleteRight && item.rightCst.id !== rightCst.id)
|
||||
)
|
||||
]);
|
||||
}
|
||||
|
||||
const handleDeleteRow = useCallback(
|
||||
(row: number) => {
|
||||
setItems(prev => {
|
||||
const newItems: IBinarySubstitution[] = [];
|
||||
prev.forEach((item, index) => {
|
||||
if (index !== row) {
|
||||
newItems.push(item);
|
||||
}
|
||||
});
|
||||
return newItems;
|
||||
});
|
||||
},
|
||||
[setItems]
|
||||
);
|
||||
|
||||
const columns = useMemo(
|
||||
() => [
|
||||
columnHelper.accessor(item => describeConstituenta(item.leftCst), {
|
||||
id: 'left_text',
|
||||
header: 'Описание',
|
||||
size: 1000,
|
||||
cell: props => <div className='text-xs text-ellipsis'>{props.getValue()}</div>
|
||||
}),
|
||||
columnHelper.accessor(item => item.leftCst.alias, {
|
||||
id: 'left_alias',
|
||||
header: () => <span className='pl-3'>Имя</span>,
|
||||
size: 65,
|
||||
cell: props => (
|
||||
<BadgeConstituenta theme={colors} value={props.row.original.leftCst} prefixID={`${prefixID}_1_`} />
|
||||
)
|
||||
}),
|
||||
columnHelper.display({
|
||||
id: 'status',
|
||||
header: '',
|
||||
size: 40,
|
||||
cell: props => <SubstitutionIcon item={props.row.original} />
|
||||
}),
|
||||
columnHelper.accessor(item => item.rightCst.alias, {
|
||||
id: 'right_alias',
|
||||
header: () => <span className='pl-3'>Имя</span>,
|
||||
size: 65,
|
||||
cell: props => (
|
||||
<BadgeConstituenta theme={colors} value={props.row.original.rightCst} prefixID={`${prefixID}_2_`} />
|
||||
)
|
||||
}),
|
||||
columnHelper.accessor(item => describeConstituenta(item.rightCst), {
|
||||
id: 'right_text',
|
||||
header: 'Описание',
|
||||
minSize: 1000,
|
||||
cell: props => <div className='text-xs text-ellipsis text-pretty'>{props.getValue()}</div>
|
||||
}),
|
||||
columnHelper.display({
|
||||
id: 'actions',
|
||||
cell: props => (
|
||||
<MiniButton
|
||||
noHover
|
||||
title='Удалить'
|
||||
icon={<IconRemove size='1rem' className='icon-red' />}
|
||||
onClick={() => handleDeleteRow(props.row.index)}
|
||||
/>
|
||||
)
|
||||
})
|
||||
],
|
||||
[handleDeleteRow, colors, prefixID]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className='flex flex-col w-full'>
|
||||
<div className='flex items-end gap-3 justify-stretch'>
|
||||
<div className='flex-grow basis-1/2'>
|
||||
<div className='flex items-center justify-between'>
|
||||
<Label text={schema1 !== schema2 ? schema1?.alias ?? 'Схема 1' : ''} />
|
||||
<div className='cc-icons'>
|
||||
<MiniButton
|
||||
title='Сохранить конституенту'
|
||||
noHover
|
||||
onClick={toggleDelete}
|
||||
icon={
|
||||
deleteRight ? (
|
||||
<IconKeepAliasOn size='1rem' className='clr-text-green' />
|
||||
) : (
|
||||
<IconKeepAliasOff size='1rem' className='clr-text-red' />
|
||||
)
|
||||
}
|
||||
/>
|
||||
<MiniButton
|
||||
title='Сохранить термин'
|
||||
noHover
|
||||
onClick={toggleTerm}
|
||||
icon={
|
||||
takeLeftTerm ? (
|
||||
<IconKeepTermOn size='1rem' className='clr-text-green' />
|
||||
) : (
|
||||
<IconKeepTermOff size='1rem' className='clr-text-red' />
|
||||
)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<SelectConstituenta
|
||||
items={schema1?.items.filter(cst => !filter1 || filter1(cst))}
|
||||
value={leftCst}
|
||||
onSelectValue={setLeftCst}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<MiniButton
|
||||
noHover
|
||||
title='Добавить в таблицу отождествлений'
|
||||
className='mb-[0.375rem] grow-0'
|
||||
icon={<IconReplace size='1.5rem' className='icon-primary' />}
|
||||
disabled={!leftCst || !rightCst || leftCst === rightCst}
|
||||
onClick={addSubstitution}
|
||||
/>
|
||||
|
||||
<div className='flex-grow basis-1/2'>
|
||||
<div className='flex items-center justify-between'>
|
||||
<Label text={schema1 !== schema2 ? schema2?.alias ?? 'Схема 2' : ''} />
|
||||
<div className='cc-icons'>
|
||||
<MiniButton
|
||||
title='Сохранить конституенту'
|
||||
noHover
|
||||
onClick={toggleDelete}
|
||||
icon={
|
||||
!deleteRight ? (
|
||||
<IconKeepAliasOn size='1rem' className='clr-text-green' />
|
||||
) : (
|
||||
<IconKeepAliasOff size='1rem' className='clr-text-red' />
|
||||
)
|
||||
}
|
||||
/>
|
||||
<MiniButton
|
||||
title='Сохранить термин'
|
||||
noHover
|
||||
onClick={toggleTerm}
|
||||
icon={
|
||||
!takeLeftTerm ? (
|
||||
<IconKeepTermOn size='1rem' className='clr-text-green' />
|
||||
) : (
|
||||
<IconKeepTermOff size='1rem' className='clr-text-red' />
|
||||
)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<SelectConstituenta
|
||||
items={schema2?.items.filter(cst => !filter2 || filter2(cst))}
|
||||
value={rightCst}
|
||||
onSelectValue={setRightCst}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<DataTable
|
||||
dense
|
||||
noHeader
|
||||
noFooter
|
||||
className='w-full text-sm border select-none cc-scroll-y'
|
||||
rows={rows}
|
||||
contentHeight='1.3rem'
|
||||
data={items}
|
||||
columns={columns}
|
||||
headPosition='0'
|
||||
noDataComponent={
|
||||
<NoData className='min-h-[2rem]'>
|
||||
<p>Список пуст</p>
|
||||
<p>Добавьте отождествление</p>
|
||||
</NoData>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default PickInlineSubstitutions;
|
|
@ -7,109 +7,96 @@ import SelectConstituenta from '@/components/select/SelectConstituenta';
|
|||
import DataTable, { createColumnHelper } from '@/components/ui/DataTable';
|
||||
import MiniButton from '@/components/ui/MiniButton';
|
||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
||||
import { LibraryItemID } from '@/models/library';
|
||||
import { ICstSubstitute, IMultiSubstitution, IOperation } from '@/models/oss';
|
||||
import { ILibraryItem } from '@/models/library';
|
||||
import { ICstSubstitute, IMultiSubstitution } from '@/models/oss';
|
||||
import { ConstituentaID, IConstituenta, IRSForm } from '@/models/rsform';
|
||||
|
||||
import {
|
||||
IconKeepAliasOff,
|
||||
IconKeepAliasOn,
|
||||
IconKeepTermOff,
|
||||
IconKeepTermOn,
|
||||
IconPageLast,
|
||||
IconPageRight,
|
||||
IconRemove,
|
||||
IconReplace
|
||||
} from '../Icons';
|
||||
import { IconPageLeft, IconPageRight, IconRemove, IconReplace } from '../Icons';
|
||||
import NoData from '../ui/NoData';
|
||||
import SelectOperation from './SelectOperation';
|
||||
|
||||
function SubstitutionIcon({ item, className }: { item: IMultiSubstitution; className?: string }) {
|
||||
if (!item.transfer_term) {
|
||||
return <IconPageRight size='1.2rem' className={className} />;
|
||||
} else {
|
||||
return <IconPageLast size='1.2rem' className={className} />;
|
||||
}
|
||||
}
|
||||
import SelectLibraryItem from './SelectLibraryItem';
|
||||
|
||||
interface PickSubstitutionsProps {
|
||||
prefixID: string;
|
||||
rows?: number;
|
||||
|
||||
operations: IOperation[];
|
||||
getSchema: (id: LibraryItemID) => IRSForm | undefined;
|
||||
getConstituenta: (id: ConstituentaID) => IConstituenta | undefined;
|
||||
getSchemaByCst: (id: ConstituentaID) => IRSForm | undefined;
|
||||
substitutions: ICstSubstitute[];
|
||||
setSubstitutions: React.Dispatch<React.SetStateAction<ICstSubstitute[]>>;
|
||||
|
||||
prefixID: string;
|
||||
rows?: number;
|
||||
allowSelfSubstitution?: boolean;
|
||||
|
||||
schemas: IRSForm[];
|
||||
filter?: (cst: IConstituenta) => boolean;
|
||||
}
|
||||
|
||||
const columnHelper = createColumnHelper<IMultiSubstitution>();
|
||||
|
||||
function PickSubstitutions({
|
||||
substitutions,
|
||||
setSubstitutions,
|
||||
prefixID,
|
||||
rows,
|
||||
operations,
|
||||
getSchema,
|
||||
getConstituenta,
|
||||
getSchemaByCst,
|
||||
substitutions,
|
||||
setSubstitutions
|
||||
schemas,
|
||||
filter,
|
||||
allowSelfSubstitution
|
||||
}: PickSubstitutionsProps) {
|
||||
const { colors } = useConceptOptions();
|
||||
|
||||
const [leftArgument, setLeftArgument] = useState<IOperation | undefined>(undefined);
|
||||
const [rightArgument, setRightArgument] = useState<IOperation | undefined>(undefined);
|
||||
const leftSchema = useMemo(
|
||||
() => (leftArgument?.result ? getSchema(leftArgument.result) : undefined),
|
||||
[leftArgument, getSchema]
|
||||
const [leftArgument, setLeftArgument] = useState<ILibraryItem | undefined>(
|
||||
schemas.length === 1 ? schemas[0] : undefined
|
||||
);
|
||||
const [rightArgument, setRightArgument] = useState<ILibraryItem | undefined>(
|
||||
schemas.length === 1 && allowSelfSubstitution ? schemas[0] : undefined
|
||||
);
|
||||
|
||||
const rightSchema = useMemo(
|
||||
() => (rightArgument?.result ? getSchema(rightArgument.result) : undefined),
|
||||
[rightArgument, getSchema]
|
||||
);
|
||||
const [leftCst, setLeftCst] = useState<IConstituenta | undefined>(undefined);
|
||||
const [rightCst, setRightCst] = useState<IConstituenta | undefined>(undefined);
|
||||
|
||||
const [deleteRight, setDeleteRight] = useState(true);
|
||||
const [takeLeftTerm, setTakeLeftTerm] = useState(true);
|
||||
const toggleDelete = () => setDeleteRight(prev => !prev);
|
||||
|
||||
const operationByConstituenta = useCallback(
|
||||
(cst: ConstituentaID): IOperation | undefined => {
|
||||
const schema = getSchemaByCst(cst);
|
||||
if (!schema) {
|
||||
return undefined;
|
||||
const getSchemaByCst = useCallback(
|
||||
(id: ConstituentaID): IRSForm | undefined => {
|
||||
for (const schema of schemas) {
|
||||
const cst = schema.cstByID.get(id);
|
||||
if (cst) {
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
const cstOperations = operations.filter(item => item.result === schema.id);
|
||||
return cstOperations.length === 1 ? cstOperations[0] : undefined;
|
||||
return undefined;
|
||||
},
|
||||
[getSchemaByCst, operations]
|
||||
[schemas]
|
||||
);
|
||||
|
||||
const getConstituenta = useCallback(
|
||||
(id: ConstituentaID): IConstituenta | undefined => {
|
||||
for (const schema of schemas) {
|
||||
const cst = schema.cstByID.get(id);
|
||||
if (cst) {
|
||||
return cst;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
[schemas]
|
||||
);
|
||||
|
||||
const substitutionData: IMultiSubstitution[] = useMemo(
|
||||
() =>
|
||||
substitutions.map(item => ({
|
||||
original_operation: operationByConstituenta(item.original),
|
||||
original_source: getSchemaByCst(item.original),
|
||||
original: getConstituenta(item.original),
|
||||
substitution: getConstituenta(item.substitution),
|
||||
substitution_operation: operationByConstituenta(item.substitution),
|
||||
transfer_term: item.transfer_term
|
||||
substitution_source: getSchemaByCst(item.substitution)
|
||||
})),
|
||||
[getConstituenta, operationByConstituenta, substitutions]
|
||||
[getConstituenta, getSchemaByCst, substitutions]
|
||||
);
|
||||
|
||||
const toggleDelete = () => setDeleteRight(prev => !prev);
|
||||
const toggleTerm = () => setTakeLeftTerm(prev => !prev);
|
||||
|
||||
function addSubstitution() {
|
||||
if (!leftCst || !rightCst) {
|
||||
return;
|
||||
}
|
||||
const newSubstitution: ICstSubstitute = {
|
||||
original: deleteRight ? rightCst.id : leftCst.id,
|
||||
substitution: deleteRight ? leftCst.id : rightCst.id,
|
||||
transfer_term: deleteRight != takeLeftTerm
|
||||
substitution: deleteRight ? leftCst.id : rightCst.id
|
||||
};
|
||||
setSubstitutions(prev => [...prev, newSubstitution]);
|
||||
setLeftCst(undefined);
|
||||
|
@ -133,11 +120,11 @@ function PickSubstitutions({
|
|||
|
||||
const columns = useMemo(
|
||||
() => [
|
||||
columnHelper.accessor(item => item.substitution_operation?.alias ?? 'N/A', {
|
||||
columnHelper.accessor(item => item.substitution_source?.alias ?? 'N/A', {
|
||||
id: 'left_schema',
|
||||
header: 'Операция',
|
||||
size: 100,
|
||||
cell: props => <div className='min-w-[10rem] text-ellipsis text-right'>{props.getValue()}</div>
|
||||
cell: props => <div className='min-w-[10.5rem] text-ellipsis text-right'>{props.getValue()}</div>
|
||||
}),
|
||||
columnHelper.accessor(item => item.substitution?.alias ?? 'N/A', {
|
||||
id: 'left_alias',
|
||||
|
@ -154,7 +141,7 @@ function PickSubstitutions({
|
|||
id: 'status',
|
||||
header: '',
|
||||
size: 40,
|
||||
cell: props => <SubstitutionIcon item={props.row.original} />
|
||||
cell: () => <IconPageRight size='1.2rem' />
|
||||
}),
|
||||
columnHelper.accessor(item => item.original?.alias ?? 'N/A', {
|
||||
id: 'right_alias',
|
||||
|
@ -167,7 +154,7 @@ function PickSubstitutions({
|
|||
'N/A'
|
||||
)
|
||||
}),
|
||||
columnHelper.accessor(item => item.original_operation?.alias ?? 'N/A', {
|
||||
columnHelper.accessor(item => item.original_source?.alias ?? 'N/A', {
|
||||
id: 'right_schema',
|
||||
header: 'Операция',
|
||||
size: 100,
|
||||
|
@ -194,96 +181,60 @@ function PickSubstitutions({
|
|||
<div className='flex flex-col w-full'>
|
||||
<div className='flex items-end gap-3 justify-stretch'>
|
||||
<div className='flex-grow flex flex-col basis-1/2'>
|
||||
<div className='cc-icons mb-1 w-fit mx-auto'>
|
||||
<MiniButton
|
||||
title='Сохранить конституенту'
|
||||
noHover
|
||||
onClick={toggleDelete}
|
||||
icon={
|
||||
deleteRight ? (
|
||||
<IconKeepAliasOn size='1rem' className='clr-text-green' />
|
||||
) : (
|
||||
<IconKeepAliasOff size='1rem' className='clr-text-red' />
|
||||
)
|
||||
}
|
||||
/>
|
||||
<MiniButton
|
||||
title='Сохранить термин'
|
||||
noHover
|
||||
onClick={toggleTerm}
|
||||
icon={
|
||||
takeLeftTerm ? (
|
||||
<IconKeepTermOn size='1rem' className='clr-text-green' />
|
||||
) : (
|
||||
<IconKeepTermOff size='1rem' className='clr-text-red' />
|
||||
)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div className='flex flex-col gap-[0.125rem] border-x border-t clr-input'>
|
||||
<SelectOperation
|
||||
<SelectLibraryItem
|
||||
noBorder
|
||||
placeholder='Выберите аргумент'
|
||||
items={operations.filter(item => item.id !== rightArgument?.id)}
|
||||
items={allowSelfSubstitution ? schemas : schemas.filter(item => item.id !== rightArgument?.id)}
|
||||
value={leftArgument}
|
||||
onSelectValue={setLeftArgument}
|
||||
/>
|
||||
<SelectConstituenta
|
||||
noBorder
|
||||
items={leftSchema?.items.filter(cst => !substitutions.find(item => item.original === cst.id))}
|
||||
items={(leftArgument as IRSForm)?.items.filter(
|
||||
cst => !substitutions.find(item => item.original === cst.id) && (!filter || filter(cst))
|
||||
)}
|
||||
value={leftCst}
|
||||
onSelectValue={setLeftCst}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex flex-col gap-1'>
|
||||
<MiniButton
|
||||
title={deleteRight ? 'Заменить правую' : 'Заменить левую'}
|
||||
onClick={toggleDelete}
|
||||
icon={
|
||||
deleteRight ? (
|
||||
<IconPageRight size='1.5rem' className='clr-text-primary' />
|
||||
) : (
|
||||
<IconPageLeft size='1.5rem' className='clr-text-primary' />
|
||||
)
|
||||
}
|
||||
/>
|
||||
|
||||
<MiniButton
|
||||
noHover
|
||||
title='Добавить в таблицу отождествлений'
|
||||
className='mb-[0.375rem] grow-0'
|
||||
icon={<IconReplace size='1.5rem' className='icon-primary' />}
|
||||
disabled={!leftCst || !rightCst || leftCst === rightCst}
|
||||
onClick={addSubstitution}
|
||||
/>
|
||||
<MiniButton
|
||||
title='Добавить в таблицу отождествлений'
|
||||
className='mb-[0.375rem] grow-0'
|
||||
icon={<IconReplace size='1.5rem' className='icon-primary' />}
|
||||
disabled={!leftCst || !rightCst || leftCst === rightCst}
|
||||
onClick={addSubstitution}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='flex-grow basis-1/2'>
|
||||
<div className='cc-icons mb-1 w-fit mx-auto'>
|
||||
<MiniButton
|
||||
title='Сохранить конституенту'
|
||||
noHover
|
||||
onClick={toggleDelete}
|
||||
icon={
|
||||
!deleteRight ? (
|
||||
<IconKeepAliasOn size='1rem' className='clr-text-green' />
|
||||
) : (
|
||||
<IconKeepAliasOff size='1rem' className='clr-text-red' />
|
||||
)
|
||||
}
|
||||
/>
|
||||
<MiniButton
|
||||
title='Сохранить термин'
|
||||
noHover
|
||||
onClick={toggleTerm}
|
||||
icon={
|
||||
!takeLeftTerm ? (
|
||||
<IconKeepTermOn size='1rem' className='clr-text-green' />
|
||||
) : (
|
||||
<IconKeepTermOff size='1rem' className='clr-text-red' />
|
||||
)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div className='flex flex-col gap-[0.125rem] border-x border-t clr-input'>
|
||||
<SelectOperation
|
||||
<SelectLibraryItem
|
||||
noBorder
|
||||
placeholder='Выберите аргумент'
|
||||
items={operations.filter(item => item.id !== leftArgument?.id)}
|
||||
items={allowSelfSubstitution ? schemas : schemas.filter(item => item.id !== leftArgument?.id)}
|
||||
value={rightArgument}
|
||||
onSelectValue={setRightArgument}
|
||||
/>
|
||||
<SelectConstituenta
|
||||
noBorder
|
||||
items={rightSchema?.items.filter(cst => !substitutions.find(item => item.original === cst.id))}
|
||||
items={(rightArgument as IRSForm)?.items.filter(
|
||||
cst => !substitutions.find(item => item.original === cst.id) && (!filter || filter(cst))
|
||||
)}
|
||||
value={rightCst}
|
||||
onSelectValue={setRightCst}
|
||||
/>
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
'use client';
|
||||
|
||||
import clsx from 'clsx';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
|
||||
import { ILibraryItem, LibraryItemID } from '@/models/library';
|
||||
import { matchLibraryItem } from '@/models/libraryAPI';
|
||||
|
||||
import { CProps } from '../props';
|
||||
import SelectSingle from '../ui/SelectSingle';
|
||||
|
||||
interface SelectLibraryItemProps extends CProps.Styling {
|
||||
items?: ILibraryItem[];
|
||||
value?: ILibraryItem;
|
||||
onSelectValue: (newValue?: ILibraryItem) => void;
|
||||
|
||||
placeholder?: string;
|
||||
noBorder?: boolean;
|
||||
}
|
||||
|
||||
function SelectLibraryItem({
|
||||
className,
|
||||
items,
|
||||
value,
|
||||
onSelectValue,
|
||||
placeholder = 'Выберите схему',
|
||||
...restProps
|
||||
}: SelectLibraryItemProps) {
|
||||
const options = useMemo(() => {
|
||||
return (
|
||||
items?.map(cst => ({
|
||||
value: cst.id,
|
||||
label: `${cst.alias}: ${cst.title}`
|
||||
})) ?? []
|
||||
);
|
||||
}, [items]);
|
||||
|
||||
const filter = useCallback(
|
||||
(option: { value: LibraryItemID | undefined; label: string }, inputValue: string) => {
|
||||
const item = items?.find(item => item.id === option.value);
|
||||
return !item ? false : matchLibraryItem(item, inputValue);
|
||||
},
|
||||
[items]
|
||||
);
|
||||
|
||||
return (
|
||||
<SelectSingle
|
||||
className={clsx('text-ellipsis', className)}
|
||||
options={options}
|
||||
value={value ? { value: value.id, label: `${value.alias}: ${value.title}` } : null}
|
||||
onChange={data => onSelectValue(items?.find(cst => cst.id === data?.value))}
|
||||
// @ts-expect-error: TODO: use type definitions from react-select in filter object
|
||||
filterOption={filter}
|
||||
placeholder={placeholder}
|
||||
{...restProps}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default SelectLibraryItem;
|
|
@ -5,7 +5,6 @@ import { useCallback, useMemo, useState } from 'react';
|
|||
|
||||
import { IconReset } from '@/components/Icons';
|
||||
import PickSchema from '@/components/select/PickSchema';
|
||||
import Checkbox from '@/components/ui/Checkbox';
|
||||
import Label from '@/components/ui/Label';
|
||||
import MiniButton from '@/components/ui/MiniButton';
|
||||
import Modal, { ModalProps } from '@/components/ui/Modal';
|
||||
|
@ -15,12 +14,11 @@ import { IOperation, IOperationSchema } from '@/models/oss';
|
|||
interface DlgChangeInputSchemaProps extends Pick<ModalProps, 'hideWindow'> {
|
||||
oss: IOperationSchema;
|
||||
target: IOperation;
|
||||
onSubmit: (newSchema: LibraryItemID | undefined, syncText: boolean) => void;
|
||||
onSubmit: (newSchema: LibraryItemID | undefined) => void;
|
||||
}
|
||||
|
||||
function DlgChangeInputSchema({ oss, hideWindow, target, onSubmit }: DlgChangeInputSchemaProps) {
|
||||
const [selected, setSelected] = useState<LibraryItemID | undefined>(target.result ?? undefined);
|
||||
const [syncText, setSyncText] = useState(target.sync_text);
|
||||
|
||||
const baseFilter = useCallback(
|
||||
(item: ILibraryItem) => !oss.schemas.includes(item.id) || item.id === selected || item.id === target.result,
|
||||
|
@ -34,7 +32,7 @@ function DlgChangeInputSchema({ oss, hideWindow, target, onSubmit }: DlgChangeIn
|
|||
}, []);
|
||||
|
||||
function handleSubmit() {
|
||||
onSubmit(selected, syncText);
|
||||
onSubmit(selected);
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -66,12 +64,6 @@ function DlgChangeInputSchema({ oss, hideWindow, target, onSubmit }: DlgChangeIn
|
|||
rows={8}
|
||||
baseFilter={baseFilter}
|
||||
/>
|
||||
<Checkbox
|
||||
value={syncText}
|
||||
setValue={setSyncText}
|
||||
label='Синхронизировать текст'
|
||||
titleHtml='Загрузить текстовые поля<br/> из концептуальной схемы'
|
||||
/>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -38,7 +38,6 @@ function DlgCreateOperation({ hideWindow, oss, onCreate }: DlgCreateOperationPro
|
|||
const [comment, setComment] = useState('');
|
||||
const [inputs, setInputs] = useState<OperationID[]>([]);
|
||||
const [attachedID, setAttachedID] = useState<LibraryItemID | undefined>(undefined);
|
||||
const [syncText, setSyncText] = useState(true);
|
||||
const [createSchema, setCreateSchema] = useState(false);
|
||||
|
||||
const isValid = useMemo(
|
||||
|
@ -65,7 +64,6 @@ function DlgCreateOperation({ hideWindow, oss, onCreate }: DlgCreateOperationPro
|
|||
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
|
||||
},
|
||||
|
@ -89,14 +87,12 @@ function DlgCreateOperation({ hideWindow, oss, onCreate }: DlgCreateOperationPro
|
|||
setTitle={setTitle}
|
||||
attachedID={attachedID}
|
||||
setAttachedID={setAttachedID}
|
||||
syncText={syncText}
|
||||
setSyncText={setSyncText}
|
||||
createSchema={createSchema}
|
||||
setCreateSchema={setCreateSchema}
|
||||
/>
|
||||
</TabPanel>
|
||||
),
|
||||
[alias, comment, title, attachedID, syncText, oss, createSchema]
|
||||
[alias, comment, title, attachedID, oss, createSchema]
|
||||
);
|
||||
|
||||
const synthesisPanel = useMemo(
|
||||
|
|
|
@ -5,7 +5,6 @@ import { useCallback, useEffect } from 'react';
|
|||
import { IconReset } from '@/components/Icons';
|
||||
import PickSchema from '@/components/select/PickSchema';
|
||||
import Checkbox from '@/components/ui/Checkbox';
|
||||
import FlexColumn from '@/components/ui/FlexColumn';
|
||||
import Label from '@/components/ui/Label';
|
||||
import MiniButton from '@/components/ui/MiniButton';
|
||||
import TextArea from '@/components/ui/TextArea';
|
||||
|
@ -25,8 +24,6 @@ interface TabInputOperationProps {
|
|||
setComment: React.Dispatch<React.SetStateAction<string>>;
|
||||
attachedID: LibraryItemID | undefined;
|
||||
setAttachedID: React.Dispatch<React.SetStateAction<LibraryItemID | undefined>>;
|
||||
syncText: boolean;
|
||||
setSyncText: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
createSchema: boolean;
|
||||
setCreateSchema: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
}
|
||||
|
@ -41,8 +38,6 @@ function TabInputOperation({
|
|||
setComment,
|
||||
attachedID,
|
||||
setAttachedID,
|
||||
syncText,
|
||||
setSyncText,
|
||||
createSchema,
|
||||
setCreateSchema
|
||||
}: TabInputOperationProps) {
|
||||
|
@ -51,9 +46,8 @@ function TabInputOperation({
|
|||
useEffect(() => {
|
||||
if (createSchema) {
|
||||
setAttachedID(undefined);
|
||||
setSyncText(true);
|
||||
}
|
||||
}, [createSchema, setAttachedID, setSyncText]);
|
||||
}, [createSchema, setAttachedID]);
|
||||
|
||||
return (
|
||||
<AnimateFade className='cc-column'>
|
||||
|
@ -62,27 +56,19 @@ function TabInputOperation({
|
|||
label='Полное название'
|
||||
value={title}
|
||||
onChange={event => setTitle(event.target.value)}
|
||||
disabled={syncText && attachedID !== undefined}
|
||||
disabled={attachedID !== undefined}
|
||||
/>
|
||||
<div className='flex gap-6'>
|
||||
<FlexColumn>
|
||||
<TextInput
|
||||
id='operation_alias'
|
||||
label='Сокращение'
|
||||
className='w-[14rem]'
|
||||
pattern={patterns.library_alias}
|
||||
title={`не более ${limits.library_alias_len} символов`}
|
||||
value={alias}
|
||||
onChange={event => setAlias(event.target.value)}
|
||||
disabled={syncText && attachedID !== undefined}
|
||||
/>
|
||||
<Checkbox
|
||||
value={syncText}
|
||||
setValue={setSyncText}
|
||||
label='Синхронизировать текст'
|
||||
titleHtml='Загрузить текстовые поля<br/> из концептуальной схемы'
|
||||
/>
|
||||
</FlexColumn>
|
||||
<TextInput
|
||||
id='operation_alias'
|
||||
label='Сокращение'
|
||||
className='w-[14rem]'
|
||||
pattern={patterns.library_alias}
|
||||
title={`не более ${limits.library_alias_len} символов`}
|
||||
value={alias}
|
||||
onChange={event => setAlias(event.target.value)}
|
||||
disabled={attachedID !== undefined}
|
||||
/>
|
||||
|
||||
<TextArea
|
||||
id='operation_comment'
|
||||
|
@ -91,7 +77,7 @@ function TabInputOperation({
|
|||
rows={3}
|
||||
value={comment}
|
||||
onChange={event => setComment(event.target.value)}
|
||||
disabled={syncText && attachedID !== undefined}
|
||||
disabled={attachedID !== undefined}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -11,9 +11,9 @@ import { prefixes } from '@/utils/constants';
|
|||
import ListConstituents from './ListConstituents';
|
||||
|
||||
interface DlgDeleteCstProps extends Pick<ModalProps, 'hideWindow'> {
|
||||
schema: IRSForm;
|
||||
selected: ConstituentaID[];
|
||||
onDelete: (items: ConstituentaID[]) => void;
|
||||
schema: IRSForm;
|
||||
}
|
||||
|
||||
function DlgDeleteCst({ hideWindow, selected, schema, onDelete }: DlgDeleteCstProps) {
|
||||
|
|
|
@ -43,7 +43,6 @@ function DlgEditOperation({ hideWindow, oss, target, onSubmit }: DlgEditOperatio
|
|||
const [alias, setAlias] = useState(target.alias);
|
||||
const [title, setTitle] = useState(target.title);
|
||||
const [comment, setComment] = useState(target.comment);
|
||||
const [syncText, setSyncText] = useState(true);
|
||||
|
||||
const [inputs, setInputs] = useState<OperationID[]>(oss.graph.expandInputs([target.id]));
|
||||
const inputOperations = useMemo(() => inputs.map(id => oss.operationByID.get(id)!), [inputs, oss.operationByID]);
|
||||
|
@ -53,6 +52,10 @@ function DlgEditOperation({ hideWindow, oss, target, onSubmit }: DlgEditOperatio
|
|||
);
|
||||
const [substitutions, setSubstitutions] = useState<ICstSubstitute[]>(oss.substitutions);
|
||||
const cache = useRSFormCache();
|
||||
const schemas = useMemo(
|
||||
() => schemasIDs.map(id => cache.getSchema(id)).filter(item => item !== undefined),
|
||||
[schemasIDs, cache]
|
||||
);
|
||||
|
||||
const isValid = useMemo(() => alias !== '', [alias]);
|
||||
|
||||
|
@ -67,8 +70,7 @@ function DlgEditOperation({ hideWindow, oss, target, onSubmit }: DlgEditOperatio
|
|||
item_data: {
|
||||
alias: alias,
|
||||
title: title,
|
||||
comment: comment,
|
||||
sync_text: syncText
|
||||
comment: comment
|
||||
},
|
||||
positions: [],
|
||||
arguments: target.operation_type !== OperationType.SYNTHESIS ? undefined : inputs,
|
||||
|
@ -87,12 +89,10 @@ function DlgEditOperation({ hideWindow, oss, target, onSubmit }: DlgEditOperatio
|
|||
setComment={setComment}
|
||||
title={title}
|
||||
setTitle={setTitle}
|
||||
syncText={syncText}
|
||||
setSyncText={setSyncText}
|
||||
/>
|
||||
</TabPanel>
|
||||
),
|
||||
[alias, comment, title, syncText]
|
||||
[alias, comment, title]
|
||||
);
|
||||
|
||||
const argumentsPanel = useMemo(
|
||||
|
@ -113,26 +113,15 @@ function DlgEditOperation({ hideWindow, oss, target, onSubmit }: DlgEditOperatio
|
|||
() => (
|
||||
<TabPanel>
|
||||
<TabSynthesis
|
||||
operations={inputOperations}
|
||||
schemas={schemas}
|
||||
loading={cache.loading}
|
||||
error={cache.error}
|
||||
getSchema={cache.getSchema}
|
||||
getConstituenta={cache.getConstituenta}
|
||||
getSchemaByCst={cache.getSchemaByCst}
|
||||
substitutions={substitutions}
|
||||
setSubstitutions={setSubstitutions}
|
||||
/>
|
||||
</TabPanel>
|
||||
),
|
||||
[
|
||||
inputOperations,
|
||||
cache.loading,
|
||||
cache.error,
|
||||
cache.getSchema,
|
||||
cache.getConstituenta,
|
||||
substitutions,
|
||||
cache.getSchemaByCst
|
||||
]
|
||||
[cache.loading, cache.error, substitutions, schemas]
|
||||
);
|
||||
|
||||
return (
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import Checkbox from '@/components/ui/Checkbox';
|
||||
import FlexColumn from '@/components/ui/FlexColumn';
|
||||
import TextArea from '@/components/ui/TextArea';
|
||||
import TextInput from '@/components/ui/TextInput';
|
||||
import AnimateFade from '@/components/wrap/AnimateFade';
|
||||
|
@ -12,20 +10,9 @@ interface TabOperationProps {
|
|||
setTitle: React.Dispatch<React.SetStateAction<string>>;
|
||||
comment: string;
|
||||
setComment: React.Dispatch<React.SetStateAction<string>>;
|
||||
syncText: boolean;
|
||||
setSyncText: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
}
|
||||
|
||||
function TabOperation({
|
||||
alias,
|
||||
setAlias,
|
||||
title,
|
||||
setTitle,
|
||||
comment,
|
||||
setComment,
|
||||
syncText,
|
||||
setSyncText
|
||||
}: TabOperationProps) {
|
||||
function TabOperation({ alias, setAlias, title, setTitle, comment, setComment }: TabOperationProps) {
|
||||
return (
|
||||
<AnimateFade className='cc-column'>
|
||||
<TextInput
|
||||
|
@ -35,23 +22,15 @@ function TabOperation({
|
|||
onChange={event => setTitle(event.target.value)}
|
||||
/>
|
||||
<div className='flex gap-6'>
|
||||
<FlexColumn>
|
||||
<TextInput
|
||||
id='operation_alias'
|
||||
label='Сокращение'
|
||||
className='w-[14rem]'
|
||||
pattern={patterns.library_alias}
|
||||
title={`не более ${limits.library_alias_len} символов`}
|
||||
value={alias}
|
||||
onChange={event => setAlias(event.target.value)}
|
||||
/>
|
||||
<Checkbox
|
||||
value={syncText}
|
||||
setValue={setSyncText}
|
||||
label='Синхронизировать текст'
|
||||
titleHtml='Загрузить текстовые поля<br/> из концептуальной схемы'
|
||||
/>
|
||||
</FlexColumn>
|
||||
<TextInput
|
||||
id='operation_alias'
|
||||
label='Сокращение'
|
||||
className='w-[14rem]'
|
||||
pattern={patterns.library_alias}
|
||||
title={`не более ${limits.library_alias_len} символов`}
|
||||
value={alias}
|
||||
onChange={event => setAlias(event.target.value)}
|
||||
/>
|
||||
|
||||
<TextArea
|
||||
id='operation_comment'
|
||||
|
|
|
@ -1,42 +1,26 @@
|
|||
import { ErrorData } from '@/components/info/InfoError';
|
||||
import PickSubstitutions from '@/components/select/PickSubstitutions';
|
||||
import DataLoader from '@/components/wrap/DataLoader';
|
||||
import { LibraryItemID } from '@/models/library';
|
||||
import { ICstSubstitute, IOperation } from '@/models/oss';
|
||||
import { ConstituentaID, IConstituenta, IRSForm } from '@/models/rsform';
|
||||
import { ICstSubstitute } from '@/models/oss';
|
||||
import { IRSForm } from '@/models/rsform';
|
||||
import { prefixes } from '@/utils/constants';
|
||||
|
||||
interface TabSynthesisProps {
|
||||
loading: boolean;
|
||||
error: ErrorData;
|
||||
|
||||
operations: IOperation[];
|
||||
getSchema: (id: LibraryItemID) => IRSForm | undefined;
|
||||
getConstituenta: (id: ConstituentaID) => IConstituenta | undefined;
|
||||
getSchemaByCst: (id: ConstituentaID) => IRSForm | undefined;
|
||||
schemas: IRSForm[];
|
||||
substitutions: ICstSubstitute[];
|
||||
setSubstitutions: React.Dispatch<React.SetStateAction<ICstSubstitute[]>>;
|
||||
}
|
||||
|
||||
function TabSynthesis({
|
||||
operations,
|
||||
loading,
|
||||
error,
|
||||
getSchema,
|
||||
getConstituenta,
|
||||
getSchemaByCst,
|
||||
substitutions,
|
||||
setSubstitutions
|
||||
}: TabSynthesisProps) {
|
||||
function TabSynthesis({ schemas, loading, error, substitutions, setSubstitutions }: TabSynthesisProps) {
|
||||
return (
|
||||
<DataLoader id='dlg-synthesis-tab' className='cc-column' isLoading={loading} error={error}>
|
||||
<DataLoader id='dlg-synthesis-tab' className='cc-column mt-3' isLoading={loading} error={error}>
|
||||
<PickSubstitutions
|
||||
schemas={schemas}
|
||||
prefixID={prefixes.dlg_cst_substitutes_list}
|
||||
rows={8}
|
||||
operations={operations}
|
||||
getSchema={getSchema}
|
||||
getConstituenta={getConstituenta}
|
||||
getSchemaByCst={getSchemaByCst}
|
||||
substitutions={substitutions}
|
||||
setSubstitutions={setSubstitutions}
|
||||
/>
|
||||
|
|
|
@ -8,7 +8,8 @@ import Modal, { ModalProps } from '@/components/ui/Modal';
|
|||
import TabLabel from '@/components/ui/TabLabel';
|
||||
import useRSFormDetails from '@/hooks/useRSFormDetails';
|
||||
import { LibraryItemID } from '@/models/library';
|
||||
import { IBinarySubstitution, IInlineSynthesisData, IRSForm } from '@/models/rsform';
|
||||
import { ICstSubstitute } from '@/models/oss';
|
||||
import { IInlineSynthesisData, IRSForm } from '@/models/rsform';
|
||||
|
||||
import TabConstituents from './TabConstituents';
|
||||
import TabSchema from './TabSchema';
|
||||
|
@ -30,7 +31,7 @@ function DlgInlineSynthesis({ hideWindow, receiver, onInlineSynthesis }: DlgInli
|
|||
|
||||
const [donorID, setDonorID] = useState<LibraryItemID | undefined>(undefined);
|
||||
const [selected, setSelected] = useState<LibraryItemID[]>([]);
|
||||
const [substitutions, setSubstitutions] = useState<IBinarySubstitution[]>([]);
|
||||
const [substitutions, setSubstitutions] = useState<ICstSubstitute[]>([]);
|
||||
|
||||
const source = useRSFormDetails({ target: donorID ? String(donorID) : undefined });
|
||||
|
||||
|
@ -44,11 +45,7 @@ function DlgInlineSynthesis({ hideWindow, receiver, onInlineSynthesis }: DlgInli
|
|||
source: source.schema?.id,
|
||||
receiver: receiver.id,
|
||||
items: selected,
|
||||
substitutions: substitutions.map(item => ({
|
||||
original: item.deleteRight ? item.rightCst.id : item.leftCst.id,
|
||||
substitution: item.deleteRight ? item.leftCst.id : item.rightCst.id,
|
||||
transfer_term: !item.deleteRight && item.takeLeftTerm
|
||||
}))
|
||||
substitutions: substitutions
|
||||
};
|
||||
onInlineSynthesis(data);
|
||||
}
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
'use client';
|
||||
|
||||
import { ErrorData } from '@/components/info/InfoError';
|
||||
import DataLoader from '@/components/wrap/DataLoader';
|
||||
import { ConstituentaID, IBinarySubstitution, IRSForm } from '@/models/rsform';
|
||||
import { prefixes } from '@/utils/constants';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
|
||||
import PickInlineSubstitutions from '../../components/select/PickInlineSubstitutions';
|
||||
import { ErrorData } from '@/components/info/InfoError';
|
||||
import PickSubstitutions from '@/components/select/PickSubstitutions';
|
||||
import DataLoader from '@/components/wrap/DataLoader';
|
||||
import { ICstSubstitute } from '@/models/oss';
|
||||
import { ConstituentaID, IConstituenta, IRSForm } from '@/models/rsform';
|
||||
import { prefixes } from '@/utils/constants';
|
||||
|
||||
interface TabSubstitutionsProps {
|
||||
receiver?: IRSForm;
|
||||
|
@ -15,8 +17,8 @@ interface TabSubstitutionsProps {
|
|||
loading?: boolean;
|
||||
error?: ErrorData;
|
||||
|
||||
substitutions: IBinarySubstitution[];
|
||||
setSubstitutions: React.Dispatch<React.SetStateAction<IBinarySubstitution[]>>;
|
||||
substitutions: ICstSubstitute[];
|
||||
setSubstitutions: React.Dispatch<React.SetStateAction<ICstSubstitute[]>>;
|
||||
}
|
||||
|
||||
function TabSubstitutions({
|
||||
|
@ -30,16 +32,22 @@ function TabSubstitutions({
|
|||
substitutions,
|
||||
setSubstitutions
|
||||
}: TabSubstitutionsProps) {
|
||||
const filter = useCallback(
|
||||
(cst: IConstituenta) => cst.id !== source?.id || selected.includes(cst.id),
|
||||
[selected, source]
|
||||
);
|
||||
|
||||
const schemas = useMemo(() => [...(source ? [source] : []), ...(receiver ? [receiver] : [])], [source, receiver]);
|
||||
|
||||
return (
|
||||
<DataLoader id='dlg-substitutions-tab' className='cc-column' isLoading={loading} error={error} hasNoData={!source}>
|
||||
<PickInlineSubstitutions
|
||||
items={substitutions}
|
||||
setItems={setSubstitutions}
|
||||
<PickSubstitutions
|
||||
substitutions={substitutions}
|
||||
setSubstitutions={setSubstitutions}
|
||||
rows={10}
|
||||
prefixID={prefixes.cst_inline_synth_substitutes}
|
||||
schema1={receiver}
|
||||
schema2={source}
|
||||
filter2={cst => selected.includes(cst.id)}
|
||||
schemas={schemas}
|
||||
filter={filter}
|
||||
/>
|
||||
</DataLoader>
|
||||
);
|
||||
|
|
|
@ -3,31 +3,25 @@
|
|||
import clsx from 'clsx';
|
||||
import { useMemo, useState } from 'react';
|
||||
|
||||
import PickInlineSubstitutions from '@/components/select/PickInlineSubstitutions';
|
||||
import PickSubstitutions from '@/components/select/PickSubstitutions';
|
||||
import Modal, { ModalProps } from '@/components/ui/Modal';
|
||||
import { useRSForm } from '@/context/RSFormContext';
|
||||
import { ICstSubstituteData } from '@/models/oss';
|
||||
import { IBinarySubstitution } from '@/models/rsform';
|
||||
import { ICstSubstitute, ICstSubstituteData } from '@/models/oss';
|
||||
import { IRSForm } from '@/models/rsform';
|
||||
import { prefixes } from '@/utils/constants';
|
||||
|
||||
interface DlgSubstituteCstProps extends Pick<ModalProps, 'hideWindow'> {
|
||||
schema: IRSForm;
|
||||
onSubstitute: (data: ICstSubstituteData) => void;
|
||||
}
|
||||
|
||||
function DlgSubstituteCst({ hideWindow, onSubstitute }: DlgSubstituteCstProps) {
|
||||
const { schema } = useRSForm();
|
||||
|
||||
const [substitutions, setSubstitutions] = useState<IBinarySubstitution[]>([]);
|
||||
function DlgSubstituteCst({ hideWindow, onSubstitute, schema }: DlgSubstituteCstProps) {
|
||||
const [substitutions, setSubstitutions] = useState<ICstSubstitute[]>([]);
|
||||
|
||||
const canSubmit = useMemo(() => substitutions.length > 0, [substitutions]);
|
||||
|
||||
function handleSubmit() {
|
||||
const data: ICstSubstituteData = {
|
||||
substitutions: substitutions.map(item => ({
|
||||
original: item.deleteRight ? item.rightCst.id : item.leftCst.id,
|
||||
substitution: item.deleteRight ? item.leftCst.id : item.rightCst.id,
|
||||
transfer_term: !item.deleteRight && item.takeLeftTerm
|
||||
}))
|
||||
substitutions: substitutions
|
||||
};
|
||||
onSubstitute(data);
|
||||
}
|
||||
|
@ -42,13 +36,13 @@ function DlgSubstituteCst({ hideWindow, onSubstitute }: DlgSubstituteCstProps) {
|
|||
onSubmit={handleSubmit}
|
||||
className={clsx('w-[40rem]', 'px-6 pb-3')}
|
||||
>
|
||||
<PickInlineSubstitutions
|
||||
items={substitutions}
|
||||
setItems={setSubstitutions}
|
||||
<PickSubstitutions
|
||||
allowSelfSubstitution
|
||||
substitutions={substitutions}
|
||||
setSubstitutions={setSubstitutions}
|
||||
rows={6}
|
||||
prefixID={prefixes.dlg_cst_substitutes_list}
|
||||
schema1={schema}
|
||||
schema2={schema}
|
||||
schemas={[schema]}
|
||||
/>
|
||||
</Modal>
|
||||
);
|
||||
|
|
|
@ -30,7 +30,6 @@ export interface IOperation {
|
|||
alias: string;
|
||||
title: string;
|
||||
comment: string;
|
||||
sync_text: boolean;
|
||||
|
||||
position_x: number;
|
||||
position_y: number;
|
||||
|
@ -63,7 +62,7 @@ export interface ITargetOperation extends IPositionsData {
|
|||
export interface IOperationCreateData extends IPositionsData {
|
||||
item_data: Pick<
|
||||
IOperation,
|
||||
'alias' | 'operation_type' | 'title' | 'comment' | 'position_x' | 'position_y' | 'result' | 'sync_text'
|
||||
'alias' | 'operation_type' | 'title' | 'comment' | 'position_x' | 'position_y' | 'result'
|
||||
>;
|
||||
arguments: OperationID[] | undefined;
|
||||
create_schema: boolean;
|
||||
|
@ -73,7 +72,7 @@ export interface IOperationCreateData extends IPositionsData {
|
|||
* Represents {@link IOperation} data, used in update process.
|
||||
*/
|
||||
export interface IOperationUpdateData extends ITargetOperation {
|
||||
item_data: Pick<IOperation, 'alias' | 'title' | 'comment' | 'sync_text'>;
|
||||
item_data: Pick<IOperation, 'alias' | 'title' | 'comment'>;
|
||||
arguments: OperationID[] | undefined;
|
||||
substitutions: ICstSubstitute[] | undefined;
|
||||
}
|
||||
|
@ -82,7 +81,6 @@ export interface IOperationUpdateData extends ITargetOperation {
|
|||
* Represents {@link IOperation} data, used in setInput process.
|
||||
*/
|
||||
export interface IOperationSetInputData extends ITargetOperation {
|
||||
sync_text: boolean;
|
||||
input: LibraryItemID | null;
|
||||
}
|
||||
|
||||
|
@ -100,7 +98,6 @@ export interface IArgument {
|
|||
export interface ICstSubstitute {
|
||||
original: ConstituentaID;
|
||||
substitution: ConstituentaID;
|
||||
transfer_term: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -114,11 +111,10 @@ export interface ICstSubstituteData {
|
|||
* Represents substitution for multi synthesis table.
|
||||
*/
|
||||
export interface IMultiSubstitution {
|
||||
original_operation: IOperation | undefined;
|
||||
original_source: ILibraryItem | undefined;
|
||||
original: IConstituenta | undefined;
|
||||
substitution: IConstituenta | undefined;
|
||||
substitution_operation: IOperation | undefined;
|
||||
transfer_term: boolean;
|
||||
substitution_source: ILibraryItem | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -247,7 +247,6 @@ export interface IBinarySubstitution {
|
|||
leftCst: IConstituenta;
|
||||
rightCst: IConstituenta;
|
||||
deleteRight: boolean;
|
||||
takeLeftTerm: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -4,8 +4,6 @@ import {
|
|||
IconGenerateNames,
|
||||
IconGenerateStructure,
|
||||
IconInlineSynthesis,
|
||||
IconKeepAliasOn,
|
||||
IconKeepTermOn,
|
||||
IconReplace,
|
||||
IconSortList,
|
||||
IconTemplates
|
||||
|
@ -60,13 +58,7 @@ function HelpRSLangOperations() {
|
|||
</h2>
|
||||
<p>
|
||||
Формирование таблицы отождествлений и ее применение к текущей схеме. В результате будет удален ряд конституент и
|
||||
их вхождения заменены на другие. Возможна настройка какой термин использовать для оставшихся конституент
|
||||
<li>
|
||||
<IconKeepAliasOn size='1.25rem' className='inline-icon' /> выбор сохраняемой конституенты
|
||||
</li>
|
||||
<li>
|
||||
<IconKeepTermOn size='1.25rem' className='inline-icon' /> выбор сохраняемого термина
|
||||
</li>
|
||||
их вхождения заменены на другие.
|
||||
</p>
|
||||
|
||||
<h2>
|
||||
|
|
|
@ -266,6 +266,12 @@ function OssFlow({ isModified, setIsModified }: OssFlowProps) {
|
|||
handleSavePositions();
|
||||
return;
|
||||
}
|
||||
if ((event.ctrlKey || event.metaKey) && event.key === 'q') {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
handleCreateOperation();
|
||||
return;
|
||||
}
|
||||
if (event.key === 'Delete') {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
|
|
@ -128,7 +128,7 @@ function ToolbarOssGraph({
|
|||
onClick={onSavePositions}
|
||||
/>
|
||||
<MiniButton
|
||||
title={prepareTooltip('Новая операция', 'Ctrl + Q')}
|
||||
titleHtml={prepareTooltip('Новая операция', 'Ctrl + Q')}
|
||||
icon={<IconNewItem size='1.25rem' className='icon-green' />}
|
||||
disabled={controller.isProcessing}
|
||||
onClick={onCreate}
|
||||
|
|
|
@ -265,14 +265,13 @@ export const OssEditState = ({ selected, setSelected, children }: OssEditStatePr
|
|||
}, []);
|
||||
|
||||
const setTargetInput = useCallback(
|
||||
(newInput: LibraryItemID | undefined, syncText: boolean) => {
|
||||
(newInput: LibraryItemID | undefined) => {
|
||||
if (!targetOperationID) {
|
||||
return;
|
||||
}
|
||||
const data: IOperationSetInputData = {
|
||||
target: targetOperationID,
|
||||
positions: positions,
|
||||
sync_text: syncText,
|
||||
input: newInput ?? null
|
||||
};
|
||||
model.setInput(data, () => toast.success(information.changesSaved));
|
||||
|
|
|
@ -646,6 +646,7 @@ export const RSEditState = ({
|
|||
) : null}
|
||||
{showSubstitute ? (
|
||||
<DlgSubstituteCst
|
||||
schema={model.schema}
|
||||
hideWindow={() => setShowSubstitute(false)} // prettier: split lines
|
||||
onSubstitute={handleSubstituteCst}
|
||||
/>
|
||||
|
|
Loading…
Reference in New Issue
Block a user