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:
|
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)
|
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))
|
||||||
|
|
@ -172,7 +172,7 @@ class LibraryViewSet(viewsets.ModelViewSet):
|
||||||
clone.location = data.get('location', m.LocationHead.USER)
|
clone.location = data.get('location', m.LocationHead.USER)
|
||||||
clone.save()
|
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(
|
return Response(
|
||||||
status=c.HTTP_201_CREATED,
|
status=c.HTTP_201_CREATED,
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -43,19 +43,18 @@ class OperationSchema:
|
||||||
return Layout.objects.get(oss_id=itemID)
|
return Layout.objects.get(oss_id=itemID)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def create_input(oss: LibraryItem, operation: Operation) -> RSFormCached:
|
def create_input(oss_id: int, operation: Operation) -> LibraryItem:
|
||||||
''' Create input RSForm for given Operation. '''
|
''' Create input RSForm for given Operation. '''
|
||||||
schema = RSFormCached.create(
|
oss = LibraryItem.objects.get(pk=oss_id)
|
||||||
owner=oss.owner,
|
schema = LibraryItem.objects.create(item_type=LibraryItemType.RSFORM, owner=oss.owner,
|
||||||
alias=operation.alias,
|
alias=operation.alias,
|
||||||
title=operation.title,
|
title=operation.title,
|
||||||
description=operation.description,
|
description=operation.description,
|
||||||
visible=False,
|
visible=False,
|
||||||
access_policy=oss.access_policy,
|
access_policy=oss.access_policy,
|
||||||
location=oss.location
|
location=oss.location)
|
||||||
)
|
Editor.set(schema.pk, oss.getQ_editors().values_list('pk', flat=True))
|
||||||
Editor.set(schema.model.pk, oss.getQ_editors().values_list('pk', flat=True))
|
operation.setQ_result(schema)
|
||||||
operation.setQ_result(schema.model)
|
|
||||||
return schema
|
return schema
|
||||||
|
|
||||||
def refresh_from_db(self) -> None:
|
def refresh_from_db(self) -> None:
|
||||||
|
|
@ -132,7 +131,7 @@ class OperationSchema:
|
||||||
if not schemas:
|
if not schemas:
|
||||||
return
|
return
|
||||||
substitutions = operation.getQ_substitutions()
|
substitutions = operation.getQ_substitutions()
|
||||||
receiver = OperationSchema.create_input(self.model, operation)
|
receiver = RSFormCached(OperationSchema.create_input(self.model.pk, operation).pk)
|
||||||
|
|
||||||
parents: dict = {}
|
parents: dict = {}
|
||||||
children: dict = {}
|
children: dict = {}
|
||||||
|
|
@ -149,7 +148,7 @@ class OperationSchema:
|
||||||
translated_substitutions.append((original, replacement))
|
translated_substitutions.append((original, replacement))
|
||||||
receiver.substitute(translated_substitutions)
|
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)
|
parent = parents.get(cst.pk)
|
||||||
assert parent is not None
|
assert parent is not None
|
||||||
Inheritance.objects.create(
|
Inheritance.objects.create(
|
||||||
|
|
|
||||||
|
|
@ -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, model: LibraryItem):
|
def __init__(self, item_id: int, context: PropagationContext) -> None:
|
||||||
self.model = model
|
self.pk = item_id
|
||||||
self.cache = OssCache(model.pk)
|
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. '''
|
||||||
|
|
@ -53,7 +55,7 @@ class OperationSchemaCached:
|
||||||
inheritance_to_delete: list[Inheritance] = []
|
inheritance_to_delete: list[Inheritance] = []
|
||||||
for child_id in children:
|
for child_id in children:
|
||||||
child_operation = self.cache.operation_by_id[child_id]
|
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:
|
if child_schema is None:
|
||||||
continue
|
continue
|
||||||
self.engine.undo_substitutions_cst(ids, child_operation, child_schema)
|
self.engine.undo_substitutions_cst(ids, child_operation, child_schema)
|
||||||
|
|
@ -70,15 +72,15 @@ class OperationSchemaCached:
|
||||||
''' Set input schema for operation. '''
|
''' Set input schema for operation. '''
|
||||||
operation = self.cache.operation_by_id[target]
|
operation = self.cache.operation_by_id[target]
|
||||||
has_children = bool(self.cache.extend_graph.outputs[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 \
|
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
|
return
|
||||||
|
|
||||||
if old_schema is not None:
|
if old_schema is not None:
|
||||||
if has_children:
|
if has_children:
|
||||||
self.before_delete_cst(old_schema.model.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,8 +90,8 @@ 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)
|
cst_list = list(Constituenta.objects.filter(schema_id=schema.pk).order_by('order'))
|
||||||
self.after_create_cst(rsform, list(rsform.constituentsQ().order_by('order')))
|
self.after_create_cst(schema.pk, cst_list)
|
||||||
|
|
||||||
def set_arguments(self, target: int, arguments: list[Operation]) -> None:
|
def set_arguments(self, target: int, arguments: list[Operation]) -> None:
|
||||||
''' Set arguments of target Operation. '''
|
''' Set arguments of target Operation. '''
|
||||||
|
|
@ -126,7 +128,7 @@ class OperationSchemaCached:
|
||||||
''' Clear all arguments for target Operation. '''
|
''' Clear all arguments for target Operation. '''
|
||||||
self.cache.ensure_loaded_subs()
|
self.cache.ensure_loaded_subs()
|
||||||
operation = self.cache.operation_by_id[target]
|
operation = self.cache.operation_by_id[target]
|
||||||
schema = self.cache.get_schema(operation)
|
schema = self.cache.get_result(operation)
|
||||||
processed: list[dict] = []
|
processed: list[dict] = []
|
||||||
deleted: list[Substitution] = []
|
deleted: list[Substitution] = []
|
||||||
for current in operation.getQ_substitutions():
|
for current in operation.getQ_substitutions():
|
||||||
|
|
@ -172,8 +174,8 @@ class OperationSchemaCached:
|
||||||
if not schemas:
|
if not schemas:
|
||||||
return False
|
return False
|
||||||
substitutions = operation.getQ_substitutions()
|
substitutions = operation.getQ_substitutions()
|
||||||
receiver = OperationSchema.create_input(self.model, self.cache.operation_by_id[operation.pk])
|
new_schema = OperationSchema.create_input(self.pk, self.cache.operation_by_id[operation.pk])
|
||||||
self.cache.insert_schema(receiver)
|
receiver = self.context.get_schema(new_schema.pk)
|
||||||
|
|
||||||
parents: dict = {}
|
parents: dict = {}
|
||||||
children: dict = {}
|
children: dict = {}
|
||||||
|
|
@ -190,7 +192,7 @@ class OperationSchemaCached:
|
||||||
translated_substitutions.append((original, replacement))
|
translated_substitutions.append((original, replacement))
|
||||||
receiver.substitute(translated_substitutions)
|
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)
|
parent = parents.get(cst.pk)
|
||||||
assert parent is not None
|
assert parent is not None
|
||||||
Inheritance.objects.create(
|
Inheritance.objects.create(
|
||||||
|
|
@ -204,31 +206,31 @@ class OperationSchemaCached:
|
||||||
receiver.resolve_all_text()
|
receiver.resolve_all_text()
|
||||||
|
|
||||||
if self.cache.extend_graph.outputs[operation.pk]:
|
if self.cache.extend_graph.outputs[operation.pk]:
|
||||||
receiver_items = list(Constituenta.objects.filter(schema=receiver.model).order_by('order'))
|
receiver_items = list(Constituenta.objects.filter(schema_id=receiver.pk).order_by('order'))
|
||||||
self.after_create_cst(receiver, receiver_items)
|
self.after_create_cst(receiver.pk, receiver_items)
|
||||||
receiver.model.save(update_fields=['time_update'])
|
|
||||||
return True
|
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. '''
|
''' 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(destinationID)
|
||||||
operation = self.cache.get_operation(destination.model.pk)
|
destination = self.context.get_schema(destinationID)
|
||||||
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]
|
||||||
for item in inheritance_to_delete:
|
for item in inheritance_to_delete:
|
||||||
self.cache.remove_inheritance(item)
|
self.cache.remove_inheritance(item)
|
||||||
Inheritance.objects.filter(operation_id=operation.pk, parent_id__in=items).delete()
|
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]:
|
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.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] = {}
|
alias_mapping: dict[str, str] = {}
|
||||||
for item in self.cache.inheritance[operation.pk]:
|
for item in self.cache.inheritance[operation.pk]:
|
||||||
if item.parent_id in destination.cache.by_id:
|
if item.parent_id in destination.cache.by_id:
|
||||||
|
|
@ -236,7 +238,7 @@ class OperationSchemaCached:
|
||||||
destination_cst = destination.cache.by_id[item.parent_id]
|
destination_cst = destination.cache.by_id[item.parent_id]
|
||||||
alias_mapping[source_cst.alias] = destination_cst.alias
|
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:
|
for (cst, new_cst) in new_items:
|
||||||
new_inheritance = Inheritance.objects.create(
|
new_inheritance = Inheritance.objects.create(
|
||||||
operation=operation,
|
operation=operation,
|
||||||
|
|
@ -245,19 +247,18 @@ class OperationSchemaCached:
|
||||||
)
|
)
|
||||||
self.cache.insert_inheritance(new_inheritance)
|
self.cache.insert_inheritance(new_inheritance)
|
||||||
new_constituents = [item[1] for item in new_items]
|
new_constituents = [item[1] for item in new_items]
|
||||||
self.after_create_cst(destination, new_constituents, exclude=[operation.pk])
|
self.after_create_cst(destinationID, new_constituents, exclude=[operation.pk])
|
||||||
destination.model.save(update_fields=['time_update'])
|
|
||||||
return new_constituents
|
return new_constituents
|
||||||
|
|
||||||
def after_create_cst(
|
def after_create_cst(
|
||||||
self, source: RSFormCached,
|
self, sourceID: int,
|
||||||
cst_list: list[Constituenta],
|
cst_list: list[Constituenta],
|
||||||
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)
|
source = self.context.get_schema(sourceID)
|
||||||
alias_mapping = create_dependant_mapping(source, cst_list)
|
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)
|
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:
|
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)
|
operation = self.cache.get_operation(schemaID)
|
||||||
self.engine.on_change_cst_type(operation.pk, target, new_type)
|
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. '''
|
''' Trigger cascade resolutions when Constituenta data is changed. '''
|
||||||
self.cache.insert_schema(source)
|
operation = self.cache.get_operation(sourceID)
|
||||||
operation = self.cache.get_operation(source.model.pk)
|
source = self.context.get_schema(sourceID)
|
||||||
depend_aliases = extract_data_references(data, old_data)
|
depend_aliases = extract_data_references(data, old_data)
|
||||||
alias_mapping: CstMapping = {}
|
alias_mapping: CstMapping = {}
|
||||||
for alias in depend_aliases:
|
for alias in depend_aliases:
|
||||||
|
|
@ -298,18 +299,18 @@ class OperationSchemaCached:
|
||||||
if target.result_id is None:
|
if target.result_id is None:
|
||||||
return
|
return
|
||||||
for argument in arguments:
|
for argument in arguments:
|
||||||
parent_schema = self.cache.get_schema(argument)
|
parent_schema = self.cache.get_result(argument)
|
||||||
if parent_schema is not None:
|
if parent_schema:
|
||||||
self.engine.delete_inherited(target.pk, [cst.pk for cst in parent_schema.cache.constituents])
|
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:
|
def after_create_arguments(self, target: Operation, arguments: list[Operation]) -> None:
|
||||||
''' Trigger cascade resolutions after arguments are created. '''
|
''' Trigger cascade resolutions after arguments are created. '''
|
||||||
schema = self.cache.get_schema(target)
|
schema = self.cache.get_result(target)
|
||||||
if schema is None:
|
if not schema:
|
||||||
return
|
return
|
||||||
for argument in arguments:
|
for argument in arguments:
|
||||||
parent_schema = self.cache.get_schema(argument)
|
parent_schema = self.cache.get_result(argument)
|
||||||
if parent_schema is None:
|
if not parent_schema:
|
||||||
continue
|
continue
|
||||||
self.engine.inherit_cst(
|
self.engine.inherit_cst(
|
||||||
target_operation=target.pk,
|
target_operation=target.pk,
|
||||||
|
|
@ -347,7 +348,7 @@ class OperationSchemaCached:
|
||||||
original_cst = schema.cache.by_id[original_id]
|
original_cst = schema.cache.by_id[original_id]
|
||||||
substitution_cst = schema.cache.by_id[substitution_id]
|
substitution_cst = schema.cache.by_id[substitution_id]
|
||||||
cst_mapping.append((original_cst, substitution_cst))
|
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)
|
schema.substitute(cst_mapping)
|
||||||
for sub in added:
|
for sub in added:
|
||||||
self.cache.insert_substitution(sub)
|
self.cache.insert_substitution(sub)
|
||||||
|
|
|
||||||
|
|
@ -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}
|
||||||
|
|
@ -60,27 +60,11 @@ class OssCache:
|
||||||
'operation_id', 'parent_id', 'child_id'):
|
'operation_id', 'parent_id', 'child_id'):
|
||||||
self.inheritance[item.operation_id].append(item)
|
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. '''
|
''' 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.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
|
|
||||||
|
|
||||||
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.model.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.model.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.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 .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. '''
|
||||||
|
|
@ -35,7 +37,7 @@ class PropagationEngine:
|
||||||
successor_id = self.cache.get_inheritor(cst_id, child_id)
|
successor_id = self.cache.get_inheritor(cst_id, child_id)
|
||||||
if successor_id is None:
|
if successor_id is None:
|
||||||
continue
|
continue
|
||||||
child_schema = self.cache.get_schema(child_operation)
|
child_schema = self.cache.get_result(child_operation)
|
||||||
if child_schema is None:
|
if child_schema is None:
|
||||||
continue
|
continue
|
||||||
if child_schema.change_cst_type(successor_id, ctype):
|
if child_schema.change_cst_type(successor_id, ctype):
|
||||||
|
|
@ -67,7 +69,7 @@ class PropagationEngine:
|
||||||
) -> None:
|
) -> None:
|
||||||
''' Execute inheritance of Constituenta. '''
|
''' Execute inheritance of Constituenta. '''
|
||||||
operation = self.cache.operation_by_id[target_operation]
|
operation = self.cache.operation_by_id[target_operation]
|
||||||
destination = self.cache.get_schema(operation)
|
destination = self.cache.get_result(operation)
|
||||||
if destination is None:
|
if destination is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
@ -104,7 +106,7 @@ class PropagationEngine:
|
||||||
successor_id = self.cache.get_inheritor(cst_id, child_id)
|
successor_id = self.cache.get_inheritor(cst_id, child_id)
|
||||||
if successor_id is None:
|
if successor_id is None:
|
||||||
continue
|
continue
|
||||||
child_schema = self.cache.get_schema(child_operation)
|
child_schema = self.cache.get_result(child_operation)
|
||||||
assert child_schema is not None
|
assert child_schema is not None
|
||||||
new_mapping = self._transform_mapping(mapping, child_operation, child_schema)
|
new_mapping = self._transform_mapping(mapping, child_operation, child_schema)
|
||||||
alias_mapping = cst_mapping_to_alias(new_mapping)
|
alias_mapping = cst_mapping_to_alias(new_mapping)
|
||||||
|
|
@ -176,7 +178,7 @@ class PropagationEngine:
|
||||||
self.cache.ensure_loaded_subs()
|
self.cache.ensure_loaded_subs()
|
||||||
for child_id in children:
|
for child_id in children:
|
||||||
child_operation = self.cache.operation_by_id[child_id]
|
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:
|
if child_schema is None:
|
||||||
continue
|
continue
|
||||||
new_substitutions = self._transform_substitutions(substitutions, child_id, child_schema)
|
new_substitutions = self._transform_substitutions(substitutions, child_id, child_schema)
|
||||||
|
|
@ -193,7 +195,7 @@ class PropagationEngine:
|
||||||
self.cache.ensure_loaded_subs()
|
self.cache.ensure_loaded_subs()
|
||||||
for child_id in children:
|
for child_id in children:
|
||||||
child_operation = self.cache.operation_by_id[child_id]
|
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:
|
if child_schema is None:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
|
@ -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_result(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:
|
||||||
|
|
@ -374,7 +376,7 @@ class PropagationEngine:
|
||||||
self.cache.ensure_loaded_subs()
|
self.cache.ensure_loaded_subs()
|
||||||
for child_id in children:
|
for child_id in children:
|
||||||
child_operation = self.cache.operation_by_id[child_id]
|
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:
|
if child_schema is None:
|
||||||
continue
|
continue
|
||||||
new_mapping = self._transform_mapping(mapping, child_operation, child_schema)
|
new_mapping = self._transform_mapping(mapping, child_operation, child_schema)
|
||||||
|
|
|
||||||
|
|
@ -1,101 +1,110 @@
|
||||||
''' Models: Change propagation facade - managing all changes in OSS. '''
|
''' Models: Change propagation facade - managing all changes in OSS. '''
|
||||||
from typing import Optional
|
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 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[LibraryItem]:
|
def _get_oss_hosts(schemaID: int) -> list[int]:
|
||||||
''' Get all hosts for LibraryItem. '''
|
''' Get all hosts for schema. '''
|
||||||
return list(LibraryItem.objects.filter(operations__result_id=schemaID).only('pk').distinct())
|
return list(LibraryItem.objects.filter(operations__result_id=schemaID).distinct().values_list('pk', flat=True))
|
||||||
|
|
||||||
|
|
||||||
class PropagationFacade:
|
class PropagationFacade:
|
||||||
''' Change propagation API. '''
|
''' Change propagation API. '''
|
||||||
|
|
||||||
@staticmethod
|
def __init__(self) -> None:
|
||||||
def after_create_cst(source: RSFormCached, new_cst: list[Constituenta],
|
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:
|
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.model.pk)
|
if not new_cst:
|
||||||
|
return
|
||||||
|
source = new_cst[0].schema_id
|
||||||
|
hosts = _get_oss_hosts(source)
|
||||||
for host in hosts:
|
for host in hosts:
|
||||||
if exclude is None or host.pk not in exclude:
|
if exclude is None or host not in exclude:
|
||||||
OperationSchemaCached(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.pk not in exclude:
|
if exclude is None or host not in exclude:
|
||||||
OperationSchemaCached(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, sourceID: int, 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.model.pk)
|
hosts = _get_oss_hosts(sourceID)
|
||||||
for host in hosts:
|
for host in hosts:
|
||||||
if exclude is None or host.pk not in exclude:
|
if exclude is None or host not in exclude:
|
||||||
OperationSchemaCached(host).after_update_cst(source, target, data, old_data)
|
self.get_oss(host).after_update_cst(sourceID, 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.pk not in exclude:
|
if exclude is None or host not in exclude:
|
||||||
OperationSchemaCached(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:
|
||||||
return
|
return
|
||||||
hosts = _get_oss_hosts(sourceID)
|
hosts = _get_oss_hosts(sourceID)
|
||||||
for host in hosts:
|
for host in hosts:
|
||||||
if exclude is None or host.pk not in exclude:
|
if exclude is None or host not in exclude:
|
||||||
OperationSchemaCached(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(item: LibraryItem, exclude: Optional[list[int]] = None) -> None:
|
|
||||||
''' Trigger cascade resolutions before schema is deleted. '''
|
''' Trigger cascade resolutions before schema is deleted. '''
|
||||||
if item.item_type != LibraryItemType.RSFORM:
|
hosts = _get_oss_hosts(target)
|
||||||
return
|
|
||||||
hosts = _get_oss_hosts(item.pk)
|
|
||||||
if not hosts:
|
if not hosts:
|
||||||
return
|
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:
|
for host in hosts:
|
||||||
if exclude is None or host.pk not in exclude:
|
if exclude is None or host not in exclude:
|
||||||
OperationSchemaCached(host).before_delete_cst(item.pk, ids)
|
self.get_oss(host).before_delete_cst(target, ids)
|
||||||
|
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.pk not in exclude:
|
if exclude is None or host not in exclude:
|
||||||
OperationSchemaCached(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.pk not in exclude:
|
if exclude is None or host not in exclude:
|
||||||
OperationSchemaCached(host).before_delete_attribution(sourceID, attributions)
|
self.get_oss(host).before_delete_attribution(sourceID, attributions)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -609,8 +609,6 @@ class RelocateConstituentsSerializer(StrictSerializer):
|
||||||
attrs['destination'] = attrs['destination'].id
|
attrs['destination'] = attrs['destination'].id
|
||||||
attrs['source'] = attrs['items'][0].schema_id
|
attrs['source'] = attrs['items'][0].schema_id
|
||||||
|
|
||||||
# TODO: check permissions for editing source and destination
|
|
||||||
|
|
||||||
if attrs['source'] == attrs['destination']:
|
if attrs['source'] == attrs['destination']:
|
||||||
raise serializers.ValidationError({
|
raise serializers.ValidationError({
|
||||||
'destination': msg.sourceEqualDestination()
|
'destination': msg.sourceEqualDestination()
|
||||||
|
|
@ -625,15 +623,6 @@ class RelocateConstituentsSerializer(StrictSerializer):
|
||||||
'items': msg.RelocatingInherited()
|
'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(
|
if Argument.objects.filter(
|
||||||
operation__result_id=attrs['destination'],
|
operation__result_id=attrs['destination'],
|
||||||
argument__result_id=attrs['source']
|
argument__result_id=attrs['source']
|
||||||
|
|
|
||||||
|
|
@ -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 Operation, OperationSchema, OperationType
|
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
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
''' Testing API: Change substitutions in OSS. '''
|
''' Testing API: Change substitutions in OSS. '''
|
||||||
|
|
||||||
from apps.oss.models import OperationSchema, OperationType
|
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
|
from shared.EndpointTester import EndpointTester, decl_endpoint
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -388,7 +388,7 @@ class TestChangeOperations(EndpointTester):
|
||||||
self.assertEqual(self.ks5.constituentsQ().count(), 8)
|
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):
|
def test_relocate_constituents_up(self):
|
||||||
ks1_old_count = self.ks1.constituentsQ().count()
|
ks1_old_count = self.ks1.constituentsQ().count()
|
||||||
ks4_old_count = self.ks4.constituentsQ().count()
|
ks4_old_count = self.ks4.constituentsQ().count()
|
||||||
|
|
@ -408,7 +408,7 @@ class TestChangeOperations(EndpointTester):
|
||||||
'items': [ks6A1.pk]
|
'items': [ks6A1.pk]
|
||||||
}
|
}
|
||||||
|
|
||||||
self.executeOK(data)
|
self.executeOK(data, item=self.owned_id)
|
||||||
ks6.model.refresh_from_db()
|
ks6.model.refresh_from_db()
|
||||||
self.ks1.model.refresh_from_db()
|
self.ks1.model.refresh_from_db()
|
||||||
self.ks4.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)
|
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):
|
def test_relocate_constituents_down(self):
|
||||||
ks1_old_count = self.ks1.constituentsQ().count()
|
ks1_old_count = self.ks1.constituentsQ().count()
|
||||||
ks4_old_count = self.ks4.constituentsQ().count()
|
ks4_old_count = self.ks4.constituentsQ().count()
|
||||||
|
|
@ -438,7 +438,7 @@ class TestChangeOperations(EndpointTester):
|
||||||
'items': [self.ks1X2.pk]
|
'items': [self.ks1X2.pk]
|
||||||
}
|
}
|
||||||
|
|
||||||
self.executeOK(data)
|
self.executeOK(data, item=self.owned_id)
|
||||||
ks6.model.refresh_from_db()
|
ks6.model.refresh_from_db()
|
||||||
self.ks1.model.refresh_from_db()
|
self.ks1.model.refresh_from_db()
|
||||||
self.ks4.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')
|
@decl_endpoint('/api/rsforms/{schema}/delete-multiple-cst', method='patch')
|
||||||
def test_delete_constituenta(self):
|
def test_delete_constituenta(self):
|
||||||
data = {'items': [self.ks1X1.pk]}
|
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.ks4D2.refresh_from_db()
|
||||||
self.ks5D4.refresh_from_db()
|
self.ks5D4.refresh_from_db()
|
||||||
self.ks6D2.refresh_from_db()
|
self.ks6D2.refresh_from_db()
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
''' Testing API: Change substitutions in OSS. '''
|
''' Testing API: Change substitutions in OSS. '''
|
||||||
|
|
||||||
from apps.oss.models import OperationSchema, OperationType
|
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
|
from shared.EndpointTester import EndpointTester, decl_endpoint
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
''' Testing API: Operation Schema - blocks manipulation. '''
|
''' Testing API: Operation Schema - blocks manipulation. '''
|
||||||
from apps.library.models import AccessPolicy, Editor, LibraryItem, LibraryItemType
|
from apps.oss.models import OperationSchema, OperationType
|
||||||
from apps.oss.models import Operation, OperationSchema, OperationType
|
|
||||||
from shared.EndpointTester import EndpointTester, decl_endpoint
|
from shared.EndpointTester import EndpointTester, decl_endpoint
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,12 @@
|
||||||
''' Testing API: Operation Schema - operations manipulation. '''
|
''' Testing API: Operation Schema - operations manipulation. '''
|
||||||
from apps.library.models import Editor, LibraryItem
|
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 apps.rsform.models import Attribution, RSForm
|
||||||
from shared.EndpointTester import EndpointTester, decl_endpoint
|
from shared.EndpointTester import EndpointTester, decl_endpoint
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -220,8 +220,9 @@ class TestOssViewset(EndpointTester):
|
||||||
self.executeBadData(data)
|
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):
|
def test_relocate_constituents(self):
|
||||||
|
self.set_params(item=self.owned_id)
|
||||||
self.populateData()
|
self.populateData()
|
||||||
self.ks1X2 = self.ks1.insert_last('X2', convention='test')
|
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.models import LibraryItem, LibraryItemType
|
||||||
from apps.library.serializers import LibraryItemSerializer
|
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 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
|
||||||
|
|
@ -291,7 +291,7 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
|
||||||
'height': position['height']
|
'height': position['height']
|
||||||
})
|
})
|
||||||
m.Layout.update_data(pk, layout)
|
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'])
|
item.save(update_fields=['time_update'])
|
||||||
|
|
||||||
return Response(
|
return Response(
|
||||||
|
|
@ -420,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).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"])
|
||||||
|
|
@ -544,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 = m.OperationSchemaCached(item)
|
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)
|
||||||
|
|
@ -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)]
|
layout = [x for x in layout if x['nodeID'] != 'o' + str(operation.pk)]
|
||||||
|
|
||||||
with transaction.atomic():
|
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'])
|
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)
|
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
|
||||||
|
|
@ -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)]
|
layout = [x for x in layout if x['nodeID'] != 'o' + str(operation.pk)]
|
||||||
|
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
oss = m.OperationSchemaCached(item)
|
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'])
|
||||||
|
|
@ -680,13 +684,13 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
|
||||||
|
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
m.Layout.update_data(pk, layout)
|
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'])
|
item.save(update_fields=['time_update'])
|
||||||
|
|
||||||
return Response(
|
return Response(
|
||||||
status=c.HTTP_200_OK,
|
status=c.HTTP_200_OK,
|
||||||
data={
|
data={
|
||||||
'new_schema': LibraryItemSerializer(schema.model).data,
|
'new_schema': LibraryItemSerializer(schema).data,
|
||||||
'oss': s.OperationSchemaSerializer(item).data
|
'oss': s.OperationSchemaSerializer(item).data
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
@ -726,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 = m.OperationSchemaCached(item)
|
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
|
||||||
|
|
@ -769,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 = m.OperationSchemaCached(item)
|
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'])
|
||||||
|
|
@ -823,24 +829,27 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
|
||||||
c.HTTP_404_NOT_FOUND: None
|
c.HTTP_404_NOT_FOUND: None
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@action(detail=False, methods=['post'], url_path='relocate-constituents')
|
@action(detail=True, methods=['post'], url_path='relocate-constituents')
|
||||||
def relocate_constituents(self, request: Request) -> Response:
|
def relocate_constituents(self, request: Request, pk) -> Response:
|
||||||
''' Relocate constituents from one schema to another. '''
|
''' Relocate constituents from one schema to another. '''
|
||||||
|
item = self._get_item()
|
||||||
serializer = s.RelocateConstituentsSerializer(data=request.data)
|
serializer = s.RelocateConstituentsSerializer(data=request.data)
|
||||||
serializer.is_valid(raise_exception=True)
|
serializer.is_valid(raise_exception=True)
|
||||||
data = serializer.validated_data
|
data = serializer.validated_data
|
||||||
ids = [cst.pk for cst in data['items']]
|
ids = [cst.pk for cst in data['items']]
|
||||||
|
destinationID = data['destination']
|
||||||
|
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
oss = m.OperationSchemaCached(LibraryItem.objects.get(pk=data['oss']))
|
propagation = m.PropagationFacade()
|
||||||
source = RSFormCached(LibraryItem.objects.get(pk=data['source']))
|
oss = propagation.get_oss(item.pk)
|
||||||
destination = RSFormCached(LibraryItem.objects.get(pk=data['destination']))
|
source = propagation.get_schema(data['source'])
|
||||||
if data['move_down']:
|
if data['move_down']:
|
||||||
oss.relocate_down(source, destination, ids)
|
oss.relocate_down(destinationID, ids)
|
||||||
m.PropagationFacade.before_delete_cst(data['source'], 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.pk, destinationID, ids)
|
||||||
m.PropagationFacade.after_create_cst(destination, new_items, exclude=[oss.model.pk])
|
propagation.after_create_cst(new_items, exclude=[oss.pk])
|
||||||
|
item.save(update_fields=['time_update'])
|
||||||
|
|
||||||
return Response(status=c.HTTP_200_OK)
|
return Response(status=c.HTTP_200_OK)
|
||||||
|
|
|
||||||
|
|
@ -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]] = {}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,21 +20,15 @@ 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, model: LibraryItem):
|
def __init__(self, item_id: int) -> None:
|
||||||
self.model = model
|
self.pk = item_id
|
||||||
self.cache: _RSFormCache = _RSFormCache(self)
|
self.cache: _RSFormCache = _RSFormCache(self)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def create(**kwargs) -> 'RSFormCached':
|
def create(**kwargs) -> 'RSFormCached':
|
||||||
''' Create LibraryItem via RSForm. '''
|
''' Create LibraryItem via RSForm. '''
|
||||||
model = LibraryItem.objects.create(item_type=LibraryItemType.RSFORM, **kwargs)
|
model = LibraryItem.objects.create(item_type=LibraryItemType.RSFORM, **kwargs)
|
||||||
return RSFormCached(model)
|
return RSFormCached(model.pk)
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def from_id(pk: int) -> 'RSFormCached':
|
|
||||||
''' Get LibraryItem by pk. '''
|
|
||||||
model = LibraryItem.objects.get(pk=pk)
|
|
||||||
return RSFormCached(model)
|
|
||||||
|
|
||||||
def get_dependant(self, target: Iterable[int]) -> set[int]:
|
def get_dependant(self, target: Iterable[int]) -> set[int]:
|
||||||
''' Get list of constituents depending on target (only 1st degree). '''
|
''' Get list of constituents depending on target (only 1st degree). '''
|
||||||
|
|
@ -51,7 +45,7 @@ class RSFormCached:
|
||||||
|
|
||||||
def constituentsQ(self) -> QuerySet[Constituenta]:
|
def constituentsQ(self) -> QuerySet[Constituenta]:
|
||||||
''' Get QuerySet containing all constituents of current RSForm. '''
|
''' 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(
|
def insert_last(
|
||||||
self,
|
self,
|
||||||
|
|
@ -62,9 +56,9 @@ class RSFormCached:
|
||||||
''' Insert new constituenta at last position. '''
|
''' Insert new constituenta at last position. '''
|
||||||
if cst_type is None:
|
if cst_type is None:
|
||||||
cst_type = guess_type(alias)
|
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(
|
result = Constituenta.objects.create(
|
||||||
schema=self.model,
|
schema_id=self.pk,
|
||||||
order=position,
|
order=position,
|
||||||
alias=alias,
|
alias=alias,
|
||||||
cst_type=cst_type,
|
cst_type=cst_type,
|
||||||
|
|
@ -83,7 +77,7 @@ class RSFormCached:
|
||||||
RSForm.shift_positions(position, 1, self.cache.constituents)
|
RSForm.shift_positions(position, 1, self.cache.constituents)
|
||||||
|
|
||||||
result = Constituenta.objects.create(
|
result = Constituenta.objects.create(
|
||||||
schema=self.model,
|
schema_id=self.pk,
|
||||||
order=position,
|
order=position,
|
||||||
alias=data['alias'],
|
alias=data['alias'],
|
||||||
cst_type=data['cst_type'],
|
cst_type=data['cst_type'],
|
||||||
|
|
@ -160,7 +154,7 @@ class RSFormCached:
|
||||||
new_constituents = deepcopy(items)
|
new_constituents = deepcopy(items)
|
||||||
for cst in new_constituents:
|
for cst in new_constituents:
|
||||||
cst.pk = None
|
cst.pk = None
|
||||||
cst.schema = self.model
|
cst.schema_id = self.pk
|
||||||
cst.order = position
|
cst.order = position
|
||||||
if mapping_alias:
|
if mapping_alias:
|
||||||
cst.alias = mapping_alias[cst.alias]
|
cst.alias = mapping_alias[cst.alias]
|
||||||
|
|
@ -263,7 +257,7 @@ class RSFormCached:
|
||||||
deleted.append(original)
|
deleted.append(original)
|
||||||
replacements.append(substitution.pk)
|
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:
|
if attributions:
|
||||||
orig_to_sub = {original.pk: substitution.pk for original, substitution in substitutions}
|
orig_to_sub = {original.pk: substitution.pk for original, substitution in substitutions}
|
||||||
orig_pks = set(orig_to_sub.keys())
|
orig_pks = set(orig_to_sub.keys())
|
||||||
|
|
@ -374,7 +368,7 @@ class RSFormCached:
|
||||||
prefix = get_type_prefix(cst_type)
|
prefix = get_type_prefix(cst_type)
|
||||||
for text in expressions:
|
for text in expressions:
|
||||||
new_item = Constituenta.objects.create(
|
new_item = Constituenta.objects.create(
|
||||||
schema=self.model,
|
schema_id=self.pk,
|
||||||
order=position,
|
order=position,
|
||||||
alias=f'{prefix}{free_index}',
|
alias=f'{prefix}{free_index}',
|
||||||
definition_formal=text,
|
definition_formal=text,
|
||||||
|
|
@ -392,7 +386,7 @@ class RSFormCached:
|
||||||
cst_list: Iterable[Constituenta] = []
|
cst_list: Iterable[Constituenta] = []
|
||||||
if not self.cache.is_loaded:
|
if not self.cache.is_loaded:
|
||||||
cst_list = Constituenta.objects \
|
cst_list = Constituenta.objects \
|
||||||
.filter(schema=self.model, cst_type=cst_type) \
|
.filter(schema_id=self.pk, cst_type=cst_type) \
|
||||||
.only('alias')
|
.only('alias')
|
||||||
else:
|
else:
|
||||||
cst_list = [cst for cst in self.cache.constituents if cst.cst_type == cst_type]
|
cst_list = [cst for cst in self.cache.constituents if cst.cst_type == cst_type]
|
||||||
|
|
@ -406,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] = {}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -123,7 +123,7 @@ class RSFormTRSSerializer(serializers.Serializer):
|
||||||
result['description'] = data.get('description', '')
|
result['description'] = data.get('description', '')
|
||||||
if 'id' in data:
|
if 'id' in data:
|
||||||
result['id'] = data['id']
|
result['id'] = data['id']
|
||||||
self.instance = RSFormCached.from_id(result['id'])
|
self.instance = RSFormCached(result['id'])
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def validate(self, attrs: dict):
|
def validate(self, attrs: dict):
|
||||||
|
|
@ -151,7 +151,7 @@ class RSFormTRSSerializer(serializers.Serializer):
|
||||||
for cst_data in validated_data['items']:
|
for cst_data in validated_data['items']:
|
||||||
cst = Constituenta(
|
cst = Constituenta(
|
||||||
alias=cst_data['alias'],
|
alias=cst_data['alias'],
|
||||||
schema=self.instance.model,
|
schema_id=self.instance.pk,
|
||||||
order=order,
|
order=order,
|
||||||
cst_type=cst_data['cstType'],
|
cst_type=cst_data['cstType'],
|
||||||
)
|
)
|
||||||
|
|
@ -163,12 +163,13 @@ class RSFormTRSSerializer(serializers.Serializer):
|
||||||
|
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
def update(self, instance: RSFormCached, validated_data) -> RSFormCached:
|
def update(self, instance: RSFormCached, validated_data) -> RSFormCached:
|
||||||
|
model = LibraryItem.objects.get(pk=instance.pk)
|
||||||
if 'alias' in validated_data:
|
if 'alias' in validated_data:
|
||||||
instance.model.alias = validated_data['alias']
|
model.alias = validated_data['alias']
|
||||||
if 'title' in validated_data:
|
if 'title' in validated_data:
|
||||||
instance.model.title = validated_data['title']
|
model.title = validated_data['title']
|
||||||
if 'description' in validated_data:
|
if 'description' in validated_data:
|
||||||
instance.model.description = validated_data['description']
|
model.description = validated_data['description']
|
||||||
|
|
||||||
order = 0
|
order = 0
|
||||||
prev_constituents = instance.constituentsQ()
|
prev_constituents = instance.constituentsQ()
|
||||||
|
|
@ -185,7 +186,7 @@ class RSFormTRSSerializer(serializers.Serializer):
|
||||||
else:
|
else:
|
||||||
cst = Constituenta(
|
cst = Constituenta(
|
||||||
alias=cst_data['alias'],
|
alias=cst_data['alias'],
|
||||||
schema=instance.model,
|
schema_id=instance.pk,
|
||||||
order=order,
|
order=order,
|
||||||
cst_type=cst_data['cstType'],
|
cst_type=cst_data['cstType'],
|
||||||
)
|
)
|
||||||
|
|
@ -199,7 +200,7 @@ class RSFormTRSSerializer(serializers.Serializer):
|
||||||
prev_cst.delete()
|
prev_cst.delete()
|
||||||
|
|
||||||
instance.resolve_all_text()
|
instance.resolve_all_text()
|
||||||
instance.model.save()
|
model.save()
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
|
|
|
||||||
|
|
@ -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())
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
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([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)
|
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(item.pk, 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)
|
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(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'])
|
||||||
|
|
||||||
|
|
@ -456,7 +459,7 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
|
||||||
item = self._get_item()
|
item = self._get_item()
|
||||||
|
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
m.OrderManager(m.RSFormCached(item)).restore_order()
|
m.OrderManager(m.RSFormCached(item.pk)).restore_order()
|
||||||
item.save(update_fields=['time_update'])
|
item.save(update_fields=['time_update'])
|
||||||
|
|
||||||
return Response(
|
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 = 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
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -731,16 +734,18 @@ def inline_synthesis(request: Request) -> HttpResponse:
|
||||||
serializer = s.InlineSynthesisSerializer(data=request.data, context={'user': request.user})
|
serializer = s.InlineSynthesisSerializer(data=request.data, context={'user': request.user})
|
||||||
serializer.is_valid(raise_exception=True)
|
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'])
|
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([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']:
|
||||||
|
|
@ -752,11 +757,11 @@ 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.model.pk, substitutions)
|
propagation.before_substitute(receiver.pk, substitutions)
|
||||||
receiver.substitute(substitutions)
|
receiver.substitute(substitutions)
|
||||||
receiver.model.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,
|
||||||
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>({
|
axiosPost<IRelocateConstituentsDTO>({
|
||||||
endpoint: `/api/oss/relocate-constituents`,
|
endpoint: `/api/oss/${itemID}/relocate-constituents`,
|
||||||
request: {
|
request: {
|
||||||
data: data,
|
data: data,
|
||||||
successMessage: infoMsg.changesSaved
|
successMessage: infoMsg.changesSaved
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,6 @@ export const useRelocateConstituents = () => {
|
||||||
onError: () => client.invalidateQueries()
|
onError: () => client.invalidateQueries()
|
||||||
});
|
});
|
||||||
return {
|
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) {
|
function onSubmit(data: IRelocateConstituentsDTO) {
|
||||||
data.items = moveTarget;
|
data.items = moveTarget;
|
||||||
if (!layout || JSON.stringify(layout) === JSON.stringify(oss.layout)) {
|
if (!layout || JSON.stringify(layout) === JSON.stringify(oss.layout)) {
|
||||||
return relocateConstituents(data);
|
return relocateConstituents({ itemID: oss.id, data: data });
|
||||||
} else {
|
} else {
|
||||||
return updatePositions({
|
return updatePositions({
|
||||||
isSilent: true,
|
isSilent: true,
|
||||||
itemID: oss.id,
|
itemID: oss.id,
|
||||||
data: layout
|
data: layout
|
||||||
}).then(() => relocateConstituents(data));
|
}).then(() => relocateConstituents({ itemID: oss.id, data: data }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user