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: def perform_destroy(self, instance: m.LibraryItem) -> None:
if instance.item_type == m.LibraryItemType.RSFORM: if instance.item_type == m.LibraryItemType.RSFORM:
PropagationFacade.before_delete_schema(instance.pk) PropagationFacade().before_delete_schema(instance.pk)
super().perform_destroy(instance) super().perform_destroy(instance)
if instance.item_type == m.LibraryItemType.OPERATION_SCHEMA: if instance.item_type == m.LibraryItemType.OPERATION_SCHEMA:
schemas = list(OperationSchema.owned_schemasQ(instance)) schemas = list(OperationSchema.owned_schemasQ(instance))

View File

@ -18,7 +18,7 @@ from .Substitution import Substitution
class OperationSchema: class OperationSchema:
''' Operations schema API wrapper. No caching, propagation and minimal side effects. ''' ''' 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 self.model = model
@staticmethod @staticmethod

View File

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

View File

@ -8,6 +8,7 @@ from apps.rsform.models import RSFormCached
from .Argument import Argument from .Argument import Argument
from .Inheritance import Inheritance from .Inheritance import Inheritance
from .Operation import Operation, OperationType from .Operation import Operation, OperationType
from .PropagationContext import PropagationContext
from .Replica import Replica from .Replica import Replica
from .Substitution import Substitution from .Substitution import Substitution
@ -15,10 +16,9 @@ from .Substitution import Substitution
class OssCache: class OssCache:
''' Cache for OSS data. ''' ''' 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._item_id = item_id
self._schemas: list[RSFormCached] = [] self._context = context
self._schema_by_id: dict[int, RSFormCached] = {}
self.operations = list(Operation.objects.filter(oss_id=item_id).only('result_id', 'operation_type')) 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} self.operation_by_id = {operation.pk: operation for operation in self.operations}
@ -64,23 +64,7 @@ class OssCache:
''' Get schema by Operation. ''' ''' Get schema by Operation. '''
if operation.result_id is None: if operation.result_id is None:
return None return None
if operation.result_id in self._schema_by_id: return self._context.get_schema(operation.result_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
def get_operation(self, schemaID: int) -> Operation: def get_operation(self, schemaID: int) -> Operation:
''' Get operation by schema. ''' ''' Get operation by schema. '''
@ -111,12 +95,6 @@ class OssCache:
return self.get_inheritor(sub.substitution_id, operation) return self.get_inheritor(sub.substitution_id, operation)
return self.get_inheritor(parent_cst, 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: def insert_argument(self, argument: Argument) -> None:
''' Insert new argument. ''' ''' Insert new argument. '''
self.graph.add_edge(argument.argument_id, argument.operation_id) self.graph.add_edge(argument.argument_id, argument.operation_id)
@ -145,19 +123,12 @@ class OssCache:
for item in inherit_to_delete: for item in inherit_to_delete:
self.inheritance[operation].remove(item) 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: def remove_operation(self, operation: int) -> None:
''' Remove operation from cache. ''' ''' Remove operation from cache. '''
target = self.operation_by_id[operation] target = self.operation_by_id[operation]
self.graph.remove_node(operation) self.graph.remove_node(operation)
self.extend_graph.remove_node(operation) self.extend_graph.remove_node(operation)
if target.result_id in self._schema_by_id: self._context.invalidate(target.result_id)
self._schemas.remove(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.replica_original: if operation in self.replica_original:
@ -182,7 +153,3 @@ class OssCache:
def remove_inheritance(self, target: Inheritance) -> None: def remove_inheritance(self, target: Inheritance) -> None:
''' Remove inheritance from cache. ''' ''' Remove inheritance from cache. '''
self.inheritance[target.operation_id].remove(target) 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 .Inheritance import Inheritance
from .Operation import Operation from .Operation import Operation
from .OssCache import OssCache from .OssCache import OssCache
from .PropagationContext import PropagationContext
from .Substitution import Substitution from .Substitution import Substitution
from .utils import ( from .utils import (
CstMapping, CstMapping,
@ -21,8 +22,9 @@ from .utils import (
class PropagationEngine: class PropagationEngine:
''' OSS changes propagation engine. ''' ''' OSS changes propagation engine. '''
def __init__(self, cache: OssCache): def __init__(self, cache: OssCache, context: PropagationContext) -> None:
self.cache = cache self.cache = cache
self.context = context
def on_change_cst_type(self, operation_id: int, cst_id: int, ctype: CstType) -> None: def on_change_cst_type(self, operation_id: int, cst_id: int, ctype: CstType) -> None:
''' Trigger cascade resolutions when Constituenta type is changed. ''' ''' Trigger cascade resolutions when Constituenta type is changed. '''
@ -213,26 +215,26 @@ class PropagationEngine:
self.on_delete_attribution(child_id, deleted) self.on_delete_attribution(child_id, deleted)
Attribution.objects.filter(pk__in=[attrib.pk for attrib in deleted]).delete() 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. ''' ''' 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: if not children:
return return
self.cache.ensure_loaded_subs() self.cache.ensure_loaded_subs()
for child_id in children: for child_id in children:
self.delete_inherited(child_id, target) 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. ''' ''' 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) schema = self.cache.get_schema(operation)
if schema is None: if schema is None:
return return
self.undo_substitutions_cst(parent_ids, operation, schema) self.undo_substitutions_cst(parents, operation, schema)
target_ids = self.cache.get_inheritors_list(parent_ids, operation_id) target_ids = self.cache.get_inheritors_list(parents, operationID)
self.on_delete_inherited(operation_id, target_ids) self.on_delete_inherited(operationID, target_ids)
if 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) schema.delete_cst(target_ids)
def undo_substitutions_cst(self, target_ids: list[int], operation: Operation, schema: RSFormCached) -> None: 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: if ignore_parents is None:
ignore_parents = [] ignore_parents = []
operation_id = target.operation_id 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 = [] dependant = []
for cst_id in original_schema.get_dependant([target.original_id]): for cst_id in original_schema.get_dependant([target.original_id]):
if cst_id not in ignore_parents: 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 apps.rsform.models import Attribution, Constituenta, CstType, RSFormCached
from .OperationSchemaCached import CstSubstitution, OperationSchemaCached from .OperationSchemaCached import CstSubstitution, OperationSchemaCached
from .PropagationContext import PropagationContext
def _get_oss_hosts(schemaID: int) -> list[int]: def _get_oss_hosts(schemaID: int) -> list[int]:
@ -15,63 +16,61 @@ def _get_oss_hosts(schemaID: int) -> list[int]:
class PropagationFacade: class PropagationFacade:
''' Change propagation API. ''' ''' Change propagation API. '''
_oss: dict[int, OperationSchemaCached] = {} def __init__(self) -> None:
self._context = PropagationContext()
self._oss: dict[int, OperationSchemaCached] = {}
@staticmethod def get_oss(self, schemaID: int) -> OperationSchemaCached:
def get_oss(schemaID: int) -> OperationSchemaCached:
''' Get OperationSchemaCached for schemaID. ''' ''' Get OperationSchemaCached for schemaID. '''
if schemaID not in PropagationFacade._oss: if schemaID not in self._oss:
PropagationFacade._oss[schemaID] = OperationSchemaCached(schemaID) self._oss[schemaID] = OperationSchemaCached(schemaID, self._context)
return PropagationFacade._oss[schemaID] return self._oss[schemaID]
@staticmethod def get_schema(self, schemaID: int) -> RSFormCached:
def reset_cache() -> None: ''' Get RSFormCached for schemaID. '''
return self._context.get_schema(schemaID)
def reset_cache(self) -> None:
''' Reset cache. ''' ''' Reset cache. '''
PropagationFacade._oss = {} self._oss = {}
@staticmethod def after_create_cst(self, source: RSFormCached, new_cst: list[Constituenta],
def after_create_cst(source: RSFormCached, new_cst: list[Constituenta],
exclude: Optional[list[int]] = None) -> None: exclude: Optional[list[int]] = None) -> None:
''' Trigger cascade resolutions when new constituenta is created. ''' ''' Trigger cascade resolutions when new constituenta is created. '''
hosts = _get_oss_hosts(source.pk) hosts = _get_oss_hosts(source.pk)
for host in hosts: for host in hosts:
if exclude is None or host not in exclude: 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(self, sourceID: int, target: int, new_type: CstType,
def after_change_cst_type(sourceID: int, target: int, new_type: CstType,
exclude: Optional[list[int]] = None) -> None: exclude: Optional[list[int]] = None) -> None:
''' Trigger cascade resolutions when constituenta type is changed. ''' ''' Trigger cascade resolutions when constituenta type is changed. '''
hosts = _get_oss_hosts(sourceID) hosts = _get_oss_hosts(sourceID)
for host in hosts: for host in hosts:
if exclude is None or host not in exclude: 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( def after_update_cst(
source: RSFormCached, self, source: RSFormCached, target: int,
target: int, data: dict, old_data: dict,
data: dict,
old_data: dict,
exclude: Optional[list[int]] = None exclude: Optional[list[int]] = None
) -> None: ) -> None:
''' Trigger cascade resolutions when constituenta data is changed. ''' ''' Trigger cascade resolutions when constituenta data is changed. '''
hosts = _get_oss_hosts(source.pk) hosts = _get_oss_hosts(source.pk)
for host in hosts: for host in hosts:
if exclude is None or host not in exclude: 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(self, sourceID: int, target: list[int],
def before_delete_cst(sourceID: int, target: list[int],
exclude: Optional[list[int]] = None) -> None: exclude: Optional[list[int]] = None) -> None:
''' Trigger cascade resolutions before constituents are deleted. ''' ''' Trigger cascade resolutions before constituents are deleted. '''
hosts = _get_oss_hosts(sourceID) hosts = _get_oss_hosts(sourceID)
for host in hosts: for host in hosts:
if exclude is None or host not in exclude: 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(self, sourceID: int, substitutions: CstSubstitution,
def before_substitute(sourceID: int, substitutions: CstSubstitution,
exclude: Optional[list[int]] = None) -> None: exclude: Optional[list[int]] = None) -> None:
''' Trigger cascade resolutions before constituents are substituted. ''' ''' Trigger cascade resolutions before constituents are substituted. '''
if not substitutions: if not substitutions:
@ -79,10 +78,9 @@ class PropagationFacade:
hosts = _get_oss_hosts(sourceID) hosts = _get_oss_hosts(sourceID)
for host in hosts: for host in hosts:
if exclude is None or host not in exclude: 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(self, target: int, exclude: Optional[list[int]] = None) -> None:
def before_delete_schema(target: int, exclude: Optional[list[int]] = None) -> None:
''' Trigger cascade resolutions before schema is deleted. ''' ''' Trigger cascade resolutions before schema is deleted. '''
hosts = _get_oss_hosts(target) hosts = _get_oss_hosts(target)
if not hosts: 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)) ids = list(Constituenta.objects.filter(schema_id=target).order_by('order').values_list('pk', flat=True))
for host in hosts: for host in hosts:
if exclude is None or host not in exclude: if exclude is None or host not in exclude:
PropagationFacade.get_oss(host).before_delete_cst(target, ids) self.get_oss(host).before_delete_cst(target, ids)
del PropagationFacade._oss[host] del self._oss[host]
@staticmethod def after_create_attribution(self, sourceID: int,
def after_create_attribution(sourceID: int,
attributions: list[Attribution], attributions: list[Attribution],
exclude: Optional[list[int]] = None) -> None: exclude: Optional[list[int]] = None) -> None:
''' Trigger cascade resolutions when Attribution is created. ''' ''' Trigger cascade resolutions when Attribution is created. '''
hosts = _get_oss_hosts(sourceID) hosts = _get_oss_hosts(sourceID)
for host in hosts: for host in hosts:
if exclude is None or host not in exclude: 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(self, sourceID: int,
def before_delete_attribution(sourceID: int,
attributions: list[Attribution], attributions: list[Attribution],
exclude: Optional[list[int]] = None) -> None: exclude: Optional[list[int]] = None) -> None:
''' Trigger cascade resolutions before Attribution is deleted. ''' ''' Trigger cascade resolutions before Attribution is deleted. '''
hosts = _get_oss_hosts(sourceID) hosts = _get_oss_hosts(sourceID)
for host in hosts: for host in hosts:
if exclude is None or host not in exclude: 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 .Operation import Operation, OperationType
from .OperationSchema import OperationSchema from .OperationSchema import OperationSchema
from .OperationSchemaCached import OperationSchemaCached from .OperationSchemaCached import OperationSchemaCached
from .PropagationContext import PropagationContext
from .PropagationFacade import PropagationFacade from .PropagationFacade import PropagationFacade
from .Replica import Replica from .Replica import Replica
from .Substitution import Substitution from .Substitution import Substitution

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,6 +1,6 @@
''' Testing API: Operation Schema. ''' ''' Testing API: Operation Schema. '''
from apps.library.models import AccessPolicy, LibraryItemType 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 apps.rsform.models import Constituenta, RSForm
from shared.EndpointTester import EndpointTester, decl_endpoint from shared.EndpointTester import EndpointTester, decl_endpoint
@ -9,7 +9,6 @@ class TestOssViewset(EndpointTester):
''' Testing OSS view. ''' ''' Testing OSS view. '''
def setUp(self): def setUp(self):
PropagationFacade.reset_cache()
super().setUp() super().setUp()
self.owned = OperationSchema.create(title='Test', alias='T1', owner=self.user) self.owned = OperationSchema.create(title='Test', alias='T1', owner=self.user)
self.owned_id = self.owned.model.pk 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.models import LibraryItem, LibraryItemType
from apps.library.serializers import LibraryItemSerializer from apps.library.serializers import LibraryItemSerializer
from apps.oss.models import PropagationFacade from apps.rsform.models import Constituenta
from apps.rsform.models import Constituenta, RSFormCached
from apps.rsform.serializers import CstTargetSerializer from apps.rsform.serializers import CstTargetSerializer
from shared import messages as msg from shared import messages as msg
from shared import permissions 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.access_policy = item.access_policy
schema_clone.location = item.location schema_clone.location = item.location
schema_clone.save() 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.result = schema_clone
new_operation.save(update_fields=["result"]) 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']) operation: m.Operation = cast(m.Operation, serializer.validated_data['target'])
with transaction.atomic(): with transaction.atomic():
oss = PropagationFacade.get_oss(item.pk) propagation = m.PropagationFacade()
oss = propagation.get_oss(item.pk)
if 'layout' in serializer.validated_data: if 'layout' in serializer.validated_data:
layout = serializer.validated_data['layout'] layout = serializer.validated_data['layout']
m.Layout.update_data(pk, 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)] layout = [x for x in layout if x['nodeID'] != 'o' + str(operation.pk)]
with transaction.atomic(): 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']) oss.delete_operation(operation.pk, serializer.validated_data['keep_constituents'])
m.Layout.update_data(pk, layout) m.Layout.update_data(pk, layout)
if old_schema is not None: if old_schema is not None:
if serializer.validated_data['delete_schema']: if serializer.validated_data['delete_schema']:
m.PropagationFacade.before_delete_schema(old_schema.pk) propagation.before_delete_schema(old_schema.pk)
old_schema.delete() old_schema.delete()
elif old_schema.is_synced(item): elif old_schema.is_synced(item):
old_schema.visible = True 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)] layout = [x for x in layout if x['nodeID'] != 'o' + str(operation.pk)]
with transaction.atomic(): with transaction.atomic():
oss = PropagationFacade.get_oss(item.pk) propagation = m.PropagationFacade()
oss = propagation.get_oss(item.pk)
m.Layout.update_data(pk, layout) m.Layout.update_data(pk, layout)
oss.delete_replica(operation.pk, keep_connections, keep_constituents) oss.delete_replica(operation.pk, keep_connections, keep_constituents)
item.save(update_fields=['time_update']) item.save(update_fields=['time_update'])
@ -727,7 +730,8 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
old_schema = target_operation.result old_schema = target_operation.result
with transaction.atomic(): 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 not None:
if old_schema.is_synced(item): if old_schema.is_synced(item):
old_schema.visible = True old_schema.visible = True
@ -770,7 +774,8 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
layout = serializer.validated_data['layout'] layout = serializer.validated_data['layout']
with transaction.atomic(): with transaction.atomic():
oss = PropagationFacade.get_oss(item.pk) propagation = m.PropagationFacade()
oss = propagation.get_oss(item.pk)
oss.execute_operation(operation) oss.execute_operation(operation)
m.Layout.update_data(pk, layout) m.Layout.update_data(pk, layout)
item.save(update_fields=['time_update']) 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']] ids = [cst.pk for cst in data['items']]
with transaction.atomic(): with transaction.atomic():
oss = PropagationFacade.get_oss(item.pk) propagation = m.PropagationFacade()
source = RSFormCached(data['source']) oss = propagation.get_oss(item.pk)
destination = RSFormCached(data['destination']) source = propagation.get_schema(data['source'])
destination = propagation.get_schema(data['destination'])
if data['move_down']: if data['move_down']:
oss.relocate_down(source, destination, ids) oss.relocate_down(destination, ids)
m.PropagationFacade.before_delete_cst(source.pk, ids) propagation.before_delete_cst(source.pk, ids)
source.delete_cst(ids) source.delete_cst(ids)
else: else:
new_items = oss.relocate_up(source, destination, ids) 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']) item.save(update_fields=['time_update'])
return Response(status=c.HTTP_200_OK) return Response(status=c.HTTP_200_OK)

