diff --git a/rsconcept/backend/apps/oss/models/OperationSchema.py b/rsconcept/backend/apps/oss/models/OperationSchema.py index 8a70446e..4b69b324 100644 --- a/rsconcept/backend/apps/oss/models/OperationSchema.py +++ b/rsconcept/backend/apps/oss/models/OperationSchema.py @@ -129,7 +129,7 @@ class OperationSchema: if schema is not None and has_children: rsform = RSForm(schema) - self.after_create_cst(rsform, list(rsform.constituents())) + self.after_create_cst(rsform, list(rsform.constituents().order_by('order'))) self.save(update_fields=['time_update']) def set_arguments(self, target: int, arguments: list[Operation]) -> None: @@ -219,17 +219,17 @@ class OperationSchema: def execute_operation(self, operation: Operation) -> bool: ''' Execute target operation. ''' - schemas: list[LibraryItem] = [arg.argument.result for arg in operation.getArguments()] + schemas: list[LibraryItem] = [arg.argument.result for arg in operation.getArguments().order_by('pk')] if None in schemas: return False substitutions = operation.getSubstitutions() - receiver = self.create_input(operation) + receiver = self.create_input(self.cache.operation_by_id[operation.pk]) parents: dict = {} children: dict = {} for operand in schemas: schema = RSForm(operand) - items = list(schema.constituents()) + items = list(schema.constituents().order_by('order')) new_items = receiver.insert_copy(items) for (i, cst) in enumerate(new_items): parents[cst.pk] = items[i] @@ -240,21 +240,23 @@ class OperationSchema: original = children[sub.original.pk] replacement = children[sub.substitution.pk] translated_substitutions.append((original, replacement)) + # TODO: add auto substitutes for diamond receiver.substitute(translated_substitutions) - # TODO: remove duplicates from diamond - - for cst in receiver.constituents(): + for cst in receiver.constituents().order_by('order'): parent = parents.get(cst.pk) assert parent is not None Inheritance.objects.create( - operation=operation, + operation_id=operation.pk, child=cst, parent=parent ) receiver.restore_order() receiver.reset_aliases() + + if len(self.cache.graph.outputs[operation.pk]) > 0: + self.after_create_cst(receiver, list(receiver.constituents().order_by('order'))) self.save(update_fields=['time_update']) return True @@ -331,7 +333,7 @@ class OperationSchema: self._execute_inherit_cst( target_operation=target.pk, source=parent_schema, - items=list(parent_schema.constituents()), + items=list(parent_schema.constituents().order_by('order')), mapping={} ) @@ -769,7 +771,7 @@ class OssCache: def insert_argument(self, argument: Argument) -> None: ''' Insert new argument. ''' - self.graph.add_edge(argument.operation_id, argument.argument_id) + self.graph.add_edge(argument.argument_id, argument.operation_id) def insert_inheritance(self, inheritance: Inheritance) -> None: ''' Insert new inheritance. ''' @@ -811,7 +813,7 @@ class OssCache: def remove_argument(self, argument: Argument) -> None: ''' Remove argument from cache. ''' - self.graph.remove_edge(argument.operation_id, argument.argument_id) + self.graph.remove_edge(argument.argument_id, argument.operation_id) def remove_substitution(self, target: Substitution) -> None: ''' Remove substitution from cache. ''' diff --git a/rsconcept/backend/apps/oss/models/PropagationFacade.py b/rsconcept/backend/apps/oss/models/PropagationFacade.py index e0e5d230..f919838d 100644 --- a/rsconcept/backend/apps/oss/models/PropagationFacade.py +++ b/rsconcept/backend/apps/oss/models/PropagationFacade.py @@ -58,4 +58,4 @@ class PropagationFacade: return schema = RSForm(item) - PropagationFacade.before_delete_cst(schema, list(schema.constituents())) + PropagationFacade.before_delete_cst(schema, list(schema.constituents().order_by('order'))) diff --git a/rsconcept/backend/apps/oss/tests/s_propagation/t_operations.py b/rsconcept/backend/apps/oss/tests/s_propagation/t_operations.py index eaff883e..4492066b 100644 --- a/rsconcept/backend/apps/oss/tests/s_propagation/t_operations.py +++ b/rsconcept/backend/apps/oss/tests/s_propagation/t_operations.py @@ -132,7 +132,7 @@ class TestChangeOperations(EndpointTester): self.assertEqual(self.ks5.constituents().count(), 6) self.assertEqual(self.ks4D1.definition_formal, r'X4 X1') self.assertEqual(self.ks4D2.definition_formal, r'X1 DEL DEL DEL D1') - self.assertEqual(self.ks5D4.definition_formal, r'X1 DEL DEL DEL D1 D2 D3') + self.assertEqual(self.ks5D4.definition_formal, r'DEL DEL X3 DEL D1 D2 D3') @decl_endpoint('/api/oss/{item}/set-input', method='patch') def test_set_input_null(self): @@ -155,7 +155,7 @@ class TestChangeOperations(EndpointTester): self.assertEqual(self.ks5.constituents().count(), 6) self.assertEqual(self.ks4D1.definition_formal, r'X4 X1') self.assertEqual(self.ks4D2.definition_formal, r'X1 DEL DEL DEL D1') - self.assertEqual(self.ks5D4.definition_formal, r'X1 DEL DEL DEL D1 D2 D3') + self.assertEqual(self.ks5D4.definition_formal, r'DEL DEL X3 DEL D1 D2 D3') @decl_endpoint('/api/oss/{item}/set-input', method='patch') def test_set_input_change_schema(self): @@ -190,7 +190,7 @@ class TestChangeOperations(EndpointTester): self.assertEqual(ks4Dks6.definition_formal, r'X5 X6') self.assertEqual(self.ks4D1.definition_formal, r'X4 X1') self.assertEqual(self.ks4D2.definition_formal, r'X1 DEL DEL DEL D1') - self.assertEqual(self.ks5D4.definition_formal, r'X1 DEL DEL DEL D1 D2 D3') + self.assertEqual(self.ks5D4.definition_formal, r'DEL DEL X3 DEL D1 D2 D3') @decl_endpoint('/api/library/{item}', method='delete') def test_delete_schema(self): @@ -206,7 +206,7 @@ class TestChangeOperations(EndpointTester): self.assertEqual(self.ks4.constituents().count(), 4) self.assertEqual(self.ks5.constituents().count(), 7) self.assertEqual(self.ks4D2.definition_formal, r'DEL X2 X3 S1 DEL') - self.assertEqual(self.ks5D4.definition_formal, r'X1 X2 X3 S1 D1 DEL D3') + self.assertEqual(self.ks5D4.definition_formal, r'X1 X2 X3 S1 DEL D2 D3') @decl_endpoint('/api/oss/{item}/delete-operation', method='patch') def test_delete_operation_and_constituents(self): @@ -227,7 +227,7 @@ class TestChangeOperations(EndpointTester): self.assertEqual(self.ks4.constituents().count(), 4) self.assertEqual(self.ks5.constituents().count(), 7) self.assertEqual(self.ks4D2.definition_formal, r'DEL X2 X3 S1 DEL') - self.assertEqual(self.ks5D4.definition_formal, r'X1 X2 X3 S1 D1 DEL D3') + self.assertEqual(self.ks5D4.definition_formal, r'X1 X2 X3 S1 DEL D2 D3') @decl_endpoint('/api/oss/{item}/delete-operation', method='patch') def test_delete_operation_keep_constituents(self): @@ -282,7 +282,7 @@ class TestChangeOperations(EndpointTester): self.assertEqual(self.ks4.constituents().count(), 5) self.assertEqual(self.ks5.constituents().count(), 7) self.assertEqual(self.ks4D2.definition_formal, r'X1 D1 X3 S1 D1') - self.assertEqual(self.ks5D4.definition_formal, r'X1 D2 X3 S1 D1 D2 D3') + self.assertEqual(self.ks5D4.definition_formal, r'D1 X2 X3 S1 D1 D2 D3') @decl_endpoint('/api/oss/{item}/update-operation', method='patch') def test_change_arguments(self): @@ -307,7 +307,7 @@ class TestChangeOperations(EndpointTester): self.assertEqual(self.ks4.constituents().count(), 4) self.assertEqual(self.ks5.constituents().count(), 6) self.assertEqual(self.ks4D2.definition_formal, r'X1 DEL DEL DEL D1') - self.assertEqual(self.ks5D4.definition_formal, r'X1 DEL DEL DEL D1 D2 D3') + self.assertEqual(self.ks5D4.definition_formal, r'DEL DEL X3 DEL D1 D2 D3') data['arguments'] = [self.operation1.pk, self.operation2.pk] self.executeOK(data=data, item=self.owned_id) @@ -320,4 +320,22 @@ class TestChangeOperations(EndpointTester): self.assertEqual(self.ks4.constituents().count(), 7) self.assertEqual(self.ks5.constituents().count(), 9) self.assertEqual(self.ks4D2.definition_formal, r'X1 DEL DEL DEL D1') - self.assertEqual(self.ks5D4.definition_formal, r'X1 DEL DEL DEL D1 D2 D3') + self.assertEqual(self.ks5D4.definition_formal, r'DEL DEL X3 DEL D1 D2 D3') + + @decl_endpoint('/api/oss/{item}/execute-operation', method='post') + def test_execute_middle_operation(self): + self.client.delete(f'/api/library/{self.ks4.model.pk}') + self.operation4.refresh_from_db() + self.ks5.refresh_from_db() + self.assertEqual(self.operation4.result, None) + self.assertEqual(self.ks5.constituents().count(), 3) + + data = { + 'target': self.operation4.pk, + 'positions': [] + } + self.executeOK(data=data, item=self.owned_id) + self.operation4.refresh_from_db() + self.ks5.refresh_from_db() + self.assertNotEqual(self.operation4.result, None) + self.assertEqual(self.ks5.constituents().count(), 8) diff --git a/rsconcept/backend/apps/oss/tests/s_propagation/t_substitutions.py b/rsconcept/backend/apps/oss/tests/s_propagation/t_substitutions.py index 1a383a42..41cd1533 100644 --- a/rsconcept/backend/apps/oss/tests/s_propagation/t_substitutions.py +++ b/rsconcept/backend/apps/oss/tests/s_propagation/t_substitutions.py @@ -135,7 +135,7 @@ class TestChangeSubstitutions(EndpointTester): self.assertEqual(subs3_4.first().substitution, self.ks3X1) self.assertEqual(self.ks4D1.definition_formal, r'S1 S1') self.assertEqual(self.ks4D2.definition_formal, r'S1 X2 X3 S1 D1') - self.assertEqual(self.ks5D4.definition_formal, r'X1 X2 X3 X1 D1 D2 D3') + self.assertEqual(self.ks5D4.definition_formal, r'X1 X2 X3 X3 D1 D2 D3') @decl_endpoint('/api/rsforms/{schema}/substitute', method='patch') def test_substitute_substitution(self): @@ -157,7 +157,7 @@ class TestChangeSubstitutions(EndpointTester): self.assertEqual(subs3_4.first().substitution, self.ks3X1) self.assertEqual(self.ks4D1.definition_formal, r'X2 X1') self.assertEqual(self.ks4D2.definition_formal, r'X1 X2 X3 X2 D1') - self.assertEqual(self.ks5D4.definition_formal, r'X1 X2 X3 X2 D1 D2 D3') + self.assertEqual(self.ks5D4.definition_formal, r'X1 X2 X3 X1 D1 D2 D3') @decl_endpoint('/api/rsforms/{schema}/delete-multiple-cst', method='patch') def test_delete_original(self): @@ -171,7 +171,7 @@ class TestChangeSubstitutions(EndpointTester): self.assertEqual(subs3_4.count(), 1) self.assertEqual(self.ks5.constituents().count(), 7) self.assertEqual(self.ks4D2.definition_formal, r'X1 X2 X3 S1 DEL') - self.assertEqual(self.ks5D4.definition_formal, r'X1 X2 X3 S1 D1 DEL D3') + self.assertEqual(self.ks5D4.definition_formal, r'X1 X2 X3 S1 DEL D2 D3') @decl_endpoint('/api/rsforms/{schema}/delete-multiple-cst', method='patch') def test_delete_substitution(self): @@ -187,4 +187,4 @@ class TestChangeSubstitutions(EndpointTester): self.assertEqual(self.ks5.constituents().count(), 7) self.assertEqual(self.ks4D1.definition_formal, r'X4 X1') self.assertEqual(self.ks4D2.definition_formal, r'X1 X2 DEL DEL D1') - self.assertEqual(self.ks5D4.definition_formal, r'X1 X2 DEL DEL D1 D2 D3') + self.assertEqual(self.ks5D4.definition_formal, r'X1 DEL X3 DEL D1 D2 D3')