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