Improting synthesis implementation pt1
This commit is contained in:
parent
8f53afa34e
commit
e144d156ac
|
@ -0,0 +1,69 @@
|
||||||
|
# Generated by Django 5.0.5 on 2024-06-21 15:47
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('rsform', '0007_location_and_flags'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='InputNode',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('vertical_coordinate', models.IntegerField(verbose_name='Вертикальная координата звена')),
|
||||||
|
('horizontal_coordinate', models.IntegerField(verbose_name='Горизонтальная координата звена')),
|
||||||
|
('rsform_id', models.IntegerField(null=True, verbose_name='Схема')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='SynthesisGraph',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('status', models.CharField(choices=[('Draft', 'Draft'), ('Completed', 'Completed'), ('Warning', 'Warning'), ('Failed', 'Failed')], max_length=20, verbose_name='Статус операции слияния')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='OperationNode',
|
||||||
|
fields=[
|
||||||
|
('inputnode_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='rsform.inputnode')),
|
||||||
|
('name', models.CharField(max_length=20, verbose_name='Название')),
|
||||||
|
('status', models.CharField(choices=[('Draft', 'Draft'), ('Completed', 'Completed'), ('Warning', 'Warning'), ('Failed', 'Failed')], max_length=20, verbose_name='Статус операции слияния')),
|
||||||
|
('left_parent', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='rsform_library_item_left', to='rsform.libraryitem', verbose_name='Левый предок')),
|
||||||
|
('right_parent', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='rsform_library_item_right', to='rsform.libraryitem', verbose_name='Правый предок')),
|
||||||
|
],
|
||||||
|
bases=('rsform.inputnode',),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='SynthesisEdge',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('decoded_id', models.CharField(max_length=30, verbose_name='Id ребра на фронте')),
|
||||||
|
('source_handle', models.CharField(max_length=30, verbose_name='')),
|
||||||
|
('node_from', models.IntegerField(verbose_name='Звено-предок')),
|
||||||
|
('node_to', models.IntegerField(verbose_name='Звено-наследник')),
|
||||||
|
('graph_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='rsform.synthesisgraph', verbose_name='Схема синтеза')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='inputnode',
|
||||||
|
name='graph_id',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='rsform.synthesisgraph', verbose_name='Схема синтеза'),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='SynthesisSubstitution',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('deleteRight', models.BooleanField(verbose_name='Удалить правую')),
|
||||||
|
('takeLeftTerm', models.BooleanField(verbose_name='Использовать термин левой')),
|
||||||
|
('graph_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='rsform.synthesisgraph', verbose_name='Схема синтеза')),
|
||||||
|
('leftCst', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='constituenta_original', to='rsform.constituenta', verbose_name='Конституента')),
|
||||||
|
('rightCst', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='constituenta_substitution', to='rsform.constituenta', verbose_name='Подстановка')),
|
||||||
|
('operation_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='rsform.operationnode', verbose_name='Операция синтеза')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
139
rsconcept/backend/apps/rsform/models/Synthesis.py
Normal file
139
rsconcept/backend/apps/rsform/models/Synthesis.py
Normal file
|
@ -0,0 +1,139 @@
|
||||||
|
from django.db.models import (
|
||||||
|
CASCADE, SET_NULL, ForeignKey, Model, PositiveIntegerField, QuerySet,
|
||||||
|
TextChoices, TextField, BooleanField, CharField, DateTimeField, JSONField, IntegerField, AutoField
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class OperationStatus(TextChoices):
|
||||||
|
DRAFT = 'Draft',
|
||||||
|
COMPLETED = 'Completed',
|
||||||
|
WARNING = 'Warning',
|
||||||
|
FAILED = 'Failed'
|
||||||
|
|
||||||
|
|
||||||
|
class GraphStatus(TextChoices):
|
||||||
|
DRAFT = 'Draft',
|
||||||
|
COMPLETED = 'Completed',
|
||||||
|
WARNING = 'Warning',
|
||||||
|
FAILED = 'Failed'
|
||||||
|
|
||||||
|
|
||||||
|
class SynthesisGraph(Model):
|
||||||
|
status: CharField = CharField(
|
||||||
|
verbose_name='Статус операции слияния',
|
||||||
|
max_length=20,
|
||||||
|
choices=GraphStatus
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class InputNode(Model):
|
||||||
|
graph_id: ForeignKey = ForeignKey(
|
||||||
|
verbose_name='Схема синтеза',
|
||||||
|
to=SynthesisGraph,
|
||||||
|
on_delete=CASCADE
|
||||||
|
)
|
||||||
|
|
||||||
|
vertical_coordinate: IntegerField = IntegerField(
|
||||||
|
verbose_name='Вертикальная координата звена',
|
||||||
|
)
|
||||||
|
|
||||||
|
horizontal_coordinate: IntegerField = IntegerField(
|
||||||
|
verbose_name='Горизонтальная координата звена',
|
||||||
|
)
|
||||||
|
|
||||||
|
rsform_id: IntegerField = IntegerField(
|
||||||
|
verbose_name='Схема',
|
||||||
|
null=True
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class OperationNode(InputNode):
|
||||||
|
name: CharField = CharField(
|
||||||
|
verbose_name='Название',
|
||||||
|
max_length=20
|
||||||
|
)
|
||||||
|
|
||||||
|
status: CharField = CharField(
|
||||||
|
verbose_name='Статус операции слияния',
|
||||||
|
max_length=20,
|
||||||
|
choices=OperationStatus
|
||||||
|
)
|
||||||
|
|
||||||
|
left_parent: ForeignKey = ForeignKey(
|
||||||
|
verbose_name='Левый предок',
|
||||||
|
to='rsform.LibraryItem',
|
||||||
|
related_name='rsform_library_item_left',
|
||||||
|
on_delete=SET_NULL,
|
||||||
|
null=True
|
||||||
|
)
|
||||||
|
|
||||||
|
right_parent: ForeignKey = ForeignKey(
|
||||||
|
verbose_name='Правый предок',
|
||||||
|
to='rsform.LibraryItem',
|
||||||
|
related_name='rsform_library_item_right',
|
||||||
|
on_delete=SET_NULL,
|
||||||
|
null=True
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class SynthesisSubstitution(Model):
|
||||||
|
graph_id: ForeignKey = ForeignKey(
|
||||||
|
verbose_name='Схема синтеза',
|
||||||
|
to=SynthesisGraph,
|
||||||
|
on_delete=CASCADE
|
||||||
|
)
|
||||||
|
|
||||||
|
operation_id: ForeignKey = ForeignKey(
|
||||||
|
verbose_name='Операция синтеза',
|
||||||
|
to=OperationNode,
|
||||||
|
on_delete=CASCADE
|
||||||
|
)
|
||||||
|
|
||||||
|
leftCst: ForeignKey = ForeignKey(
|
||||||
|
verbose_name='Конституента',
|
||||||
|
to='Constituenta',
|
||||||
|
related_name='constituenta_original',
|
||||||
|
on_delete=SET_NULL,
|
||||||
|
null=True
|
||||||
|
)
|
||||||
|
|
||||||
|
rightCst: ForeignKey = ForeignKey(
|
||||||
|
verbose_name='Подстановка',
|
||||||
|
to='Constituenta',
|
||||||
|
related_name='constituenta_substitution',
|
||||||
|
on_delete=SET_NULL,
|
||||||
|
null=True
|
||||||
|
)
|
||||||
|
|
||||||
|
deleteRight: BooleanField = BooleanField(
|
||||||
|
verbose_name='Удалить правую'
|
||||||
|
)
|
||||||
|
|
||||||
|
takeLeftTerm: BooleanField = BooleanField(
|
||||||
|
verbose_name='Использовать термин левой'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class SynthesisEdge(Model):
|
||||||
|
decoded_id: CharField = CharField(
|
||||||
|
verbose_name='Id ребра на фронте',
|
||||||
|
max_length=30,
|
||||||
|
)
|
||||||
|
|
||||||
|
source_handle: CharField = CharField(
|
||||||
|
verbose_name='',
|
||||||
|
max_length=30,
|
||||||
|
)
|
||||||
|
graph_id: ForeignKey = ForeignKey(
|
||||||
|
verbose_name='Схема синтеза',
|
||||||
|
to=SynthesisGraph,
|
||||||
|
on_delete=CASCADE
|
||||||
|
)
|
||||||
|
|
||||||
|
node_from: IntegerField = IntegerField(
|
||||||
|
verbose_name='Звено-предок',
|
||||||
|
)
|
||||||
|
|
||||||
|
node_to: IntegerField = IntegerField(
|
||||||
|
verbose_name='Звено-наследник',
|
||||||
|
)
|
|
@ -38,3 +38,14 @@ from .schema_typing import (
|
||||||
NewVersionResponse,
|
NewVersionResponse,
|
||||||
ResultTextResponse
|
ResultTextResponse
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from .io_pyconcept import PyConceptAdapter
|
||||||
|
from .io_files import (
|
||||||
|
FileSerializer,
|
||||||
|
RSFormUploadSerializer,
|
||||||
|
RSFormTRSSerializer
|
||||||
|
)
|
||||||
|
|
||||||
|
from .synthesis import (
|
||||||
|
SynthesisGraphSerializer
|
||||||
|
)
|
||||||
|
|
|
@ -405,11 +405,11 @@ class InlineSynthesisSerializer(serializers.Serializer):
|
||||||
user = cast(User, self.context['user'])
|
user = cast(User, self.context['user'])
|
||||||
schema_in = cast(LibraryItem, attrs['source'])
|
schema_in = cast(LibraryItem, attrs['source'])
|
||||||
schema_out = cast(LibraryItem, attrs['receiver'])
|
schema_out = cast(LibraryItem, attrs['receiver'])
|
||||||
if user.is_anonymous or (schema_out.owner != user and not user.is_staff):
|
#if user.is_anonymous or (schema_out.owner != user and not user.is_staff):
|
||||||
raise PermissionDenied({
|
# raise PermissionDenied({
|
||||||
'message': msg.schemaNotOwned(),
|
# 'message': msg.schemaNotOwned(),
|
||||||
'object_id': schema_in.id
|
# 'object_id': schema_in.id
|
||||||
})
|
# })
|
||||||
constituents = cast(list[Constituenta], attrs['items'])
|
constituents = cast(list[Constituenta], attrs['items'])
|
||||||
for cst in constituents:
|
for cst in constituents:
|
||||||
if cst.schema != schema_in:
|
if cst.schema != schema_in:
|
||||||
|
|
112
rsconcept/backend/apps/rsform/serializers/synthesis.py
Normal file
112
rsconcept/backend/apps/rsform/serializers/synthesis.py
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
from rest_framework import serializers
|
||||||
|
from .data_access import CstSubstituteSerializerBase
|
||||||
|
from rest_framework.serializers import PrimaryKeyRelatedField as PKField
|
||||||
|
|
||||||
|
from ..models import Constituenta, LibraryItem
|
||||||
|
from ..models.Synthesis import SynthesisGraph, SynthesisEdge, InputNode, OperationNode, SynthesisSubstitution
|
||||||
|
|
||||||
|
|
||||||
|
class SynthesisGraphSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = SynthesisGraph
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
def create(self, validated_data):
|
||||||
|
graph, created = SynthesisGraph.objects.update_or_create(
|
||||||
|
id=validated_data['id'],
|
||||||
|
defaults={'status': validated_data['status']}
|
||||||
|
)
|
||||||
|
return graph
|
||||||
|
|
||||||
|
|
||||||
|
class InputNodeSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = InputNode
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
def create(self, validated_data_list):
|
||||||
|
for validated_data in validated_data_list:
|
||||||
|
input_node, created = InputNode.objects.update_or_create(
|
||||||
|
id=validated_data['id'] if validated_data.get('id') else None,
|
||||||
|
defaults={
|
||||||
|
'graph_id': validated_data['graph_id'],
|
||||||
|
'vertical_coordinate': validated_data['vertical_coordinate'],
|
||||||
|
'horizontal_coordinate': validated_data['horizontal_coordinate'],
|
||||||
|
'rsform_id': validated_data['rsform_id'],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
class OperationNodeSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = OperationNode
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
def create(self, validated_data_list):
|
||||||
|
operations = []
|
||||||
|
for validated_data in validated_data_list:
|
||||||
|
operation_node, created = OperationNode.objects.update_or_create(
|
||||||
|
id=validated_data['id'],
|
||||||
|
defaults={
|
||||||
|
'graph_id': validated_data['graph_id'],
|
||||||
|
'vertical_coordinate': validated_data['vertical_coordinate'],
|
||||||
|
'horizontal_coordinate': validated_data['horizontal_coordinate'],
|
||||||
|
'rsform_id': validated_data['rsform_id'],
|
||||||
|
'left_parent': validated_data.get('left_parent'),
|
||||||
|
'right_parent': validated_data.get('right_parent'),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
operations.append(operation_node)
|
||||||
|
return operations
|
||||||
|
|
||||||
|
|
||||||
|
class SynthesisSubstitutionSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = SynthesisSubstitution
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
def create(self, validated_data_list):
|
||||||
|
substitutions = []
|
||||||
|
for validated_data in validated_data_list:
|
||||||
|
substitution, created = SynthesisSubstitution.objects.update_or_create(
|
||||||
|
id=validated_data['id'],
|
||||||
|
defaults={
|
||||||
|
'operation_id': validated_data['operation_id'],
|
||||||
|
'graph_id': validated_data['graph_id'],
|
||||||
|
'leftCst': validated_data['leftCst'],
|
||||||
|
'rightCst': validated_data['rightCst'],
|
||||||
|
'deleteRight': validated_data['deleteRight'],
|
||||||
|
'takeLeftTerm': validated_data['takeLeftTerm'],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
substitutions.append(substitution)
|
||||||
|
return substitutions
|
||||||
|
|
||||||
|
|
||||||
|
class SynthesisEdgeSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = SynthesisEdge
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
def create(self, validated_data_list):
|
||||||
|
for validated_data in validated_data_list:
|
||||||
|
substitution, created = SynthesisEdge.objects.update_or_create(
|
||||||
|
id=validated_data['id'],
|
||||||
|
defaults={
|
||||||
|
'graph_id': validated_data['graph_id'],
|
||||||
|
'decoded_id': validated_data['decoded_id'],
|
||||||
|
'source_handle': validated_data['source_handle'],
|
||||||
|
'node_from': validated_data['node_from'],
|
||||||
|
'node_to': validated_data['node_to'],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
class RunSingleSynthesis(serializers.Serializer):
|
||||||
|
operationId = serializers.IntegerField()
|
||||||
|
|
||||||
|
|
||||||
|
class RunSingleSynthesisResponse(serializers.Serializer):
|
||||||
|
rsformId = serializers.IntegerField()
|
|
@ -30,6 +30,9 @@ urlpatterns = [
|
||||||
path('cctext/inflect', views.inflect),
|
path('cctext/inflect', views.inflect),
|
||||||
path('cctext/generate-lexeme', views.generate_lexeme),
|
path('cctext/generate-lexeme', views.generate_lexeme),
|
||||||
path('cctext/parse', views.parse_text),
|
path('cctext/parse', views.parse_text),
|
||||||
|
path('synthesis/run_single', views.run_synthesis_view),
|
||||||
|
path('synthesis/run_all', views.run_sythesis_graph_view),
|
||||||
|
path('synthesis/save', views.save_synthesis_graph),
|
||||||
|
path('synthesis/<int:pk_item>', views.get_synthesis_graph),
|
||||||
path('', include(library_router.urls)),
|
path('', include(library_router.urls)),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
''' Utility functions '''
|
''' Utility functions '''
|
||||||
|
import copy
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
@ -66,3 +67,15 @@ def filename_for_schema(alias: str) -> str:
|
||||||
# are not supported by some browsers
|
# are not supported by some browsers
|
||||||
return 'Schema.trs'
|
return 'Schema.trs'
|
||||||
return alias + '.trs'
|
return alias + '.trs'
|
||||||
|
|
||||||
|
|
||||||
|
def clone_rsform(rsform):
|
||||||
|
rsform_copy = copy.deepcopy(rsform)
|
||||||
|
rsform_copy.item.pk = None
|
||||||
|
# rsform_copy.item.owner = "System"
|
||||||
|
rsform_copy.item.comment = "Temporary cloned rsform"
|
||||||
|
rsform_copy.item.save()
|
||||||
|
|
||||||
|
rsform_copy.insert_copy(items=[cst for cst in rsform.item.constituenta_set.all()], position=1)
|
||||||
|
rsform_copy.item.save()
|
||||||
|
return rsform_copy
|
||||||
|
|
|
@ -6,3 +6,10 @@ from .operations import inline_synthesis
|
||||||
from .rsforms import RSFormViewSet, TrsImportView, create_rsform
|
from .rsforms import RSFormViewSet, TrsImportView, create_rsform
|
||||||
from .rslang import convert_to_ascii, convert_to_math, parse_expression
|
from .rslang import convert_to_ascii, convert_to_math, parse_expression
|
||||||
from .versions import VersionViewset, create_version, export_file, retrieve_version
|
from .versions import VersionViewset, create_version, export_file, retrieve_version
|
||||||
|
|
||||||
|
from .synthesis import (
|
||||||
|
run_synthesis_view,
|
||||||
|
run_sythesis_graph_view,
|
||||||
|
save_synthesis_graph,
|
||||||
|
get_synthesis_graph,
|
||||||
|
)
|
||||||
|
|
224
rsconcept/backend/apps/rsform/views/synthesis.py
Normal file
224
rsconcept/backend/apps/rsform/views/synthesis.py
Normal file
|
@ -0,0 +1,224 @@
|
||||||
|
import copy
|
||||||
|
|
||||||
|
from drf_spectacular.utils import extend_schema
|
||||||
|
from rest_framework import status
|
||||||
|
from rest_framework.decorators import api_view
|
||||||
|
from rest_framework.request import Request
|
||||||
|
from rest_framework.response import Response
|
||||||
|
from django.db.models import Q
|
||||||
|
from rest_framework.views import APIView
|
||||||
|
|
||||||
|
from ..models.Constituenta import Constituenta
|
||||||
|
from ..models.LibraryItem import LibraryItem
|
||||||
|
from ..models.api_RSForm import RSForm
|
||||||
|
from ..models.Synthesis import SynthesisGraph, InputNode, OperationNode, SynthesisSubstitution, SynthesisEdge
|
||||||
|
from ..serializers import RSFormSerializer, SynthesisGraphSerializer, InlineSynthesisSerializer
|
||||||
|
from typing import cast
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
|
||||||
|
from ..serializers.data_access import CstBaseSerializer, CstSerializer
|
||||||
|
from ..serializers.synthesis import OperationNodeSerializer, InputNodeSerializer, \
|
||||||
|
SynthesisSubstitutionSerializer, SynthesisEdgeSerializer, RunSingleSynthesis, RunSingleSynthesisResponse
|
||||||
|
from ..utils import clone_rsform
|
||||||
|
|
||||||
|
|
||||||
|
@extend_schema(
|
||||||
|
summary='Get synthesis graph',
|
||||||
|
tags=['Synthesis'],
|
||||||
|
auth=None
|
||||||
|
)
|
||||||
|
@api_view(['GET'])
|
||||||
|
def get_synthesis_graph(request: Request, pk_item: int):
|
||||||
|
input_nodes = InputNode.objects.filter(graph_id=pk_item)
|
||||||
|
operation_nodes = OperationNode.objects.filter(graph_id=pk_item)
|
||||||
|
edges = SynthesisEdge.objects.filter(graph_id=pk_item)
|
||||||
|
substitutions = []
|
||||||
|
for operation_node in operation_nodes:
|
||||||
|
substitution_batch = SynthesisSubstitution.objects.filter(operation_id=operation_node.id)
|
||||||
|
for substitution in substitution_batch:
|
||||||
|
substitutions.append(substitution)
|
||||||
|
|
||||||
|
synthesis_graph = SynthesisGraphSerializer()
|
||||||
|
synthesis_graph.create(validated_data={'id': pk_item, 'status': 'Draft'})
|
||||||
|
input_nodes = InputNodeSerializer(instance=input_nodes, many=True)
|
||||||
|
operation_nodes = (OperationNodeSerializer(instance=operation_nodes, many=True))
|
||||||
|
edges = SynthesisEdgeSerializer(instance=edges, many=True)
|
||||||
|
substitutions = SynthesisSubstitutionSerializer(instance=substitutions, many=True)
|
||||||
|
for substitution in substitutions.data:
|
||||||
|
substitution['leftCst'] = CstSerializer(instance=Constituenta.objects.get(id=substitution['leftCst'])).data
|
||||||
|
substitution['rightCst'] = CstSerializer(instance=Constituenta.objects.get(id=substitution['rightCst'])).data
|
||||||
|
return Response(data={
|
||||||
|
'graph': synthesis_graph.data,
|
||||||
|
'input_nodes': input_nodes.data,
|
||||||
|
'operation_nodes': operation_nodes.data,
|
||||||
|
'edges': edges.data,
|
||||||
|
'substitutions': substitutions.data,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
@extend_schema(
|
||||||
|
summary='Save synthesis graph',
|
||||||
|
tags=['Synthesis'],
|
||||||
|
request=SynthesisGraphSerializer,
|
||||||
|
responses={status.HTTP_200_OK: RSFormSerializer},
|
||||||
|
auth=None
|
||||||
|
)
|
||||||
|
@api_view(['POST'])
|
||||||
|
def save_synthesis_graph(request: Request):
|
||||||
|
graph_data = request.data.get('graph')
|
||||||
|
input_nodes_data = request.data.get('input_nodes')
|
||||||
|
operation_nodes_data = request.data.get('operation_nodes')
|
||||||
|
edges_data = request.data.get('edges')
|
||||||
|
substitutions_data = request.data.get('substitutions')
|
||||||
|
|
||||||
|
synthesis_graph_serializer = SynthesisGraphSerializer()
|
||||||
|
graph = synthesis_graph_serializer.create(validated_data=graph_data)
|
||||||
|
|
||||||
|
InputNode.objects.filter(graph_id=graph).delete()
|
||||||
|
OperationNode.objects.filter(graph_id=graph).delete()
|
||||||
|
SynthesisEdge.objects.filter(graph_id=graph).delete()
|
||||||
|
SynthesisSubstitution.objects.filter(graph_id=graph).delete()
|
||||||
|
|
||||||
|
input_node_serializer = InputNodeSerializer()
|
||||||
|
for input_node in input_nodes_data:
|
||||||
|
input_node['graph_id'] = graph
|
||||||
|
input_node_serializer.create(validated_data_list=input_nodes_data)
|
||||||
|
|
||||||
|
for operation_node in operation_nodes_data:
|
||||||
|
operation_node['graph_id'] = graph
|
||||||
|
operation_node['left_parent'] = LibraryItem.objects.get(id=operation_node['left_parent'])
|
||||||
|
operation_node['right_parent'] = LibraryItem.objects.get(id=operation_node['right_parent'])
|
||||||
|
operation_node_serializer = OperationNodeSerializer()
|
||||||
|
operations = operation_node_serializer.create(validated_data_list=operation_nodes_data)
|
||||||
|
|
||||||
|
for edge in edges_data:
|
||||||
|
edge['graph_id'] = graph
|
||||||
|
|
||||||
|
edge_serializer = SynthesisEdgeSerializer()
|
||||||
|
edge_serializer.create(validated_data_list=edges_data)
|
||||||
|
|
||||||
|
operations_dict = {operation.id: operation for operation in operations}
|
||||||
|
for substitution_data in substitutions_data:
|
||||||
|
substitution_data['operation_id'] = operations_dict[substitution_data['operation_id']]
|
||||||
|
substitution_data['rightCst'] = Constituenta.objects.get(id=substitution_data['rightCst']['id'])
|
||||||
|
substitution_data['leftCst'] = Constituenta.objects.get(id=substitution_data['leftCst']['id'])
|
||||||
|
substitution_data['graph_id'] = graph
|
||||||
|
|
||||||
|
substitution_serializer = SynthesisSubstitutionSerializer()
|
||||||
|
substitutions = substitution_serializer.create(validated_data_list=substitutions_data)
|
||||||
|
|
||||||
|
return Response(synthesis_graph_serializer.data, status=status.HTTP_201_CREATED)
|
||||||
|
|
||||||
|
|
||||||
|
@extend_schema(
|
||||||
|
summary='Run synthesis graph',
|
||||||
|
tags=['Synthesis'],
|
||||||
|
request=RunSingleSynthesis,
|
||||||
|
responses={status.HTTP_200_OK: RunSingleSynthesisResponse},
|
||||||
|
auth=None
|
||||||
|
)
|
||||||
|
@api_view(['POST'])
|
||||||
|
def run_sythesis_graph_view(request: Request):
|
||||||
|
serializer = RunSingleSynthesis(data=request.data)
|
||||||
|
serializer.is_valid(raise_exception=True)
|
||||||
|
for atomic_synthesis in serializer.validated_data:
|
||||||
|
run_synthesis(atomic_synthesis)
|
||||||
|
|
||||||
|
|
||||||
|
@extend_schema(
|
||||||
|
summary='Run synthesis operation',
|
||||||
|
tags=['Synthesis'],
|
||||||
|
request=RunSingleSynthesis,
|
||||||
|
responses={status.HTTP_200_OK: RunSingleSynthesisResponse},
|
||||||
|
auth=None
|
||||||
|
)
|
||||||
|
@api_view(['POST'])
|
||||||
|
def run_synthesis_view(request: Request):
|
||||||
|
serializer = RunSingleSynthesis(
|
||||||
|
data=request.data
|
||||||
|
)
|
||||||
|
serializer.is_valid(raise_exception=True)
|
||||||
|
return run_synthesis(serializer=serializer)
|
||||||
|
|
||||||
|
|
||||||
|
def run_synthesis(serializer: RunSingleSynthesis):
|
||||||
|
operation_id = serializer.data['operationId']
|
||||||
|
operation = OperationNode.objects.get(id=operation_id)
|
||||||
|
|
||||||
|
left_schema = RSForm(operation.left_parent)
|
||||||
|
right_schema = RSForm(operation.right_parent)
|
||||||
|
substitutions = SynthesisSubstitution.objects.filter(operation_id=operation_id)
|
||||||
|
|
||||||
|
left_schema_copy = clone_rsform(left_schema)
|
||||||
|
right_constituents = right_schema.item.constituenta_set.filter()
|
||||||
|
left_schema_copy.insert_copy(right_constituents)
|
||||||
|
|
||||||
|
for substitution in substitutions:
|
||||||
|
original = cast(Constituenta, substitution.leftCst)
|
||||||
|
replacement = cast(Constituenta, substitution.rightCst)
|
||||||
|
left_schema_copy.substitute(original, replacement, (not substitution.deleteRight) and substitution.takeLeftTerm)
|
||||||
|
|
||||||
|
left_schema.restore_order()
|
||||||
|
return Response(
|
||||||
|
status=status.HTTP_200_OK,
|
||||||
|
data=RSFormSerializer(left_schema_copy.item).data
|
||||||
|
)
|
||||||
|
|
||||||
|
right_rsform_copy = clone_rsform(right_schema)
|
||||||
|
|
||||||
|
serializer.is_valid(raise_exception=True)
|
||||||
|
|
||||||
|
try:
|
||||||
|
mapping = serializer.validated_data['mapping']
|
||||||
|
left_cst_pks = [x.get("left_cst_pk") for x in mapping]
|
||||||
|
right_cst_pks = [x.get("right_cst_pk") for x in mapping]
|
||||||
|
directions = [x.get("mapping_direction") for x in mapping]
|
||||||
|
left_csts = left_schema.item.constituenta_set.filter(pk__in=left_cst_pks)
|
||||||
|
right_csts = right_schema.item.constituenta_set.filter(pk__in=right_cst_pks)
|
||||||
|
|
||||||
|
left_mapping_dict = {left.alias: right.alias for left, right, direction in
|
||||||
|
zip(left_csts, right_csts, directions) if
|
||||||
|
not direction}
|
||||||
|
right_mapping_dict = {right.alias: left.alias for left, right, direction in
|
||||||
|
zip(left_csts, right_csts, directions)
|
||||||
|
if direction}
|
||||||
|
|
||||||
|
left_schema_copy.apply_mapping(mapping=left_mapping_dict)
|
||||||
|
right_rsform_copy.apply_mapping(mapping=right_mapping_dict)
|
||||||
|
left_schema_copy.resolve_all_text()
|
||||||
|
right_rsform_copy.resolve_all_text()
|
||||||
|
left_schema_copy.item.save()
|
||||||
|
right_rsform_copy.item.save()
|
||||||
|
|
||||||
|
for left, right in zip(left_csts, right_csts):
|
||||||
|
# left_rsform_copy.substitute(original=left, substitution=right, transfer_term=False)
|
||||||
|
# right_rsform_copy.substitute(original=right, substitution=left, transfer_term=False)
|
||||||
|
left_schema_copy.item.save()
|
||||||
|
right_rsform_copy.item.save()
|
||||||
|
|
||||||
|
right_cst_pks = set(right_cst_pks)
|
||||||
|
for cst in right_rsform_copy.item.constituenta_set.all():
|
||||||
|
if cst.pk not in right_cst_pks:
|
||||||
|
max_idx = left_schema.get_max_index(cst.cst_type)
|
||||||
|
left_schema_copy.insert_copy(items=[cst], position=max_idx + 1)
|
||||||
|
left_schema_copy.item.save()
|
||||||
|
|
||||||
|
right_rsform_copy.item.delete()
|
||||||
|
|
||||||
|
serializer = RSFormParseSerializer(cast(LibraryItem, left_schema_copy.item))
|
||||||
|
|
||||||
|
# TODO: remove next line
|
||||||
|
left_schema_copy.item.delete()
|
||||||
|
|
||||||
|
return Response(
|
||||||
|
status=status.HTTP_200_OK,
|
||||||
|
data=serializer.data
|
||||||
|
)
|
||||||
|
# TODO: rework 500
|
||||||
|
except Exception as e:
|
||||||
|
left_schema_copy.item.delete()
|
||||||
|
right_rsform_copy.item.delete()
|
||||||
|
raise e
|
||||||
|
return Response(
|
||||||
|
status=status.HTTP_500_INTERNAL_SERVER_ERROR
|
||||||
|
)
|
15
rsconcept/frontend/.vscode/launch.json
vendored
Normal file
15
rsconcept/frontend/.vscode/launch.json
vendored
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
// Use IntelliSense to learn about possible attributes.
|
||||||
|
// Hover to view descriptions of existing attributes.
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"type": "chrome",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Launch Chrome against localhost",
|
||||||
|
"url": "http://localhost:8080",
|
||||||
|
"webRoot": "${workspaceFolder}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
341
rsconcept/frontend/package-lock.json
generated
341
rsconcept/frontend/package-lock.json
generated
|
@ -9,6 +9,7 @@
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@lezer/lr": "^1.4.1",
|
"@lezer/lr": "^1.4.1",
|
||||||
|
"@reactflow/core": "^11.8.3",
|
||||||
"@tanstack/react-table": "^8.17.3",
|
"@tanstack/react-table": "^8.17.3",
|
||||||
"@uiw/codemirror-themes": "^4.22.2",
|
"@uiw/codemirror-themes": "^4.22.2",
|
||||||
"@uiw/react-codemirror": "^4.22.2",
|
"@uiw/react-codemirror": "^4.22.2",
|
||||||
|
@ -2834,6 +2835,53 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@reactflow/core": {
|
||||||
|
"version": "11.11.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@reactflow/core/-/core-11.11.3.tgz",
|
||||||
|
"integrity": "sha512-+adHdUa7fJSEM93fWfjQwyWXeI92a1eLKwWbIstoCakHpL8UjzwhEh6sn+mN2h/59MlVI7Ehr1iGTt3MsfcIFA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/d3": "^7.4.0",
|
||||||
|
"@types/d3-drag": "^3.0.1",
|
||||||
|
"@types/d3-selection": "^3.0.3",
|
||||||
|
"@types/d3-zoom": "^3.0.1",
|
||||||
|
"classcat": "^5.0.3",
|
||||||
|
"d3-drag": "^3.0.0",
|
||||||
|
"d3-selection": "^3.0.0",
|
||||||
|
"d3-zoom": "^3.0.0",
|
||||||
|
"zustand": "^4.4.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=17",
|
||||||
|
"react-dom": ">=17"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@reactflow/core/node_modules/zustand": {
|
||||||
|
"version": "4.5.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.2.tgz",
|
||||||
|
"integrity": "sha512-2cN1tPkDVkwCy5ickKrI7vijSjPksFRfqS6237NzT0vqSsztTNnQdHw9mmN7uBdk3gceVXU0a+21jFzFzAc9+g==",
|
||||||
|
"dependencies": {
|
||||||
|
"use-sync-external-store": "1.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.7.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": ">=16.8",
|
||||||
|
"immer": ">=9.0.6",
|
||||||
|
"react": ">=16.8"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"immer": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@remix-run/router": {
|
"node_modules/@remix-run/router": {
|
||||||
"version": "1.16.1",
|
"version": "1.16.1",
|
||||||
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.16.1.tgz",
|
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.16.1.tgz",
|
||||||
|
@ -2954,12 +3002,239 @@
|
||||||
"@babel/types": "^7.20.7"
|
"@babel/types": "^7.20.7"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/d3": {
|
||||||
|
"version": "7.4.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.3.tgz",
|
||||||
|
"integrity": "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/d3-array": "*",
|
||||||
|
"@types/d3-axis": "*",
|
||||||
|
"@types/d3-brush": "*",
|
||||||
|
"@types/d3-chord": "*",
|
||||||
|
"@types/d3-color": "*",
|
||||||
|
"@types/d3-contour": "*",
|
||||||
|
"@types/d3-delaunay": "*",
|
||||||
|
"@types/d3-dispatch": "*",
|
||||||
|
"@types/d3-drag": "*",
|
||||||
|
"@types/d3-dsv": "*",
|
||||||
|
"@types/d3-ease": "*",
|
||||||
|
"@types/d3-fetch": "*",
|
||||||
|
"@types/d3-force": "*",
|
||||||
|
"@types/d3-format": "*",
|
||||||
|
"@types/d3-geo": "*",
|
||||||
|
"@types/d3-hierarchy": "*",
|
||||||
|
"@types/d3-interpolate": "*",
|
||||||
|
"@types/d3-path": "*",
|
||||||
|
"@types/d3-polygon": "*",
|
||||||
|
"@types/d3-quadtree": "*",
|
||||||
|
"@types/d3-random": "*",
|
||||||
|
"@types/d3-scale": "*",
|
||||||
|
"@types/d3-scale-chromatic": "*",
|
||||||
|
"@types/d3-selection": "*",
|
||||||
|
"@types/d3-shape": "*",
|
||||||
|
"@types/d3-time": "*",
|
||||||
|
"@types/d3-time-format": "*",
|
||||||
|
"@types/d3-timer": "*",
|
||||||
|
"@types/d3-transition": "*",
|
||||||
|
"@types/d3-zoom": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-array": {
|
||||||
|
"version": "3.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz",
|
||||||
|
"integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg=="
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-axis": {
|
||||||
|
"version": "3.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.6.tgz",
|
||||||
|
"integrity": "sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/d3-selection": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-brush": {
|
||||||
|
"version": "3.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.6.tgz",
|
||||||
|
"integrity": "sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/d3-selection": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-chord": {
|
||||||
|
"version": "3.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.6.tgz",
|
||||||
|
"integrity": "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg=="
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-color": {
|
||||||
|
"version": "3.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz",
|
||||||
|
"integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A=="
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-contour": {
|
||||||
|
"version": "3.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.6.tgz",
|
||||||
|
"integrity": "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/d3-array": "*",
|
||||||
|
"@types/geojson": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-delaunay": {
|
||||||
|
"version": "6.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz",
|
||||||
|
"integrity": "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw=="
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-dispatch": {
|
||||||
|
"version": "3.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.6.tgz",
|
||||||
|
"integrity": "sha512-4fvZhzMeeuBJYZXRXrRIQnvUYfyXwYmLsdiN7XXmVNQKKw1cM8a5WdID0g1hVFZDqT9ZqZEY5pD44p24VS7iZQ=="
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-drag": {
|
||||||
|
"version": "3.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz",
|
||||||
|
"integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/d3-selection": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-dsv": {
|
||||||
|
"version": "3.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.7.tgz",
|
||||||
|
"integrity": "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g=="
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-ease": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA=="
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-fetch": {
|
||||||
|
"version": "3.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.7.tgz",
|
||||||
|
"integrity": "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/d3-dsv": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-force": {
|
||||||
|
"version": "3.0.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.9.tgz",
|
||||||
|
"integrity": "sha512-IKtvyFdb4Q0LWna6ymywQsEYjK/94SGhPrMfEr1TIc5OBeziTi+1jcCvttts8e0UWZIxpasjnQk9MNk/3iS+kA=="
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-format": {
|
||||||
|
"version": "3.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.4.tgz",
|
||||||
|
"integrity": "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g=="
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-geo": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/geojson": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-hierarchy": {
|
||||||
|
"version": "3.1.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.7.tgz",
|
||||||
|
"integrity": "sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg=="
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-interpolate": {
|
||||||
|
"version": "3.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz",
|
||||||
|
"integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/d3-color": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-path": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-P2dlU/q51fkOc/Gfl3Ul9kicV7l+ra934qBFXCFhrZMOL6du1TM0pm1ThYvENukyOn5h9v+yMJ9Fn5JK4QozrQ=="
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-polygon": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA=="
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-quadtree": {
|
||||||
|
"version": "3.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz",
|
||||||
|
"integrity": "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg=="
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-random": {
|
||||||
|
"version": "3.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.3.tgz",
|
||||||
|
"integrity": "sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ=="
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-scale": {
|
||||||
|
"version": "4.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.8.tgz",
|
||||||
|
"integrity": "sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/d3-time": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-scale-chromatic": {
|
||||||
|
"version": "3.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.0.3.tgz",
|
||||||
|
"integrity": "sha512-laXM4+1o5ImZv3RpFAsTRn3TEkzqkytiOY0Dz0sq5cnd1dtNlk6sHLon4OvqaiJb28T0S/TdsBI3Sjsy+keJrw=="
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-selection": {
|
||||||
|
"version": "3.0.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.10.tgz",
|
||||||
|
"integrity": "sha512-cuHoUgS/V3hLdjJOLTT691+G2QoqAjCVLmr4kJXR4ha56w1Zdu8UUQ5TxLRqudgNjwXeQxKMq4j+lyf9sWuslg=="
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-shape": {
|
||||||
|
"version": "3.1.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.6.tgz",
|
||||||
|
"integrity": "sha512-5KKk5aKGu2I+O6SONMYSNflgiP0WfZIQvVUMan50wHsLG1G94JlxEVnCpQARfTtzytuY0p/9PXXZb3I7giofIA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/d3-path": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-time": {
|
||||||
|
"version": "3.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.3.tgz",
|
||||||
|
"integrity": "sha512-2p6olUZ4w3s+07q3Tm2dbiMZy5pCDfYwtLXXHUnVzXgQlZ/OyPtUz6OL382BkOuGlLXqfT+wqv8Fw2v8/0geBw=="
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-time-format": {
|
||||||
|
"version": "4.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.3.tgz",
|
||||||
|
"integrity": "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg=="
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-timer": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw=="
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-transition": {
|
||||||
|
"version": "3.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.8.tgz",
|
||||||
|
"integrity": "sha512-ew63aJfQ/ms7QQ4X7pk5NxQ9fZH/z+i24ZfJ6tJSfqxJMrYLiK01EAs2/Rtw/JreGUsS3pLPNV644qXFGnoZNQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/d3-selection": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-zoom": {
|
||||||
|
"version": "3.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.8.tgz",
|
||||||
|
"integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/d3-interpolate": "*",
|
||||||
|
"@types/d3-selection": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/draco3d": {
|
"node_modules/@types/draco3d": {
|
||||||
"version": "1.4.10",
|
"version": "1.4.10",
|
||||||
"resolved": "https://registry.npmjs.org/@types/draco3d/-/draco3d-1.4.10.tgz",
|
"resolved": "https://registry.npmjs.org/@types/draco3d/-/draco3d-1.4.10.tgz",
|
||||||
"integrity": "sha512-AX22jp8Y7wwaBgAixaSvkoG4M/+PlAcm3Qs4OW8yT9DM4xUpWKeFhLueTAyZF39pviAdcDdeJoACapiAceqNcw==",
|
"integrity": "sha512-AX22jp8Y7wwaBgAixaSvkoG4M/+PlAcm3Qs4OW8yT9DM4xUpWKeFhLueTAyZF39pviAdcDdeJoACapiAceqNcw==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/geojson": {
|
||||||
|
"version": "7946.0.14",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.14.tgz",
|
||||||
|
"integrity": "sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg=="
|
||||||
|
},
|
||||||
"node_modules/@types/graceful-fs": {
|
"node_modules/@types/graceful-fs": {
|
||||||
"version": "4.1.9",
|
"version": "4.1.9",
|
||||||
"resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz",
|
"resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz",
|
||||||
|
@ -4211,6 +4486,11 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/classcat": {
|
||||||
|
"version": "5.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/classcat/-/classcat-5.0.5.tgz",
|
||||||
|
"integrity": "sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w=="
|
||||||
|
},
|
||||||
"node_modules/classnames": {
|
"node_modules/classnames": {
|
||||||
"version": "2.5.1",
|
"version": "2.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz",
|
||||||
|
@ -4577,6 +4857,26 @@
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/d3-drag": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==",
|
||||||
|
"dependencies": {
|
||||||
|
"d3-dispatch": "1 - 3",
|
||||||
|
"d3-selection": "3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/d3-ease": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/d3-force-3d": {
|
"node_modules/d3-force-3d": {
|
||||||
"version": "3.0.5",
|
"version": "3.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/d3-force-3d/-/d3-force-3d-3.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/d3-force-3d/-/d3-force-3d-3.0.5.tgz",
|
||||||
|
@ -4654,6 +4954,14 @@
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/d3-selection": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/d3-time": {
|
"node_modules/d3-time": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz",
|
||||||
|
@ -4687,6 +4995,39 @@
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/d3-transition": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==",
|
||||||
|
"dependencies": {
|
||||||
|
"d3-color": "1 - 3",
|
||||||
|
"d3-dispatch": "1 - 3",
|
||||||
|
"d3-ease": "1 - 3",
|
||||||
|
"d3-interpolate": "1 - 3",
|
||||||
|
"d3-timer": "1 - 3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"d3-selection": "2 - 3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/d3-zoom": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==",
|
||||||
|
"dependencies": {
|
||||||
|
"d3-dispatch": "1 - 3",
|
||||||
|
"d3-drag": "2 - 3",
|
||||||
|
"d3-interpolate": "1 - 3",
|
||||||
|
"d3-selection": "2 - 3",
|
||||||
|
"d3-transition": "2 - 3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/debounce": {
|
"node_modules/debounce": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz",
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
"@uiw/codemirror-themes": "^4.22.2",
|
"@uiw/codemirror-themes": "^4.22.2",
|
||||||
"@uiw/react-codemirror": "^4.22.2",
|
"@uiw/react-codemirror": "^4.22.2",
|
||||||
"axios": "^1.7.2",
|
"axios": "^1.7.2",
|
||||||
|
"@reactflow/core": "^11.8.3",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"framer-motion": "^10.18.0",
|
"framer-motion": "^10.18.0",
|
||||||
"js-file-download": "^0.4.12",
|
"js-file-download": "^0.4.12",
|
||||||
|
|
|
@ -16,6 +16,7 @@ import UserProfilePage from '@/pages/UserProfilePage';
|
||||||
|
|
||||||
import ApplicationLayout from './ApplicationLayout';
|
import ApplicationLayout from './ApplicationLayout';
|
||||||
import { routes } from './urls';
|
import { routes } from './urls';
|
||||||
|
import SynthesisPage from "@/pages/SynthesisPage";
|
||||||
|
|
||||||
export const Router = createBrowserRouter([
|
export const Router = createBrowserRouter([
|
||||||
{
|
{
|
||||||
|
@ -70,6 +71,10 @@ export const Router = createBrowserRouter([
|
||||||
{
|
{
|
||||||
path: routes.manuals,
|
path: routes.manuals,
|
||||||
element: <ManualsPage />
|
element: <ManualsPage />
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: routes.synthesis,
|
||||||
|
element: <SynthesisPage />
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ import {
|
||||||
LibraryItemType
|
LibraryItemType
|
||||||
} from '@/models/library';
|
} from '@/models/library';
|
||||||
import { ILibraryCreateData } from '@/models/library';
|
import { ILibraryCreateData } from '@/models/library';
|
||||||
import { IOperationSchemaData } from '@/models/oss';
|
import { IOperationSchemaData, IRunSynthesis, IRunSynthesisResponse } from '@/models/oss';
|
||||||
import {
|
import {
|
||||||
IConstituentaList,
|
IConstituentaList,
|
||||||
IConstituentaMeta,
|
IConstituentaMeta,
|
||||||
|
@ -51,6 +51,7 @@ import {
|
||||||
IUserUpdatePassword
|
IUserUpdatePassword
|
||||||
} from '@/models/user';
|
} from '@/models/user';
|
||||||
import { buildConstants } from '@/utils/buildConstants';
|
import { buildConstants } from '@/utils/buildConstants';
|
||||||
|
import { ISynthesisGraphData } from '@/models/oss.ts';
|
||||||
|
|
||||||
const defaultOptions = {
|
const defaultOptions = {
|
||||||
xsrfCookieName: 'csrftoken',
|
xsrfCookieName: 'csrftoken',
|
||||||
|
@ -85,6 +86,7 @@ interface IFrontRequest<RequestData, ResponseData> {
|
||||||
export interface FrontPush<DataType> extends IFrontRequest<DataType, undefined> {
|
export interface FrontPush<DataType> extends IFrontRequest<DataType, undefined> {
|
||||||
data: DataType;
|
data: DataType;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FrontPull<DataType> extends IFrontRequest<undefined, DataType> {
|
export interface FrontPull<DataType> extends IFrontRequest<undefined, DataType> {
|
||||||
onSuccess: DataCallback<DataType>;
|
onSuccess: DataCallback<DataType>;
|
||||||
}
|
}
|
||||||
|
@ -94,7 +96,8 @@ export interface FrontExchange<RequestData, ResponseData> extends IFrontRequest<
|
||||||
onSuccess: DataCallback<ResponseData>;
|
onSuccess: DataCallback<ResponseData>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FrontAction extends IFrontRequest<undefined, undefined> {}
|
export interface FrontAction extends IFrontRequest<undefined, undefined> {
|
||||||
|
}
|
||||||
|
|
||||||
interface IAxiosRequest<RequestData, ResponseData> {
|
interface IAxiosRequest<RequestData, ResponseData> {
|
||||||
endpoint: string;
|
endpoint: string;
|
||||||
|
@ -234,26 +237,10 @@ export function postCloneLibraryItem(target: string, request: FrontExchange<IRSF
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getOssDetails(target: string, request: FrontPull<IOperationSchemaData>) {
|
export function getOssDetails(target: string, request: FrontPull<IOperationSchemaData>) {
|
||||||
request.onSuccess({
|
AxiosGet({
|
||||||
id: Number(target),
|
endpoint: `/api/synthesis/${target}`,
|
||||||
comment: '123',
|
request: request
|
||||||
alias: 'oss1',
|
|
||||||
access_policy: AccessPolicy.PUBLIC,
|
|
||||||
editors: [],
|
|
||||||
owner: 1,
|
|
||||||
item_type: LibraryItemType.OSS,
|
|
||||||
location: '/U',
|
|
||||||
read_only: false,
|
|
||||||
subscribers: [],
|
|
||||||
time_create: '0',
|
|
||||||
time_update: '0',
|
|
||||||
title: 'TestOss',
|
|
||||||
visible: false
|
|
||||||
});
|
});
|
||||||
// AxiosGet({
|
|
||||||
// endpoint: `/api/oss/${target}`, // TODO: endpoint to access OSS
|
|
||||||
// request: request
|
|
||||||
// });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getRSFormDetails(target: string, version: string, request: FrontPull<IRSFormData>) {
|
export function getRSFormDetails(target: string, version: string, request: FrontPull<IRSFormData>) {
|
||||||
|
@ -437,13 +424,28 @@ export function patchUploadTRS(target: string, request: FrontExchange<IRSFormUpl
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
export function patchInlineSynthesis(request: FrontExchange<IInlineSynthesisData, IRSFormData>) {
|
|
||||||
|
export function patchInlineSynthesis(request: FrontExchange<ISynthesisGraphData, ISynthesisGraphData>) {
|
||||||
AxiosPatch({
|
AxiosPatch({
|
||||||
endpoint: `/api/operations/inline-synthesis`,
|
endpoint: `/api/operations/inline-synthesis`,
|
||||||
request: request
|
request: request
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function runSingleSynthesis(request: FrontExchange<IRunSynthesis, IRunSynthesisResponse>) {
|
||||||
|
AxiosPost({
|
||||||
|
endpoint: `/api/synthesis/run_single`,
|
||||||
|
request: request
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function postSynthesisGraph(request: FrontExchange<ISynthesisGraphData, ISynthesisGraphData>) {
|
||||||
|
AxiosPost({
|
||||||
|
endpoint: `/api/synthesis/save`,
|
||||||
|
request: request
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function postResolveText(schema: string, request: FrontExchange<ITextRequest, IResolutionData>) {
|
export function postResolveText(schema: string, request: FrontExchange<ITextRequest, IResolutionData>) {
|
||||||
AxiosPost({
|
AxiosPost({
|
||||||
endpoint: `/api/rsforms/${schema}/resolve`,
|
endpoint: `/api/rsforms/${schema}/resolve`,
|
||||||
|
@ -520,10 +522,10 @@ function AxiosGet<ResponseData>({ endpoint, request, options }: IAxiosRequest<un
|
||||||
}
|
}
|
||||||
|
|
||||||
function AxiosPost<RequestData, ResponseData>({
|
function AxiosPost<RequestData, ResponseData>({
|
||||||
endpoint,
|
endpoint,
|
||||||
request,
|
request,
|
||||||
options
|
options
|
||||||
}: IAxiosRequest<RequestData, ResponseData>) {
|
}: IAxiosRequest<RequestData, ResponseData>) {
|
||||||
if (request.setLoading) request.setLoading(true);
|
if (request.setLoading) request.setLoading(true);
|
||||||
axiosInstance
|
axiosInstance
|
||||||
.post<ResponseData>(endpoint, request.data, options)
|
.post<ResponseData>(endpoint, request.data, options)
|
||||||
|
@ -539,10 +541,10 @@ function AxiosPost<RequestData, ResponseData>({
|
||||||
}
|
}
|
||||||
|
|
||||||
function AxiosDelete<RequestData, ResponseData>({
|
function AxiosDelete<RequestData, ResponseData>({
|
||||||
endpoint,
|
endpoint,
|
||||||
request,
|
request,
|
||||||
options
|
options
|
||||||
}: IAxiosRequest<RequestData, ResponseData>) {
|
}: IAxiosRequest<RequestData, ResponseData>) {
|
||||||
if (request.setLoading) request.setLoading(true);
|
if (request.setLoading) request.setLoading(true);
|
||||||
axiosInstance
|
axiosInstance
|
||||||
.delete<ResponseData>(endpoint, options)
|
.delete<ResponseData>(endpoint, options)
|
||||||
|
@ -558,10 +560,10 @@ function AxiosDelete<RequestData, ResponseData>({
|
||||||
}
|
}
|
||||||
|
|
||||||
function AxiosPatch<RequestData, ResponseData>({
|
function AxiosPatch<RequestData, ResponseData>({
|
||||||
endpoint,
|
endpoint,
|
||||||
request,
|
request,
|
||||||
options
|
options
|
||||||
}: IAxiosRequest<RequestData, ResponseData>) {
|
}: IAxiosRequest<RequestData, ResponseData>) {
|
||||||
if (request.setLoading) request.setLoading(true);
|
if (request.setLoading) request.setLoading(true);
|
||||||
axiosInstance
|
axiosInstance
|
||||||
.patch<ResponseData>(endpoint, request.data, options)
|
.patch<ResponseData>(endpoint, request.data, options)
|
||||||
|
|
|
@ -19,7 +19,8 @@ export const routes = {
|
||||||
help: 'manuals',
|
help: 'manuals',
|
||||||
rsforms: 'rsforms',
|
rsforms: 'rsforms',
|
||||||
oss: 'oss',
|
oss: 'oss',
|
||||||
icons: 'icons'
|
icons: 'icons',
|
||||||
|
synthesis: 'synthesis'
|
||||||
};
|
};
|
||||||
|
|
||||||
interface SchemaProps {
|
interface SchemaProps {
|
||||||
|
|
61
rsconcept/frontend/src/components/ui/Synthesis/InputNode.tsx
Normal file
61
rsconcept/frontend/src/components/ui/Synthesis/InputNode.tsx
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
import { memo, type FC } from 'react';
|
||||||
|
import { Handle, Position, type NodeProps } from '@reactflow/core';
|
||||||
|
import Button from '@/components/ui/Button.tsx';
|
||||||
|
import { PiPlugsConnected } from 'react-icons/pi';
|
||||||
|
import { CiSquareRemove } from 'react-icons/ci';
|
||||||
|
import MiniButton from '@/components/ui/MiniButton.tsx';
|
||||||
|
import { useSynthesis } from '@/pages/OssPage/SynthesisContext.tsx';
|
||||||
|
|
||||||
|
interface InputNodeProps {
|
||||||
|
id: string;
|
||||||
|
data: {
|
||||||
|
label: string;
|
||||||
|
onDelete: (nodeId: string) => void;
|
||||||
|
};
|
||||||
|
bound_rsform_id: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const InputNode: FC<InputNodeProps> = ({ id, data,bound_rsform_id }) => {
|
||||||
|
const controller = useSynthesis();
|
||||||
|
const { label, onDelete } = data;
|
||||||
|
|
||||||
|
const handleDelete = () => {
|
||||||
|
onDelete(id);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClick = () =>{
|
||||||
|
controller.selectNode(id);
|
||||||
|
controller.showSelectInput();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Handle type="target" position={Position.Bottom} />
|
||||||
|
<div>
|
||||||
|
<MiniButton className="float-right"
|
||||||
|
icon={<CiSquareRemove className="icon-red" />}
|
||||||
|
title="Удалить"
|
||||||
|
onClick={handleDelete}
|
||||||
|
color={'red'}
|
||||||
|
/>
|
||||||
|
<div>
|
||||||
|
Тип: <strong>Ввод</strong>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
Схема:{controller.getBind(id) === undefined? '': controller.getBind(id)}
|
||||||
|
<strong>
|
||||||
|
<MiniButton className="float-right"
|
||||||
|
icon={<PiPlugsConnected className="icon-green" />}
|
||||||
|
title="Привязать схему"
|
||||||
|
onClick={() => {handleClick()}}
|
||||||
|
/>
|
||||||
|
</strong>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(InputNode);
|
|
@ -0,0 +1,91 @@
|
||||||
|
import { memo, type FC, type CSSProperties } from 'react';
|
||||||
|
import { Handle, Position, type NodeProps } from '@reactflow/core';
|
||||||
|
import MiniButton from '@/components/ui/MiniButton.tsx';
|
||||||
|
import { IoGitNetworkSharp } from 'react-icons/io5';
|
||||||
|
import { useSynthesis } from '@/pages/OssPage/SynthesisContext.tsx';
|
||||||
|
import { CiSquareRemove } from 'react-icons/ci';
|
||||||
|
import { VscDebugStart } from "react-icons/vsc";
|
||||||
|
|
||||||
|
const sourceHandleStyleA: CSSProperties = { left: 50 };
|
||||||
|
const sourceHandleStyleB: CSSProperties = {
|
||||||
|
right: 50,
|
||||||
|
left: 'auto'
|
||||||
|
};
|
||||||
|
|
||||||
|
interface OperationNodeProps {
|
||||||
|
id: string;
|
||||||
|
data: {
|
||||||
|
label: string;
|
||||||
|
onDelete: (nodeId: string) => void;
|
||||||
|
};
|
||||||
|
xPos: number,
|
||||||
|
yPos: number,
|
||||||
|
}
|
||||||
|
|
||||||
|
const OperationNode: FC<OperationNodeProps> = ({ id, data, xPos, yPos }) => {
|
||||||
|
const controller = useSynthesis();
|
||||||
|
const { label, onDelete } = data;
|
||||||
|
|
||||||
|
const handleDelete = () => {
|
||||||
|
onDelete(id);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubstitution = () =>{
|
||||||
|
controller.selectNode(id);
|
||||||
|
controller.showSynthesis();
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSynthesis = () => {
|
||||||
|
controller.singleSynthesis(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Handle type="target" position={Position.Bottom} />
|
||||||
|
<div>
|
||||||
|
<MiniButton className="float-right"
|
||||||
|
icon={<CiSquareRemove className="icon-red" />}
|
||||||
|
title="Удалить"
|
||||||
|
onClick={handleDelete}
|
||||||
|
color={'red'}
|
||||||
|
/>
|
||||||
|
<div>
|
||||||
|
Тип: <strong>Отождествление</strong>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
Схема:{' '}
|
||||||
|
<strong>
|
||||||
|
</strong>
|
||||||
|
<MiniButton
|
||||||
|
className="float-right"
|
||||||
|
icon={<VscDebugStart className="icon-green" />}
|
||||||
|
title="Синтез"
|
||||||
|
onClick={() => handleSynthesis()}
|
||||||
|
/>
|
||||||
|
<MiniButton
|
||||||
|
className="float-right"
|
||||||
|
icon={<IoGitNetworkSharp className="icon-green" />}
|
||||||
|
title="Отождествления"
|
||||||
|
onClick={() => handleSubstitution()}
|
||||||
|
/>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Handle
|
||||||
|
type="source"
|
||||||
|
position={Position.Top}
|
||||||
|
id="a"
|
||||||
|
style={sourceHandleStyleA}
|
||||||
|
/>
|
||||||
|
<Handle
|
||||||
|
type="source"
|
||||||
|
position={Position.Top}
|
||||||
|
id="b"
|
||||||
|
style={sourceHandleStyleB}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(OperationNode);
|
|
@ -0,0 +1,21 @@
|
||||||
|
// Reexporting reaOperation types to wrap in 'use client'.
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import { GraphCanvas as OperationUI } from 'reagraph';
|
||||||
|
|
||||||
|
export {
|
||||||
|
type GraphEdge,
|
||||||
|
type GraphNode,
|
||||||
|
type GraphCanvasRef,
|
||||||
|
Sphere,
|
||||||
|
useSelection,
|
||||||
|
type CollapseProps
|
||||||
|
} from 'reagraph';
|
||||||
|
export { type LayoutTypes as OperationLayout } from 'reagraph';
|
||||||
|
|
||||||
|
import { ThreeEvent } from '@react-three/fiber';
|
||||||
|
|
||||||
|
export type OperationMouseEvent = ThreeEvent<MouseEvent>;
|
||||||
|
export type OperationPointerEvent = ThreeEvent<PointerEvent>;
|
||||||
|
|
||||||
|
export default OperationUI;
|
|
@ -0,0 +1,18 @@
|
||||||
|
.Flow {
|
||||||
|
flex-grow: 1;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.react-flow__node-input {
|
||||||
|
border: 1px solid #555;
|
||||||
|
padding: 10px;
|
||||||
|
width: 150px;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.react-flow__node-custom {
|
||||||
|
border: 1px solid #555;
|
||||||
|
padding: 10px;
|
||||||
|
width: 250px;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
import { useCallback, useMemo } from 'react';
|
||||||
|
import {
|
||||||
|
ReactFlow,
|
||||||
|
addEdge,
|
||||||
|
useNodesState,
|
||||||
|
useEdgesState,
|
||||||
|
type Connection,
|
||||||
|
type Edge,
|
||||||
|
type Node, OnSelectionChangeParams
|
||||||
|
} from '@reactflow/core';
|
||||||
|
|
||||||
|
|
||||||
|
import OperationNode from './OperationNode';
|
||||||
|
import InputNode from './InputNode';
|
||||||
|
|
||||||
|
// this is important! You need to import the styles from the lib to make it work
|
||||||
|
import '@reactflow/core/dist/style.css';
|
||||||
|
|
||||||
|
import './SynthesisFlow.css';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { useSynthesis } from '@/pages/OssPage/SynthesisContext.tsx';
|
||||||
|
import { useConceptOptions } from '@/context/OptionsContext.tsx';
|
||||||
|
|
||||||
|
|
||||||
|
const nodeTypes = {
|
||||||
|
custom: OperationNode,
|
||||||
|
input: InputNode
|
||||||
|
};
|
||||||
|
|
||||||
|
function Flow() {
|
||||||
|
const controller = useSynthesis();
|
||||||
|
const { calculateHeight, darkMode } = useConceptOptions();
|
||||||
|
const canvasWidth = useMemo(() => {
|
||||||
|
return 'calc(100vw - 1rem)';
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const canvasHeight = useMemo(() => calculateHeight('1.75rem + 4px'), [calculateHeight]);
|
||||||
|
return (
|
||||||
|
<div className="relative" style={{ height: canvasHeight, width: canvasWidth }}>
|
||||||
|
<ReactFlow
|
||||||
|
nodes={controller.getNodes()}
|
||||||
|
onNodesChange={controller.onNodesChange}
|
||||||
|
onNodesDelete={controller.onNodesDelete}
|
||||||
|
edges={controller.getEdges()}
|
||||||
|
onEdgesChange={controller.onEdgesChange}
|
||||||
|
onConnect={controller.onConnect}
|
||||||
|
fitView
|
||||||
|
nodeTypes={nodeTypes}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Flow;
|
|
@ -6,6 +6,7 @@ import { ConstituentaID, IRSForm, ISubstitution } from '@/models/rsform';
|
||||||
import { prefixes } from '@/utils/constants';
|
import { prefixes } from '@/utils/constants';
|
||||||
|
|
||||||
import PickSubstitutions from '../../components/select/PickSubstitutions';
|
import PickSubstitutions from '../../components/select/PickSubstitutions';
|
||||||
|
import { ISynthesisSubstitution } from '@/models/oss.ts';
|
||||||
|
|
||||||
interface SubstitutionsTabProps {
|
interface SubstitutionsTabProps {
|
||||||
receiver?: IRSForm;
|
receiver?: IRSForm;
|
||||||
|
@ -16,7 +17,7 @@ interface SubstitutionsTabProps {
|
||||||
error?: ErrorData;
|
error?: ErrorData;
|
||||||
|
|
||||||
substitutions: ISubstitution[];
|
substitutions: ISubstitution[];
|
||||||
setSubstitutions: React.Dispatch<React.SetStateAction<ISubstitution[]>>;
|
setSubstitutions: React.Dispatch<React.SetStateAction<ISubstitution[] | ISynthesisSubstitution[]>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
function SubstitutionsTab({
|
function SubstitutionsTab({
|
||||||
|
|
|
@ -0,0 +1,93 @@
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
|
import Checkbox from '@/components/ui/Checkbox.tsx';
|
||||||
|
import FileInput from '@/components/ui/FileInput.tsx';
|
||||||
|
import Modal, { ModalProps } from '@/components/ui/Modal.tsx';
|
||||||
|
import { useRSForm } from '@/context/RSFormContext.tsx';
|
||||||
|
import { IRSForm, IRSFormUploadData, ISubstitution } from '@/models/rsform.ts';
|
||||||
|
import { EXTEOR_TRS_FILE } from '@/utils/constants.ts';
|
||||||
|
import { TabList, TabPanel, Tabs } from 'react-tabs';
|
||||||
|
import clsx from 'clsx';
|
||||||
|
import TabLabel from '@/components/ui/TabLabel.tsx';
|
||||||
|
import SubstitutionsTab from '@/dialogs/DlgInlineSynthesis/SubstitutionsTab.tsx';
|
||||||
|
import useRSFormDetails from '@/hooks/useRSFormDetails.ts';
|
||||||
|
import { LibraryItemID } from '@/models/library.ts';
|
||||||
|
import { ISynthesisData } from '@/models/synthesis.ts';
|
||||||
|
import { TabID } from '@/dialogs/DlgInlineSynthesis/DlgInlineSynthesis.tsx';
|
||||||
|
import SynthesisSubstitutionsTab from '@/pages/OssPage/SynthesisSubstitutionsTab.tsx';
|
||||||
|
import SchemaTab from '@/dialogs/DlgInlineSynthesis/SchemaTab.tsx';
|
||||||
|
import {
|
||||||
|
Node
|
||||||
|
} from '@reactflow/core';
|
||||||
|
import { useSynthesis } from '@/pages/OssPage/SynthesisContext.tsx';
|
||||||
|
|
||||||
|
interface DlgCreateSynthesisProps extends Pick<ModalProps, 'hideWindow'> {
|
||||||
|
nodeId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum SynthesisTabID {
|
||||||
|
SCHEMA = 0,
|
||||||
|
SUBSTITUTIONS = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
function DlgSelectInputScheme({ nodeId, hideWindow }: DlgCreateSynthesisProps) {
|
||||||
|
const controller = useSynthesis();
|
||||||
|
|
||||||
|
const [activeTab, setActiveTab] = useState(SynthesisTabID.SCHEMA);
|
||||||
|
const [selected, setSelected] = useState<LibraryItemID[]>([]);
|
||||||
|
const [substitutions, setSubstitutions] = useState<ISubstitution[]>([]);
|
||||||
|
const [donorID, setDonorID] = useState<LibraryItemID | undefined>(undefined);
|
||||||
|
|
||||||
|
|
||||||
|
const schemaPanel = useMemo(
|
||||||
|
() => (
|
||||||
|
<TabPanel>
|
||||||
|
<SchemaTab selected={donorID} setSelected={setDonorID} />
|
||||||
|
</TabPanel>
|
||||||
|
),
|
||||||
|
[donorID]
|
||||||
|
);
|
||||||
|
|
||||||
|
function handleSubmit() {
|
||||||
|
if (donorID !== undefined) {
|
||||||
|
controller.updateBounds(nodeId, donorID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function validate() {
|
||||||
|
if (donorID === undefined) {
|
||||||
|
toast.error('Выберите источник конституент');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
header="Синтез концептуальных скем"
|
||||||
|
hideWindow={hideWindow}
|
||||||
|
submitText="Привязать"
|
||||||
|
className="w-[25rem] px-6"
|
||||||
|
canSubmit={validate()}
|
||||||
|
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
>
|
||||||
|
<Tabs
|
||||||
|
selectedTabClassName="clr-selected"
|
||||||
|
className="flex flex-col"
|
||||||
|
selectedIndex={activeTab}
|
||||||
|
onSelect={setActiveTab}
|
||||||
|
>
|
||||||
|
<TabList className={clsx('mb-3 self-center', 'flex', 'border divide-x rounded-none')}>
|
||||||
|
<TabLabel label="Схема" title="Источник конституент" className="w-[8rem]" />
|
||||||
|
</TabList>
|
||||||
|
{schemaPanel}
|
||||||
|
</Tabs>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DlgSelectInputScheme;
|
120
rsconcept/frontend/src/dialogs/DlgOssGraph/DlgSynthesis.tsx
Normal file
120
rsconcept/frontend/src/dialogs/DlgOssGraph/DlgSynthesis.tsx
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useMemo, useState } from 'react';
|
||||||
|
|
||||||
|
import Modal, { ModalProps } from '@/components/ui/Modal.tsx';
|
||||||
|
import { TabList, TabPanel, Tabs } from 'react-tabs';
|
||||||
|
import clsx from 'clsx';
|
||||||
|
import TabLabel from '@/components/ui/TabLabel.tsx';
|
||||||
|
import useRSFormDetails from '@/hooks/useRSFormDetails.ts';
|
||||||
|
import SynthesisSubstitutionsTab from '@/pages/OssPage/SynthesisSubstitutionsTab.tsx';
|
||||||
|
import { useSynthesis } from '@/pages/OssPage/SynthesisContext.tsx';
|
||||||
|
import { ISynthesisSubstitution } from '@/models/oss.ts';
|
||||||
|
|
||||||
|
interface DlgCreateSynthesisProps extends Pick<ModalProps, 'hideWindow'> {
|
||||||
|
nodeId: string;
|
||||||
|
onSynthesis: (data: ISynthesisSubstitution[]) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum SynthesisTabID {
|
||||||
|
SCHEMA = 0,
|
||||||
|
SUBSTITUTIONS = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
function DlgSynthesis({ hideWindow, nodeId, onSynthesis }: DlgCreateSynthesisProps) {
|
||||||
|
const controller = useSynthesis();
|
||||||
|
|
||||||
|
const [activeTab, setActiveTab] = useState(SynthesisTabID.SCHEMA);
|
||||||
|
const sourceLeft = useRSFormDetails({
|
||||||
|
target: controller.getNodeParentsRsform(nodeId)[0] ?
|
||||||
|
String(controller.getNodeParentsRsform(nodeId)[0]) : undefined
|
||||||
|
});
|
||||||
|
const sourceRight = useRSFormDetails({
|
||||||
|
target: controller.getNodeParentsRsform(nodeId)[1] ?
|
||||||
|
String(controller.getNodeParentsRsform(nodeId)[1]) : undefined
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
//const validated = useMemo(() => !!source.schema && selected.length > 0, [source.schema, selected]);
|
||||||
|
function handleSubmit() {
|
||||||
|
const parents = controller.getNodeParentsRsform(nodeId);
|
||||||
|
|
||||||
|
if (parents.length != 2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const data: ISynthesisSubstitution[] = controller.substitutions.map((item) => ({
|
||||||
|
id: null,
|
||||||
|
operation_id: nodeId,
|
||||||
|
leftCst: item.leftCst,
|
||||||
|
rightCst: item.rightCst,
|
||||||
|
deleteRight: item.deleteRight,
|
||||||
|
takeLeftTerm: item.takeLeftTerm
|
||||||
|
}));
|
||||||
|
controller.setSubstitutions(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
function validated() {
|
||||||
|
const parents = controller.getNodeParentsRsform(nodeId);
|
||||||
|
return parents.length == 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
const schemaPanel = useMemo(
|
||||||
|
() => (
|
||||||
|
<TabPanel></TabPanel>
|
||||||
|
), []
|
||||||
|
);
|
||||||
|
|
||||||
|
const selectedSubstitutions = useMemo(
|
||||||
|
() => controller.getSubstitution(nodeId),
|
||||||
|
[controller, nodeId]
|
||||||
|
);
|
||||||
|
|
||||||
|
const setSelectedSubstitutions = (newElement: ISynthesisSubstitution[]) => {
|
||||||
|
controller.updateSubstitution(nodeId, newElement, controller.setSubstitutions);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const substitutesPanel = useMemo(
|
||||||
|
() => (
|
||||||
|
<TabPanel>
|
||||||
|
<SynthesisSubstitutionsTab
|
||||||
|
receiver={sourceLeft.schema}
|
||||||
|
source={sourceRight.schema}
|
||||||
|
substitutions={selectedSubstitutions}
|
||||||
|
setSubstitutions={setSelectedSubstitutions}
|
||||||
|
/>
|
||||||
|
</TabPanel>
|
||||||
|
),
|
||||||
|
[sourceLeft.schema, sourceRight.schema, controller]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
header="Синтез концептуальных схем"
|
||||||
|
hideWindow={hideWindow}
|
||||||
|
submitText="Сохранить"
|
||||||
|
className="w-[25rem] px-6"
|
||||||
|
canSubmit={validated}
|
||||||
|
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
|
||||||
|
>
|
||||||
|
<Tabs
|
||||||
|
selectedTabClassName="clr-selected"
|
||||||
|
className="flex flex-col"
|
||||||
|
selectedIndex={activeTab}
|
||||||
|
onSelect={setActiveTab}
|
||||||
|
>
|
||||||
|
<TabList className={clsx('mb-3 self-center', 'flex', 'border divide-x rounded-none')}>
|
||||||
|
<TabLabel label="Схема" title="Источник конституент" className="w-[8rem]" />
|
||||||
|
<TabLabel label="Отождествления" title="Таблица отождествлений" className="w-[8rem]" />
|
||||||
|
</TabList>
|
||||||
|
{schemaPanel}
|
||||||
|
{substitutesPanel}
|
||||||
|
</Tabs>
|
||||||
|
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DlgSynthesis;
|
|
@ -6,6 +6,7 @@ import { getOssDetails } from '@/app/backendAPI';
|
||||||
import { type ErrorData } from '@/components/info/InfoError';
|
import { type ErrorData } from '@/components/info/InfoError';
|
||||||
import { IOperationSchema, IOperationSchemaData } from '@/models/oss';
|
import { IOperationSchema, IOperationSchemaData } from '@/models/oss';
|
||||||
import { OssLoader } from '@/models/OssLoader';
|
import { OssLoader } from '@/models/OssLoader';
|
||||||
|
import { AccessPolicy, LibraryItemType } from '@/models/library.ts';
|
||||||
|
|
||||||
function useOssDetails({ target }: { target?: string }) {
|
function useOssDetails({ target }: { target?: string }) {
|
||||||
const [schema, setInner] = useState<IOperationSchema | undefined>(undefined);
|
const [schema, setInner] = useState<IOperationSchema | undefined>(undefined);
|
||||||
|
@ -27,6 +28,24 @@ function useOssDetails({ target }: { target?: string }) {
|
||||||
if (!target) {
|
if (!target) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const staticData = {
|
||||||
|
id: Number(target),
|
||||||
|
comment: '123',
|
||||||
|
alias: 'oss1',
|
||||||
|
access_policy: AccessPolicy.PUBLIC,
|
||||||
|
editors: [],
|
||||||
|
owner: 1,
|
||||||
|
item_type: LibraryItemType.OSS,
|
||||||
|
location: '/U',
|
||||||
|
read_only: false,
|
||||||
|
subscribers: [],
|
||||||
|
time_create: '0',
|
||||||
|
time_update: '0',
|
||||||
|
title: 'TestOss',
|
||||||
|
visible: false
|
||||||
|
};
|
||||||
|
|
||||||
getOssDetails(target, {
|
getOssDetails(target, {
|
||||||
showError: true,
|
showError: true,
|
||||||
setLoading: setCustomLoading ?? setLoading,
|
setLoading: setCustomLoading ?? setLoading,
|
||||||
|
@ -35,7 +54,11 @@ function useOssDetails({ target }: { target?: string }) {
|
||||||
setError(error);
|
setError(error);
|
||||||
},
|
},
|
||||||
onSuccess: schema => {
|
onSuccess: schema => {
|
||||||
setSchema(schema);
|
const combinedData = {
|
||||||
|
...staticData,
|
||||||
|
...schema
|
||||||
|
}
|
||||||
|
setSchema(combinedData);
|
||||||
if (callback) callback();
|
if (callback) callback();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -11,13 +11,14 @@ import { IOperationSchema, IOperationSchemaData } from './oss';
|
||||||
export class OssLoader {
|
export class OssLoader {
|
||||||
private schema: IOperationSchemaData;
|
private schema: IOperationSchemaData;
|
||||||
|
|
||||||
|
|
||||||
constructor(input: IOperationSchemaData) {
|
constructor(input: IOperationSchemaData) {
|
||||||
this.schema = input;
|
this.schema = input;
|
||||||
}
|
}
|
||||||
|
|
||||||
produceOSS(): IOperationSchema {
|
produceOSS(): IOperationSchema {
|
||||||
const result = this.schema as IOperationSchema;
|
const result = this.schema as IOperationSchema;
|
||||||
result.producedData = [1, 2, 3]; // TODO: put data processing here
|
//result.producedData = [1, 2, 3]; // TODO: put data processing here
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,17 +3,65 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ILibraryItemData } from './library';
|
import { ILibraryItemData } from './library';
|
||||||
|
import { UserID } from './user';
|
||||||
|
import { IConstituenta, ISubstitution } from '@/models/rsform.ts';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents backend data for Schema of Synthesis Operations.
|
* Represents backend data for Schema of Synthesis Operations.
|
||||||
*/
|
*/
|
||||||
export interface IOperationSchemaData extends ILibraryItemData {
|
export interface IOperationSchemaData extends ILibraryItemData {
|
||||||
additional_data?: number[];
|
input_nodes: IInputNode[];
|
||||||
|
operation_nodes: ISynthesisNode[];
|
||||||
|
edges: ISynthesisEdge[];
|
||||||
|
substitutions: ISynthesisSubstitution[];
|
||||||
|
graph: ISynthesisGraph;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ISynthesisGraph {
|
||||||
|
id: number;
|
||||||
|
status: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IInputNode {
|
||||||
|
id: number | null;
|
||||||
|
graph_id: number;
|
||||||
|
vertical_coordinate: number;
|
||||||
|
horizontal_coordinate: number;
|
||||||
|
rsform_id: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ISynthesisNode extends IInputNode {
|
||||||
|
id: number | null;
|
||||||
|
name: string;
|
||||||
|
status: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ISynthesisEdge {
|
||||||
|
id: number | null;
|
||||||
|
decoded_id: string;
|
||||||
|
source_handle: string;
|
||||||
|
graph_id: number;
|
||||||
|
node_from: string;
|
||||||
|
node_to: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ISynthesisSubstitution extends ISubstitution {
|
||||||
|
id: number | null;
|
||||||
|
graph_id: number;
|
||||||
|
operation_id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IRunSynthesis {
|
||||||
|
operationId: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IRunSynthesisResponse {
|
||||||
|
rsformId: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents Schema of Synthesis Operations.
|
* Represents Schema of Synthesis Operations.
|
||||||
*/
|
*/
|
||||||
export interface IOperationSchema extends IOperationSchemaData {
|
export interface IOperationSchema extends IOperationSchemaData {
|
||||||
producedData: number[]; // TODO: modify this to store calculated state on load
|
subscribers: UserID[];
|
||||||
}
|
editors: UserID[];
|
||||||
|
|
|
@ -1,13 +1,23 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
|
import { ReactFlowProvider } from '@reactflow/core';
|
||||||
|
|
||||||
|
import SynthesisFlow from '@/components/ui/Synthesis/SynthesisFlow.tsx';
|
||||||
import AnimateFade from '@/components/wrap/AnimateFade';
|
import AnimateFade from '@/components/wrap/AnimateFade';
|
||||||
|
import { SynthesisState } from '@/pages/OssPage/SynthesisContext.tsx';
|
||||||
|
import SynthesisToolbar from '@/pages/OssPage/SynthesisToolbar.tsx';
|
||||||
|
|
||||||
function EditorOssGraph() {
|
function EditorOssGraph() {
|
||||||
// TODO: Implement OSS editing UI here
|
// TODO: Implement OSS editing UI here
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AnimateFade>
|
<AnimateFade>
|
||||||
<div className='py-3'>Реализация графического интерфейса</div>
|
<ReactFlowProvider>
|
||||||
|
<SynthesisState synthesisSchemaID='1'>
|
||||||
|
<SynthesisToolbar />
|
||||||
|
<SynthesisFlow />
|
||||||
|
</SynthesisState>
|
||||||
|
</ReactFlowProvider>
|
||||||
</AnimateFade>
|
</AnimateFade>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
345
rsconcept/frontend/src/pages/OssPage/SynthesisContext.tsx
Normal file
345
rsconcept/frontend/src/pages/OssPage/SynthesisContext.tsx
Normal file
|
@ -0,0 +1,345 @@
|
||||||
|
import { IRSFormData, ISubstitution } from '@/models/rsform.ts';
|
||||||
|
import { DataCallback, runSingleSynthesis, postSynthesisGraph } from '@/app/backendAPI.ts';
|
||||||
|
import { ISynthesisData } from '@/models/synthesis.ts';
|
||||||
|
import { createContext, Dispatch, SetStateAction, useCallback, useContext, useEffect, useState } from 'react';
|
||||||
|
import DlgSynthesis from '@/dialogs/DlgOssGraph/DlgSynthesis.tsx';
|
||||||
|
import {
|
||||||
|
Node,
|
||||||
|
Edge,
|
||||||
|
useNodesState,
|
||||||
|
useEdgesState,
|
||||||
|
type Connection,
|
||||||
|
addEdge,
|
||||||
|
getIncomers,
|
||||||
|
getOutgoers,
|
||||||
|
getConnectedEdges
|
||||||
|
} from '@reactflow/core';
|
||||||
|
import DlgSelectInputScheme from '@/dialogs/DlgOssGraph/DlgSelectInputScheme.tsx';
|
||||||
|
import { IOperationSchemaData, IRunSynthesis, ISynthesisSubstitution } from '@/models/oss.ts';
|
||||||
|
import { useOSS } from '@/context/OssContext.tsx';
|
||||||
|
import viewConstituents from '@/pages/RSFormPage/ViewConstituents';
|
||||||
|
|
||||||
|
interface ISynthesisContext {
|
||||||
|
synthesisSchemaID: string;
|
||||||
|
singleSynthesis: (nodeId: number) => void;
|
||||||
|
showSynthesis: () => void;
|
||||||
|
showSelectInput: () => void;
|
||||||
|
selectNode: (nodeId: string) => void;
|
||||||
|
getSelectedNode: () => string | undefined;
|
||||||
|
|
||||||
|
addLibrarySchema: () => void;
|
||||||
|
addSynthesisOperation: () => void;
|
||||||
|
removeItem: () => void;
|
||||||
|
runSynthesisLayer: () => void;
|
||||||
|
|
||||||
|
getNodes: () => Node[],
|
||||||
|
getEdges: () => Edge[]
|
||||||
|
|
||||||
|
setNodes: (nodes: Node[]) => void;
|
||||||
|
setEdges: (nodes: Edge[]) => void;
|
||||||
|
|
||||||
|
onNodesChange: any,
|
||||||
|
onEdgesChange: any,
|
||||||
|
onNodesDelete: any,
|
||||||
|
onConnect: any,
|
||||||
|
addBind: () => void,
|
||||||
|
|
||||||
|
updateBounds: (nodeId: string, newRsform: number) => void,
|
||||||
|
getBind: (nodeId: string) => number
|
||||||
|
getNodeParentsRsform: (nodeId: string) => number[]
|
||||||
|
saveGraph: () => void;
|
||||||
|
substitutions: ISynthesisSubstitution[]
|
||||||
|
setSubstitutions: () => void,
|
||||||
|
getSubstitution: (id: string) => ISynthesisSubstitution[],
|
||||||
|
updateSubstitution: (id: string, substitution: ISynthesisSubstitution[], setSubstitutions: React.Dispatch<React.SetStateAction<ISynthesisSubstitution[]>>) => void,
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IBoundMap {
|
||||||
|
nodeId: string,
|
||||||
|
rsformId: number
|
||||||
|
}
|
||||||
|
|
||||||
|
const SynthesisContext = createContext<ISynthesisContext | null>(null);
|
||||||
|
|
||||||
|
interface SynthesisStateProps {
|
||||||
|
synthesisSchemaID: string;
|
||||||
|
children: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useSynthesis = () => {
|
||||||
|
const context = useContext(SynthesisContext);
|
||||||
|
if (context === null) {
|
||||||
|
throw new Error('useSynthesis has to be used within <SynthesisState.Provider>');
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const SynthesisState = ({ synthesisSchemaID, children }: SynthesisStateProps) => {
|
||||||
|
const [showSynthesisModal, setShowSynthesisModal] = useState(false);
|
||||||
|
const [showSelectInputModal, setShowSelectInputModal] = useState(false);
|
||||||
|
const [selectedNode, setSelectedNode] = useState<Node | null>(null);
|
||||||
|
|
||||||
|
const [nodes, setNodes, onNodesChange] = useNodesState([]);
|
||||||
|
const [edges, setEdges, onEdgesChange] = useEdgesState([]);
|
||||||
|
|
||||||
|
const [bounds, setBounds] = useState<IBoundMap[]>([]);
|
||||||
|
const [substitutionBounds, setSubstitutionBounds] = useState<IBoundMap[]>([]);
|
||||||
|
const [substitutions, setSubstitutions] = useState<ISynthesisSubstitution[]>([]);
|
||||||
|
const { schema, loading, errorLoading } = useOSS();
|
||||||
|
const ossSchema = useOSS();
|
||||||
|
|
||||||
|
|
||||||
|
const getSubstitution = (operation_id: string): ISynthesisSubstitution[] => {
|
||||||
|
return substitutions.filter(substitution => substitution.operation_id === operation_id);
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateSubstitution = (
|
||||||
|
operation_id: number,
|
||||||
|
newElements: ISubstitution[],
|
||||||
|
setSubstitutions: React.Dispatch<React.SetStateAction<ISynthesisSubstitution[]>>
|
||||||
|
) => {
|
||||||
|
if (!Array.isArray(newElements)) {
|
||||||
|
console.error('newElements should be an array.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setSubstitutions((prevSubstitutions) => {
|
||||||
|
// Обновление существующих элементов
|
||||||
|
const updatedSubstitutions = prevSubstitutions.map((substitution) => {
|
||||||
|
const newElement = newElements.find((el) => el.id === substitution.id);
|
||||||
|
if (newElement) {
|
||||||
|
// Обновляем только соответствующие элементы
|
||||||
|
return { ...substitution, ...newElement, operation_id: substitution.operation_id };
|
||||||
|
}
|
||||||
|
return substitution;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Добавление новых элементов с присвоением operation_id
|
||||||
|
const newSubstitutions = newElements
|
||||||
|
.filter((newElement) => !prevSubstitutions.some((sub) => sub.id === newElement.id))
|
||||||
|
.map((newElement) => ({ ...newElement, operation_id }));
|
||||||
|
|
||||||
|
return [...updatedSubstitutions, ...newSubstitutions];
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const extractEdgeId = (edgeId: string) => {
|
||||||
|
const matches = edgeId.match(/\d+/g);
|
||||||
|
const combined = matches ? matches.join('') : '';
|
||||||
|
return Number(combined);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
function saveGraph() {
|
||||||
|
const data: IOperationSchemaData = {
|
||||||
|
graph: {
|
||||||
|
id: schema?.id,
|
||||||
|
status: 'Draft'
|
||||||
|
},
|
||||||
|
input_nodes: nodes.filter((node) => node.type == 'input').map(item =>
|
||||||
|
({
|
||||||
|
id: item.id,
|
||||||
|
vertical_coordinate: item.position.y,
|
||||||
|
horizontal_coordinate: item.position.x,
|
||||||
|
rsform_id: getBind(item.id)
|
||||||
|
})),
|
||||||
|
operation_nodes: nodes.filter((node) => node.type == 'custom').map(item =>
|
||||||
|
({
|
||||||
|
id: item.id,
|
||||||
|
vertical_coordinate: item.position.y,
|
||||||
|
horizontal_coordinate: item.position.x,
|
||||||
|
left_parent: getNodeParentsRsform(item.id)[0],
|
||||||
|
right_parent: getNodeParentsRsform(item.id)[1],
|
||||||
|
rsform_id: getBind(item.id),
|
||||||
|
name: 'name',
|
||||||
|
status: 'status'
|
||||||
|
})
|
||||||
|
),
|
||||||
|
edges: edges.map(item => ({
|
||||||
|
id: extractEdgeId(item.id),
|
||||||
|
decoded_id: item.id,
|
||||||
|
source_handle: item.sourceHandle,
|
||||||
|
node_from: item.source,
|
||||||
|
node_to: item.target
|
||||||
|
})
|
||||||
|
),
|
||||||
|
substitutions: substitutions
|
||||||
|
};
|
||||||
|
|
||||||
|
postSynthesisGraph({
|
||||||
|
data: data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (ossSchema.schema !== undefined) {
|
||||||
|
const initialNodes = [
|
||||||
|
...ossSchema.schema.input_nodes.map((node) => ({
|
||||||
|
id: node.id?.toString() || 'null',
|
||||||
|
data: { label: '123' },
|
||||||
|
position: { x: node.horizontal_coordinate, y: node.vertical_coordinate },
|
||||||
|
type: 'input'
|
||||||
|
})),
|
||||||
|
...ossSchema.schema.operation_nodes.map((node) => ({
|
||||||
|
id: node.id?.toString() || 'null',
|
||||||
|
data: { label: '123' },
|
||||||
|
position: { x: node.horizontal_coordinate, y: node.vertical_coordinate },
|
||||||
|
type: 'custom'
|
||||||
|
}))
|
||||||
|
];
|
||||||
|
|
||||||
|
const initialEdges = ossSchema.schema.edges.map((edge) => ({
|
||||||
|
id: edge.decoded_id,
|
||||||
|
source: String(edge.node_from),
|
||||||
|
sourceHandle: edge.source_handle,
|
||||||
|
target: String(edge.node_to)
|
||||||
|
}));
|
||||||
|
//const initialEdges = [{ id: 'reactflow__edge-2a-0', source: '2', target: '0' }, { id: 'reactflow__edge-2b-1', sourceHandle: 'b',source: '2', target: '1' }];
|
||||||
|
|
||||||
|
|
||||||
|
setNodes(initialNodes);
|
||||||
|
setEdges(initialEdges);
|
||||||
|
setSubstitutions(ossSchema.schema.substitutions);
|
||||||
|
[...ossSchema.schema.input_nodes, ...ossSchema.schema.operation_nodes].forEach((node) => {
|
||||||
|
const nodeId = node.id?.toString() || 'null';
|
||||||
|
const rsformId = node.rsform_id; // Предполагаем, что rsform_id есть в данных нод
|
||||||
|
updateBounds(nodeId, rsformId);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}, [ossSchema]);
|
||||||
|
|
||||||
|
const getBind = (nodeId: string) => {
|
||||||
|
const bound = bounds.find((item) => item.nodeId == nodeId);
|
||||||
|
return bound ? bound.rsformId : null;
|
||||||
|
|
||||||
|
};
|
||||||
|
const getBounds = (nodeId: string[]) => {
|
||||||
|
const parentBounds = bounds.filter((item) => item.nodeId in nodeId);
|
||||||
|
return parentBounds.map((item) => item.rsformId);
|
||||||
|
};
|
||||||
|
|
||||||
|
function getNodeParentsRsform(nodeId: string) {
|
||||||
|
const parentEdges = edges.filter((edge) => edge.source === nodeId);
|
||||||
|
const parentNodeIds = parentEdges.map((edge) => edge.target);
|
||||||
|
return getBounds(parentNodeIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateBounds = (nodeId: string, newRsform: number) => {
|
||||||
|
setBounds((prevItems) => {
|
||||||
|
const existingItem = prevItems.find((item) => item.nodeId === nodeId);
|
||||||
|
|
||||||
|
if (existingItem) {
|
||||||
|
return prevItems.map((item) =>
|
||||||
|
item.nodeId === nodeId ? { ...item, rsformId: newRsform } : item
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return [...prevItems, { nodeId: nodeId, rsformId: newRsform }];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onConnect = useCallback(
|
||||||
|
(params: Connection | Edge) => setEdges((eds) => addEdge(params, eds)),
|
||||||
|
[setEdges]
|
||||||
|
);
|
||||||
|
|
||||||
|
const onNodeDelete = useCallback(
|
||||||
|
(nodeId: string) => {
|
||||||
|
setNodes((nodes) => nodes.filter((node) => node.id !== nodeId));
|
||||||
|
setEdges((edges) =>
|
||||||
|
edges.filter((edge) => edge.source !== nodeId && edge.target !== nodeId)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
|
const singleSynthesis = useCallback(
|
||||||
|
(operationId: number) => {
|
||||||
|
const data: IRunSynthesis = {
|
||||||
|
operationId: Number(operationId)
|
||||||
|
};
|
||||||
|
runSingleSynthesis({ data: data });
|
||||||
|
},
|
||||||
|
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
const newLibrarySchema = {
|
||||||
|
id: String(nodes.length > 0 ? 1 + Math.max(...nodes.map(item => Number(item.id))) : 0),
|
||||||
|
type: 'input',
|
||||||
|
position: { x: 250, y: 5 },
|
||||||
|
data: {
|
||||||
|
label: 'Node 1', onDelete: onNodeDelete
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const newOperation = {
|
||||||
|
id: String(nodes.length > 0 ? 1 + Math.max(...nodes.map(item => Number(item.id))) : 0),
|
||||||
|
type: 'custom',
|
||||||
|
position: { x: 350, y: 20 },
|
||||||
|
data: {
|
||||||
|
label: 'Node 1', onDelete: onNodeDelete
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const selectNode = useCallback(
|
||||||
|
(nodeId: string) => {
|
||||||
|
for (const node of nodes) {
|
||||||
|
if (node.id === nodeId) {
|
||||||
|
setSelectedNode(node);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [nodes]
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<SynthesisContext.Provider value={{
|
||||||
|
synthesisSchemaID: synthesisSchemaID,
|
||||||
|
singleSynthesis: singleSynthesis,
|
||||||
|
showSynthesis: () => setShowSynthesisModal(true),
|
||||||
|
showSelectInput: () => setShowSelectInputModal(true),
|
||||||
|
selectNode: (nodeId) => selectNode(nodeId),
|
||||||
|
getSelectedNode: () => selectedNode?.id,
|
||||||
|
addLibrarySchema: () => {
|
||||||
|
setNodes([...nodes, newLibrarySchema]);
|
||||||
|
},
|
||||||
|
addSynthesisOperation: () => {
|
||||||
|
setNodes([...nodes, newOperation]);
|
||||||
|
},
|
||||||
|
setNodes: (nodes: Node[]) => {
|
||||||
|
setNodes(nodes);
|
||||||
|
},
|
||||||
|
setEdges: (edges: Edge[]) => {
|
||||||
|
setEdges(edges);
|
||||||
|
},
|
||||||
|
getNodes: () => nodes,
|
||||||
|
getEdges: () => edges,
|
||||||
|
onNodesChange: onNodesChange,
|
||||||
|
onEdgesChange: onEdgesChange,
|
||||||
|
onConnect: onConnect,
|
||||||
|
updateBounds: updateBounds,
|
||||||
|
getNodeParentsRsform: getNodeParentsRsform,
|
||||||
|
getBind: getBind,
|
||||||
|
saveGraph: saveGraph,
|
||||||
|
setSubstitutions: setSubstitutions,
|
||||||
|
substitutions: substitutions,
|
||||||
|
updateSubstitution: updateSubstitution,
|
||||||
|
getSubstitution: getSubstitution
|
||||||
|
|
||||||
|
}}>
|
||||||
|
{showSynthesisModal ? (<DlgSynthesis
|
||||||
|
hideWindow={() => setShowSynthesisModal(false)}
|
||||||
|
nodeId={selectedNode?.id}
|
||||||
|
onSynthesis={() => singleSynthesis}
|
||||||
|
/>) : null}
|
||||||
|
{showSelectInputModal ? (<DlgSelectInputScheme
|
||||||
|
nodeId={selectedNode?.id}
|
||||||
|
hideWindow={() => setShowSelectInputModal(false)}
|
||||||
|
/>) : null}
|
||||||
|
{children}
|
||||||
|
|
||||||
|
</SynthesisContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
|
@ -0,0 +1,9 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function SynthesisOperation = () => {
|
||||||
|
return (<div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import { ErrorData } from '@/components/info/InfoError.tsx';
|
||||||
|
import DataLoader from '@/components/wrap/DataLoader.tsx';
|
||||||
|
import { IRSForm, ISubstitution } from '@/models/rsform.ts';
|
||||||
|
import { prefixes } from '@/utils/constants.ts';
|
||||||
|
import PickSubstitutions from '../../components/select/PickSubstitutions';
|
||||||
|
|
||||||
|
interface SynthesisSubstitutionsTabProps {
|
||||||
|
receiver?: IRSForm;
|
||||||
|
source?: IRSForm;
|
||||||
|
|
||||||
|
error?: ErrorData;
|
||||||
|
|
||||||
|
substitutions: ISubstitution[];
|
||||||
|
setSubstitutions: React.Dispatch<React.SetStateAction<ISubstitution[]>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
function SynthesisSubstitutionsTab({
|
||||||
|
source,
|
||||||
|
receiver,
|
||||||
|
error,
|
||||||
|
substitutions,
|
||||||
|
setSubstitutions
|
||||||
|
}: SynthesisSubstitutionsTabProps) {
|
||||||
|
return (
|
||||||
|
<DataLoader id="dlg-substitutions-tab" className="cc-column" isLoading={false} error={error} hasNoData={!source}>
|
||||||
|
<PickSubstitutions
|
||||||
|
items={substitutions}
|
||||||
|
setItems={setSubstitutions}
|
||||||
|
rows={10}
|
||||||
|
prefixID={prefixes.cst_inline_synth_substitutes}
|
||||||
|
schema1={receiver}
|
||||||
|
schema2={source}
|
||||||
|
/>
|
||||||
|
</DataLoader>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SynthesisSubstitutionsTab;
|
45
rsconcept/frontend/src/pages/OssPage/SynthesisToolbar.tsx
Normal file
45
rsconcept/frontend/src/pages/OssPage/SynthesisToolbar.tsx
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
import { useSynthesis } from '@/pages/OssPage/SynthesisContext.tsx';
|
||||||
|
import Button from '@/components/ui/Button.tsx';
|
||||||
|
import { IoMdAdd, IoMdRemove } from 'react-icons/io';
|
||||||
|
import { MdCallMerge } from 'react-icons/md';
|
||||||
|
import Overlay from '@/components/ui/Overlay.tsx';
|
||||||
|
import { MdLibraryAdd } from 'react-icons/md';
|
||||||
|
|
||||||
|
import { VscRunAll, VscSave } from 'react-icons/vsc';
|
||||||
|
|
||||||
|
function SynthesisToolbar() {
|
||||||
|
const controller = useSynthesis();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Overlay position="top-1 right-1/2 translate-x-1/2" className="flex">
|
||||||
|
<Button
|
||||||
|
title="Добавить схему из библиотеки"
|
||||||
|
icon={<MdLibraryAdd />}
|
||||||
|
onClick={() => controller.addLibrarySchema()}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
title="Добавить операцию слияния"
|
||||||
|
icon={<MdCallMerge />}
|
||||||
|
onClick={() => controller.addSynthesisOperation()}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
title={'Удалить форму'}
|
||||||
|
icon={<IoMdRemove />}
|
||||||
|
onClick={() => controller.removeItem()}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
icon={<VscRunAll />}
|
||||||
|
title="Синтез"
|
||||||
|
onClick={() =>{}}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
icon={<VscSave />}
|
||||||
|
title="Сохранить"
|
||||||
|
onClick={() => controller.saveGraph()}
|
||||||
|
/>
|
||||||
|
</Overlay>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SynthesisToolbar;
|
165
rsconcept/frontend/src/pages/SynthesisPage/SynthesisPage.tsx
Normal file
165
rsconcept/frontend/src/pages/SynthesisPage/SynthesisPage.tsx
Normal file
|
@ -0,0 +1,165 @@
|
||||||
|
//import {useConceptTheme} from '@/context/ThemeContext';
|
||||||
|
/*import {useCallback, useState} from "react";
|
||||||
|
import {GraphNode} from "@/models/Graph.ts";
|
||||||
|
import {list} from "postcss";*/
|
||||||
|
import Overlay from '@/components/ui/Overlay';
|
||||||
|
import Button from '@/components/ui/Button';
|
||||||
|
import {MdCallMerge} from "react-icons/md";
|
||||||
|
import {IoMdAdd, IoMdRemove} from 'react-icons/io';
|
||||||
|
import {BsDownload, BsUpload} from 'react-icons/bs';
|
||||||
|
//import {AiOutlineClose} from 'react-icons/ai';
|
||||||
|
import {useCallback, useState} from 'react';
|
||||||
|
//import PropTypes from "prop-types";
|
||||||
|
//import {useConceptNavigation} from "@/context/NavigationContext.tsx";
|
||||||
|
import DlgSynthesis from "@/dialogs/DlgOssGraph/DlgSynthesis.tsx";
|
||||||
|
import SynthesisFlow from "@/components/ui/Synthesis/SynthesisFlow.tsx";
|
||||||
|
import {IRSFormData} from "@/models/rsform.ts";
|
||||||
|
import {toast} from "react-toastify";
|
||||||
|
import {useRSForm} from "@/context/RSFormContext.tsx";
|
||||||
|
import {DataCallback, runSingleSynthesis} from "@/app/backendAPI.ts";
|
||||||
|
import {ISynthesisData} from "@/models/synthesis.ts";
|
||||||
|
import useRSFormDetails from "@/hooks/useRSFormDetails.ts";
|
||||||
|
import {useLibrary} from "@/context/LibraryContext.tsx";
|
||||||
|
import {SynthesisState, useSynthesis} from "@/pages/OssPage/SynthesisContext.tsx";
|
||||||
|
import SynthesisToolbar from "@/pages/OssPage/SynthesisToolbar.tsx";
|
||||||
|
import {useParams} from "react-router-dom";
|
||||||
|
|
||||||
|
export const SynthesisPage = () => {
|
||||||
|
//const router = useConceptNavigation();
|
||||||
|
//const [sampleData, setSampleData] = useState([[1, 2, 3], [4, 5, 6]])
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SynthesisPage;
|
||||||
|
|
||||||
|
|
||||||
|
//onCancel={setShowSynthesisModal(false)}
|
||||||
|
/*
|
||||||
|
const modalContainerStyle: React.CSSProperties = {
|
||||||
|
position: 'fixed',
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
};
|
||||||
|
|
||||||
|
const modalContentStyle: React.CSSProperties = {
|
||||||
|
backgroundColor: '#fff',
|
||||||
|
padding: '20px',
|
||||||
|
borderRadius: '8px',
|
||||||
|
boxShadow: '0 2px 4px rgba(0, 0, 0, 0.2)',
|
||||||
|
};
|
||||||
|
|
||||||
|
const modalCloseButtonStyle: React.CSSProperties = {
|
||||||
|
outline: 'none',
|
||||||
|
cursor: 'pointer',
|
||||||
|
background: 'transparent',
|
||||||
|
color: '#C62828',
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
alignItems: 'center'
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const SynthesisPage = () => {
|
||||||
|
|
||||||
|
};
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
const SynthesisGraphBody = () => {
|
||||||
|
const [synthesisGraphs, setSynthesisGraphs] = useState([]);
|
||||||
|
|
||||||
|
return (synthesisGraphs &&
|
||||||
|
<div>
|
||||||
|
asd
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function SynthesisGraph({
|
||||||
|
nodes,
|
||||||
|
edges,
|
||||||
|
onSelect,
|
||||||
|
onEdit
|
||||||
|
}) {
|
||||||
|
const handleNodeClick = useCallback(
|
||||||
|
(node: GraphNode) => {
|
||||||
|
|
||||||
|
}, [onSelect, onEdit]
|
||||||
|
)
|
||||||
|
|
||||||
|
const createNewNode = () => {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const createNewEdge = () => {
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
/*export class SynthesisNode {
|
||||||
|
private id: number;
|
||||||
|
private edges: list[number]
|
||||||
|
|
||||||
|
constructor(id) {
|
||||||
|
this.id = id
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
/*const SynthesisModal = ({title, isOpen, onCancel, onSubmit}) => {
|
||||||
|
return (showSynthesisModal ?
|
||||||
|
<div style={modalContainerStyle}>
|
||||||
|
<div style={modalContentStyle}>
|
||||||
|
<Button
|
||||||
|
style={modalCloseButtonStyle}
|
||||||
|
title='Отмена'
|
||||||
|
icon={<AiOutlineClose/>}
|
||||||
|
onClick={onCancel}
|
||||||
|
|
||||||
|
|
||||||
|
/>
|
||||||
|
|
||||||
|
<SynthesisTable/>
|
||||||
|
<Button
|
||||||
|
title='Синтез'
|
||||||
|
icon={<MdCallMerge/>}
|
||||||
|
onClick={onSubmit}
|
||||||
|
/>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
SynthesisModal.propTypes = {
|
||||||
|
title: PropTypes.string,
|
||||||
|
isOpen: PropTypes.bool,
|
||||||
|
onCancel: PropTypes.func,
|
||||||
|
onSubmit: PropTypes.func
|
||||||
|
}
|
||||||
|
|
||||||
|
SynthesisModal.deafaultProps = {
|
||||||
|
title: "Синтез",
|
||||||
|
isOpen: false,
|
||||||
|
onCancel: () => {
|
||||||
|
},
|
||||||
|
onSubmit: () => {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const SynthesisTable = () => {
|
||||||
|
return (
|
||||||
|
<tbody>
|
||||||
|
|
||||||
|
</tbody>
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
1
rsconcept/frontend/src/pages/SynthesisPage/index.tsx
Normal file
1
rsconcept/frontend/src/pages/SynthesisPage/index.tsx
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export { default } from './SynthesisPage';
|
Loading…
Reference in New Issue
Block a user