R: Refactoring caches pt2

This commit is contained in:
Ivan 2025-11-08 21:44:25 +03:00
parent 41e2df21ef
commit 0242cd5c20
25 changed files with 157 additions and 167 deletions

View File

@ -67,7 +67,7 @@ class LibraryViewSet(viewsets.ModelViewSet):
def perform_destroy(self, instance: m.LibraryItem) -> None:
if instance.item_type == m.LibraryItemType.RSFORM:
PropagationFacade.before_delete_schema(instance.pk)
PropagationFacade().before_delete_schema(instance.pk)
super().perform_destroy(instance)
if instance.item_type == m.LibraryItemType.OPERATION_SCHEMA:
schemas = list(OperationSchema.owned_schemasQ(instance))

View File

@ -18,7 +18,7 @@ from .Substitution import Substitution
class OperationSchema:
''' Operations schema API wrapper. No caching, propagation and minimal side effects. '''
def __init__(self, model: LibraryItem):
def __init__(self, model: LibraryItem) -> None:
self.model = model
@staticmethod

View File

@ -11,6 +11,7 @@ from .Inheritance import Inheritance
from .Operation import Operation
from .OperationSchema import OperationSchema
from .OssCache import OssCache
from .PropagationContext import PropagationContext
from .PropagationEngine import PropagationEngine
from .Substitution import Substitution
from .utils import CstMapping, CstSubstitution, create_dependant_mapping, extract_data_references
@ -19,10 +20,11 @@ from .utils import CstMapping, CstSubstitution, create_dependant_mapping, extrac
class OperationSchemaCached:
''' Operations schema API with caching. '''
def __init__(self, item_id: int):
def __init__(self, item_id: int, context: PropagationContext) -> None:
self.pk = item_id
self.cache = OssCache(item_id)
self.engine = PropagationEngine(self.cache)
self.context = context
self.cache = OssCache(item_id, context)
self.engine = PropagationEngine(self.cache, context)
def delete_replica(self, target: int, keep_connections: bool = False, keep_constituents: bool = False):
''' Delete Replica Operation. '''
@ -78,7 +80,7 @@ class OperationSchemaCached:
if old_schema is not None:
if has_children:
self.before_delete_cst(old_schema.pk, [cst.pk for cst in old_schema.cache.constituents])
self.cache.remove_schema(old_schema)
self.context.invalidate(old_schema.pk)
operation.setQ_result(schema)
if schema is not None:
@ -88,7 +90,7 @@ class OperationSchemaCached:
operation.save(update_fields=['alias', 'title', 'description'])
if schema is not None and has_children:
rsform = RSFormCached(schema.pk)
rsform = self.context.get_schema(schema.pk)
self.after_create_cst(rsform, list(rsform.constituentsQ().order_by('order')))
def set_arguments(self, target: int, arguments: list[Operation]) -> None:
@ -173,8 +175,7 @@ class OperationSchemaCached:
return False
substitutions = operation.getQ_substitutions()
new_schema = OperationSchema.create_input(self.pk, self.cache.operation_by_id[operation.pk])
receiver = RSFormCached(new_schema.pk)
self.cache.insert_schema(receiver)
receiver = self.context.get_schema(new_schema.pk)
parents: dict = {}
children: dict = {}
@ -209,11 +210,10 @@ class OperationSchemaCached:
self.after_create_cst(receiver, receiver_items)
return True
def relocate_down(self, source: RSFormCached, destination: RSFormCached, items: list[int]):
def relocate_down(self, destination: RSFormCached, items: list[int]):
''' Move list of Constituents to destination Schema inheritor. '''
self.cache.ensure_loaded_subs()
self.cache.insert_schema(source)
self.cache.insert_schema(destination)
operation = self.cache.get_operation(destination.pk)
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]
@ -225,8 +225,6 @@ class OperationSchemaCached:
item_ids: list[int]) -> list[Constituenta]:
''' Move list of Constituents upstream to destination Schema. '''
self.cache.ensure_loaded_subs()
self.cache.insert_schema(source)
self.cache.insert_schema(destination)
operation = self.cache.get_operation(source.pk)
alias_mapping: dict[str, str] = {}
@ -254,7 +252,6 @@ class OperationSchemaCached:
exclude: Optional[list[int]] = None
) -> None:
''' Trigger cascade resolutions when new Constituenta is created. '''
self.cache.insert_schema(source)
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)
@ -266,7 +263,6 @@ class OperationSchemaCached:
def after_update_cst(self, source: RSFormCached, target: int, data: dict, old_data: dict) -> None:
''' Trigger cascade resolutions when Constituenta data is changed. '''
self.cache.insert_schema(source)
operation = self.cache.get_operation(source.pk)
depend_aliases = extract_data_references(data, old_data)
alias_mapping: CstMapping = {}

