F: Implementing references pt2
This commit is contained in:
parent
13e56f51ea
commit
266fdf0c30
|
@ -39,34 +39,29 @@ class OperationSchemaCached:
|
||||||
|
|
||||||
def delete_reference(self, target: int, keep_connections: bool = False, keep_constituents: bool = False):
|
def delete_reference(self, target: int, keep_connections: bool = False, keep_constituents: bool = False):
|
||||||
''' Delete Reference Operation. '''
|
''' Delete Reference Operation. '''
|
||||||
|
if not keep_connections:
|
||||||
|
self.delete_operation(target, keep_constituents)
|
||||||
|
return
|
||||||
self.cache.ensure_loaded_subs()
|
self.cache.ensure_loaded_subs()
|
||||||
operation = self.cache.operation_by_id[target]
|
operation = self.cache.operation_by_id[target]
|
||||||
if keep_connections:
|
reference_target = self.cache.reference_target.get(target)
|
||||||
referred_operations = operation.getQ_reference_target()
|
if reference_target:
|
||||||
if len(referred_operations) == 1:
|
for arg in operation.getQ_as_argument():
|
||||||
referred_operation = referred_operations[0]
|
arg.argument_id = reference_target
|
||||||
for arg in operation.getQ_as_argument():
|
arg.save()
|
||||||
arg.pk = None
|
|
||||||
arg.argument = referred_operation
|
|
||||||
arg.save()
|
|
||||||
else:
|
|
||||||
pass
|
|
||||||
# if target.result_id is not None:
|
|
||||||
# self.before_delete_cst(schema, schema.cache.constituents) # TODO: use operation instead of schema
|
|
||||||
|
|
||||||
self.cache.remove_operation(target)
|
self.cache.remove_operation(target)
|
||||||
operation.delete()
|
operation.delete()
|
||||||
|
|
||||||
|
|
||||||
def delete_operation(self, target: int, keep_constituents: bool = False):
|
def delete_operation(self, target: int, keep_constituents: bool = False):
|
||||||
''' Delete Operation. '''
|
''' Delete Operation. '''
|
||||||
self.cache.ensure_loaded_subs()
|
self.cache.ensure_loaded_subs()
|
||||||
operation = self.cache.operation_by_id[target]
|
operation = self.cache.operation_by_id[target]
|
||||||
schema = self.cache.get_schema(operation)
|
|
||||||
children = self.cache.graph.outputs[target]
|
children = self.cache.graph.outputs[target]
|
||||||
if schema is not None and len(children) > 0:
|
if operation.result is not None and len(children) > 0:
|
||||||
ids = [cst.pk for cst in schema.cache.constituents]
|
ids = list(Constituenta.objects.filter(schema=operation.result).values_list('pk', flat=True))
|
||||||
if not keep_constituents:
|
if not keep_constituents:
|
||||||
self.before_delete_cst(schema.model.pk, ids)
|
self._cascade_delete_inherited(operation.pk, ids)
|
||||||
else:
|
else:
|
||||||
inheritance_to_delete: list[Inheritance] = []
|
inheritance_to_delete: list[Inheritance] = []
|
||||||
for child_id in children:
|
for child_id in children:
|
||||||
|
@ -837,6 +832,8 @@ class OssCache:
|
||||||
del self._schema_by_id[target.result_id]
|
del self._schema_by_id[target.result_id]
|
||||||
self.operations.remove(self.operation_by_id[operation])
|
self.operations.remove(self.operation_by_id[operation])
|
||||||
del self.operation_by_id[operation]
|
del self.operation_by_id[operation]
|
||||||
|
if operation in self.reference_target:
|
||||||
|
del self.reference_target[operation]
|
||||||
if self.is_loaded_subs:
|
if self.is_loaded_subs:
|
||||||
del self.substitutions[operation]
|
del self.substitutions[operation]
|
||||||
del self.inheritance[operation]
|
del self.inheritance[operation]
|
||||||
|
|
|
@ -80,23 +80,42 @@ class ReferencePropagationTestCase(EndpointTester):
|
||||||
self.owned.set_arguments(self.operation5.pk, [self.operation4, self.operation3])
|
self.owned.set_arguments(self.operation5.pk, [self.operation4, self.operation3])
|
||||||
self.owned.set_substitutions(self.operation5.pk, [{
|
self.owned.set_substitutions(self.operation5.pk, [{
|
||||||
'original': self.ks4X1,
|
'original': self.ks4X1,
|
||||||
'substitution': self.ks1X1
|
'substitution': self.ks1X2
|
||||||
}])
|
}])
|
||||||
self.owned.execute_operation(self.operation5)
|
self.owned.execute_operation(self.operation5)
|
||||||
self.operation5.refresh_from_db()
|
self.operation5.refresh_from_db()
|
||||||
self.ks5 = RSForm(self.operation5.result)
|
self.ks5 = RSForm(self.operation5.result)
|
||||||
self.ks5D4 = self.ks5.insert_last(
|
self.ks5D4 = self.ks5.insert_last(
|
||||||
alias='D4',
|
alias='D4',
|
||||||
definition_formal=r'X1 X2 X3 S1 D1 D2 D3',
|
definition_formal=r'X1 X2 X3 X4 S1 D1 D2 D3',
|
||||||
convention='KS5D4'
|
convention='KS5D4'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.operation6 = self.owned.create_operation(
|
||||||
|
alias='6',
|
||||||
|
operation_type=OperationType.SYNTHESIS
|
||||||
|
)
|
||||||
|
self.owned.set_arguments(self.operation6.pk, [self.operation2, self.operation3])
|
||||||
|
self.owned.set_substitutions(self.operation6.pk, [{
|
||||||
|
'original': self.ks2X1,
|
||||||
|
'substitution': self.ks1X1
|
||||||
|
}])
|
||||||
|
self.owned.execute_operation(self.operation6)
|
||||||
|
self.operation6.refresh_from_db()
|
||||||
|
self.ks6 = RSForm(self.operation6.result)
|
||||||
|
self.ks6D2 = self.ks6.insert_last(
|
||||||
|
alias='D2',
|
||||||
|
definition_formal=r'X1 X2 X3 S1 D1',
|
||||||
|
convention='KS6D2'
|
||||||
|
)
|
||||||
|
|
||||||
self.layout_data = [
|
self.layout_data = [
|
||||||
{'nodeID': 'o' + str(self.operation1.pk), 'x': 0, 'y': 0, 'width': 150, 'height': 40},
|
{'nodeID': 'o' + str(self.operation1.pk), 'x': 0, 'y': 0, 'width': 150, 'height': 40},
|
||||||
{'nodeID': 'o' + str(self.operation2.pk), 'x': 0, 'y': 0, 'width': 150, 'height': 40},
|
{'nodeID': 'o' + str(self.operation2.pk), 'x': 0, 'y': 0, 'width': 150, 'height': 40},
|
||||||
{'nodeID': 'o' + str(self.operation3.pk), 'x': 0, 'y': 0, 'width': 150, 'height': 40},
|
{'nodeID': 'o' + str(self.operation3.pk), 'x': 0, 'y': 0, 'width': 150, 'height': 40},
|
||||||
{'nodeID': 'o' + str(self.operation4.pk), 'x': 0, 'y': 0, 'width': 150, 'height': 40},
|
{'nodeID': 'o' + str(self.operation4.pk), 'x': 0, 'y': 0, 'width': 150, 'height': 40},
|
||||||
{'nodeID': 'o' + str(self.operation5.pk), 'x': 0, 'y': 0, 'width': 150, 'height': 40}
|
{'nodeID': 'o' + str(self.operation5.pk), 'x': 0, 'y': 0, 'width': 150, 'height': 40},
|
||||||
|
{'nodeID': 'o' + str(self.operation6.pk), 'x': 0, 'y': 0, 'width': 150, 'height': 40},
|
||||||
]
|
]
|
||||||
layout = OperationSchema.layoutQ(self.owned_id)
|
layout = OperationSchema.layoutQ(self.owned_id)
|
||||||
layout.data = self.layout_data
|
layout.data = self.layout_data
|
||||||
|
@ -106,94 +125,22 @@ class ReferencePropagationTestCase(EndpointTester):
|
||||||
def test_reference_creation(self):
|
def test_reference_creation(self):
|
||||||
''' Test reference creation. '''
|
''' Test reference creation. '''
|
||||||
self.assertEqual(self.operation1.result, self.operation3.result)
|
self.assertEqual(self.operation1.result, self.operation3.result)
|
||||||
|
self.assertEqual(self.ks1.constituentsQ().count(), 3)
|
||||||
|
self.assertEqual(self.ks2.constituentsQ().count(), 3)
|
||||||
|
self.assertEqual(self.ks4.constituentsQ().count(), 6)
|
||||||
|
self.assertEqual(self.ks5.constituentsQ().count(), 9)
|
||||||
|
self.assertEqual(self.ks6.constituentsQ().count(), 6)
|
||||||
|
|
||||||
|
|
||||||
|
@decl_endpoint('/api/oss/{item}/delete-operation', method='patch')
|
||||||
|
def test_delete_target_propagation(self):
|
||||||
|
''' Test propagation when deleting a target operation. '''
|
||||||
|
data = {
|
||||||
|
'layout': self.layout_data,
|
||||||
|
'target': self.operation1.pk
|
||||||
|
}
|
||||||
|
self.executeOK(data=data, item=self.owned_id)
|
||||||
|
self.assertEqual(self.ks6.constituentsQ().count(), 4)
|
||||||
|
# self.assertEqual(self.ks5.constituentsQ().count(), 5)
|
||||||
|
|
||||||
# @decl_endpoint('/api/oss/{item}/delete-reference', method='patch')
|
# TODO: add more tests
|
||||||
# def test_delete_reference_propagation(self):
|
|
||||||
# ''' Test propagation when deleting a reference operation. '''
|
|
||||||
# schema_cached = OperationSchemaCached(self.schema2)
|
|
||||||
# # Ensure reference exists
|
|
||||||
# self.assertIn(self.reference.pk, schema_cached.cache.operation_by_id)
|
|
||||||
# # Delete the reference
|
|
||||||
# schema_cached.delete_reference(self.reference.pk)
|
|
||||||
# # Reference should be deleted
|
|
||||||
# with self.assertRaises(Reference.DoesNotExist):
|
|
||||||
# Reference.objects.get(pk=self.reference.pk)
|
|
||||||
# # Operation2 should still exist
|
|
||||||
# self.assertTrue(Operation.objects.filter(pk=self.op2.pk).exists())
|
|
||||||
|
|
||||||
|
|
||||||
# @decl_endpoint('/api/oss/{item}/set-arguments', method='patch')
|
|
||||||
# def test_set_arguments_propagation(self):
|
|
||||||
# ''' Test propagation when setting arguments for a reference. '''
|
|
||||||
# schema_cached = OperationSchemaCached(self.schema2)
|
|
||||||
# # Add op1 as argument to op2
|
|
||||||
# schema_cached.set_arguments(self.op2.pk, [self.op1])
|
|
||||||
# op2 = Operation.objects.get(pk=self.op2.pk)
|
|
||||||
# args = list(op2.getQ_arguments())
|
|
||||||
# self.assertEqual(len(args), 1)
|
|
||||||
# self.assertEqual(args[0].argument, self.op1)
|
|
||||||
|
|
||||||
|
|
||||||
# @decl_endpoint('/api/oss/{item}/delete-operation', method='patch')
|
|
||||||
# def test_delete_operation_with_reference(self):
|
|
||||||
# ''' Test propagation when deleting an operation that is referenced. '''
|
|
||||||
# schema_cached = OperationSchemaCached(self.schema1)
|
|
||||||
# # op1 is referenced by reference
|
|
||||||
# self.assertEqual(self.reference.getQ_reference_target(), self.op1)
|
|
||||||
# # Delete op1
|
|
||||||
# schema_cached.delete_operation(self.op1.pk)
|
|
||||||
# # op1 should be deleted
|
|
||||||
# with self.assertRaises(Operation.DoesNotExist):
|
|
||||||
# Operation.objects.get(pk=self.op1.pk)
|
|
||||||
# # Reference should be deleted as well
|
|
||||||
# self.assertFalse(Reference.objects.filter(pk=self.reference.pk).exists())
|
|
||||||
|
|
||||||
|
|
||||||
# @decl_endpoint('/api/oss/{item}/add-constituent', method='patch')
|
|
||||||
# def test_add_constituent_propagation(self):
|
|
||||||
# ''' Test propagation when adding a constituent to a referenced schema. '''
|
|
||||||
# # Add a new constituent to schema1 (referenced by op1, which is referenced by reference)
|
|
||||||
# new_cst = Constituenta.objects.create(
|
|
||||||
# schema=self.schema1, alias='cst_new', title='New Constituenta', cst_type=CstType.ATTRIBUTE
|
|
||||||
# )
|
|
||||||
# # Simulate propagation: after adding, the reference should still be valid and schema1 should have the new cst
|
|
||||||
# self.assertTrue(Constituenta.objects.filter(pk=new_cst.pk, schema=self.schema1).exists())
|
|
||||||
# # The reference's target operation's result should include the new constituent
|
|
||||||
# op1 = Operation.objects.get(pk=self.op1.pk)
|
|
||||||
# self.assertEqual(op1.result, self.schema1)
|
|
||||||
# self.assertIn(new_cst, Constituenta.objects.filter(schema=op1.result))
|
|
||||||
|
|
||||||
|
|
||||||
# @decl_endpoint('/api/oss/{item}/remove-constituent', method='patch')
|
|
||||||
# def test_remove_constituent_propagation(self):
|
|
||||||
# ''' Test propagation when removing a constituent from a referenced schema. '''
|
|
||||||
# # Remove cst2 from schema1
|
|
||||||
# self.cst2.delete()
|
|
||||||
# # The reference's target operation's result should not include cst2
|
|
||||||
# op1 = Operation.objects.get(pk=self.op1.pk)
|
|
||||||
# self.assertEqual(op1.result, self.schema1)
|
|
||||||
# self.assertNotIn(self.cst2, Constituenta.objects.filter(schema=op1.result))
|
|
||||||
# # Reference should still be valid
|
|
||||||
# self.assertEqual(self.reference.getQ_reference_target(), self.op1)
|
|
||||||
|
|
||||||
|
|
||||||
# @decl_endpoint('/api/oss/{item}/add-constituent-to-referenced-schema', method='patch')
|
|
||||||
# def test_propagation_to_multiple_references(self):
|
|
||||||
# ''' Test propagation when a schema is referenced by multiple references and constituents are added. '''
|
|
||||||
# # Create another reference to op1
|
|
||||||
# reference2 = Reference.objects.create(
|
|
||||||
# alias='ref2', title='Reference 2', type=OperationType.REFERENCE, result=self.schema2
|
|
||||||
# )
|
|
||||||
# reference2.setQ_reference_target(self.op1)
|
|
||||||
# reference2.save()
|
|
||||||
# # Add a new constituent to schema1
|
|
||||||
# new_cst = Constituenta.objects.create(
|
|
||||||
# schema=self.schema1, alias='cst_multi', title='Multi Constituenta', cst_type=CstType.ATTRIBUTE
|
|
||||||
# )
|
|
||||||
# # Both references should still be valid and op1's result should include the new constituent
|
|
||||||
# op1 = Operation.objects.get(pk=self.op1.pk)
|
|
||||||
# self.assertIn(new_cst, Constituenta.objects.filter(schema=op1.result))
|
|
||||||
# self.assertEqual(self.reference.getQ_reference_target(), self.op1)
|
|
||||||
# self.assertEqual(reference2.getQ_reference_target(), self.op1)
|
|
||||||
|
|
|
@ -27,6 +27,7 @@ export class OssLoader {
|
||||||
private itemByNodeID = new Map<string, IOssItem>();
|
private itemByNodeID = new Map<string, IOssItem>();
|
||||||
private blockByID = new Map<number, IBlock>();
|
private blockByID = new Map<number, IBlock>();
|
||||||
private schemaIDs: number[] = [];
|
private schemaIDs: number[] = [];
|
||||||
|
private extendedGraph = new Graph();
|
||||||
|
|
||||||
constructor(input: RO<IOperationSchemaDTO>) {
|
constructor(input: RO<IOperationSchemaDTO>) {
|
||||||
this.oss = structuredClone(input) as unknown as IOperationSchema;
|
this.oss = structuredClone(input) as unknown as IOperationSchema;
|
||||||
|
@ -47,6 +48,7 @@ export class OssLoader {
|
||||||
result.hierarchy = this.hierarchy;
|
result.hierarchy = this.hierarchy;
|
||||||
result.schemas = this.schemaIDs;
|
result.schemas = this.schemaIDs;
|
||||||
result.stats = this.calculateStats();
|
result.stats = this.calculateStats();
|
||||||
|
result.extendedGraph = this.extendedGraph;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,6 +59,7 @@ export class OssLoader {
|
||||||
this.itemByNodeID.set(operation.nodeID, operation);
|
this.itemByNodeID.set(operation.nodeID, operation);
|
||||||
this.operationByID.set(operation.id, operation);
|
this.operationByID.set(operation.id, operation);
|
||||||
this.graph.addNode(operation.id);
|
this.graph.addNode(operation.id);
|
||||||
|
this.extendedGraph.addNode(operation.id);
|
||||||
this.hierarchy.addNode(operation.nodeID);
|
this.hierarchy.addNode(operation.nodeID);
|
||||||
if (operation.parent) {
|
if (operation.parent) {
|
||||||
this.hierarchy.addEdge(constructNodeID(NodeType.BLOCK, operation.parent), operation.nodeID);
|
this.hierarchy.addEdge(constructNodeID(NodeType.BLOCK, operation.parent), operation.nodeID);
|
||||||
|
@ -75,7 +78,13 @@ export class OssLoader {
|
||||||
}
|
}
|
||||||
|
|
||||||
private createGraph() {
|
private createGraph() {
|
||||||
this.oss.arguments.forEach(argument => this.graph.addEdge(argument.argument, argument.operation));
|
this.oss.arguments.forEach(argument => {
|
||||||
|
this.graph.addEdge(argument.argument, argument.operation);
|
||||||
|
this.extendedGraph.addEdge(argument.argument, argument.operation);
|
||||||
|
});
|
||||||
|
this.oss.references.forEach(reference => {
|
||||||
|
this.extendedGraph.addEdge(reference.target, reference.reference);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private extractSchemas() {
|
private extractSchemas() {
|
||||||
|
@ -102,7 +111,7 @@ export class OssLoader {
|
||||||
break;
|
break;
|
||||||
case OperationType.REFERENCE:
|
case OperationType.REFERENCE:
|
||||||
const ref = this.oss.references.find(item => item.reference === operationID);
|
const ref = this.oss.references.find(item => item.reference === operationID);
|
||||||
const target = !!ref ? this.oss.operationByID.get(ref.target) : null;
|
const target = !!ref ? this.operationByID.get(ref.target) : null;
|
||||||
if (!target || !ref) {
|
if (!target || !ref) {
|
||||||
throw new Error(`Reference ${operationID} not found`);
|
throw new Error(`Reference ${operationID} not found`);
|
||||||
}
|
}
|
||||||
|
@ -128,13 +137,13 @@ export class OssLoader {
|
||||||
}
|
}
|
||||||
|
|
||||||
private inferConsolidation(operationID: number): boolean {
|
private inferConsolidation(operationID: number): boolean {
|
||||||
const inputs = this.graph.expandInputs([operationID]);
|
const inputs = this.extendedGraph.expandInputs([operationID]);
|
||||||
if (inputs.length === 0) {
|
if (inputs.length === 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const ancestors = [...inputs];
|
const ancestors = [...inputs];
|
||||||
inputs.forEach(input => {
|
inputs.forEach(input => {
|
||||||
ancestors.push(...this.graph.expandAllInputs([input]));
|
ancestors.push(...this.extendedGraph.expandAllInputs([input]));
|
||||||
});
|
});
|
||||||
const unique = new Set(ancestors);
|
const unique = new Set(ancestors);
|
||||||
return unique.size < ancestors.length;
|
return unique.size < ancestors.length;
|
||||||
|
|
|
@ -18,6 +18,9 @@ export function TabArguments() {
|
||||||
} = useFormContext<ICreateSynthesisDTO>();
|
} = useFormContext<ICreateSynthesisDTO>();
|
||||||
const inputs = useWatch({ control, name: 'arguments' });
|
const inputs = useWatch({ control, name: 'arguments' });
|
||||||
|
|
||||||
|
const references = manager.oss.references.filter(item => inputs.includes(item.target)).map(item => item.reference);
|
||||||
|
const filtered = manager.oss.operations.filter(item => !references.includes(item.id));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='cc-fade-in cc-column'>
|
<div className='cc-fade-in cc-column'>
|
||||||
<TextInput
|
<TextInput
|
||||||
|
@ -64,7 +67,7 @@ export function TabArguments() {
|
||||||
name='arguments'
|
name='arguments'
|
||||||
control={control}
|
control={control}
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<PickMultiOperation items={manager.oss.operations} value={field.value} onChange={field.onChange} rows={6} />
|
<PickMultiOperation items={filtered} value={field.value} onChange={field.onChange} rows={6} />
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { Controller, useForm } from 'react-hook-form';
|
import { Controller, useForm, useWatch } from 'react-hook-form';
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
|
|
||||||
import { HelpTopic } from '@/features/help';
|
import { HelpTopic } from '@/features/help';
|
||||||
|
@ -32,6 +32,7 @@ export function DlgDeleteReference() {
|
||||||
keep_connections: false
|
keep_connections: false
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
const keep_connections = useWatch({ control, name: 'keep_connections' });
|
||||||
|
|
||||||
function onSubmit(data: IDeleteReferenceDTO) {
|
function onSubmit(data: IDeleteReferenceDTO) {
|
||||||
return deleteReference({ itemID: oss.id, data: data });
|
return deleteReference({ itemID: oss.id, data: data });
|
||||||
|
@ -47,19 +48,6 @@ export function DlgDeleteReference() {
|
||||||
helpTopic={HelpTopic.CC_PROPAGATION}
|
helpTopic={HelpTopic.CC_PROPAGATION}
|
||||||
>
|
>
|
||||||
<TextInput disabled dense noBorder id='operation_alias' label='Операция' value={target.alias} />
|
<TextInput disabled dense noBorder id='operation_alias' label='Операция' value={target.alias} />
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
name='keep_constituents'
|
|
||||||
render={({ field }) => (
|
|
||||||
<Checkbox
|
|
||||||
label='Сохранить наследованные конституенты'
|
|
||||||
titleHtml='Наследованные конституенты <br/>превратятся в дописанные'
|
|
||||||
value={field.value}
|
|
||||||
onChange={field.onChange}
|
|
||||||
disabled={target.result === null}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name='keep_connections'
|
name='keep_connections'
|
||||||
|
@ -73,6 +61,19 @@ export function DlgDeleteReference() {
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name='keep_constituents'
|
||||||
|
render={({ field }) => (
|
||||||
|
<Checkbox
|
||||||
|
label='Сохранить наследованные конституенты'
|
||||||
|
titleHtml='Наследованные конституенты <br/>превратятся в дописанные'
|
||||||
|
value={field.value}
|
||||||
|
onChange={field.onChange}
|
||||||
|
disabled={target.result === null || keep_connections}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
</ModalForm>
|
</ModalForm>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
'use client';
|
'use client';
|
||||||
import { Controller, useFormContext } from 'react-hook-form';
|
import { Controller, useFormContext, useWatch } from 'react-hook-form';
|
||||||
|
|
||||||
import { Label } from '@/components/input';
|
import { Label } from '@/components/input';
|
||||||
import { useDialogsStore } from '@/stores/dialogs';
|
import { useDialogsStore } from '@/stores/dialogs';
|
||||||
|
@ -12,7 +12,12 @@ import { type DlgEditOperationProps } from './dlg-edit-operation';
|
||||||
export function TabArguments() {
|
export function TabArguments() {
|
||||||
const { control, setValue } = useFormContext<IUpdateOperationDTO>();
|
const { control, setValue } = useFormContext<IUpdateOperationDTO>();
|
||||||
const { manager, target } = useDialogsStore(state => state.props as DlgEditOperationProps);
|
const { manager, target } = useDialogsStore(state => state.props as DlgEditOperationProps);
|
||||||
const potentialCycle = [target.id, ...manager.oss.graph.expandAllOutputs([target.id])];
|
const args = useWatch({ control, name: 'arguments' });
|
||||||
|
|
||||||
|
const references = manager.oss.references
|
||||||
|
.filter(item => args.includes(item.target) || item.target === target.id)
|
||||||
|
.map(item => item.reference);
|
||||||
|
const potentialCycle = [target.id, ...references, ...manager.oss.graph.expandAllOutputs([target.id])];
|
||||||
const filtered = manager.oss.operations.filter(item => !potentialCycle.includes(item.id));
|
const filtered = manager.oss.operations.filter(item => !potentialCycle.includes(item.id));
|
||||||
|
|
||||||
function handleChangeArguments(prev: number[], newValue: number[]) {
|
function handleChangeArguments(prev: number[], newValue: number[]) {
|
||||||
|
|
|
@ -85,6 +85,7 @@ export interface IOperationSchema extends Omit<IOperationSchemaDTO, 'operations'
|
||||||
blocks: IBlock[];
|
blocks: IBlock[];
|
||||||
|
|
||||||
graph: Graph;
|
graph: Graph;
|
||||||
|
extendedGraph: Graph;
|
||||||
hierarchy: Graph<string>;
|
hierarchy: Graph<string>;
|
||||||
schemas: number[];
|
schemas: number[];
|
||||||
stats: IOperationSchemaStats;
|
stats: IOperationSchemaStats;
|
||||||
|
|
|
@ -206,15 +206,32 @@ export function MenuOperation({ operation, onHide }: MenuOperationProps) {
|
||||||
}).then(response => setTimeout(() => setSelected([`o${response.new_operation}`]), PARAMETER.refreshTimeout));
|
}).then(response => setTimeout(() => setSelected([`o${response.new_operation}`]), PARAMETER.refreshTimeout));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleSelectTarget() {
|
||||||
|
onHide();
|
||||||
|
if (operation.operation_type !== OperationType.REFERENCE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setSelected([`o${operation.target}`]);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<DropdownButton
|
{operation.operation_type !== OperationType.REFERENCE ? (
|
||||||
text='Редактировать'
|
<DropdownButton
|
||||||
title='Редактировать операцию'
|
text='Редактировать'
|
||||||
icon={<IconEdit2 size='1rem' className='icon-primary' />}
|
title='Редактировать операцию'
|
||||||
onClick={handleEditOperation}
|
icon={<IconEdit2 size='1rem' className='icon-primary' />}
|
||||||
disabled={!isMutable || isProcessing}
|
onClick={handleEditOperation}
|
||||||
/>
|
disabled={!isMutable || isProcessing}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<DropdownButton
|
||||||
|
text='Оригинал'
|
||||||
|
title='Выделить оригинал'
|
||||||
|
icon={<IconReference size='1rem' className='icon-primary' />}
|
||||||
|
onClick={handleSelectTarget}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
{operation.result ? (
|
{operation.result ? (
|
||||||
<DropdownButton
|
<DropdownButton
|
||||||
|
|
|
@ -109,7 +109,7 @@ export const OssEditState = ({ itemID, children }: React.PropsWithChildren<OssEd
|
||||||
}
|
}
|
||||||
|
|
||||||
function canDeleteOperation(target: IOperation) {
|
function canDeleteOperation(target: IOperation) {
|
||||||
if (target.operation_type === OperationType.INPUT) {
|
if (target.operation_type === OperationType.INPUT || target.operation_type === OperationType.REFERENCE) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return schema.graph.expandOutputs([target.id]).length === 0;
|
return schema.graph.expandOutputs([target.id]).length === 0;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user