mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-11-15 17:21:38 +03:00
R: Refactor constituenta insertion
This commit is contained in:
parent
48de17acc7
commit
e9f31c0e7a
|
|
@ -54,7 +54,7 @@ class LibraryItemCloneSerializer(StrictSerializer):
|
|||
model = LibraryItem
|
||||
exclude = ['id', 'item_type', 'owner', 'read_only']
|
||||
|
||||
items = PKField(many=True, queryset=Constituenta.objects.all().only('pk', 'schema_id'))
|
||||
items = PKField(many=True, queryset=Constituenta.objects.all().only('schema_id'))
|
||||
item_data = ItemCloneData()
|
||||
|
||||
def validate_items(self, value):
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ from rest_framework.request import Request
|
|||
from rest_framework.response import Response
|
||||
|
||||
from apps.oss.models import Layout, Operation, OperationSchema, PropagationFacade
|
||||
from apps.rsform.models import Attribution, RSFormCached
|
||||
from apps.rsform.models import RSFormCached
|
||||
from apps.rsform.serializers import RSFormParseSerializer
|
||||
from apps.users.models import User
|
||||
from shared import permissions
|
||||
|
|
@ -172,22 +172,7 @@ class LibraryViewSet(viewsets.ModelViewSet):
|
|||
clone.location = data.get('location', m.LocationHead.USER)
|
||||
clone.save()
|
||||
|
||||
cst_map: dict[int, int] = {}
|
||||
cst_list: list[int] = []
|
||||
need_filter = 'items' in request.data and request.data['items']
|
||||
for cst in RSFormCached(item).constituentsQ():
|
||||
if not need_filter or cst.pk in request.data['items']:
|
||||
old_pk = cst.pk
|
||||
cst.pk = None
|
||||
cst.schema = clone
|
||||
cst.save()
|
||||
cst_map[old_pk] = cst.pk
|
||||
cst_list.append(old_pk)
|
||||
for attr in Attribution.objects.filter(container__in=cst_list, attribute__in=cst_list):
|
||||
attr.pk = None
|
||||
attr.container_id = cst_map[attr.container_id]
|
||||
attr.attribute_id = cst_map[attr.attribute_id]
|
||||
attr.save()
|
||||
RSFormCached(clone).insert_from(item.pk, request.data['items'] if 'items' in request.data else None)
|
||||
|
||||
return Response(
|
||||
status=c.HTTP_201_CREATED,
|
||||
|
|
|
|||
|
|
@ -137,11 +137,10 @@ class OperationSchema:
|
|||
parents: dict = {}
|
||||
children: dict = {}
|
||||
for operand in schemas:
|
||||
items = list(Constituenta.objects.filter(schema_id=operand).order_by('order'))
|
||||
new_items = receiver.insert_copy(items)
|
||||
for (i, cst) in enumerate(new_items):
|
||||
parents[cst.pk] = items[i]
|
||||
children[items[i].pk] = cst
|
||||
new_items = receiver.insert_from(operand)
|
||||
for (old_cst, new_cst) in new_items:
|
||||
parents[new_cst.pk] = old_cst
|
||||
children[old_cst.pk] = new_cst
|
||||
|
||||
translated_substitutions: list[tuple[Constituenta, Constituenta]] = []
|
||||
for sub in substitutions:
|
||||
|
|
|
|||
|
|
@ -178,11 +178,10 @@ class OperationSchemaCached:
|
|||
parents: dict = {}
|
||||
children: dict = {}
|
||||
for operand in schemas:
|
||||
items = list(Constituenta.objects.filter(schema_id=operand).order_by('order'))
|
||||
new_items = receiver.insert_copy(items)
|
||||
for (i, cst) in enumerate(new_items):
|
||||
parents[cst.pk] = items[i]
|
||||
children[items[i].pk] = cst
|
||||
new_items = receiver.insert_from(operand)
|
||||
for (old_cst, new_cst) in new_items:
|
||||
parents[new_cst.pk] = old_cst
|
||||
children[old_cst.pk] = new_cst
|
||||
|
||||
translated_substitutions: list[tuple[Constituenta, Constituenta]] = []
|
||||
for sub in substitutions:
|
||||
|
|
@ -223,7 +222,7 @@ class OperationSchemaCached:
|
|||
Inheritance.objects.filter(operation_id=operation.pk, parent_id__in=items).delete()
|
||||
|
||||
def relocate_up(self, source: RSFormCached, destination: RSFormCached,
|
||||
items: list[Constituenta]) -> list[Constituenta]:
|
||||
item_ids: list[int]) -> list[Constituenta]:
|
||||
''' Move list of Constituents upstream to destination Schema. '''
|
||||
self.cache.ensure_loaded_subs()
|
||||
self.cache.insert_schema(source)
|
||||
|
|
@ -237,17 +236,18 @@ class OperationSchemaCached:
|
|||
destination_cst = destination.cache.by_id[item.parent_id]
|
||||
alias_mapping[source_cst.alias] = destination_cst.alias
|
||||
|
||||
new_items = destination.insert_copy(items, initial_mapping=alias_mapping)
|
||||
for index, cst in enumerate(new_items):
|
||||
new_items = destination.insert_from(source.model.pk, item_ids, alias_mapping)
|
||||
for (cst, new_cst) in new_items:
|
||||
new_inheritance = Inheritance.objects.create(
|
||||
operation=operation,
|
||||
child=items[index],
|
||||
parent=cst
|
||||
child=cst,
|
||||
parent=new_cst
|
||||
)
|
||||
self.cache.insert_inheritance(new_inheritance)
|
||||
self.after_create_cst(destination, new_items, exclude=[operation.pk])
|
||||
new_constituents = [item[1] for item in new_items]
|
||||
self.after_create_cst(destination, new_constituents, exclude=[operation.pk])
|
||||
destination.model.save(update_fields=['time_update'])
|
||||
return new_items
|
||||
return new_constituents
|
||||
|
||||
def after_create_cst(
|
||||
self, source: RSFormCached,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ from typing import Optional
|
|||
|
||||
from rest_framework.serializers import ValidationError
|
||||
|
||||
from apps.rsform.models import INSERT_LAST, Attribution, Constituenta, CstType, RSFormCached
|
||||
from apps.rsform.models import Attribution, Constituenta, CstType, RSFormCached
|
||||
|
||||
from .Inheritance import Inheritance
|
||||
from .Operation import Operation
|
||||
|
|
@ -76,11 +76,11 @@ class PropagationEngine:
|
|||
alias_mapping = cst_mapping_to_alias(new_mapping)
|
||||
insert_where = self._determine_insert_position(items[0].pk, operation, source, destination)
|
||||
new_cst_list = destination.insert_copy(items, insert_where, alias_mapping)
|
||||
for index, cst in enumerate(new_cst_list):
|
||||
for (cst, new_cst) in zip(items, new_cst_list):
|
||||
new_inheritance = Inheritance.objects.create(
|
||||
operation=operation,
|
||||
child=cst,
|
||||
parent=items[index]
|
||||
child=new_cst,
|
||||
parent=cst
|
||||
)
|
||||
self.cache.insert_inheritance(new_inheritance)
|
||||
new_mapping = {alias_mapping[alias]: cst for alias, cst in new_mapping.items()}
|
||||
|
|
@ -145,28 +145,28 @@ class PropagationEngine:
|
|||
|
||||
self.cache.ensure_loaded_subs()
|
||||
|
||||
existing_associations = set(
|
||||
existing_attributions = set(
|
||||
Attribution.objects.filter(
|
||||
container__schema_id=operation.result_id,
|
||||
).values_list('container_id', 'attribute_id')
|
||||
)
|
||||
|
||||
new_associations: list[Attribution] = []
|
||||
for assoc in items:
|
||||
new_container = self.cache.get_inheritor(assoc.container_id, target)
|
||||
new_attribute = self.cache.get_inheritor(assoc.attribute_id, target)
|
||||
new_attributions: list[Attribution] = []
|
||||
for attrib in items:
|
||||
new_container = self.cache.get_inheritor(attrib.container_id, target)
|
||||
new_attribute = self.cache.get_inheritor(attrib.attribute_id, target)
|
||||
if new_container is None or new_attribute is None \
|
||||
or new_attribute == new_container \
|
||||
or (new_container, new_attribute) in existing_associations:
|
||||
or (new_container, new_attribute) in existing_attributions:
|
||||
continue
|
||||
|
||||
new_associations.append(Attribution(
|
||||
new_attributions.append(Attribution(
|
||||
container_id=new_container,
|
||||
attribute_id=new_attribute
|
||||
))
|
||||
if new_associations:
|
||||
new_associations = Attribution.objects.bulk_create(new_associations)
|
||||
self.on_inherit_attribution(target, new_associations)
|
||||
if new_attributions:
|
||||
new_attributions = Attribution.objects.bulk_create(new_attributions)
|
||||
self.on_inherit_attribution(target, new_attributions)
|
||||
|
||||
def on_before_substitute(self, operationID: int, substitutions: CstSubstitution) -> None:
|
||||
''' Trigger cascade resolutions when Constituenta substitution is executed. '''
|
||||
|
|
@ -286,7 +286,7 @@ class PropagationEngine:
|
|||
operation: Operation,
|
||||
source: RSFormCached,
|
||||
destination: RSFormCached
|
||||
) -> int:
|
||||
) -> Optional[int]:
|
||||
''' Determine insert_after for new constituenta. '''
|
||||
prototype = source.cache.by_id[prototype_id]
|
||||
prototype_index = source.cache.constituents.index(prototype)
|
||||
|
|
@ -295,7 +295,7 @@ class PropagationEngine:
|
|||
prev_cst = source.cache.constituents[prototype_index - 1]
|
||||
inherited_prev_id = self.cache.get_successor(prev_cst.pk, operation.pk)
|
||||
if inherited_prev_id is None:
|
||||
return INSERT_LAST
|
||||
return None
|
||||
prev_cst = destination.cache.by_id[inherited_prev_id]
|
||||
prev_index = destination.cache.constituents.index(prev_cst)
|
||||
return prev_index + 1
|
||||
|
|
|
|||
|
|
@ -602,7 +602,7 @@ class RelocateConstituentsSerializer(StrictSerializer):
|
|||
items = PKField(
|
||||
many=True,
|
||||
allow_empty=False,
|
||||
queryset=Constituenta.objects.all()
|
||||
queryset=Constituenta.objects.all().only('schema_id')
|
||||
)
|
||||
|
||||
def validate(self, attrs):
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ from rest_framework.response import Response
|
|||
|
||||
from apps.library.models import LibraryItem, LibraryItemType
|
||||
from apps.library.serializers import LibraryItemSerializer
|
||||
from apps.rsform.models import Attribution, Constituenta, RSFormCached
|
||||
from apps.rsform.models import Constituenta, RSFormCached
|
||||
from apps.rsform.serializers import CstTargetSerializer
|
||||
from shared import messages as msg
|
||||
from shared import permissions
|
||||
|
|
@ -23,40 +23,6 @@ from .. import models as m
|
|||
from .. import serializers as s
|
||||
|
||||
|
||||
def _create_clone(prototype: LibraryItem, operation: m.Operation, oss: LibraryItem) -> LibraryItem:
|
||||
''' Create clone of prototype schema for operation. '''
|
||||
clone = deepcopy(prototype)
|
||||
clone.pk = None
|
||||
clone.owner = oss.owner
|
||||
clone.title = operation.title
|
||||
clone.alias = operation.alias
|
||||
clone.description = operation.description
|
||||
clone.visible = False
|
||||
clone.read_only = False
|
||||
clone.access_policy = oss.access_policy
|
||||
clone.location = oss.location
|
||||
clone.save()
|
||||
|
||||
cst_map: dict[int, int] = {}
|
||||
cst_list: list[int] = []
|
||||
|
||||
for cst in Constituenta.objects.filter(schema_id=prototype.pk):
|
||||
old_pk = cst.pk
|
||||
cst_copy = deepcopy(cst)
|
||||
cst_copy.pk = None
|
||||
cst_copy.schema = clone
|
||||
cst_copy.save()
|
||||
cst_map[old_pk] = cst_copy.pk
|
||||
cst_list.append(old_pk)
|
||||
|
||||
for attr in Attribution.objects.filter(container__in=cst_list, attribute__in=cst_list):
|
||||
attr.pk = None
|
||||
attr.container_id = cst_map[attr.container_id]
|
||||
attr.attribute_id = cst_map[attr.attribute_id]
|
||||
attr.save()
|
||||
return clone
|
||||
|
||||
|
||||
@extend_schema(tags=['OSS'])
|
||||
@extend_schema_view()
|
||||
class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.RetrieveAPIView):
|
||||
|
|
@ -442,7 +408,21 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
|
|||
|
||||
if serializer.validated_data['clone_source']:
|
||||
prototype: LibraryItem = serializer.validated_data['source']
|
||||
new_operation.result = _create_clone(prototype, new_operation, item)
|
||||
|
||||
schema_clone = deepcopy(prototype)
|
||||
schema_clone.pk = None
|
||||
schema_clone.owner = item.owner
|
||||
schema_clone.title = new_operation.title
|
||||
schema_clone.alias = new_operation.alias
|
||||
schema_clone.description = new_operation.description
|
||||
schema_clone.visible = False
|
||||
schema_clone.read_only = False
|
||||
schema_clone.access_policy = item.access_policy
|
||||
schema_clone.location = item.location
|
||||
schema_clone.save()
|
||||
RSFormCached(schema_clone).insert_from(prototype.pk)
|
||||
|
||||
new_operation.result = schema_clone
|
||||
new_operation.save(update_fields=["result"])
|
||||
|
||||
item.save(update_fields=['time_update'])
|
||||
|
|
@ -860,7 +840,7 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
|
|||
m.PropagationFacade.before_delete_cst(data['source'], ids)
|
||||
source.delete_cst(ids)
|
||||
else:
|
||||
new_items = oss.relocate_up(source, destination, data['items'])
|
||||
new_items = oss.relocate_up(source, destination, ids)
|
||||
m.PropagationFacade.after_create_cst(destination, new_items, exclude=[oss.model.pk])
|
||||
|
||||
return Response(status=c.HTTP_200_OK)
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ from .api_RSLanguage import get_type_prefix, guess_type
|
|||
from .Attribution import Attribution
|
||||
from .Constituenta import Constituenta, CstType, extract_entities, extract_globals
|
||||
|
||||
INSERT_LAST: int = -1
|
||||
DELETED_ALIAS = 'DEL'
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ from shared import messages as msg
|
|||
from .api_RSLanguage import generate_structure, get_type_prefix, guess_type
|
||||
from .Attribution import Attribution
|
||||
from .Constituenta import Constituenta, CstType
|
||||
from .RSForm import DELETED_ALIAS, INSERT_LAST, RSForm
|
||||
from .RSForm import DELETED_ALIAS, RSForm
|
||||
|
||||
|
||||
class RSFormCached:
|
||||
|
|
@ -76,7 +76,7 @@ class RSFormCached:
|
|||
def create_cst(self, data: dict, insert_after: Optional[Constituenta] = None) -> Constituenta:
|
||||
''' Create constituenta from data. '''
|
||||
self.cache.ensure_loaded_terms()
|
||||
if insert_after is not None:
|
||||
if insert_after:
|
||||
position = self.cache.by_id[insert_after.pk].order + 1
|
||||
else:
|
||||
position = len(self.cache.constituents)
|
||||
|
|
@ -109,52 +109,77 @@ class RSFormCached:
|
|||
RSForm.resolve_term_change(self.cache.constituents, [result.pk], self.cache.by_alias, self.cache.by_id)
|
||||
return result
|
||||
|
||||
def insert_from(
|
||||
self, sourceID: int,
|
||||
items_list: Optional[list[int]] = None,
|
||||
initial_mapping: Optional[dict[str, str]] = None
|
||||
) -> list[tuple[Constituenta, Constituenta]]:
|
||||
''' Insert copy of constituents from source schema. '''
|
||||
if not items_list:
|
||||
items = list(Constituenta.objects.filter(schema_id=sourceID).order_by('order'))
|
||||
else:
|
||||
items = list(Constituenta.objects.filter(pk__in=items_list, schema_id=sourceID).order_by('order'))
|
||||
if not items:
|
||||
return []
|
||||
new_constituents = self.insert_copy(items=items, initial_mapping=initial_mapping)
|
||||
return list(zip(items, new_constituents))
|
||||
|
||||
def insert_copy(
|
||||
self,
|
||||
items: list[Constituenta],
|
||||
position: int = INSERT_LAST,
|
||||
position: Optional[int] = None,
|
||||
initial_mapping: Optional[dict[str, str]] = None
|
||||
) -> list[Constituenta]:
|
||||
''' Insert copy of target constituents updating references. '''
|
||||
count = len(items)
|
||||
if count == 0:
|
||||
if not items:
|
||||
return []
|
||||
|
||||
self.cache.ensure_loaded()
|
||||
lastPosition = len(self.cache.constituents)
|
||||
if position == INSERT_LAST:
|
||||
position = lastPosition
|
||||
last_position = len(self.cache.constituents)
|
||||
if not position:
|
||||
position = last_position
|
||||
else:
|
||||
position = max(0, min(position, lastPosition))
|
||||
RSForm.shift_positions(position, count, self.cache.constituents)
|
||||
position = max(0, min(position, last_position))
|
||||
|
||||
indices: dict[str, int] = {}
|
||||
for (value, _) in CstType.choices:
|
||||
indices[value] = -1
|
||||
was_empty = last_position == 0
|
||||
if not was_empty and position != last_position:
|
||||
RSForm.shift_positions(position, len(items), self.cache.constituents)
|
||||
|
||||
mapping: dict[str, str] = initial_mapping.copy() if initial_mapping else {}
|
||||
for cst in items:
|
||||
if indices[cst.cst_type] == -1:
|
||||
indices[cst.cst_type] = self._get_max_index(cst.cst_type)
|
||||
indices[cst.cst_type] = indices[cst.cst_type] + 1
|
||||
newAlias = f'{get_type_prefix(cst.cst_type)}{indices[cst.cst_type]}'
|
||||
mapping[cst.alias] = newAlias
|
||||
mapping_alias: dict[str, str] = initial_mapping.copy() if initial_mapping else {}
|
||||
if not was_empty:
|
||||
indices: dict[str, int] = {}
|
||||
for (value, _) in CstType.choices:
|
||||
indices[value] = self._get_max_index(value)
|
||||
|
||||
result = deepcopy(items)
|
||||
for cst in result:
|
||||
for cst in items:
|
||||
indices[cst.cst_type] = indices[cst.cst_type] + 1
|
||||
newAlias = f'{get_type_prefix(cst.cst_type)}{indices[cst.cst_type]}'
|
||||
mapping_alias[cst.alias] = newAlias
|
||||
|
||||
source_ids = [cst.id for cst in items]
|
||||
new_constituents = deepcopy(items)
|
||||
for cst in new_constituents:
|
||||
cst.pk = None
|
||||
cst.schema = self.model
|
||||
cst.order = position
|
||||
cst.alias = mapping[cst.alias]
|
||||
cst.apply_mapping(mapping)
|
||||
if mapping_alias:
|
||||
cst.alias = mapping_alias[cst.alias]
|
||||
cst.apply_mapping(mapping_alias)
|
||||
position = position + 1
|
||||
|
||||
new_cst = Constituenta.objects.bulk_create(result)
|
||||
new_constituents = Constituenta.objects.bulk_create(new_constituents)
|
||||
|
||||
# TODO: duplicate attributions
|
||||
mapping_id: dict[int, int] = {source_ids[i]: new_constituents[i].id for i in range(len(source_ids))}
|
||||
attributions = list(Attribution.objects.filter(container__in=source_ids, attribute__in=source_ids))
|
||||
for attr in attributions:
|
||||
attr.pk = None
|
||||
attr.container_id = mapping_id[attr.container_id]
|
||||
attr.attribute_id = mapping_id[attr.attribute_id]
|
||||
|
||||
self.cache.insert_multi(new_cst)
|
||||
return result
|
||||
Attribution.objects.bulk_create(attributions)
|
||||
|
||||
self.cache.insert_multi(new_constituents)
|
||||
return new_constituents
|
||||
|
||||
# pylint: disable=too-many-branches
|
||||
def update_cst(self, target: int, data: dict) -> dict:
|
||||
|
|
|
|||
|
|
@ -3,5 +3,5 @@
|
|||
from .Attribution import Attribution
|
||||
from .Constituenta import Constituenta, CstType, extract_globals, replace_entities, replace_globals
|
||||
from .OrderManager import OrderManager
|
||||
from .RSForm import DELETED_ALIAS, INSERT_LAST, RSForm
|
||||
from .RSForm import DELETED_ALIAS, RSForm
|
||||
from .RSFormCached import RSFormCached
|
||||
|
|
|
|||
|
|
@ -436,7 +436,7 @@ class InlineSynthesisSerializer(StrictSerializer):
|
|||
''' Serializer: Inline synthesis operation input. '''
|
||||
receiver = PKField(many=False, queryset=LibraryItem.objects.all().only('owner_id'))
|
||||
source = PKField(many=False, queryset=LibraryItem.objects.all().only('owner_id')) # type: ignore
|
||||
items = PKField(many=True, queryset=Constituenta.objects.all())
|
||||
items = PKField(many=True, queryset=Constituenta.objects.all().only('schema_id'))
|
||||
substitutions = serializers.ListField(
|
||||
child=SubstitutionSerializerBase()
|
||||
)
|
||||
|
|
|
|||
|
|
@ -732,25 +732,24 @@ def inline_synthesis(request: Request) -> HttpResponse:
|
|||
serializer.is_valid(raise_exception=True)
|
||||
|
||||
receiver = m.RSFormCached(serializer.validated_data['receiver'])
|
||||
items = cast(list[m.Constituenta], serializer.validated_data['items'])
|
||||
if not items:
|
||||
source = cast(LibraryItem, serializer.validated_data['source'])
|
||||
items = list(m.Constituenta.objects.filter(schema=source).order_by('order'))
|
||||
target_cst = cast(list[m.Constituenta], serializer.validated_data['items'])
|
||||
source = cast(LibraryItem, serializer.validated_data['source'])
|
||||
target_ids = [item.pk for item in target_cst] if target_cst else None
|
||||
|
||||
with transaction.atomic():
|
||||
new_items = receiver.insert_copy(items)
|
||||
PropagationFacade.after_create_cst(receiver, new_items)
|
||||
new_items = receiver.insert_from(source.pk, target_ids)
|
||||
target_ids = [item[0].pk for item in new_items]
|
||||
mapping_ids = {cst.pk: new_cst for (cst, new_cst) in new_items}
|
||||
PropagationFacade.after_create_cst(receiver, [item[1] for item in new_items])
|
||||
|
||||
substitutions: list[tuple[m.Constituenta, m.Constituenta]] = []
|
||||
for substitution in serializer.validated_data['substitutions']:
|
||||
original = cast(m.Constituenta, substitution['original'])
|
||||
replacement = cast(m.Constituenta, substitution['substitution'])
|
||||
if original in items:
|
||||
index = next(i for (i, cst) in enumerate(items) if cst.pk == original.pk)
|
||||
original = new_items[index]
|
||||
if original.pk in target_ids:
|
||||
original = mapping_ids[original.pk]
|
||||
else:
|
||||
index = next(i for (i, cst) in enumerate(items) if cst.pk == replacement.pk)
|
||||
replacement = new_items[index]
|
||||
replacement = mapping_ids[replacement.pk]
|
||||
substitutions.append((original, replacement))
|
||||
|
||||
PropagationFacade.before_substitute(receiver.model.pk, substitutions)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user