From 9e409c14f09e1c0a054cb1112c4905e2ca666f08 Mon Sep 17 00:00:00 2001 From: Ivan <8611739+IRBorisov@users.noreply.github.com> Date: Sun, 9 Nov 2025 13:47:16 +0300 Subject: [PATCH] R: Refactoring caches pt3 --- .../apps/oss/models/OperationSchemaCached.py | 48 +++++++++++-------- rsconcept/backend/apps/oss/models/OssCache.py | 2 +- .../apps/oss/models/PropagationEngine.py | 14 +++--- .../apps/oss/models/PropagationFacade.py | 19 ++++---- .../apps/oss/serializers/data_access.py | 2 - rsconcept/backend/apps/oss/views/oss.py | 8 ++-- .../backend/apps/rsform/views/rsforms.py | 8 ++-- 7 files changed, 52 insertions(+), 49 deletions(-) diff --git a/rsconcept/backend/apps/oss/models/OperationSchemaCached.py b/rsconcept/backend/apps/oss/models/OperationSchemaCached.py index 0eb4f0a2..33ed70cb 100644 --- a/rsconcept/backend/apps/oss/models/OperationSchemaCached.py +++ b/rsconcept/backend/apps/oss/models/OperationSchemaCached.py @@ -55,7 +55,7 @@ class OperationSchemaCached: inheritance_to_delete: list[Inheritance] = [] for child_id in children: child_operation = self.cache.operation_by_id[child_id] - child_schema = self.cache.get_schema(child_operation) + child_schema = self.cache.get_result(child_operation) if child_schema is None: continue self.engine.undo_substitutions_cst(ids, child_operation, child_schema) @@ -72,7 +72,7 @@ class OperationSchemaCached: ''' Set input schema for operation. ''' operation = self.cache.operation_by_id[target] has_children = bool(self.cache.extend_graph.outputs[target]) - old_schema = self.cache.get_schema(operation) + old_schema = self.cache.get_result(operation) if schema is None and old_schema is None or \ (schema is not None and old_schema is not None and schema.pk == old_schema.pk): return @@ -90,8 +90,8 @@ class OperationSchemaCached: operation.save(update_fields=['alias', 'title', 'description']) if schema is not None and has_children: - rsform = self.context.get_schema(schema.pk) - self.after_create_cst(rsform, list(rsform.constituentsQ().order_by('order'))) + cst_list = list(Constituenta.objects.filter(schema_id=schema.pk).order_by('order')) + self.after_create_cst(schema.pk, cst_list) def set_arguments(self, target: int, arguments: list[Operation]) -> None: ''' Set arguments of target Operation. ''' @@ -128,7 +128,7 @@ class OperationSchemaCached: ''' Clear all arguments for target Operation. ''' self.cache.ensure_loaded_subs() operation = self.cache.operation_by_id[target] - schema = self.cache.get_schema(operation) + schema = self.cache.get_result(operation) processed: list[dict] = [] deleted: list[Substitution] = [] for current in operation.getQ_substitutions(): @@ -207,26 +207,30 @@ class OperationSchemaCached: if self.cache.extend_graph.outputs[operation.pk]: receiver_items = list(Constituenta.objects.filter(schema_id=receiver.pk).order_by('order')) - self.after_create_cst(receiver, receiver_items) + self.after_create_cst(receiver.pk, receiver_items) return True - def relocate_down(self, destination: RSFormCached, items: list[int]): + def relocate_down(self, destinationID: int, items: list[int]): ''' Move list of Constituents to destination Schema inheritor. ''' self.cache.ensure_loaded_subs() - operation = self.cache.get_operation(destination.pk) + operation = self.cache.get_operation(destinationID) + destination = self.context.get_schema(destinationID) self.engine.undo_substitutions_cst(items, operation, destination) inheritance_to_delete = [item for item in self.cache.inheritance[operation.pk] if item.parent_id in items] for item in inheritance_to_delete: self.cache.remove_inheritance(item) Inheritance.objects.filter(operation_id=operation.pk, parent_id__in=items).delete() - def relocate_up(self, source: RSFormCached, destination: RSFormCached, + def relocate_up(self, sourceID: int, destinationID: int, item_ids: list[int]) -> list[Constituenta]: ''' Move list of Constituents upstream to destination Schema. ''' self.cache.ensure_loaded_subs() - operation = self.cache.get_operation(source.pk) + source = self.context.get_schema(sourceID) + destination = self.context.get_schema(destinationID) + + operation = self.cache.get_operation(sourceID) alias_mapping: dict[str, str] = {} for item in self.cache.inheritance[operation.pk]: if item.parent_id in destination.cache.by_id: @@ -234,7 +238,7 @@ class OperationSchemaCached: destination_cst = destination.cache.by_id[item.parent_id] alias_mapping[source_cst.alias] = destination_cst.alias - new_items = destination.insert_from(source.pk, item_ids, alias_mapping) + new_items = destination.insert_from(sourceID, item_ids, alias_mapping) for (cst, new_cst) in new_items: new_inheritance = Inheritance.objects.create( operation=operation, @@ -243,15 +247,16 @@ class OperationSchemaCached: ) self.cache.insert_inheritance(new_inheritance) new_constituents = [item[1] for item in new_items] - self.after_create_cst(destination, new_constituents, exclude=[operation.pk]) + self.after_create_cst(destinationID, new_constituents, exclude=[operation.pk]) return new_constituents def after_create_cst( - self, source: RSFormCached, + self, sourceID: int, cst_list: list[Constituenta], exclude: Optional[list[int]] = None ) -> None: ''' Trigger cascade resolutions when new Constituenta is created. ''' + source = self.context.get_schema(sourceID) alias_mapping = create_dependant_mapping(source, cst_list) operation = self.cache.get_operation(source.pk) self.engine.on_inherit_cst(operation.pk, source, cst_list, alias_mapping, exclude) @@ -261,9 +266,10 @@ class OperationSchemaCached: operation = self.cache.get_operation(schemaID) self.engine.on_change_cst_type(operation.pk, target, new_type) - def after_update_cst(self, source: RSFormCached, target: int, data: dict, old_data: dict) -> None: + def after_update_cst(self, sourceID: int, target: int, data: dict, old_data: dict) -> None: ''' Trigger cascade resolutions when Constituenta data is changed. ''' - operation = self.cache.get_operation(source.pk) + operation = self.cache.get_operation(sourceID) + source = self.context.get_schema(sourceID) depend_aliases = extract_data_references(data, old_data) alias_mapping: CstMapping = {} for alias in depend_aliases: @@ -293,18 +299,18 @@ class OperationSchemaCached: if target.result_id is None: return for argument in arguments: - parent_schema = self.cache.get_schema(argument) - if parent_schema is not None: + parent_schema = self.cache.get_result(argument) + if parent_schema: self.engine.delete_inherited(target.pk, [cst.pk for cst in parent_schema.cache.constituents]) def after_create_arguments(self, target: Operation, arguments: list[Operation]) -> None: ''' Trigger cascade resolutions after arguments are created. ''' - schema = self.cache.get_schema(target) - if schema is None: + schema = self.cache.get_result(target) + if not schema: return for argument in arguments: - parent_schema = self.cache.get_schema(argument) - if parent_schema is None: + parent_schema = self.cache.get_result(argument) + if not parent_schema: continue self.engine.inherit_cst( target_operation=target.pk, diff --git a/rsconcept/backend/apps/oss/models/OssCache.py b/rsconcept/backend/apps/oss/models/OssCache.py index 78e9303d..8ce62845 100644 --- a/rsconcept/backend/apps/oss/models/OssCache.py +++ b/rsconcept/backend/apps/oss/models/OssCache.py @@ -60,7 +60,7 @@ class OssCache: 'operation_id', 'parent_id', 'child_id'): self.inheritance[item.operation_id].append(item) - def get_schema(self, operation: Operation) -> Optional[RSFormCached]: + def get_result(self, operation: Operation) -> Optional[RSFormCached]: ''' Get schema by Operation. ''' if operation.result_id is None: return None diff --git a/rsconcept/backend/apps/oss/models/PropagationEngine.py b/rsconcept/backend/apps/oss/models/PropagationEngine.py index a82322c2..79af2863 100644 --- a/rsconcept/backend/apps/oss/models/PropagationEngine.py +++ b/rsconcept/backend/apps/oss/models/PropagationEngine.py @@ -37,7 +37,7 @@ class PropagationEngine: successor_id = self.cache.get_inheritor(cst_id, child_id) if successor_id is None: continue - child_schema = self.cache.get_schema(child_operation) + child_schema = self.cache.get_result(child_operation) if child_schema is None: continue if child_schema.change_cst_type(successor_id, ctype): @@ -69,7 +69,7 @@ class PropagationEngine: ) -> None: ''' Execute inheritance of Constituenta. ''' operation = self.cache.operation_by_id[target_operation] - destination = self.cache.get_schema(operation) + destination = self.cache.get_result(operation) if destination is None: return @@ -106,7 +106,7 @@ class PropagationEngine: successor_id = self.cache.get_inheritor(cst_id, child_id) if successor_id is None: continue - child_schema = self.cache.get_schema(child_operation) + child_schema = self.cache.get_result(child_operation) assert child_schema is not None new_mapping = self._transform_mapping(mapping, child_operation, child_schema) alias_mapping = cst_mapping_to_alias(new_mapping) @@ -178,7 +178,7 @@ class PropagationEngine: self.cache.ensure_loaded_subs() for child_id in children: child_operation = self.cache.operation_by_id[child_id] - child_schema = self.cache.get_schema(child_operation) + child_schema = self.cache.get_result(child_operation) if child_schema is None: continue new_substitutions = self._transform_substitutions(substitutions, child_id, child_schema) @@ -195,7 +195,7 @@ class PropagationEngine: self.cache.ensure_loaded_subs() for child_id in children: child_operation = self.cache.operation_by_id[child_id] - child_schema = self.cache.get_schema(child_operation) + child_schema = self.cache.get_result(child_operation) if child_schema is None: continue @@ -227,7 +227,7 @@ class PropagationEngine: def delete_inherited(self, operationID: int, parents: list[int]) -> None: ''' Execute deletion of Constituenta inheritance. ''' operation = self.cache.operation_by_id[operationID] - schema = self.cache.get_schema(operation) + schema = self.cache.get_result(operation) if schema is None: return self.undo_substitutions_cst(parents, operation, schema) @@ -376,7 +376,7 @@ class PropagationEngine: self.cache.ensure_loaded_subs() for child_id in children: child_operation = self.cache.operation_by_id[child_id] - child_schema = self.cache.get_schema(child_operation) + child_schema = self.cache.get_result(child_operation) if child_schema is None: continue new_mapping = self._transform_mapping(mapping, child_operation, child_schema) diff --git a/rsconcept/backend/apps/oss/models/PropagationFacade.py b/rsconcept/backend/apps/oss/models/PropagationFacade.py index 60aa7535..b865f42b 100644 --- a/rsconcept/backend/apps/oss/models/PropagationFacade.py +++ b/rsconcept/backend/apps/oss/models/PropagationFacade.py @@ -10,7 +10,7 @@ from .PropagationContext import PropagationContext def _get_oss_hosts(schemaID: int) -> list[int]: ''' Get all hosts for schema. ''' - return list(LibraryItem.objects.filter(operations__result_id=schemaID).values_list('pk', flat=True)) + return list(LibraryItem.objects.filter(operations__result_id=schemaID).distinct().values_list('pk', flat=True)) class PropagationFacade: @@ -30,14 +30,13 @@ class PropagationFacade: ''' Get RSFormCached for schemaID. ''' return self._context.get_schema(schemaID) - def reset_cache(self) -> None: - ''' Reset cache. ''' - self._oss = {} - - def after_create_cst(self, source: RSFormCached, new_cst: list[Constituenta], + def after_create_cst(self, new_cst: list[Constituenta], exclude: Optional[list[int]] = None) -> None: ''' Trigger cascade resolutions when new constituenta is created. ''' - hosts = _get_oss_hosts(source.pk) + if not new_cst: + return + source = new_cst[0].schema_id + hosts = _get_oss_hosts(source) for host in hosts: if exclude is None or host not in exclude: self.get_oss(host).after_create_cst(source, new_cst) @@ -52,15 +51,15 @@ class PropagationFacade: # pylint: disable=too-many-arguments, too-many-positional-arguments def after_update_cst( - self, source: RSFormCached, target: int, + self, sourceID: int, target: int, data: dict, old_data: dict, exclude: Optional[list[int]] = None ) -> None: ''' Trigger cascade resolutions when constituenta data is changed. ''' - hosts = _get_oss_hosts(source.pk) + hosts = _get_oss_hosts(sourceID) for host in hosts: if exclude is None or host not in exclude: - self.get_oss(host).after_update_cst(source, target, data, old_data) + self.get_oss(host).after_update_cst(sourceID, target, data, old_data) def before_delete_cst(self, sourceID: int, target: list[int], exclude: Optional[list[int]] = None) -> None: diff --git a/rsconcept/backend/apps/oss/serializers/data_access.py b/rsconcept/backend/apps/oss/serializers/data_access.py index 7bf614e3..1855127b 100644 --- a/rsconcept/backend/apps/oss/serializers/data_access.py +++ b/rsconcept/backend/apps/oss/serializers/data_access.py @@ -609,8 +609,6 @@ class RelocateConstituentsSerializer(StrictSerializer): attrs['destination'] = attrs['destination'].id attrs['source'] = attrs['items'][0].schema_id - # TODO: check permissions for editing source and destination - if attrs['source'] == attrs['destination']: raise serializers.ValidationError({ 'destination': msg.sourceEqualDestination() diff --git a/rsconcept/backend/apps/oss/views/oss.py b/rsconcept/backend/apps/oss/views/oss.py index b6b200ea..5530d12f 100644 --- a/rsconcept/backend/apps/oss/views/oss.py +++ b/rsconcept/backend/apps/oss/views/oss.py @@ -837,19 +837,19 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev serializer.is_valid(raise_exception=True) data = serializer.validated_data ids = [cst.pk for cst in data['items']] + destinationID = data['destination'] with transaction.atomic(): propagation = m.PropagationFacade() oss = propagation.get_oss(item.pk) source = propagation.get_schema(data['source']) - destination = propagation.get_schema(data['destination']) if data['move_down']: - oss.relocate_down(destination, ids) + oss.relocate_down(destinationID, ids) propagation.before_delete_cst(source.pk, ids) source.delete_cst(ids) else: - new_items = oss.relocate_up(source, destination, ids) - propagation.after_create_cst(destination, new_items, exclude=[oss.pk]) + new_items = oss.relocate_up(source.pk, destinationID, ids) + propagation.after_create_cst(new_items, exclude=[oss.pk]) item.save(update_fields=['time_update']) return Response(status=c.HTTP_200_OK) diff --git a/rsconcept/backend/apps/rsform/views/rsforms.py b/rsconcept/backend/apps/rsform/views/rsforms.py index 9cab39dc..89af3d05 100644 --- a/rsconcept/backend/apps/rsform/views/rsforms.py +++ b/rsconcept/backend/apps/rsform/views/rsforms.py @@ -94,7 +94,7 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr propagation = PropagationFacade() schema = propagation.get_schema(item.pk) new_cst = schema.create_cst(data, insert_after) - propagation.after_create_cst(schema, [new_cst]) + propagation.after_create_cst([new_cst]) item.save(update_fields=['time_update']) return Response( @@ -129,7 +129,7 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr propagation = PropagationFacade() schema = propagation.get_schema(item.pk) old_data = schema.update_cst(cst.pk, data) - propagation.after_update_cst(schema, cst.pk, data, old_data) + propagation.after_update_cst(item.pk, cst.pk, data, old_data) if 'alias' in data and data['alias'] != cst.alias: cst.refresh_from_db() changed_type = 'cst_type' in data and cst.cst_type != data['cst_type'] @@ -213,7 +213,7 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr propagation = PropagationFacade() schema = propagation.get_schema(item.pk) new_cst = schema.produce_structure(cst, cst_parse) - propagation.after_create_cst(schema, new_cst) + propagation.after_create_cst(new_cst) item.save(update_fields=['time_update']) return Response( status=c.HTTP_200_OK, @@ -745,7 +745,7 @@ def inline_synthesis(request: Request) -> HttpResponse: new_items = receiver.insert_from(source.pk, target_ids) target_ids = [item[0].pk for item in new_items] mapping_ids = {cst.pk: new_cst for (cst, new_cst) in new_items} - propagation.after_create_cst(receiver, [item[1] for item in new_items]) + propagation.after_create_cst([item[1] for item in new_items]) substitutions: list[tuple[m.Constituenta, m.Constituenta]] = [] for substitution in serializer.validated_data['substitutions']: