mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 13:00:39 +03:00
Refactor insert functions for RSForm
Add option to insert copy constituents
This commit is contained in:
parent
a6822e2f2b
commit
68b6891ae4
|
@ -10,7 +10,7 @@ def renameTrivial(name: str):
|
|||
def substituteTrivial(name: str):
|
||||
return f'Отождествление конституенты с собой не корректно: {name}'
|
||||
|
||||
def renameTaken(name: str):
|
||||
def aliasTaken(name: str):
|
||||
return f'Имя уже используется: {name}'
|
||||
|
||||
def pyconceptFailure():
|
||||
|
@ -25,7 +25,7 @@ def libraryTypeUnexpected():
|
|||
def exteorFileVersionNotSupported():
|
||||
return 'Некорректный формат файла Экстеор. Сохраните файл в новой версии'
|
||||
|
||||
def positionNegative():
|
||||
def invalidPosition():
|
||||
return 'Invalid position: should be positive integer'
|
||||
|
||||
def constituentaNoStructure():
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
''' Models: RSForm API. '''
|
||||
from typing import Iterable, Optional, cast
|
||||
from typing import Dict, Iterable, Optional, cast
|
||||
|
||||
from django.db import transaction
|
||||
from django.db.models import QuerySet
|
||||
|
@ -15,6 +15,9 @@ from ..graph import Graph
|
|||
from .. import messages as msg
|
||||
|
||||
|
||||
_INSERT_LAST: int = -1
|
||||
|
||||
|
||||
class RSForm:
|
||||
''' RSForm is math form of conceptual schema. '''
|
||||
def __init__(self, item: LibraryItem):
|
||||
|
@ -87,17 +90,13 @@ class RSForm:
|
|||
return result
|
||||
|
||||
@transaction.atomic
|
||||
def insert_at(self, position: int, alias: str, insert_type: CstType) -> 'Constituenta':
|
||||
def insert_new(self, alias: str, insert_type: CstType, position: int = _INSERT_LAST) -> Constituenta:
|
||||
''' Insert new constituenta at given position.
|
||||
All following constituents order is shifted by 1 position. '''
|
||||
if position <= 0:
|
||||
raise ValidationError(msg.positionNegative())
|
||||
if self.constituents().filter(alias=alias).exists():
|
||||
raise ValidationError(msg.renameTaken(alias))
|
||||
currentSize = self.constituents().count()
|
||||
position = max(1, min(position, currentSize + 1))
|
||||
raise ValidationError(msg.aliasTaken(alias))
|
||||
position = self._get_insert_position(position)
|
||||
self._shift_positions(position, 1)
|
||||
|
||||
result = Constituenta.objects.create(
|
||||
schema=self.item,
|
||||
order=position,
|
||||
|
@ -109,25 +108,49 @@ class RSForm:
|
|||
return result
|
||||
|
||||
@transaction.atomic
|
||||
def insert_last(self, alias: str, insert_type: CstType) -> 'Constituenta':
|
||||
''' Insert new constituenta at last position. '''
|
||||
if self.constituents().filter(alias=alias).exists():
|
||||
raise ValidationError(msg.renameTaken(alias))
|
||||
position = 1
|
||||
if self.constituents().exists():
|
||||
position += self.constituents().count()
|
||||
result = Constituenta.objects.create(
|
||||
schema=self.item,
|
||||
order=position,
|
||||
alias=alias,
|
||||
cst_type=insert_type
|
||||
)
|
||||
def insert_copy(self, items: list[Constituenta], position: int = _INSERT_LAST) -> list[Constituenta]:
|
||||
''' Insert copy of target constituents updating references. '''
|
||||
count = len(items)
|
||||
if count == 0:
|
||||
return []
|
||||
|
||||
position = self._get_insert_position(position)
|
||||
self._shift_positions(position, count)
|
||||
|
||||
indices: Dict[str, int] = {}
|
||||
for (value, _) in CstType.choices:
|
||||
indices[value] = self.get_max_index(cast(CstType, value))
|
||||
|
||||
mapping: Dict[str, str] = {}
|
||||
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[cst.alias] = newAlias
|
||||
|
||||
result: list[Constituenta] = []
|
||||
for cst in items:
|
||||
newCst = Constituenta.objects.create(
|
||||
schema=self.item,
|
||||
order=position,
|
||||
alias=mapping[cst.alias],
|
||||
cst_type=cst.cst_type,
|
||||
convention=cst.convention,
|
||||
term_raw=cst.term_raw,
|
||||
term_resolved=cst.term_resolved,
|
||||
term_forms=cst.term_forms,
|
||||
definition_raw=cst.definition_raw,
|
||||
definition_formal=cst.definition_formal,
|
||||
definition_resolved=cst.definition_resolved
|
||||
)
|
||||
newCst.apply_mapping(mapping)
|
||||
newCst.save()
|
||||
position = position + 1
|
||||
result.append(newCst)
|
||||
self.item.save()
|
||||
result.refresh_from_db()
|
||||
return result
|
||||
|
||||
@transaction.atomic
|
||||
def move_cst(self, listCst: list['Constituenta'], target: int):
|
||||
def move_cst(self, listCst: list[Constituenta], target: int):
|
||||
''' Move list of constituents to specific position '''
|
||||
count_moved = 0
|
||||
count_top = 0
|
||||
|
@ -159,7 +182,7 @@ class RSForm:
|
|||
self.item.save()
|
||||
|
||||
@transaction.atomic
|
||||
def create_cst(self, data: dict, insert_after: Optional[str]=None) -> 'Constituenta':
|
||||
def create_cst(self, data: dict, insert_after: Optional[str]=None) -> Constituenta:
|
||||
''' Create new cst from data. '''
|
||||
resolver = self.resolver()
|
||||
cst = self._insert_new(data, insert_after)
|
||||
|
@ -182,8 +205,8 @@ class RSForm:
|
|||
@transaction.atomic
|
||||
def substitute(
|
||||
self,
|
||||
original: 'Constituenta',
|
||||
substitution: 'Constituenta',
|
||||
original: Constituenta,
|
||||
substitution: Constituenta,
|
||||
transfer_term: bool
|
||||
):
|
||||
''' Execute constituenta substitution. '''
|
||||
|
@ -296,6 +319,22 @@ class RSForm:
|
|||
cst.order += shift
|
||||
Constituenta.objects.bulk_update(update_list, ['order'])
|
||||
|
||||
def _get_last_position(self):
|
||||
if self.constituents().exists():
|
||||
return self.constituents().count()
|
||||
else:
|
||||
return 0
|
||||
|
||||
def _get_insert_position(self, position: int) -> int:
|
||||
if position <= 0 and position != _INSERT_LAST:
|
||||
raise ValidationError(msg.invalidPosition())
|
||||
lastPosition = self._get_last_position()
|
||||
if position == _INSERT_LAST:
|
||||
position = lastPosition + 1
|
||||
else:
|
||||
position = max(1, min(position, lastPosition + 1))
|
||||
return position
|
||||
|
||||
@transaction.atomic
|
||||
def _reset_order(self):
|
||||
order = 1
|
||||
|
@ -305,12 +344,12 @@ class RSForm:
|
|||
cst.save()
|
||||
order += 1
|
||||
|
||||
def _insert_new(self, data: dict, insert_after: Optional[str]=None) -> 'Constituenta':
|
||||
def _insert_new(self, data: dict, insert_after: Optional[str]=None) -> Constituenta:
|
||||
if insert_after is not None:
|
||||
cst_after = Constituenta.objects.get(pk=insert_after)
|
||||
return self.insert_at(cst_after.order + 1, data['alias'], data['cst_type'])
|
||||
return self.insert_new(data['alias'], data['cst_type'], cst_after.order + 1)
|
||||
else:
|
||||
return self.insert_last(data['alias'], data['cst_type'])
|
||||
return self.insert_new(data['alias'], data['cst_type'])
|
||||
|
||||
def _term_graph(self) -> Graph:
|
||||
result = Graph()
|
||||
|
|
|
@ -260,7 +260,7 @@ class CstRenameSerializer(serializers.Serializer):
|
|||
})
|
||||
if RSForm(schema).constituents().filter(alias=new_alias).exists():
|
||||
raise serializers.ValidationError({
|
||||
'alias': msg.renameTaken(new_alias)
|
||||
'alias': msg.aliasTaken(new_alias)
|
||||
})
|
||||
return attrs
|
||||
|
||||
|
|
|
@ -47,14 +47,14 @@ class TestRSFormViewset(APITestCase):
|
|||
|
||||
def test_contents(self):
|
||||
schema = RSForm.create(title='Title1')
|
||||
schema.insert_last(alias='X1', insert_type=CstType.BASE)
|
||||
schema.insert_new(alias='X1', insert_type=CstType.BASE)
|
||||
response = self.client.get(f'/api/rsforms/{schema.item.id}/contents')
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
|
||||
def test_details(self):
|
||||
schema = RSForm.create(title='Test', owner=self.user)
|
||||
x1 = schema.insert_at(1, 'X1', CstType.BASE)
|
||||
x2 = schema.insert_at(2, 'X2', CstType.BASE)
|
||||
x1 = schema.insert_new('X1', CstType.BASE, 1)
|
||||
x2 = schema.insert_new('X2', CstType.BASE, 2)
|
||||
x1.term_raw = 'человек'
|
||||
x1.term_resolved = 'человек'
|
||||
x2.term_raw = '@{X1|plur}'
|
||||
|
@ -78,7 +78,7 @@ class TestRSFormViewset(APITestCase):
|
|||
|
||||
def test_check(self):
|
||||
schema = RSForm.create(title='Test')
|
||||
schema.insert_at(1, 'X1', CstType.BASE)
|
||||
schema.insert_new('X1', CstType.BASE, 1)
|
||||
data = {'expression': 'X1=X1'}
|
||||
response = self.client.post(
|
||||
f'/api/rsforms/{schema.item.id}/check',
|
||||
|
@ -99,7 +99,7 @@ class TestRSFormViewset(APITestCase):
|
|||
|
||||
def test_resolve(self):
|
||||
schema = RSForm.create(title='Test')
|
||||
x1 = schema.insert_at(1, 'X1', CstType.BASE)
|
||||
x1 = schema.insert_new('X1', CstType.BASE, 1)
|
||||
x1.term_resolved = 'синий слон'
|
||||
x1.save()
|
||||
data = {'text': '@{1|редкий} @{X1|plur,datv}'}
|
||||
|
@ -139,7 +139,7 @@ class TestRSFormViewset(APITestCase):
|
|||
|
||||
def test_export_trs(self):
|
||||
schema = RSForm.create(title='Test')
|
||||
schema.insert_at(1, 'X1', CstType.BASE)
|
||||
schema.insert_new('X1', CstType.BASE, 1)
|
||||
response = self.client.get(f'/api/rsforms/{schema.item.id}/export-trs')
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(response.headers['Content-Disposition'], 'attachment; filename=Schema.trs')
|
||||
|
|
|
@ -170,17 +170,17 @@ class TestRSForm(TestCase):
|
|||
|
||||
def test_insert_at(self):
|
||||
schema = RSForm.create(title='Test')
|
||||
cst1 = schema.insert_at(1, 'X1', CstType.BASE)
|
||||
cst1 = schema.insert_new('X1', CstType.BASE, 1)
|
||||
self.assertEqual(cst1.order, 1)
|
||||
self.assertEqual(cst1.schema, schema.item)
|
||||
|
||||
cst2 = schema.insert_at(1, 'X2', CstType.BASE)
|
||||
cst2 = schema.insert_new('X2', CstType.BASE, 1)
|
||||
cst1.refresh_from_db()
|
||||
self.assertEqual(cst2.order, 1)
|
||||
self.assertEqual(cst2.schema, schema.item)
|
||||
self.assertEqual(cst1.order, 2)
|
||||
|
||||
cst3 = schema.insert_at(4, 'X3', CstType.BASE)
|
||||
cst3 = schema.insert_new('X3', CstType.BASE, 4)
|
||||
cst2.refresh_from_db()
|
||||
cst1.refresh_from_db()
|
||||
self.assertEqual(cst3.order, 3)
|
||||
|
@ -188,7 +188,7 @@ class TestRSForm(TestCase):
|
|||
self.assertEqual(cst2.order, 1)
|
||||
self.assertEqual(cst1.order, 2)
|
||||
|
||||
cst4 = schema.insert_at(3, 'X4', CstType.BASE)
|
||||
cst4 = schema.insert_new('X4', CstType.BASE, 3)
|
||||
cst3.refresh_from_db()
|
||||
cst2.refresh_from_db()
|
||||
cst1.refresh_from_db()
|
||||
|
@ -201,40 +201,40 @@ class TestRSForm(TestCase):
|
|||
def test_insert_at_invalid_position(self):
|
||||
schema = RSForm.create(title='Test')
|
||||
with self.assertRaises(ValidationError):
|
||||
schema.insert_at(0, 'X5', CstType.BASE)
|
||||
schema.insert_new('X5', CstType.BASE, 0)
|
||||
|
||||
def test_insert_at_invalid_alias(self):
|
||||
schema = RSForm.create(title='Test')
|
||||
schema.insert_at(1, 'X1', CstType.BASE)
|
||||
schema.insert_new('X1', CstType.BASE, 1)
|
||||
with self.assertRaises(ValidationError):
|
||||
schema.insert_at(2, 'X1', CstType.BASE)
|
||||
schema.insert_new('X1', CstType.BASE, 2)
|
||||
|
||||
def test_insert_at_reorder(self):
|
||||
schema = RSForm.create(title='Test')
|
||||
schema.insert_at(1, 'X1', CstType.BASE)
|
||||
d1 = schema.insert_at(2, 'D1', CstType.TERM)
|
||||
d2 = schema.insert_at(1, 'D2', CstType.TERM)
|
||||
schema.insert_new('X1', CstType.BASE, 1)
|
||||
d1 = schema.insert_new('D1', CstType.TERM, 2)
|
||||
d2 = schema.insert_new('D2', CstType.TERM, 1)
|
||||
d1.refresh_from_db()
|
||||
self.assertEqual(d1.order, 3)
|
||||
self.assertEqual(d2.order, 1)
|
||||
|
||||
x2 = schema.insert_at(4, 'X2', CstType.BASE)
|
||||
x2 = schema.insert_new('X2', CstType.BASE, 4)
|
||||
self.assertEqual(x2.order, 4)
|
||||
|
||||
def test_insert_last(self):
|
||||
schema = RSForm.create(title='Test')
|
||||
cst1 = schema.insert_last('X1', CstType.BASE)
|
||||
cst1 = schema.insert_new('X1', CstType.BASE)
|
||||
self.assertEqual(cst1.order, 1)
|
||||
self.assertEqual(cst1.schema, schema.item)
|
||||
|
||||
cst2 = schema.insert_last('X2', CstType.BASE)
|
||||
cst2 = schema.insert_new('X2', CstType.BASE)
|
||||
self.assertEqual(cst2.order, 2)
|
||||
self.assertEqual(cst2.schema, schema.item)
|
||||
self.assertEqual(cst1.order, 1)
|
||||
|
||||
def test_create_cst_resolve(self):
|
||||
schema = RSForm.create(title='Test')
|
||||
cst1 = schema.insert_last('X1', CstType.BASE)
|
||||
cst1 = schema.insert_new('X1', CstType.BASE)
|
||||
cst1.term_raw = '@{X2|datv}'
|
||||
cst1.definition_raw = '@{X1|datv} @{X2|datv}'
|
||||
cst1.save()
|
||||
|
@ -250,11 +250,40 @@ class TestRSForm(TestCase):
|
|||
self.assertEqual(cst2.term_resolved, 'слон')
|
||||
self.assertEqual(cst2.definition_resolved, 'слонам слоны')
|
||||
|
||||
def test_insert_copy(self):
|
||||
schema = RSForm.create(title='Test')
|
||||
x1 = schema.insert_new('X10', CstType.BASE)
|
||||
s1 = schema.insert_new('S11', CstType.STRUCTURED)
|
||||
x1.convention = 'Test'
|
||||
s1.definition_formal = x1.alias
|
||||
s1.definition_raw = '@{X10|plur}'
|
||||
x1.save()
|
||||
s1.save()
|
||||
|
||||
result = schema.insert_copy([s1, x1], 2)
|
||||
self.assertEqual(len(result), 2)
|
||||
|
||||
s1.refresh_from_db()
|
||||
self.assertEqual(s1.order, 4)
|
||||
|
||||
x2 = result[1]
|
||||
self.assertEqual(x2.order, 3)
|
||||
self.assertEqual(x2.alias, 'X11')
|
||||
self.assertEqual(x2.cst_type, CstType.BASE)
|
||||
self.assertEqual(x2.convention, x1.convention)
|
||||
|
||||
s2 = result[0]
|
||||
self.assertEqual(s2.order, 2)
|
||||
self.assertEqual(s2.alias, 'S12')
|
||||
self.assertEqual(s2.cst_type, CstType.STRUCTURED)
|
||||
self.assertEqual(s2.definition_formal, x2.alias)
|
||||
self.assertEqual(s2.definition_raw, '@{X11|plur}')
|
||||
|
||||
def test_apply_mapping(self):
|
||||
schema = RSForm.create(title='Test')
|
||||
x1 = schema.insert_last('X1', CstType.BASE)
|
||||
x2 = schema.insert_last('X11', CstType.BASE)
|
||||
d1 = schema.insert_last('D1', CstType.TERM)
|
||||
x1 = schema.insert_new('X1', CstType.BASE)
|
||||
x2 = schema.insert_new('X11', CstType.BASE)
|
||||
d1 = schema.insert_new('D1', CstType.TERM)
|
||||
d1.definition_formal = 'X1 = X11 = X2'
|
||||
d1.definition_raw = '@{X11|sing}'
|
||||
d1.convention = 'X1'
|
||||
|
@ -272,9 +301,9 @@ class TestRSForm(TestCase):
|
|||
|
||||
def test_substitute(self):
|
||||
schema = RSForm.create(title='Test')
|
||||
x1 = schema.insert_last('X1', CstType.BASE)
|
||||
x2 = schema.insert_last('X2', CstType.BASE)
|
||||
d1 = schema.insert_last('D1', CstType.TERM)
|
||||
x1 = schema.insert_new('X1', CstType.BASE)
|
||||
x2 = schema.insert_new('X2', CstType.BASE)
|
||||
d1 = schema.insert_new('D1', CstType.TERM)
|
||||
d1.definition_formal = x1.alias
|
||||
d1.save()
|
||||
x1.term_raw = 'Test'
|
||||
|
@ -291,10 +320,10 @@ class TestRSForm(TestCase):
|
|||
|
||||
def test_move_cst(self):
|
||||
schema = RSForm.create(title='Test')
|
||||
x1 = schema.insert_last('X1', CstType.BASE)
|
||||
x2 = schema.insert_last('X2', CstType.BASE)
|
||||
d1 = schema.insert_last('D1', CstType.TERM)
|
||||
d2 = schema.insert_last('D2', CstType.TERM)
|
||||
x1 = schema.insert_new('X1', CstType.BASE)
|
||||
x2 = schema.insert_new('X2', CstType.BASE)
|
||||
d1 = schema.insert_new('D1', CstType.TERM)
|
||||
d2 = schema.insert_new('D2', CstType.TERM)
|
||||
schema.move_cst([x2, d2], 1)
|
||||
x1.refresh_from_db()
|
||||
x2.refresh_from_db()
|
||||
|
@ -307,8 +336,8 @@ class TestRSForm(TestCase):
|
|||
|
||||
def test_move_cst_down(self):
|
||||
schema = RSForm.create(title='Test')
|
||||
x1 = schema.insert_last('X1', CstType.BASE)
|
||||
x2 = schema.insert_last('X2', CstType.BASE)
|
||||
x1 = schema.insert_new('X1', CstType.BASE)
|
||||
x2 = schema.insert_new('X2', CstType.BASE)
|
||||
schema.move_cst([x1], 2)
|
||||
x1.refresh_from_db()
|
||||
x2.refresh_from_db()
|
||||
|
@ -317,9 +346,9 @@ class TestRSForm(TestCase):
|
|||
|
||||
def test_reset_aliases(self):
|
||||
schema = RSForm.create(title='Test')
|
||||
x1 = schema.insert_last('X11', CstType.BASE)
|
||||
x2 = schema.insert_last('X21', CstType.BASE)
|
||||
d1 = schema.insert_last('D11', CstType.TERM)
|
||||
x1 = schema.insert_new('X11', CstType.BASE)
|
||||
x2 = schema.insert_new('X21', CstType.BASE)
|
||||
d1 = schema.insert_new('D11', CstType.TERM)
|
||||
x1.term_raw = 'человек'
|
||||
x1.term_resolved = 'человек'
|
||||
d1.convention = 'D11 - cool'
|
||||
|
|
Loading…
Reference in New Issue
Block a user