View File

@ -8,6 +8,7 @@ from apps.rsform.models import RSFormCached
from .Argument import Argument
from .Inheritance import Inheritance
from .Operation import Operation, OperationType
from .PropagationContext import PropagationContext
from .Replica import Replica
from .Substitution import Substitution
@ -15,10 +16,9 @@ from .Substitution import Substitution
class OssCache:
''' Cache for OSS data. '''
def __init__(self, item_id: int):
def __init__(self, item_id: int, context: PropagationContext) -> None:
self._item_id = item_id
self._schemas: list[RSFormCached] = []
self._schema_by_id: dict[int, RSFormCached] = {}
self._context = context
self.operations = list(Operation.objects.filter(oss_id=item_id).only('result_id', 'operation_type'))
self.operation_by_id = {operation.pk: operation for operation in self.operations}
@ -64,23 +64,7 @@ class OssCache:
''' Get schema by Operation. '''
if operation.result_id is None:
return None
if operation.result_id in self._schema_by_id:
return self._schema_by_id[operation.result_id]
else:
schema = RSFormCached(operation.result_id)
schema.cache.ensure_loaded()
self._insert_new(schema)
return schema
def get_schema_by_id(self, target: int) -> RSFormCached:
''' Get schema by Operation. '''
if target in self._schema_by_id:
return self._schema_by_id[target]
else:
schema = RSFormCached(target)
schema.cache.ensure_loaded()
self._insert_new(schema)
return schema
return self._context.get_schema(operation.result_id)
def get_operation(self, schemaID: int) -> Operation:
''' Get operation by schema. '''
@ -111,12 +95,6 @@ class OssCache:
return self.get_inheritor(sub.substitution_id, operation)
return self.get_inheritor(parent_cst, operation)
def insert_schema(self, schema: RSFormCached) -> None:
''' Insert new schema. '''
if not self._schema_by_id.get(schema.pk):
schema.cache.ensure_loaded()
self._insert_new(schema)
def insert_argument(self, argument: Argument) -> None:
''' Insert new argument. '''
self.graph.add_edge(argument.argument_id, argument.operation_id)
@ -145,19 +123,12 @@ class OssCache:
for item in inherit_to_delete:
self.inheritance[operation].remove(item)
def remove_schema(self, schema: RSFormCached) -> None:
''' Remove schema from cache. '''
self._schemas.remove(schema)
del self._schema_by_id[schema.pk]
def remove_operation(self, operation: int) -> None:
''' Remove operation from cache. '''
target = self.operation_by_id[operation]
self.graph.remove_node(operation)
self.extend_graph.remove_node(operation)
if target.result_id in self._schema_by_id:
self._schemas.remove(self._schema_by_id[target.result_id])
del self._schema_by_id[target.result_id]
self._context.invalidate(target.result_id)
self.operations.remove(self.operation_by_id[operation])
del self.operation_by_id[operation]
if operation in self.replica_original:
@ -182,7 +153,3 @@ class OssCache:
def remove_inheritance(self, target: Inheritance) -> None:
''' Remove inheritance from cache. '''
self.inheritance[target.operation_id].remove(target)
def _insert_new(self, schema: RSFormCached) -> None:
self._schemas.append(schema)
self._schema_by_id[schema.pk] = schema

View File

@ -0,0 +1,27 @@
''' Models: Propagation context. '''
from apps.rsform.models import RSFormCached
class PropagationContext:
''' Propagation context. '''
def __init__(self) -> None:
self._cache: dict[int, RSFormCached] = {}
def get_schema(self, item_id: int) -> RSFormCached:
''' Get schema by ID. '''
if item_id not in self._cache:
schema = RSFormCached(item_id)
schema.cache.ensure_loaded()
self._cache[item_id] = schema
return self._cache[item_id]
def clear(self) -> None:
''' Clear cache. '''
self._cache = {}
def invalidate(self, item_id: int | None) -> None:
''' Invalidate schema by ID. '''
if item_id in self._cache:
del self._cache[item_id]

View File