View File

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

View File

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

View File

@ -21,7 +21,7 @@ DELETED_ALIAS = 'DEL'
class RSForm: class RSForm:
''' RSForm wrapper. No caching, each mutation requires querying. ''' ''' 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 assert model.item_type == LibraryItemType.RSFORM
self.model = model self.model = model

View File

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

View File

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

View File

@ -12,7 +12,7 @@ from ..models import Constituenta, CstType
class PyConceptAdapter: class PyConceptAdapter:
''' RSForm adapter for interacting with pyconcept module. ''' ''' RSForm adapter for interacting with pyconcept module. '''
def __init__(self, data: Union[int, dict]): def __init__(self, data: Union[int, dict]) -> None:
try: try:
if 'items' in cast(dict, data): if 'items' in cast(dict, data):
self.data = self._prepare_request_raw(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(schema1.constituentsQ().exists())
self.assertFalse(schema2.constituentsQ().exists()) self.assertFalse(schema2.constituentsQ().exists())
Constituenta.objects.create(alias='X1', schema=schema1.model, order=0) Constituenta.objects.create(alias='X1', schema_id=schema1.pk, order=0)
Constituenta.objects.create(alias='X2', schema=schema1.model, order=1) Constituenta.objects.create(alias='X2', schema_id=schema1.pk, order=1)
self.assertTrue(schema1.constituentsQ().exists()) self.assertTrue(schema1.constituentsQ().exists())
self.assertFalse(schema2.constituentsQ().exists()) self.assertFalse(schema2.constituentsQ().exists())
self.assertEqual(schema1.constituentsQ().count(), 2) self.assertEqual(schema1.constituentsQ().count(), 2)
@ -32,7 +32,7 @@ class TestRSFormCached(DBTester):
def test_insert_last(self): def test_insert_last(self):
x1 = self.schema.insert_last('X1') x1 = self.schema.insert_last('X1')
self.assertEqual(x1.order, 0) 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): def test_create_cst(self):
@ -115,8 +115,8 @@ class TestRSFormCached(DBTester):
definition_formal='X2 = X3' definition_formal='X2 = X3'
) )
test_ks = RSFormCached.create(title='Test') test_ks = RSFormCached.create(title='Test')
test_ks.insert_from(self.schema.model.pk) test_ks.insert_from(self.schema.pk)
items = Constituenta.objects.filter(schema=test_ks.model).order_by('order') items = Constituenta.objects.filter(schema_id=test_ks.pk).order_by('order')
self.assertEqual(len(items), 4) self.assertEqual(len(items), 4)
self.assertEqual(items[0].alias, 'X2') self.assertEqual(items[0].alias, 'X2')
self.assertEqual(items[1].alias, 'D2') self.assertEqual(items[1].alias, 'D2')
@ -200,7 +200,7 @@ class TestRSFormCached(DBTester):
self.schema.substitute([(x1, x2)]) self.schema.substitute([(x1, x2)])
self.assertEqual(self.schema.constituentsQ().count(), 3) 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=d2).exists())
self.assertTrue(Attribution.objects.filter(container=x2, attribute=d1).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'] insert_after = data['insert_after']
with transaction.atomic(): with transaction.atomic():
schema = m.RSFormCached(item.pk) propagation = PropagationFacade()
schema = propagation.get_schema(item.pk)
new_cst = schema.create_cst(data, insert_after) 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']) item.save(update_fields=['time_update'])
return Response( return Response(
@ -125,9 +126,10 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
data = serializer.validated_data['item_data'] data = serializer.validated_data['item_data']
with transaction.atomic(): with transaction.atomic():
schema = m.RSFormCached(item.pk) propagation = PropagationFacade()
schema = propagation.get_schema(item.pk)
old_data = schema.update_cst(cst.pk, data) 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: if 'alias' in data and data['alias'] != cst.alias:
cst.refresh_from_db() cst.refresh_from_db()
changed_type = 'cst_type' in data and cst.cst_type != data['cst_type'] 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() cst.save()
schema.apply_mapping(mapping=mapping, change_aliases=False) schema.apply_mapping(mapping=mapping, change_aliases=False)
if changed_type: 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']) item.save(update_fields=['time_update'])
return Response( return Response(
status=c.HTTP_200_OK, status=c.HTTP_200_OK,
@ -208,9 +210,10 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
) )
with transaction.atomic(): with transaction.atomic():
schema = m.RSFormCached(item.pk) propagation = PropagationFacade()
schema = propagation.get_schema(item.pk)
new_cst = schema.produce_structure(cst, cst_parse) 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']) item.save(update_fields=['time_update'])
return Response( return Response(
status=c.HTTP_200_OK, status=c.HTTP_200_OK,
@ -245,7 +248,7 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
original = cast(m.Constituenta, substitution['original']) original = cast(m.Constituenta, substitution['original'])
replacement = cast(m.Constituenta, substitution['substitution']) replacement = cast(m.Constituenta, substitution['substitution'])
substitutions.append((original, replacement)) substitutions.append((original, replacement))
PropagationFacade.before_substitute(item.pk, substitutions) PropagationFacade().before_substitute(item.pk, substitutions)
schema.substitute(substitutions) schema.substitute(substitutions)
item.save(update_fields=['time_update']) item.save(update_fields=['time_update'])
@ -275,7 +278,7 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
with transaction.atomic(): with transaction.atomic():
schema = m.RSForm(item) 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) schema.delete_cst(cst_list)
item.save(update_fields=['time_update']) item.save(update_fields=['time_update'])
@ -305,11 +308,11 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
attribute = serializer.validated_data['attribute'] attribute = serializer.validated_data['attribute']
with transaction.atomic(): with transaction.atomic():
new_association = m.Attribution.objects.create( new_attribution = m.Attribution.objects.create(
container=container, container=container,
attribute=attribute attribute=attribute
) )
PropagationFacade.after_create_attribution(item.pk, [new_association]) PropagationFacade().after_create_attribution(item.pk, [new_attribution])
item.save(update_fields=['time_update']) item.save(update_fields=['time_update'])
return Response( return Response(
@ -345,7 +348,7 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
'container': msg.invalidAssociation() '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() m.Attribution.objects.filter(pk__in=[attrib.pk for attrib in target]).delete()
item.save(update_fields=['time_update']) item.save(update_fields=['time_update'])
@ -375,7 +378,7 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
with transaction.atomic(): with transaction.atomic():
target = list(m.Attribution.objects.filter(container=serializer.validated_data['target'])) target = list(m.Attribution.objects.filter(container=serializer.validated_data['target']))
if 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() m.Attribution.objects.filter(pk__in=[attrib.pk for attrib in target]).delete()
item.save(update_fields=['time_update']) 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 = s.RSFormTRSSerializer(data=data, context={'load_meta': load_metadata})
serializer.is_valid(raise_exception=True) serializer.is_valid(raise_exception=True)
result: m.RSForm = serializer.save() result: m.RSFormCached = serializer.save()
return Response( return Response(
status=c.HTTP_200_OK, status=c.HTTP_200_OK,
data=s.RSFormParseSerializer(result.model).data data=s.RSFormParseSerializer(LibraryItem.objects.get(pk=result.pk)).data
) )
@extend_schema( @extend_schema(
@ -651,10 +654,10 @@ class TrsImportView(views.APIView):
_prepare_rsform_data(data, request, owner) _prepare_rsform_data(data, request, owner)
serializer = s.RSFormTRSSerializer(data=data, context={'load_meta': True}) serializer = s.RSFormTRSSerializer(data=data, context={'load_meta': True})
serializer.is_valid(raise_exception=True) serializer.is_valid(raise_exception=True)
schema: m.RSForm = serializer.save() schema: m.RSFormCached = serializer.save()
return Response( return Response(
status=c.HTTP_201_CREATED, 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) _prepare_rsform_data(data, request, owner)
serializer_rsform = s.RSFormTRSSerializer(data=data, context={'load_meta': True}) serializer_rsform = s.RSFormTRSSerializer(data=data, context={'load_meta': True})
serializer_rsform.is_valid(raise_exception=True) serializer_rsform.is_valid(raise_exception=True)
schema: m.RSForm = serializer_rsform.save() schema: m.RSFormCached = serializer_rsform.save()
return Response( return Response(
status=c.HTTP_201_CREATED, 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) serializer.is_valid(raise_exception=True)
item = cast(LibraryItem, serializer.validated_data['receiver']) item = cast(LibraryItem, serializer.validated_data['receiver'])
receiver = m.RSFormCached(item.pk)
target_cst = cast(list[m.Constituenta], serializer.validated_data['items']) target_cst = cast(list[m.Constituenta], serializer.validated_data['items'])
source = cast(LibraryItem, serializer.validated_data['source']) source = cast(LibraryItem, serializer.validated_data['source'])
target_ids = [item.pk for item in target_cst] if target_cst else None target_ids = [item.pk for item in target_cst] if target_cst else None
with transaction.atomic(): with transaction.atomic():
propagation = PropagationFacade()
receiver = propagation.get_schema(item.pk)
new_items = receiver.insert_from(source.pk, target_ids) new_items = receiver.insert_from(source.pk, target_ids)
target_ids = [item[0].pk for item in new_items] target_ids = [item[0].pk for item in new_items]
mapping_ids = {cst.pk: new_cst for (cst, new_cst) 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]] = [] substitutions: list[tuple[m.Constituenta, m.Constituenta]] = []
for substitution in serializer.validated_data['substitutions']: for substitution in serializer.validated_data['substitutions']:
@ -753,7 +757,7 @@ def inline_synthesis(request: Request) -> HttpResponse:
replacement = mapping_ids[replacement.pk] replacement = mapping_ids[replacement.pk]
substitutions.append((original, replacement)) substitutions.append((original, replacement))
PropagationFacade.before_substitute(receiver.pk, substitutions) propagation.before_substitute(receiver.pk, substitutions)
receiver.substitute(substitutions) receiver.substitute(substitutions)
item.save(update_fields=['time_update']) item.save(update_fields=['time_update'])