F: Implement attribution merging

This commit is contained in:
Ivan 2025-10-27 16:15:08 +03:00
parent 5f767ea26f
commit ac266f6880
3 changed files with 88 additions and 1 deletions

View File

@ -12,6 +12,7 @@ from shared import messages as msg
from ..graph import Graph
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
@ -271,6 +272,25 @@ class RSForm:
mapping[original.alias] = substitution.alias
deleted.append(original.pk)
replacements.append(substitution.pk)
attributions = list(Attribution.objects.filter(container__schema=self.model))
if attributions:
orig_to_sub = {original.pk: substitution.pk for original, substitution in substitutions}
orig_pks = set(orig_to_sub.keys())
for attr in attributions:
if attr.container_id not in orig_pks and attr.attribute_id not in orig_pks:
continue
container_id = orig_to_sub.get(attr.container_id)
container_id = container_id if container_id is not None else attr.container_id
attr_id = orig_to_sub.get(attr.attribute_id)
attr_id = attr_id if attr_id is not None else attr.attribute_id
if not any(a.container_id == container_id and a.attribute_id == attr_id for a in attributions):
attr.attribute_id = attr_id
attr.container_id = container_id
attr.save()
Constituenta.objects.filter(pk__in=deleted).delete()
cst_list = Constituenta.objects.filter(schema=self.model).only(
'alias', 'cst_type', 'definition_formal',

View File

@ -12,6 +12,7 @@ from apps.library.models import LibraryItem, LibraryItemType
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
@ -149,6 +150,9 @@ class RSFormCached:
position = position + 1
new_cst = Constituenta.objects.bulk_create(result)
# TODO: duplicate attributions
self.cache.insert_multi(new_cst)
return result
@ -233,6 +237,25 @@ class RSFormCached:
mapping[original.alias] = substitution.alias
deleted.append(original)
replacements.append(substitution.pk)
attributions = list(Attribution.objects.filter(container__schema=self.model))
if attributions:
orig_to_sub = {original.pk: substitution.pk for original, substitution in substitutions}
orig_pks = set(orig_to_sub.keys())
for attr in attributions:
if attr.container_id not in orig_pks and attr.attribute_id not in orig_pks:
continue
container_id = orig_to_sub.get(attr.container_id)
container_id = container_id if container_id is not None else attr.container_id
attr_id = orig_to_sub.get(attr.attribute_id)
attr_id = attr_id if attr_id is not None else attr.attribute_id
if not any(a.container_id == container_id and a.attribute_id == attr_id for a in attributions):
attr.attribute_id = attr_id
attr.container_id = container_id
attr.save()
self.cache.remove_multi(deleted)
Constituenta.objects.filter(pk__in=[cst.pk for cst in deleted]).delete()
RSForm.save_order(self.cache.constituents)

View File

@ -238,11 +238,55 @@ class TestRSFormViewset(EndpointTester):
'substitution': d2.pk
}
]}
response = self.executeOK(data, item=self.owned_id)
self.executeOK(data, item=self.owned_id)
d3.refresh_from_db()
self.assertEqual(d3.definition_formal, r'D1 \ D2')
@decl_endpoint('/api/rsforms/{item}/substitute', method='patch')
def test_substitute_with_attributions(self):
self.set_params(item=self.owned_id)
# Create two base items
x1 = self.owned.insert_last('X1')
x2 = self.owned.insert_last('X2')
# Create two attributes to be attributions
a1 = self.owned.insert_last('A1', cst_type=CstType.BASE)
a2 = self.owned.insert_last('A2', cst_type=CstType.BASE)
# Create attributions: X1 -> A1, X2 -> A2
Attribution = self.owned.constituentsQ().model._meta.apps.get_model('rsform', 'Attribution')
Attribution.objects.create(container=x1, attribute=a1)
Attribution.objects.create(container=x2, attribute=a2)
# Substitute x1 with x2
data = {
'substitutions': [{
'original': x1.pk,
'substitution': x2.pk
}]
}
self.executeOK(data, item=self.owned_id)
# Fetch updated attributions
attributions = Attribution.objects.filter(
container__in=[x1.pk, x2.pk],
attribute__in=[a1.pk, a2.pk]
)
self.assertEqual(len(attributions), 2)
# Confirm the attribution with container originally x1 is now x2, and there are no duplicates
containers = set()
attributes = set()
for attr in attributions:
containers.add(attr.container_id)
attributes.add(attr.attribute_id)
self.assertIn(x2.pk, containers)
self.assertIn(a1.pk, attributes)
self.assertIn(a2.pk, attributes)
@decl_endpoint('/api/rsforms/{item}/create-cst', method='post')
def test_create_constituenta_data(self):
data = {