@ -8,6 +8,7 @@ from apps.rsform.models import Attribution, Constituenta, CstType, RSFormCached
from .Inheritance import Inheritance
from .Operation import Operation
from .OssCache import OssCache
from .PropagationContext import PropagationContext
from .Substitution import Substitution
from .utils import (
CstMapping,
@ -21,8 +22,9 @@ from .utils import (
class PropagationEngine:
''' OSS changes propagation engine. '''
def __init__(self, cache: OssCache):
def __init__(self, cache: OssCache, context: PropagationContext) -> None:
self.cache = cache
self.context = context
def on_change_cst_type(self, operation_id: int, cst_id: int, ctype: CstType) -> None:
''' Trigger cascade resolutions when Constituenta type is changed. '''
@ -213,26 +215,26 @@ class PropagationEngine:
self.on_delete_attribution(child_id, deleted)
Attribution.objects.filter(pk__in=[attrib.pk for attrib in deleted]).delete()
def on_delete_inherited(self, operation: int, target: list[int]) -> None:
def on_delete_inherited(self, operationID: int, target: list[int]) -> None:
''' Trigger cascade resolutions when Constituenta inheritance is deleted. '''
children = self.cache.extend_graph.outputs[operation]
children = self.cache.extend_graph.outputs[operationID]
if not children:
return
self.cache.ensure_loaded_subs()
for child_id in children:
self.delete_inherited(child_id, target)
def delete_inherited(self, operation_id: int, parent_ids: list[int]) -> None:
def delete_inherited(self, operationID: int, parents: list[int]) -> None:
''' Execute deletion of Constituenta inheritance. '''
operation = self.cache.operation_by_id[operation_id]
operation = self.cache.operation_by_id[operationID]
schema = self.cache.get_schema(operation)
if schema is None:
return
self.undo_substitutions_cst(parent_ids, operation, schema)
target_ids = self.cache.get_inheritors_list(parent_ids, operation_id)
self.on_delete_inherited(operation_id, target_ids)
self.undo_substitutions_cst(parents, operation, schema)
target_ids = self.cache.get_inheritors_list(parents, operationID)
self.on_delete_inherited(operationID, target_ids)
if target_ids:
self.cache.remove_cst(operation_id, target_ids)
self.cache.remove_cst(operationID, target_ids)
schema.delete_cst(target_ids)
def undo_substitutions_cst(self, target_ids: list[int], operation: Operation, schema: RSFormCached) -> None:
@ -254,7 +256,7 @@ class PropagationEngine:
if ignore_parents is None:
ignore_parents = []
operation_id = target.operation_id
original_schema = self.cache.get_schema_by_id(target.original.schema_id)
original_schema = self.context.get_schema(target.original.schema_id)
dependant = []
for cst_id in original_schema.get_dependant([target.original_id]):
if cst_id not in ignore_parents:

View File

@ -5,6 +5,7 @@ from apps.library.models import LibraryItem
from apps.rsform.models import Attribution, Constituenta, CstType, RSFormCached
from .OperationSchemaCached import CstSubstitution, OperationSchemaCached
from .PropagationContext import PropagationContext
def _get_oss_hosts(schemaID: int) -> list[int]:
@ -15,63 +16,61 @@ def _get_oss_hosts(schemaID: int) -> list[int]:
class PropagationFacade:
''' Change propagation API. '''
_oss: dict[int, OperationSchemaCached] = {}
def __init__(self) -> None:
self._context = PropagationContext()
self._oss: dict[int, OperationSchemaCached] = {}
@staticmethod
def get_oss(schemaID: int) -> OperationSchemaCached:
def get_oss(self, schemaID: int) -> OperationSchemaCached:
''' Get OperationSchemaCached for schemaID. '''
if schemaID not in PropagationFacade._oss:
PropagationFacade._oss[schemaID] = OperationSchemaCached(schemaID)
return PropagationFacade._oss[schemaID]
if schemaID not in self._oss:
self._oss[schemaID] = OperationSchemaCached(schemaID, self._context)
return self._oss[schemaID]
@staticmethod
def reset_cache() -> None:
def get_schema(self, schemaID: int) -> RSFormCached:
''' Get RSFormCached for schemaID. '''
return self._context.get_schema(schemaID)
def reset_cache(self) -> None:
''' Reset cache. '''
PropagationFacade._oss = {}
self._oss = {}
@staticmethod
def after_create_cst(source: RSFormCached, new_cst: list[Constituenta],
def after_create_cst(self, source: RSFormCached, new_cst: list[Constituenta],
exclude: Optional[list[int]] = None) -> None:
''' Trigger cascade resolutions when new constituenta is created. '''
hosts = _get_oss_hosts(source.pk)
for host in hosts:
if exclude is None or host not in exclude:
PropagationFacade.get_oss(host).after_create_cst(source, new_cst)
self.get_oss(host).after_create_cst(source, new_cst)
@staticmethod
def after_change_cst_type(sourceID: int, target: int, new_type: CstType,
def after_change_cst_type(self, sourceID: int, target: int, new_type: CstType,
exclude: Optional[list[int]] = None) -> None:
''' Trigger cascade resolutions when constituenta type is changed. '''
hosts = _get_oss_hosts(sourceID)
for host in hosts:
if exclude is None or host not in exclude:
PropagationFacade.get_oss(host).after_change_cst_type(sourceID, target, new_type)
self.get_oss(host).after_change_cst_type(sourceID, target, new_type)
@staticmethod
# pylint: disable=too-many-arguments, too-many-positional-arguments
def after_update_cst(
source: RSFormCached,
target: int,
data: dict,
old_data: dict,
self, source: RSFormCached, 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)
for host in hosts:
if exclude is None or host not in exclude:
PropagationFacade.get_oss(host).after_update_cst(source, target, data, old_data)
self.get_oss(host).after_update_cst(source, target, data, old_data)
@staticmethod
def before_delete_cst(sourceID: int, target: list[int],
def before_delete_cst(self, sourceID: int, target: list[int],
exclude: Optional[list[int]] = None) -> None:
''' Trigger cascade resolutions before constituents are deleted. '''
hosts = _get_oss_hosts(sourceID)
for host in hosts:
if exclude is None or host not in exclude:
PropagationFacade.get_oss(host).before_delete_cst(sourceID, target)
self.get_oss(host).before_delete_cst(sourceID, target)
@staticmethod
def before_substitute(sourceID: int, substitutions: CstSubstitution,
def before_substitute(self, sourceID: int, substitutions: CstSubstitution,
exclude: Optional[list[int]] = None) -> None:
''' Trigger cascade resolutions before constituents are substituted. '''
if not substitutions:
@ -79,10 +78,9 @@ class PropagationFacade:
hosts = _get_oss_hosts(sourceID)
for host in hosts:
if exclude is None or host not in exclude:
PropagationFacade.get_oss(host).before_substitute(sourceID, substitutions)
self.get_oss(host).before_substitute(sourceID, substitutions)
@staticmethod
def before_delete_schema(target: int, exclude: Optional[list[int]] = None) -> None:
def before_delete_schema(self, target: int, exclude: Optional[list[int]] = None) -> None:
''' Trigger cascade resolutions before schema is deleted. '''
hosts = _get_oss_hosts(target)
if not hosts:
@ -91,25 +89,23 @@ class PropagationFacade:
ids = list(Constituenta.objects.filter(schema_id=target).order_by('order').values_list('pk', flat=True))
for host in hosts:
if exclude is None or host not in exclude:
PropagationFacade.get_oss(host).before_delete_cst(target, ids)
del PropagationFacade._oss[host]
self.get_oss(host).before_delete_cst(target, ids)
del self._oss[host]
@staticmethod
def after_create_attribution(sourceID: int,
def after_create_attribution(self, sourceID: int,
attributions: list[Attribution],
exclude: Optional[list[int]] = None) -> None:
''' Trigger cascade resolutions when Attribution is created. '''
hosts = _get_oss_hosts(sourceID)
for host in hosts:
if exclude is None or host not in exclude:
PropagationFacade.get_oss(host).after_create_attribution(sourceID, attributions)
self.get_oss(host).after_create_attribution(sourceID, attributions)
@staticmethod
def before_delete_attribution(sourceID: int,
def before_delete_attribution(self, sourceID: int,
attributions: list[Attribution],
exclude: Optional[list[int]] = None) -> None:
''' Trigger cascade resolutions before Attribution is deleted. '''
hosts = _get_oss_hosts(sourceID)
for host in hosts:
if exclude is None or host not in exclude:
PropagationFacade.get_oss(host).before_delete_attribution(sourceID, attributions)
self.get_oss(host).before_delete_attribution(sourceID, attributions)

View File

@ -7,6 +7,7 @@ from .Layout import Layout
from .Operation import Operation, OperationType
from .OperationSchema import OperationSchema
from .OperationSchemaCached import OperationSchemaCached
from .PropagationContext import PropagationContext
from .PropagationFacade import PropagationFacade
from .Replica import Replica
from .Substitution import Substitution

View File

@ -1,6 +1,6 @@
''' Testing API: Change attributes of OSS and RSForms. '''
from apps.library.models import AccessPolicy, Editor, LibraryItem, LocationHead
from apps.oss.models import OperationSchema, OperationType, PropagationFacade
from apps.oss.models import OperationSchema, OperationType
from apps.rsform.models import RSForm
from apps.users.models import User
from shared.EndpointTester import EndpointTester, decl_endpoint
@ -11,7 +11,6 @@ class TestChangeAttributes(EndpointTester):
def setUp(self):
PropagationFacade.reset_cache()
super().setUp()
self.user3 = User.objects.create(
username='UserTest3',

View File

@ -1,6 +1,6 @@
''' Testing API: Change constituents in OSS. '''
from apps.oss.models import OperationSchema, OperationType, PropagationFacade
from apps.oss.models import OperationSchema, OperationType
from apps.rsform.models import Attribution, Constituenta, CstType, RSForm
from shared.EndpointTester import EndpointTester, decl_endpoint
@ -9,7 +9,6 @@ class TestChangeConstituents(EndpointTester):
''' Testing Constituents change propagation in OSS. '''
def setUp(self):
PropagationFacade.reset_cache()
super().setUp()
self.owned = OperationSchema.create(
title='Test',

View File

@ -1,6 +1,6 @@
''' Testing API: Change substitutions in OSS. '''
from apps.oss.models import OperationSchema, OperationType, PropagationFacade
from apps.oss.models import OperationSchema, OperationType
from apps.rsform.models import Constituenta, RSForm
from shared.EndpointTester import EndpointTester, decl_endpoint
@ -9,7 +9,6 @@ class TestChangeOperations(EndpointTester):
''' Testing Operations change propagation in OSS. '''
def setUp(self):
PropagationFacade.reset_cache()
super().setUp()
self.owned = OperationSchema.create(
title='Test',

View File

@ -1,6 +1,6 @@
''' Testing API: Propagate changes through references in OSS. '''
from apps.oss.models import Inheritance, OperationSchema, OperationType, PropagationFacade
from apps.oss.models import Inheritance, OperationSchema, OperationType
from apps.rsform.models import Constituenta, CstType, RSForm
from shared.EndpointTester import EndpointTester, decl_endpoint
@ -9,7 +9,6 @@ class ReferencePropagationTestCase(EndpointTester):
''' Test propagation through references in OSS. '''
def setUp(self):
PropagationFacade.reset_cache()
super().setUp()
self.owned = OperationSchema.create(

View File

@ -1,6 +1,6 @@
''' Testing API: Change substitutions in OSS. '''
from apps.oss.models import OperationSchema, OperationType, PropagationFacade
from apps.oss.models import OperationSchema, OperationType
from apps.rsform.models import Constituenta, RSForm
from shared.EndpointTester import EndpointTester, decl_endpoint
@ -10,7 +10,6 @@ class TestChangeSubstitutions(EndpointTester):
def setUp(self):
PropagationFacade.reset_cache()
super().setUp()
self.owned = OperationSchema.create(
title='Test',

View File

@ -1,5 +1,5 @@
''' Testing API: Operation Schema - blocks manipulation. '''
from apps.oss.models import OperationSchema, OperationType, PropagationFacade
from apps.oss.models import OperationSchema, OperationType
from shared.EndpointTester import EndpointTester, decl_endpoint
@ -8,7 +8,6 @@ class TestOssBlocks(EndpointTester):
def setUp(self):
PropagationFacade.reset_cache()
super().setUp()
self.owned = OperationSchema.create(title='Test', alias='T1', owner=self.user)
self.owned_id = self.owned.model.pk

View File

@ -5,7 +5,6 @@ from apps.oss.models import (
Operation,
OperationSchema,
OperationType,
PropagationFacade,
Replica
)
from apps.rsform.models import Attribution, RSForm
@ -17,7 +16,6 @@ class TestOssOperations(EndpointTester):
def setUp(self):
PropagationFacade.reset_cache()
super().setUp()
self.owned = OperationSchema.create(title='Test', alias='T1', owner=self.user)
self.owned_id = self.owned.model.pk

View File

@ -1,6 +1,6 @@
''' Testing API: Operation Schema. '''
from apps.library.models import AccessPolicy, LibraryItemType
from apps.oss.models import OperationSchema, OperationType, PropagationFacade
from apps.oss.models import OperationSchema, OperationType
from apps.rsform.models import Constituenta, RSForm
from shared.EndpointTester import EndpointTester, decl_endpoint
@ -9,7 +9,6 @@ class TestOssViewset(EndpointTester):
''' Testing OSS view. '''
def setUp(self):
PropagationFacade.reset_cache()
super().setUp()
self.owned = OperationSchema.create(title='Test', alias='T1', owner=self.user)
self.owned_id = self.owned.model.pk

View File

@ -14,8 +14,7 @@ from rest_framework.response import Response
from apps.library.models import LibraryItem, LibraryItemType
from apps.library.serializers import LibraryItemSerializer
from apps.oss.models import PropagationFacade
from apps.rsform.models import Constituenta, RSFormCached
from apps.rsform.models import Constituenta
from apps.rsform.serializers import CstTargetSerializer
from shared import messages as msg
from shared import permissions
@ -421,7 +420,8 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
schema_clone.access_policy = item.access_policy
schema_clone.location = item.location
schema_clone.save()
RSFormCached(schema_clone.pk).insert_from(prototype.pk)
m.PropagationFacade().get_schema(schema_clone.pk).insert_from(prototype.pk)
new_operation.result = schema_clone
new_operation.save(update_fields=["result"])
@ -545,7 +545,8 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
operation: m.Operation = cast(m.Operation, serializer.validated_data['target'])
with transaction.atomic():
oss = PropagationFacade.get_oss(item.pk)
propagation = m.PropagationFacade()
oss = propagation.get_oss(item.pk)
if 'layout' in serializer.validated_data:
layout = serializer.validated_data['layout']
m.Layout.update_data(pk, layout)
@ -600,12 +601,13 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
layout = [x for x in layout if x['nodeID'] != 'o' + str(operation.pk)]
with transaction.atomic():
oss = PropagationFacade.get_oss(item.pk)
propagation = m.PropagationFacade()
oss = propagation.get_oss(item.pk)
oss.delete_operation(operation.pk, serializer.validated_data['keep_constituents'])
m.Layout.update_data(pk, layout)
if old_schema is not None:
if serializer.validated_data['delete_schema']:
m.PropagationFacade.before_delete_schema(old_schema.pk)
propagation.before_delete_schema(old_schema.pk)
old_schema.delete()
elif old_schema.is_synced(item):
old_schema.visible = True
@ -641,7 +643,8 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
layout = [x for x in layout if x['nodeID'] != 'o' + str(operation.pk)]
with transaction.atomic():
oss = PropagationFacade.get_oss(item.pk)
propagation = m.PropagationFacade()
oss = propagation.get_oss(item.pk)
m.Layout.update_data(pk, layout)
oss.delete_replica(operation.pk, keep_connections, keep_constituents)
item.save(update_fields=['time_update'])
@ -727,7 +730,8 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
old_schema = target_operation.result
with transaction.atomic():
oss = PropagationFacade.get_oss(item.pk)
propagation = m.PropagationFacade()
oss = propagation.get_oss(item.pk)
if old_schema is not None:
if old_schema.is_synced(item):
old_schema.visible = True
@ -770,7 +774,8 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
layout = serializer.validated_data['layout']
with transaction.atomic():
oss = PropagationFacade.get_oss(item.pk)
propagation = m.PropagationFacade()
oss = propagation.get_oss(item.pk)
oss.execute_operation(operation)
m.Layout.update_data(pk, layout)
item.save(update_fields=['time_update'])
@ -834,16 +839,17 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
ids = [cst.pk for cst in data['items']]
with transaction.atomic():
oss = PropagationFacade.get_oss(item.pk)
source = RSFormCached(data['source'])
destination = RSFormCached(data['destination'])
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(source, destination, ids)
m.PropagationFacade.before_delete_cst(source.pk, ids)
oss.relocate_down(destination, ids)
propagation.before_delete_cst(source.pk, ids)
source.delete_cst(ids)
else:
new_items = oss.relocate_up(source, destination, ids)
m.PropagationFacade.after_create_cst(destination, new_items, exclude=[oss.pk])
propagation.after_create_cst(destination, new_items, exclude=[oss.pk])
item.save(update_fields=['time_update'])
return Response(status=c.HTTP_200_OK)

View File

@ -8,7 +8,7 @@ ItemType = TypeVar("ItemType")
class Graph(Generic[ItemType]):
''' Directed graph. '''
def __init__(self, graph: Optional[dict[ItemType, list[ItemType]]] = None):
def __init__(self, graph: Optional[dict[ItemType, list[ItemType]]] = None) -> None:
if graph is None:
self.outputs: dict[ItemType, list[ItemType]] = {}
self.inputs: dict[ItemType, list[ItemType]] = {}

View File

@ -8,7 +8,7 @@ from .SemanticInfo import SemanticInfo
class OrderManager:
''' Ordering helper class '''
def __init__(self, schema: RSFormCached):
def __init__(self, schema: RSFormCached) -> None:
self._semantic = SemanticInfo(schema)
self._items = schema.cache.constituents
self._cst_by_ID = schema.cache.by_id

View File

@ -21,7 +21,7 @@ DELETED_ALIAS = 'DEL'
class RSForm:
''' RSForm wrapper. No caching, each mutation requires querying. '''
def __init__(self, model: LibraryItem):
def __init__(self, model: LibraryItem) -> None:
assert model.item_type == LibraryItemType.RSFORM
self.model = model

View File

@ -20,7 +20,7 @@ from .RSForm import DELETED_ALIAS, RSForm
class RSFormCached:
''' RSForm cached. Caching allows to avoid querying for each method call. '''
def __init__(self, item_id: int):
def __init__(self, item_id: int) -> None:
self.pk = item_id
self.cache: _RSFormCache = _RSFormCache(self)
@ -400,7 +400,7 @@ class RSFormCached:
class _RSFormCache:
''' Cache for RSForm constituents. '''
def __init__(self, schema: 'RSFormCached'):
def __init__(self, schema: 'RSFormCached') -> None:
self._schema = schema
self.constituents: list[Constituenta] = []
self.by_id: dict[int, Constituenta] = {}

View File

@ -16,7 +16,7 @@ from .RSFormCached import RSFormCached
class SemanticInfo:
''' Semantic information derived from constituents. '''
def __init__(self, schema: RSFormCached):
def __init__(self, schema: RSFormCached) -> None:
schema.cache.ensure_loaded()
self._items = schema.cache.constituents
self._cst_by_ID = schema.cache.by_id

View File

@ -12,7 +12,7 @@ from ..models import Constituenta, CstType
class PyConceptAdapter:
''' RSForm adapter for interacting with pyconcept module. '''
def __init__(self, data: Union[int, dict]):
def __init__(self, data: Union[int, dict]) -> None:
try:
if 'items' in cast(dict, data):
self.data = self._prepare_request_raw(cast(dict, data))

View File

@ -22,8 +22,8 @@ class TestRSFormCached(DBTester):
self.assertFalse(schema1.constituentsQ().exists())
self.assertFalse(schema2.constituentsQ().exists())
Constituenta.objects.create(alias='X1', schema=schema1.model, order=0)
Constituenta.objects.create(alias='X2', schema=schema1.model, order=1)
Constituenta.objects.create(alias='X1', schema_id=schema1.pk, order=0)
Constituenta.objects.create(alias='X2', schema_id=schema1.pk, order=1)
self.assertTrue(schema1.constituentsQ().exists())
self.assertFalse(schema2.constituentsQ().exists())
self.assertEqual(schema1.constituentsQ().count(), 2)
@ -32,7 +32,7 @@ class TestRSFormCached(DBTester):
def test_insert_last(self):
x1 = self.schema.insert_last('X1')
self.assertEqual(x1.order, 0)
self.assertEqual(x1.schema, self.schema.model)
self.assertEqual(x1.schema_id, self.schema.pk)
def test_create_cst(self):
@ -115,8 +115,8 @@ class TestRSFormCached(DBTester):
definition_formal='X2 = X3'
)
test_ks = RSFormCached.create(title='Test')
test_ks.insert_from(self.schema.model.pk)
items = Constituenta.objects.filter(schema=test_ks.model).order_by('order')
test_ks.insert_from(self.schema.pk)
items = Constituenta.objects.filter(schema_id=test_ks.pk).order_by('order')
self.assertEqual(len(items), 4)
self.assertEqual(items[0].alias, 'X2')
self.assertEqual(items[1].alias, 'D2')
@ -200,7 +200,7 @@ class TestRSFormCached(DBTester):
self.schema.substitute([(x1, x2)])
self.assertEqual(self.schema.constituentsQ().count(), 3)
self.assertEqual(Attribution.objects.filter(container__schema=self.schema.model).count(), 2)
self.assertEqual(Attribution.objects.filter(container__schema_id=self.schema.pk).count(), 2)
self.assertTrue(Attribution.objects.filter(container=x2, attribute=d2).exists())
self.assertTrue(Attribution.objects.filter(container=x2, attribute=d1).exists())

View File

@ -91,9 +91,10 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
insert_after = data['insert_after']
with transaction.atomic():
schema = m.RSFormCached(item.pk)
propagation = PropagationFacade()
schema = propagation.get_schema(item.pk)
new_cst = schema.create_cst(data, insert_after)
PropagationFacade.after_create_cst(schema, [new_cst])
propagation.after_create_cst(schema, [new_cst])
item.save(update_fields=['time_update'])
return Response(
@ -125,9 +126,10 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
data = serializer.validated_data['item_data']
with transaction.atomic():
schema = m.RSFormCached(item.pk)
propagation = PropagationFacade()
schema = propagation.get_schema(item.pk)
old_data = schema.update_cst(cst.pk, data)
PropagationFacade.after_update_cst(schema, cst.pk, data, old_data)
propagation.after_update_cst(schema, 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']
@ -138,7 +140,7 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
cst.save()
schema.apply_mapping(mapping=mapping, change_aliases=False)
if changed_type:
PropagationFacade.after_change_cst_type(item.pk, cst.pk, cast(m.CstType, cst.cst_type))
propagation.after_change_cst_type(item.pk, cst.pk, cast(m.CstType, cst.cst_type))
item.save(update_fields=['time_update'])
return Response(
status=c.HTTP_200_OK,
@ -208,9 +210,10 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
)
with transaction.atomic():
schema = m.RSFormCached(item.pk)
propagation = PropagationFacade()
schema = propagation.get_schema(item.pk)
new_cst = schema.produce_structure(cst, cst_parse)
PropagationFacade.after_create_cst(schema, new_cst)
propagation.after_create_cst(schema, new_cst)
item.save(update_fields=['time_update'])
return Response(
status=c.HTTP_200_OK,
@ -245,7 +248,7 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
original = cast(m.Constituenta, substitution['original'])
replacement = cast(m.Constituenta, substitution['substitution'])
substitutions.append((original, replacement))
PropagationFacade.before_substitute(item.pk, substitutions)
PropagationFacade().before_substitute(item.pk, substitutions)
schema.substitute(substitutions)
item.save(update_fields=['time_update'])
@ -275,7 +278,7 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
with transaction.atomic():
schema = m.RSForm(item)
PropagationFacade.before_delete_cst(item.pk, [cst.pk for cst in cst_list])
PropagationFacade().before_delete_cst(item.pk, [cst.pk for cst in cst_list])
schema.delete_cst(cst_list)
item.save(update_fields=['time_update'])
@ -305,11 +308,11 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
attribute = serializer.validated_data['attribute']
with transaction.atomic():
new_association = m.Attribution.objects.create(
new_attribution = m.Attribution.objects.create(
container=container,
attribute=attribute
)
PropagationFacade.after_create_attribution(item.pk, [new_association])
PropagationFacade().after_create_attribution(item.pk, [new_attribution])
item.save(update_fields=['time_update'])
return Response(
@ -345,7 +348,7 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
'container': msg.invalidAssociation()
})
PropagationFacade.before_delete_attribution(item.pk, target)
PropagationFacade().before_delete_attribution(item.pk, target)
m.Attribution.objects.filter(pk__in=[attrib.pk for attrib in target]).delete()
item.save(update_fields=['time_update'])
@ -375,7 +378,7 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
with transaction.atomic():
target = list(m.Attribution.objects.filter(container=serializer.validated_data['target']))
if target:
PropagationFacade.before_delete_attribution(item.pk, target)
PropagationFacade().before_delete_attribution(item.pk, target)
m.Attribution.objects.filter(pk__in=[attrib.pk for attrib in target]).delete()
item.save(update_fields=['time_update'])
@ -493,10 +496,10 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
serializer = s.RSFormTRSSerializer(data=data, context={'load_meta': load_metadata})
serializer.is_valid(raise_exception=True)
result: m.RSForm = serializer.save()
result: m.RSFormCached = serializer.save()
return Response(
status=c.HTTP_200_OK,
data=s.RSFormParseSerializer(result.model).data
data=s.RSFormParseSerializer(LibraryItem.objects.get(pk=result.pk)).data
)
@extend_schema(
@ -651,10 +654,10 @@ class TrsImportView(views.APIView):
_prepare_rsform_data(data, request, owner)
serializer = s.RSFormTRSSerializer(data=data, context={'load_meta': True})
serializer.is_valid(raise_exception=True)
schema: m.RSForm = serializer.save()
schema: m.RSFormCached = serializer.save()
return Response(
status=c.HTTP_201_CREATED,
data=LibraryItemSerializer(schema.model).data
data=LibraryItemSerializer(LibraryItem.objects.get(pk=schema.pk)).data
)
@ -687,10 +690,10 @@ def create_rsform(request: Request) -> HttpResponse:
_prepare_rsform_data(data, request, owner)
serializer_rsform = s.RSFormTRSSerializer(data=data, context={'load_meta': True})
serializer_rsform.is_valid(raise_exception=True)
schema: m.RSForm = serializer_rsform.save()
schema: m.RSFormCached = serializer_rsform.save()
return Response(
status=c.HTTP_201_CREATED,
data=LibraryItemSerializer(schema.model).data
data=LibraryItemSerializer(LibraryItem.objects.get(pk=schema.pk)).data
)
@ -732,16 +735,17 @@ def inline_synthesis(request: Request) -> HttpResponse:
serializer.is_valid(raise_exception=True)
item = cast(LibraryItem, serializer.validated_data['receiver'])
receiver = m.RSFormCached(item.pk)
target_cst = cast(list[m.Constituenta], serializer.validated_data['items'])
source = cast(LibraryItem, serializer.validated_data['source'])
target_ids = [item.pk for item in target_cst] if target_cst else None
with transaction.atomic():
propagation = PropagationFacade()
receiver = propagation.get_schema(item.pk)
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}
PropagationFacade.after_create_cst(receiver, [item[1] for item in new_items])
propagation.after_create_cst(receiver, [item[1] for item in new_items])
substitutions: list[tuple[m.Constituenta, m.Constituenta]] = []
for substitution in serializer.validated_data['substitutions']:
@ -753,7 +757,7 @@ def inline_synthesis(request: Request) -> HttpResponse:
replacement = mapping_ids[replacement.pk]
substitutions.append((original, replacement))
PropagationFacade.before_substitute(receiver.pk, substitutions)
propagation.before_substitute(receiver.pk, substitutions)
receiver.substitute(substitutions)
item.save(update_fields=['time_update'])