mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-11-15 17:21:38 +03:00
R: Refactoring caches
Improve rsform and oss caching in propagation scenarios
This commit is contained in:
parent
c875a549c9
commit
380877e485
|
|
@ -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)
|
||||
PropagationFacade().before_delete_schema(instance.pk)
|
||||
super().perform_destroy(instance)
|
||||
if instance.item_type == m.LibraryItemType.OPERATION_SCHEMA:
|
||||
schemas = list(OperationSchema.owned_schemasQ(instance))
|
||||
|
|
@ -172,7 +172,7 @@ class LibraryViewSet(viewsets.ModelViewSet):
|
|||
clone.location = data.get('location', m.LocationHead.USER)
|
||||
clone.save()
|
||||
|
||||
RSFormCached(clone).insert_from(item.pk, request.data['items'] if 'items' in request.data else None)
|
||||
RSFormCached(clone.pk).insert_from(item.pk, request.data['items'] if 'items' in request.data else None)
|
||||
|
||||
return Response(
|
||||
status=c.HTTP_201_CREATED,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -43,19 +43,18 @@ class OperationSchema:
|
|||
return Layout.objects.get(oss_id=itemID)
|
||||
|
||||
@staticmethod
|
||||
def create_input(oss: LibraryItem, operation: Operation) -> RSFormCached:
|
||||
def create_input(oss_id: int, operation: Operation) -> LibraryItem:
|
||||
''' Create input RSForm for given Operation. '''
|
||||
schema = RSFormCached.create(
|
||||
owner=oss.owner,
|
||||
alias=operation.alias,
|
||||
title=operation.title,
|
||||
description=operation.description,
|
||||
visible=False,
|
||||
access_policy=oss.access_policy,
|
||||
location=oss.location
|
||||
)
|
||||
Editor.set(schema.model.pk, oss.getQ_editors().values_list('pk', flat=True))
|
||||
operation.setQ_result(schema.model)
|
||||
oss = LibraryItem.objects.get(pk=oss_id)
|
||||
schema = LibraryItem.objects.create(item_type=LibraryItemType.RSFORM, owner=oss.owner,
|
||||
alias=operation.alias,
|
||||
title=operation.title,
|
||||
description=operation.description,
|
||||
visible=False,
|
||||
access_policy=oss.access_policy,
|
||||
location=oss.location)
|
||||
Editor.set(schema.pk, oss.getQ_editors().values_list('pk', flat=True))
|
||||
operation.setQ_result(schema)
|
||||
return schema
|
||||
|
||||
def refresh_from_db(self) -> None:
|
||||
|
|
@ -132,7 +131,7 @@ class OperationSchema:
|
|||
if not schemas:
|
||||
return
|
||||
substitutions = operation.getQ_substitutions()
|
||||
receiver = OperationSchema.create_input(self.model, operation)
|
||||
receiver = RSFormCached(OperationSchema.create_input(self.model.pk, operation).pk)
|
||||
|
||||
parents: dict = {}
|
||||
children: dict = {}
|
||||
|
|
@ -149,7 +148,7 @@ class OperationSchema:
|
|||
translated_substitutions.append((original, replacement))
|
||||
receiver.substitute(translated_substitutions)
|
||||
|
||||
for cst in Constituenta.objects.filter(schema=receiver.model).order_by('order'):
|
||||
for cst in Constituenta.objects.filter(schema_id=receiver.pk).order_by('order'):
|
||||
parent = parents.get(cst.pk)
|
||||
assert parent is not None
|
||||
Inheritance.objects.create(
|
||||
|
|
|
|||
|
|
@ -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, model: LibraryItem):
|
||||
self.model = model
|
||||
self.cache = OssCache(model.pk)
|
||||
self.engine = PropagationEngine(self.cache)
|
||||
def __init__(self, item_id: int, context: PropagationContext) -> None:
|
||||
self.pk = item_id
|
||||
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. '''
|
||||
|
|
@ -53,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)
|
||||
|
|
@ -70,15 +72,15 @@ 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.model.pk):
|
||||
(schema is not None and old_schema is not None and schema.pk == old_schema.pk):
|
||||
return
|
||||
|
||||
if old_schema is not None:
|
||||
if has_children:
|
||||
self.before_delete_cst(old_schema.model.pk, [cst.pk for cst in old_schema.cache.constituents])
|
||||
self.cache.remove_schema(old_schema)
|
||||
self.before_delete_cst(old_schema.pk, [cst.pk for cst in old_schema.cache.constituents])
|
||||
self.context.invalidate(old_schema.pk)
|
||||
|
||||
operation.setQ_result(schema)
|
||||
if schema is not None:
|
||||
|
|
@ -88,8 +90,8 @@ class OperationSchemaCached:
|
|||
operation.save(update_fields=['alias', 'title', 'description'])
|
||||
|
||||
if schema is not None and has_children:
|
||||
rsform = RSFormCached(schema)
|
||||
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. '''
|
||||
|
|
@ -126,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():
|
||||
|
|
@ -172,8 +174,8 @@ class OperationSchemaCached:
|
|||
if not schemas:
|
||||
return False
|
||||
substitutions = operation.getQ_substitutions()
|
||||
receiver = OperationSchema.create_input(self.model, self.cache.operation_by_id[operation.pk])
|
||||
self.cache.insert_schema(receiver)
|
||||
new_schema = OperationSchema.create_input(self.pk, self.cache.operation_by_id[operation.pk])
|
||||
receiver = self.context.get_schema(new_schema.pk)
|
||||
|
||||
parents: dict = {}
|
||||
children: dict = {}
|
||||
|
|
@ -190,7 +192,7 @@ class OperationSchemaCached:
|
|||
translated_substitutions.append((original, replacement))
|
||||
receiver.substitute(translated_substitutions)
|
||||
|
||||
for cst in Constituenta.objects.filter(schema=receiver.model).order_by('order'):
|
||||
for cst in Constituenta.objects.filter(schema_id=receiver.pk).order_by('order'):
|
||||
parent = parents.get(cst.pk)
|
||||
assert parent is not None
|
||||
Inheritance.objects.create(
|
||||
|
|
@ -204,31 +206,31 @@ class OperationSchemaCached:
|
|||
receiver.resolve_all_text()
|
||||
|
||||
if self.cache.extend_graph.outputs[operation.pk]:
|
||||
receiver_items = list(Constituenta.objects.filter(schema=receiver.model).order_by('order'))
|
||||
self.after_create_cst(receiver, receiver_items)
|
||||
receiver.model.save(update_fields=['time_update'])
|
||||
receiver_items = list(Constituenta.objects.filter(schema_id=receiver.pk).order_by('order'))
|
||||
self.after_create_cst(receiver.pk, receiver_items)
|
||||
return True
|
||||
|
||||
def relocate_down(self, source: RSFormCached, 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()
|
||||
self.cache.insert_schema(source)
|
||||
self.cache.insert_schema(destination)
|
||||
operation = self.cache.get_operation(destination.model.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()
|
||||
self.cache.insert_schema(source)
|
||||
self.cache.insert_schema(destination)
|
||||
|
||||
operation = self.cache.get_operation(source.model.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:
|
||||
|
|
@ -236,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.model.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,
|
||||
|
|
@ -245,19 +247,18 @@ 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])
|
||||
destination.model.save(update_fields=['time_update'])
|
||||
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. '''
|
||||
self.cache.insert_schema(source)
|
||||
source = self.context.get_schema(sourceID)
|
||||
alias_mapping = create_dependant_mapping(source, cst_list)
|
||||
operation = self.cache.get_operation(source.model.pk)
|
||||
operation = self.cache.get_operation(source.pk)
|
||||
self.engine.on_inherit_cst(operation.pk, source, cst_list, alias_mapping, exclude)
|
||||
|
||||
def after_change_cst_type(self, schemaID: int, target: int, new_type: CstType) -> None:
|
||||
|
|
@ -265,10 +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. '''
|
||||
self.cache.insert_schema(source)
|
||||
operation = self.cache.get_operation(source.model.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:
|
||||
|
|
@ -298,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,
|
||||
|
|
@ -347,7 +348,7 @@ class OperationSchemaCached:
|
|||
original_cst = schema.cache.by_id[original_id]
|
||||
substitution_cst = schema.cache.by_id[substitution_id]
|
||||
cst_mapping.append((original_cst, substitution_cst))
|
||||
self.before_substitute(schema.model.pk, cst_mapping)
|
||||
self.before_substitute(schema.pk, cst_mapping)
|
||||
schema.substitute(cst_mapping)
|
||||
for sub in added:
|
||||
self.cache.insert_substitution(sub)
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
@ -60,27 +60,11 @@ 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
|
||||
if operation.result_id in self._schema_by_id:
|
||||
return self._schema_by_id[operation.result_id]
|
||||
else:
|
||||
schema = RSFormCached.from_id(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.from_id(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.model.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.model.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.model.pk] = schema
|
||||
|
|
|
|||
27
rsconcept/backend/apps/oss/models/PropagationContext.py
Normal file
27
rsconcept/backend/apps/oss/models/PropagationContext.py
Normal 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]
|
||||
|
|
@ -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. '''
|
||||
|
|
@ -35,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):
|
||||
|
|
@ -67,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
|
||||
|
||||
|
|
@ -104,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)
|
||||
|
|
@ -176,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)
|
||||
|
|
@ -193,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
|
||||
|
||||
|
|
@ -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]
|
||||
schema = self.cache.get_schema(operation)
|
||||
operation = self.cache.operation_by_id[operationID]
|
||||
schema = self.cache.get_result(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:
|
||||
|
|
@ -374,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)
|
||||
|
|
|
|||
|
|
@ -1,101 +1,110 @@
|
|||
''' Models: Change propagation facade - managing all changes in OSS. '''
|
||||
from typing import Optional
|
||||
|
||||
from apps.library.models import LibraryItem, LibraryItemType
|
||||
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[LibraryItem]:
|
||||
''' Get all hosts for LibraryItem. '''
|
||||
return list(LibraryItem.objects.filter(operations__result_id=schemaID).only('pk').distinct())
|
||||
def _get_oss_hosts(schemaID: int) -> list[int]:
|
||||
''' Get all hosts for schema. '''
|
||||
return list(LibraryItem.objects.filter(operations__result_id=schemaID).distinct().values_list('pk', flat=True))
|
||||
|
||||
|
||||
class PropagationFacade:
|
||||
''' Change propagation API. '''
|
||||
|
||||
@staticmethod
|
||||
def after_create_cst(source: RSFormCached, new_cst: list[Constituenta],
|
||||
def __init__(self) -> None:
|
||||
self._context = PropagationContext()
|
||||
self._oss: dict[int, OperationSchemaCached] = {}
|
||||
|
||||
def get_oss(self, schemaID: int) -> OperationSchemaCached:
|
||||
''' Get OperationSchemaCached for schemaID. '''
|
||||
if schemaID not in self._oss:
|
||||
self._oss[schemaID] = OperationSchemaCached(schemaID, self._context)
|
||||
return self._oss[schemaID]
|
||||
|
||||
def get_schema(self, schemaID: int) -> RSFormCached:
|
||||
''' Get RSFormCached for schemaID. '''
|
||||
return self._context.get_schema(schemaID)
|
||||
|
||||
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.model.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.pk not in exclude:
|
||||
OperationSchemaCached(host).after_create_cst(source, new_cst)
|
||||
if exclude is None or host not in exclude:
|
||||
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.pk not in exclude:
|
||||
OperationSchemaCached(host).after_change_cst_type(sourceID, target, new_type)
|
||||
if exclude is None or host not in exclude:
|
||||
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, 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.model.pk)
|
||||
hosts = _get_oss_hosts(sourceID)
|
||||
for host in hosts:
|
||||
if exclude is None or host.pk not in exclude:
|
||||
OperationSchemaCached(host).after_update_cst(source, target, data, old_data)
|
||||
if exclude is None or host not in exclude:
|
||||
self.get_oss(host).after_update_cst(sourceID, 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.pk not in exclude:
|
||||
OperationSchemaCached(host).before_delete_cst(sourceID, target)
|
||||
if exclude is None or host not in exclude:
|
||||
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:
|
||||
return
|
||||
hosts = _get_oss_hosts(sourceID)
|
||||
for host in hosts:
|
||||
if exclude is None or host.pk not in exclude:
|
||||
OperationSchemaCached(host).before_substitute(sourceID, substitutions)
|
||||
if exclude is None or host not in exclude:
|
||||
self.get_oss(host).before_substitute(sourceID, substitutions)
|
||||
|
||||
@staticmethod
|
||||
def before_delete_schema(item: LibraryItem, 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. '''
|
||||
if item.item_type != LibraryItemType.RSFORM:
|
||||
return
|
||||
hosts = _get_oss_hosts(item.pk)
|
||||
hosts = _get_oss_hosts(target)
|
||||
if not hosts:
|
||||
return
|
||||
|
||||
ids = list(Constituenta.objects.filter(schema=item).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:
|
||||
if exclude is None or host.pk not in exclude:
|
||||
OperationSchemaCached(host).before_delete_cst(item.pk, ids)
|
||||
if exclude is None or host not in exclude:
|
||||
self.get_oss(host).before_delete_cst(target, ids)
|
||||
del self._oss[host]
|
||||
|
||||
@staticmethod
|
||||
def after_create_attribution(sourceID: int, attributions: list[Attribution],
|
||||
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.pk not in exclude:
|
||||
OperationSchemaCached(host).after_create_attribution(sourceID, attributions)
|
||||
if exclude is None or host not in exclude:
|
||||
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.pk not in exclude:
|
||||
OperationSchemaCached(host).before_delete_attribution(sourceID, attributions)
|
||||
if exclude is None or host not in exclude:
|
||||
self.get_oss(host).before_delete_attribution(sourceID, attributions)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
@ -625,15 +623,6 @@ class RelocateConstituentsSerializer(StrictSerializer):
|
|||
'items': msg.RelocatingInherited()
|
||||
})
|
||||
|
||||
oss = LibraryItem.objects \
|
||||
.filter(operations__result_id=attrs['destination']) \
|
||||
.filter(operations__result_id=attrs['source']).only('id')
|
||||
if not oss.exists():
|
||||
raise serializers.ValidationError({
|
||||
'destination': msg.schemasNotConnected()
|
||||
})
|
||||
attrs['oss'] = oss[0].pk
|
||||
|
||||
if Argument.objects.filter(
|
||||
operation__result_id=attrs['destination'],
|
||||
argument__result_id=attrs['source']
|
||||
|
|
|
|||
|
|
@ -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 Operation, OperationSchema, OperationType
|
||||
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
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
''' Testing API: Change substitutions in OSS. '''
|
||||
|
||||
from apps.oss.models import OperationSchema, OperationType
|
||||
from apps.rsform.models import Constituenta, CstType, RSForm
|
||||
from apps.rsform.models import Constituenta, RSForm
|
||||
from shared.EndpointTester import EndpointTester, decl_endpoint
|
||||
|
||||
|
||||
|
|
@ -388,7 +388,7 @@ class TestChangeOperations(EndpointTester):
|
|||
self.assertEqual(self.ks5.constituentsQ().count(), 8)
|
||||
|
||||
|
||||
@decl_endpoint('/api/oss/relocate-constituents', method='post')
|
||||
@decl_endpoint('/api/oss/{item}/relocate-constituents', method='post')
|
||||
def test_relocate_constituents_up(self):
|
||||
ks1_old_count = self.ks1.constituentsQ().count()
|
||||
ks4_old_count = self.ks4.constituentsQ().count()
|
||||
|
|
@ -408,7 +408,7 @@ class TestChangeOperations(EndpointTester):
|
|||
'items': [ks6A1.pk]
|
||||
}
|
||||
|
||||
self.executeOK(data)
|
||||
self.executeOK(data, item=self.owned_id)
|
||||
ks6.model.refresh_from_db()
|
||||
self.ks1.model.refresh_from_db()
|
||||
self.ks4.model.refresh_from_db()
|
||||
|
|
@ -418,7 +418,7 @@ class TestChangeOperations(EndpointTester):
|
|||
self.assertEqual(self.ks4.constituentsQ().count(), ks4_old_count + 1)
|
||||
|
||||
|
||||
@decl_endpoint('/api/oss/relocate-constituents', method='post')
|
||||
@decl_endpoint('/api/oss/{item}/relocate-constituents', method='post')
|
||||
def test_relocate_constituents_down(self):
|
||||
ks1_old_count = self.ks1.constituentsQ().count()
|
||||
ks4_old_count = self.ks4.constituentsQ().count()
|
||||
|
|
@ -438,7 +438,7 @@ class TestChangeOperations(EndpointTester):
|
|||
'items': [self.ks1X2.pk]
|
||||
}
|
||||
|
||||
self.executeOK(data)
|
||||
self.executeOK(data, item=self.owned_id)
|
||||
ks6.model.refresh_from_db()
|
||||
self.ks1.model.refresh_from_db()
|
||||
self.ks4.model.refresh_from_db()
|
||||
|
|
|
|||
|
|
@ -163,7 +163,7 @@ class ReferencePropagationTestCase(EndpointTester):
|
|||
@decl_endpoint('/api/rsforms/{schema}/delete-multiple-cst', method='patch')
|
||||
def test_delete_constituenta(self):
|
||||
data = {'items': [self.ks1X1.pk]}
|
||||
response = self.executeOK(data, schema=self.ks1.model.pk)
|
||||
self.executeOK(data, schema=self.ks1.model.pk)
|
||||
self.ks4D2.refresh_from_db()
|
||||
self.ks5D4.refresh_from_db()
|
||||
self.ks6D2.refresh_from_db()
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
''' Testing API: Change substitutions in OSS. '''
|
||||
|
||||
from apps.oss.models import OperationSchema, OperationType
|
||||
from apps.rsform.models import Constituenta, CstType, RSForm
|
||||
from apps.rsform.models import Constituenta, RSForm
|
||||
from shared.EndpointTester import EndpointTester, decl_endpoint
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
''' Testing API: Operation Schema - blocks manipulation. '''
|
||||
from apps.library.models import AccessPolicy, Editor, LibraryItem, LibraryItemType
|
||||
from apps.oss.models import Operation, OperationSchema, OperationType
|
||||
from apps.oss.models import OperationSchema, OperationType
|
||||
from shared.EndpointTester import EndpointTester, decl_endpoint
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,12 @@
|
|||
''' Testing API: Operation Schema - operations manipulation. '''
|
||||
from apps.library.models import Editor, LibraryItem
|
||||
from apps.oss.models import Argument, Operation, OperationSchema, OperationType, Replica
|
||||
from apps.oss.models import (
|
||||
Argument,
|
||||
Operation,
|
||||
OperationSchema,
|
||||
OperationType,
|
||||
Replica
|
||||
)
|
||||
from apps.rsform.models import Attribution, RSForm
|
||||
from shared.EndpointTester import EndpointTester, decl_endpoint
|
||||
|
||||
|
|
|
|||
|
|
@ -220,8 +220,9 @@ class TestOssViewset(EndpointTester):
|
|||
self.executeBadData(data)
|
||||
|
||||
|
||||
@decl_endpoint('/api/oss/relocate-constituents', method='post')
|
||||
@decl_endpoint('/api/oss/{item}/relocate-constituents', method='post')
|
||||
def test_relocate_constituents(self):
|
||||
self.set_params(item=self.owned_id)
|
||||
self.populateData()
|
||||
self.ks1X2 = self.ks1.insert_last('X2', convention='test')
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ from rest_framework.response import Response
|
|||
|
||||
from apps.library.models import LibraryItem, LibraryItemType
|
||||
from apps.library.serializers import LibraryItemSerializer
|
||||
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
|
||||
|
|
@ -291,7 +291,7 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
|
|||
'height': position['height']
|
||||
})
|
||||
m.Layout.update_data(pk, layout)
|
||||
m.OperationSchema.create_input(item, new_operation)
|
||||
m.OperationSchema.create_input(item.pk, new_operation)
|
||||
item.save(update_fields=['time_update'])
|
||||
|
||||
return Response(
|
||||
|
|
@ -420,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).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"])
|
||||
|
|
@ -544,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 = m.OperationSchemaCached(item)
|
||||
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)
|
||||
|
|
@ -599,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 = m.OperationSchemaCached(item)
|
||||
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)
|
||||
propagation.before_delete_schema(old_schema.pk)
|
||||
old_schema.delete()
|
||||
elif old_schema.is_synced(item):
|
||||
old_schema.visible = True
|
||||
|
|
@ -640,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 = m.OperationSchemaCached(item)
|
||||
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'])
|
||||
|
|
@ -680,13 +684,13 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
|
|||
|
||||
with transaction.atomic():
|
||||
m.Layout.update_data(pk, layout)
|
||||
schema = m.OperationSchema.create_input(item, operation)
|
||||
schema = m.OperationSchema.create_input(item.pk, operation)
|
||||
item.save(update_fields=['time_update'])
|
||||
|
||||
return Response(
|
||||
status=c.HTTP_200_OK,
|
||||
data={
|
||||
'new_schema': LibraryItemSerializer(schema.model).data,
|
||||
'new_schema': LibraryItemSerializer(schema).data,
|
||||
'oss': s.OperationSchemaSerializer(item).data
|
||||
}
|
||||
)
|
||||
|
|
@ -726,7 +730,8 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
|
|||
old_schema = target_operation.result
|
||||
|
||||
with transaction.atomic():
|
||||
oss = m.OperationSchemaCached(item)
|
||||
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
|
||||
|
|
@ -769,7 +774,8 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
|
|||
layout = serializer.validated_data['layout']
|
||||
|
||||
with transaction.atomic():
|
||||
oss = m.OperationSchemaCached(item)
|
||||
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'])
|
||||
|
|
@ -823,24 +829,27 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
|
|||
c.HTTP_404_NOT_FOUND: None
|
||||
}
|
||||
)
|
||||
@action(detail=False, methods=['post'], url_path='relocate-constituents')
|
||||
def relocate_constituents(self, request: Request) -> Response:
|
||||
@action(detail=True, methods=['post'], url_path='relocate-constituents')
|
||||
def relocate_constituents(self, request: Request, pk) -> Response:
|
||||
''' Relocate constituents from one schema to another. '''
|
||||
item = self._get_item()
|
||||
serializer = s.RelocateConstituentsSerializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
data = serializer.validated_data
|
||||
ids = [cst.pk for cst in data['items']]
|
||||
destinationID = data['destination']
|
||||
|
||||
with transaction.atomic():
|
||||
oss = m.OperationSchemaCached(LibraryItem.objects.get(pk=data['oss']))
|
||||
source = RSFormCached(LibraryItem.objects.get(pk=data['source']))
|
||||
destination = RSFormCached(LibraryItem.objects.get(pk=data['destination']))
|
||||
propagation = m.PropagationFacade()
|
||||
oss = propagation.get_oss(item.pk)
|
||||
source = propagation.get_schema(data['source'])
|
||||
if data['move_down']:
|
||||
oss.relocate_down(source, destination, ids)
|
||||
m.PropagationFacade.before_delete_cst(data['source'], 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)
|
||||
m.PropagationFacade.after_create_cst(destination, new_items, exclude=[oss.model.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)
|
||||
|
|
|
|||
|
|
@ -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]] = {}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -20,21 +20,15 @@ from .RSForm import DELETED_ALIAS, RSForm
|
|||
class RSFormCached:
|
||||
''' RSForm cached. Caching allows to avoid querying for each method call. '''
|
||||
|
||||
def __init__(self, model: LibraryItem):
|
||||
self.model = model
|
||||
def __init__(self, item_id: int) -> None:
|
||||
self.pk = item_id
|
||||
self.cache: _RSFormCache = _RSFormCache(self)
|
||||
|
||||
@staticmethod
|
||||
def create(**kwargs) -> 'RSFormCached':
|
||||
''' Create LibraryItem via RSForm. '''
|
||||
model = LibraryItem.objects.create(item_type=LibraryItemType.RSFORM, **kwargs)
|
||||
return RSFormCached(model)
|
||||
|
||||
@staticmethod
|
||||
def from_id(pk: int) -> 'RSFormCached':
|
||||
''' Get LibraryItem by pk. '''
|
||||
model = LibraryItem.objects.get(pk=pk)
|
||||
return RSFormCached(model)
|
||||
return RSFormCached(model.pk)
|
||||
|
||||
def get_dependant(self, target: Iterable[int]) -> set[int]:
|
||||
''' Get list of constituents depending on target (only 1st degree). '''
|
||||
|
|
@ -51,7 +45,7 @@ class RSFormCached:
|
|||
|
||||
def constituentsQ(self) -> QuerySet[Constituenta]:
|
||||
''' Get QuerySet containing all constituents of current RSForm. '''
|
||||
return Constituenta.objects.filter(schema=self.model)
|
||||
return Constituenta.objects.filter(schema_id=self.pk)
|
||||
|
||||
def insert_last(
|
||||
self,
|
||||
|
|
@ -62,9 +56,9 @@ class RSFormCached:
|
|||
''' Insert new constituenta at last position. '''
|
||||
if cst_type is None:
|
||||
cst_type = guess_type(alias)
|
||||
position = Constituenta.objects.filter(schema=self.model).count()
|
||||
position = Constituenta.objects.filter(schema_id=self.pk).count()
|
||||
result = Constituenta.objects.create(
|
||||
schema=self.model,
|
||||
schema_id=self.pk,
|
||||
order=position,
|
||||
alias=alias,
|
||||
cst_type=cst_type,
|
||||
|
|
@ -83,7 +77,7 @@ class RSFormCached:
|
|||
RSForm.shift_positions(position, 1, self.cache.constituents)
|
||||
|
||||
result = Constituenta.objects.create(
|
||||
schema=self.model,
|
||||
schema_id=self.pk,
|
||||
order=position,
|
||||
alias=data['alias'],
|
||||
cst_type=data['cst_type'],
|
||||
|
|
@ -160,7 +154,7 @@ class RSFormCached:
|
|||
new_constituents = deepcopy(items)
|
||||
for cst in new_constituents:
|
||||
cst.pk = None
|
||||
cst.schema = self.model
|
||||
cst.schema_id = self.pk
|
||||
cst.order = position
|
||||
if mapping_alias:
|
||||
cst.alias = mapping_alias[cst.alias]
|
||||
|
|
@ -263,7 +257,7 @@ class RSFormCached:
|
|||
deleted.append(original)
|
||||
replacements.append(substitution.pk)
|
||||
|
||||
attributions = list(Attribution.objects.filter(container__schema=self.model))
|
||||
attributions = list(Attribution.objects.filter(container__schema_id=self.pk))
|
||||
if attributions:
|
||||
orig_to_sub = {original.pk: substitution.pk for original, substitution in substitutions}
|
||||
orig_pks = set(orig_to_sub.keys())
|
||||
|
|
@ -374,7 +368,7 @@ class RSFormCached:
|
|||
prefix = get_type_prefix(cst_type)
|
||||
for text in expressions:
|
||||
new_item = Constituenta.objects.create(
|
||||
schema=self.model,
|
||||
schema_id=self.pk,
|
||||
order=position,
|
||||
alias=f'{prefix}{free_index}',
|
||||
definition_formal=text,
|
||||
|
|
@ -392,7 +386,7 @@ class RSFormCached:
|
|||
cst_list: Iterable[Constituenta] = []
|
||||
if not self.cache.is_loaded:
|
||||
cst_list = Constituenta.objects \
|
||||
.filter(schema=self.model, cst_type=cst_type) \
|
||||
.filter(schema_id=self.pk, cst_type=cst_type) \
|
||||
.only('alias')
|
||||
else:
|
||||
cst_list = [cst for cst in self.cache.constituents if cst.cst_type == cst_type]
|
||||
|
|
@ -406,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] = {}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ class RSFormTRSSerializer(serializers.Serializer):
|
|||
result['description'] = data.get('description', '')
|
||||
if 'id' in data:
|
||||
result['id'] = data['id']
|
||||
self.instance = RSFormCached.from_id(result['id'])
|
||||
self.instance = RSFormCached(result['id'])
|
||||
return result
|
||||
|
||||
def validate(self, attrs: dict):
|
||||
|
|
@ -151,7 +151,7 @@ class RSFormTRSSerializer(serializers.Serializer):
|
|||
for cst_data in validated_data['items']:
|
||||
cst = Constituenta(
|
||||
alias=cst_data['alias'],
|
||||
schema=self.instance.model,
|
||||
schema_id=self.instance.pk,
|
||||
order=order,
|
||||
cst_type=cst_data['cstType'],
|
||||
)
|
||||
|
|
@ -163,12 +163,13 @@ class RSFormTRSSerializer(serializers.Serializer):
|
|||
|
||||
@transaction.atomic
|
||||
def update(self, instance: RSFormCached, validated_data) -> RSFormCached:
|
||||
model = LibraryItem.objects.get(pk=instance.pk)
|
||||
if 'alias' in validated_data:
|
||||
instance.model.alias = validated_data['alias']
|
||||
model.alias = validated_data['alias']
|
||||
if 'title' in validated_data:
|
||||
instance.model.title = validated_data['title']
|
||||
model.title = validated_data['title']
|
||||
if 'description' in validated_data:
|
||||
instance.model.description = validated_data['description']
|
||||
model.description = validated_data['description']
|
||||
|
||||
order = 0
|
||||
prev_constituents = instance.constituentsQ()
|
||||
|
|
@ -185,7 +186,7 @@ class RSFormTRSSerializer(serializers.Serializer):
|
|||
else:
|
||||
cst = Constituenta(
|
||||
alias=cst_data['alias'],
|
||||
schema=instance.model,
|
||||
schema_id=instance.pk,
|
||||
order=order,
|
||||
cst_type=cst_data['cstType'],
|
||||
)
|
||||
|
|
@ -199,7 +200,7 @@ class RSFormTRSSerializer(serializers.Serializer):
|
|||
prev_cst.delete()
|
||||
|
||||
instance.resolve_all_text()
|
||||
instance.model.save()
|
||||
model.save()
|
||||
return instance
|
||||
|
||||
@staticmethod
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
||||
|
|
|
|||
|
|
@ -91,9 +91,10 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
|
|||
insert_after = data['insert_after']
|
||||
|
||||
with transaction.atomic():
|
||||
schema = m.RSFormCached(item)
|
||||
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([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)
|
||||
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(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']
|
||||
|
|
@ -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)
|
||||
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(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'])
|
||||
|
||||
|
|
@ -456,7 +459,7 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
|
|||
item = self._get_item()
|
||||
|
||||
with transaction.atomic():
|
||||
m.OrderManager(m.RSFormCached(item)).restore_order()
|
||||
m.OrderManager(m.RSFormCached(item.pk)).restore_order()
|
||||
item.save(update_fields=['time_update'])
|
||||
|
||||
return Response(
|
||||
|
|
@ -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
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -731,16 +734,18 @@ def inline_synthesis(request: Request) -> HttpResponse:
|
|||
serializer = s.InlineSynthesisSerializer(data=request.data, context={'user': request.user})
|
||||
serializer.is_valid(raise_exception=True)
|
||||
|
||||
receiver = m.RSFormCached(serializer.validated_data['receiver'])
|
||||
item = cast(LibraryItem, serializer.validated_data['receiver'])
|
||||
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([item[1] for item in new_items])
|
||||
|
||||
substitutions: list[tuple[m.Constituenta, m.Constituenta]] = []
|
||||
for substitution in serializer.validated_data['substitutions']:
|
||||
|
|
@ -752,11 +757,11 @@ def inline_synthesis(request: Request) -> HttpResponse:
|
|||
replacement = mapping_ids[replacement.pk]
|
||||
substitutions.append((original, replacement))
|
||||
|
||||
PropagationFacade.before_substitute(receiver.model.pk, substitutions)
|
||||
propagation.before_substitute(receiver.pk, substitutions)
|
||||
receiver.substitute(substitutions)
|
||||
receiver.model.save(update_fields=['time_update'])
|
||||
item.save(update_fields=['time_update'])
|
||||
|
||||
return Response(
|
||||
status=c.HTTP_200_OK,
|
||||
data=s.RSFormParseSerializer(receiver.model).data
|
||||
data=s.RSFormParseSerializer(item).data
|
||||
)
|
||||
|
|
|
|||
|
|
@ -215,9 +215,9 @@ export const ossApi = {
|
|||
}
|
||||
}),
|
||||
|
||||
relocateConstituents: (data: IRelocateConstituentsDTO) =>
|
||||
relocateConstituents: ({ itemID, data }: { itemID: number; data: IRelocateConstituentsDTO }) =>
|
||||
axiosPost<IRelocateConstituentsDTO>({
|
||||
endpoint: `/api/oss/relocate-constituents`,
|
||||
endpoint: `/api/oss/${itemID}/relocate-constituents`,
|
||||
request: {
|
||||
data: data,
|
||||
successMessage: infoMsg.changesSaved
|
||||
|
|
|
|||
|
|
@ -19,6 +19,6 @@ export const useRelocateConstituents = () => {
|
|||
onError: () => client.invalidateQueries()
|
||||
});
|
||||
return {
|
||||
relocateConstituents: (data: IRelocateConstituentsDTO) => mutation.mutateAsync(data)
|
||||
relocateConstituents: (data: { itemID: number; data: IRelocateConstituentsDTO }) => mutation.mutateAsync(data)
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -106,13 +106,13 @@ export function DlgRelocateConstituents() {
|
|||
function onSubmit(data: IRelocateConstituentsDTO) {
|
||||
data.items = moveTarget;
|
||||
if (!layout || JSON.stringify(layout) === JSON.stringify(oss.layout)) {
|
||||
return relocateConstituents(data);
|
||||
return relocateConstituents({ itemID: oss.id, data: data });
|
||||
} else {
|
||||
return updatePositions({
|
||||
isSilent: true,
|
||||
itemID: oss.id,
|
||||
data: layout
|
||||
}).then(() => relocateConstituents(data));
|
||||
}).then(() => relocateConstituents({ itemID: oss.id, data: data }));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user