mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 21:10:38 +03:00
R: Move update-cst to RSForm
This commit is contained in:
parent
c97e24dc63
commit
727c1b3ab6
|
@ -95,10 +95,6 @@ class Constituenta(Model):
|
||||||
verbose_name = 'Конституента'
|
verbose_name = 'Конституента'
|
||||||
verbose_name_plural = 'Конституенты'
|
verbose_name_plural = 'Конституенты'
|
||||||
|
|
||||||
def get_absolute_url(self):
|
|
||||||
''' URL access. '''
|
|
||||||
return reverse('constituenta-detail', kwargs={'pk': self.pk})
|
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return f'{self.alias}'
|
return f'{self.alias}'
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,7 @@ class CstSerializer(serializers.ModelSerializer):
|
||||||
''' serializer metadata. '''
|
''' serializer metadata. '''
|
||||||
model = Constituenta
|
model = Constituenta
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
read_only_fields = ('id', 'order', 'alias', 'cst_type', 'definition_resolved', 'term_resolved')
|
read_only_fields = ('id', 'schema', 'order', 'alias', 'cst_type', 'definition_resolved', 'term_resolved')
|
||||||
|
|
||||||
def update(self, instance: Constituenta, validated_data) -> Constituenta:
|
def update(self, instance: Constituenta, validated_data) -> Constituenta:
|
||||||
data = validated_data # Note: use alias for better code readability
|
data = validated_data # Note: use alias for better code readability
|
||||||
|
|
|
@ -20,12 +20,6 @@ class TestConstituenta(TestCase):
|
||||||
self.assertEqual(str(cst), testStr)
|
self.assertEqual(str(cst), testStr)
|
||||||
|
|
||||||
|
|
||||||
def test_url(self):
|
|
||||||
testStr = 'X1'
|
|
||||||
cst = Constituenta.objects.create(alias=testStr, schema=self.schema1.model, order=1, convention='Test')
|
|
||||||
self.assertEqual(cst.get_absolute_url(), f'/api/constituents/{cst.pk}')
|
|
||||||
|
|
||||||
|
|
||||||
def test_order_not_null(self):
|
def test_order_not_null(self):
|
||||||
with self.assertRaises(IntegrityError):
|
with self.assertRaises(IntegrityError):
|
||||||
Constituenta.objects.create(alias='X1', schema=self.schema1.model)
|
Constituenta.objects.create(alias='X1', schema=self.schema1.model)
|
||||||
|
|
|
@ -208,11 +208,11 @@ class TestRSForm(TestCase):
|
||||||
definition_formal=x1.alias
|
definition_formal=x1.alias
|
||||||
)
|
)
|
||||||
|
|
||||||
self.schema.substitute(x1, x2, True)
|
self.schema.substitute(x1, x2)
|
||||||
x2.refresh_from_db()
|
x2.refresh_from_db()
|
||||||
d1.refresh_from_db()
|
d1.refresh_from_db()
|
||||||
self.assertEqual(self.schema.constituents().count(), 2)
|
self.assertEqual(self.schema.constituents().count(), 2)
|
||||||
self.assertEqual(x2.term_raw, 'Test')
|
self.assertEqual(x2.term_raw, 'Test2')
|
||||||
self.assertEqual(d1.definition_formal, x2.alias)
|
self.assertEqual(d1.definition_formal, x2.alias)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
''' Tests for REST API. '''
|
''' Tests for REST API. '''
|
||||||
from .t_cctext import *
|
from .t_cctext import *
|
||||||
from .t_constituents import *
|
|
||||||
from .t_operations import *
|
|
||||||
from .t_rsforms import *
|
from .t_rsforms import *
|
||||||
from .t_rslang import *
|
from .t_rslang import *
|
||||||
|
|
|
@ -1,102 +0,0 @@
|
||||||
''' Testing API: Constituents. '''
|
|
||||||
from apps.rsform.models import Constituenta, CstType, RSForm
|
|
||||||
from shared.EndpointTester import EndpointTester, decl_endpoint
|
|
||||||
|
|
||||||
|
|
||||||
class TestConstituentaAPI(EndpointTester):
|
|
||||||
''' Testing Constituenta view. '''
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
super().setUp()
|
|
||||||
self.rsform_owned = RSForm.create(title='Test', alias='T1', owner=self.user)
|
|
||||||
self.rsform_unowned = RSForm.create(title='Test2', alias='T2')
|
|
||||||
self.cst1 = Constituenta.objects.create(
|
|
||||||
alias='X1',
|
|
||||||
cst_type=CstType.BASE,
|
|
||||||
schema=self.rsform_owned.model,
|
|
||||||
order=1,
|
|
||||||
convention='Test',
|
|
||||||
term_raw='Test1',
|
|
||||||
term_resolved='Test1R',
|
|
||||||
term_forms=[{'text': 'form1', 'tags': 'sing,datv'}])
|
|
||||||
self.cst2 = Constituenta.objects.create(
|
|
||||||
alias='X2',
|
|
||||||
cst_type=CstType.BASE,
|
|
||||||
schema=self.rsform_unowned.model,
|
|
||||||
order=1,
|
|
||||||
convention='Test1',
|
|
||||||
term_raw='Test2',
|
|
||||||
term_resolved='Test2R'
|
|
||||||
)
|
|
||||||
self.cst3 = Constituenta.objects.create(
|
|
||||||
alias='X3',
|
|
||||||
schema=self.rsform_owned.model,
|
|
||||||
order=2,
|
|
||||||
term_raw='Test3',
|
|
||||||
term_resolved='Test3',
|
|
||||||
definition_raw='Test1',
|
|
||||||
definition_resolved='Test2'
|
|
||||||
)
|
|
||||||
self.invalid_cst = self.cst3.pk + 1337
|
|
||||||
|
|
||||||
|
|
||||||
@decl_endpoint('/api/constituents/{item}', method='get')
|
|
||||||
def test_retrieve(self):
|
|
||||||
self.executeNotFound(item=self.invalid_cst)
|
|
||||||
response = self.executeOK(item=self.cst1.pk)
|
|
||||||
self.assertEqual(response.data['alias'], self.cst1.alias)
|
|
||||||
self.assertEqual(response.data['convention'], self.cst1.convention)
|
|
||||||
|
|
||||||
|
|
||||||
@decl_endpoint('/api/constituents/{item}', method='patch')
|
|
||||||
def test_partial_update(self):
|
|
||||||
data = {'convention': 'tt'}
|
|
||||||
self.executeForbidden(data=data, item=self.cst2.pk)
|
|
||||||
|
|
||||||
self.logout()
|
|
||||||
self.executeForbidden(data=data, item=self.cst1.pk)
|
|
||||||
|
|
||||||
self.login()
|
|
||||||
response = self.executeOK(data=data, item=self.cst1.pk)
|
|
||||||
self.cst1.refresh_from_db()
|
|
||||||
self.assertEqual(response.data['convention'], 'tt')
|
|
||||||
self.assertEqual(self.cst1.convention, 'tt')
|
|
||||||
|
|
||||||
self.executeOK(data=data, item=self.cst1.pk)
|
|
||||||
|
|
||||||
|
|
||||||
@decl_endpoint('/api/constituents/{item}', method='patch')
|
|
||||||
def test_update_resolved_no_refs(self):
|
|
||||||
data = {
|
|
||||||
'term_raw': 'New term',
|
|
||||||
'definition_raw': 'New def'
|
|
||||||
}
|
|
||||||
response = self.executeOK(data=data, item=self.cst3.pk)
|
|
||||||
self.cst3.refresh_from_db()
|
|
||||||
self.assertEqual(response.data['term_resolved'], 'New term')
|
|
||||||
self.assertEqual(self.cst3.term_resolved, 'New term')
|
|
||||||
self.assertEqual(response.data['definition_resolved'], 'New def')
|
|
||||||
self.assertEqual(self.cst3.definition_resolved, 'New def')
|
|
||||||
|
|
||||||
|
|
||||||
@decl_endpoint('/api/constituents/{item}', method='patch')
|
|
||||||
def test_update_resolved_refs(self):
|
|
||||||
data = {
|
|
||||||
'term_raw': '@{X1|nomn,sing}',
|
|
||||||
'definition_raw': '@{X1|nomn,sing} @{X1|sing,datv}'
|
|
||||||
}
|
|
||||||
response = self.executeOK(data=data, item=self.cst3.pk)
|
|
||||||
self.cst3.refresh_from_db()
|
|
||||||
self.assertEqual(self.cst3.term_resolved, self.cst1.term_resolved)
|
|
||||||
self.assertEqual(response.data['term_resolved'], self.cst1.term_resolved)
|
|
||||||
self.assertEqual(self.cst3.definition_resolved, f'{self.cst1.term_resolved} form1')
|
|
||||||
self.assertEqual(response.data['definition_resolved'], f'{self.cst1.term_resolved} form1')
|
|
||||||
|
|
||||||
|
|
||||||
@decl_endpoint('/api/constituents/{item}', method='patch')
|
|
||||||
def test_readonly_cst_fields(self):
|
|
||||||
data = {'alias': 'X33', 'order': 10}
|
|
||||||
response = self.executeOK(data=data, item=self.cst1.pk)
|
|
||||||
self.assertEqual(response.data['alias'], 'X1')
|
|
||||||
self.assertEqual(response.data['alias'], self.cst1.alias)
|
|
||||||
self.assertEqual(response.data['order'], self.cst1.order)
|
|
|
@ -1,80 +0,0 @@
|
||||||
''' Testing API: Operations. '''
|
|
||||||
from apps.rsform.models import Constituenta, CstType, RSForm
|
|
||||||
from shared.EndpointTester import EndpointTester, decl_endpoint
|
|
||||||
|
|
||||||
|
|
||||||
class TestInlineSynthesis(EndpointTester):
|
|
||||||
''' Testing Operations endpoints. '''
|
|
||||||
|
|
||||||
|
|
||||||
@decl_endpoint('/api/operations/inline-synthesis', method='patch')
|
|
||||||
def setUp(self):
|
|
||||||
super().setUp()
|
|
||||||
self.schema1 = RSForm.create(title='Test1', alias='T1', owner=self.user)
|
|
||||||
self.schema2 = RSForm.create(title='Test2', alias='T2', owner=self.user)
|
|
||||||
self.unowned = RSForm.create(title='Test3', alias='T3')
|
|
||||||
|
|
||||||
|
|
||||||
def test_inline_synthesis_inputs(self):
|
|
||||||
invalid_id = 1338
|
|
||||||
data = {
|
|
||||||
'receiver': self.unowned.model.pk,
|
|
||||||
'source': self.schema1.model.pk,
|
|
||||||
'items': [],
|
|
||||||
'substitutions': []
|
|
||||||
}
|
|
||||||
self.executeForbidden(data=data)
|
|
||||||
|
|
||||||
data['receiver'] = invalid_id
|
|
||||||
self.executeBadData(data=data)
|
|
||||||
|
|
||||||
data['receiver'] = self.schema1.model.pk
|
|
||||||
data['source'] = invalid_id
|
|
||||||
self.executeBadData(data=data)
|
|
||||||
|
|
||||||
data['source'] = self.schema1.model.pk
|
|
||||||
self.executeOK(data=data)
|
|
||||||
|
|
||||||
data['items'] = [invalid_id]
|
|
||||||
self.executeBadData(data=data)
|
|
||||||
|
|
||||||
|
|
||||||
def test_inline_synthesis(self):
|
|
||||||
ks1_x1 = self.schema1.insert_new('X1', term_raw='KS1X1') # -> delete
|
|
||||||
ks1_x2 = self.schema1.insert_new('X2', term_raw='KS1X2') # -> X2
|
|
||||||
ks1_s1 = self.schema1.insert_new('S1', definition_formal='X2', term_raw='KS1S1') # -> S1
|
|
||||||
ks1_d1 = self.schema1.insert_new('D1', definition_formal=r'S1\X1\X2') # -> D1
|
|
||||||
ks2_x1 = self.schema2.insert_new('X1', term_raw='KS2X1') # -> delete
|
|
||||||
ks2_x2 = self.schema2.insert_new('X2', term_raw='KS2X2') # -> X4
|
|
||||||
ks2_s1 = self.schema2.insert_new('S1', definition_formal='X2×X2', term_raw='KS2S1') # -> S2
|
|
||||||
ks2_d1 = self.schema2.insert_new('D1', definition_formal=r'S1\X1\X2') # -> D2
|
|
||||||
ks2_a1 = self.schema2.insert_new('A1', definition_formal='1=1') # -> not included in items
|
|
||||||
|
|
||||||
data = {
|
|
||||||
'receiver': self.schema1.model.pk,
|
|
||||||
'source': self.schema2.model.pk,
|
|
||||||
'items': [ks2_x1.pk, ks2_x2.pk, ks2_s1.pk, ks2_d1.pk],
|
|
||||||
'substitutions': [
|
|
||||||
{
|
|
||||||
'original': ks1_x1.pk,
|
|
||||||
'substitution': ks2_s1.pk
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'original': ks2_x1.pk,
|
|
||||||
'substitution': ks1_s1.pk
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
response = self.executeOK(data=data)
|
|
||||||
result = {item['alias']: item for item in response.data['items']}
|
|
||||||
self.assertEqual(len(result), 6)
|
|
||||||
self.assertEqual(result['X2']['term_raw'], ks1_x2.term_raw)
|
|
||||||
self.assertEqual(result['X2']['order'], 1)
|
|
||||||
self.assertEqual(result['X4']['term_raw'], ks2_x2.term_raw)
|
|
||||||
self.assertEqual(result['X4']['order'], 2)
|
|
||||||
self.assertEqual(result['S1']['term_raw'], ks2_x1.term_raw)
|
|
||||||
self.assertEqual(result['S2']['term_raw'], ks2_s1.term_raw)
|
|
||||||
self.assertEqual(result['S1']['definition_formal'], 'X2')
|
|
||||||
self.assertEqual(result['S2']['definition_formal'], 'X4×X4')
|
|
||||||
self.assertEqual(result['D1']['definition_formal'], r'S1\S2\X2')
|
|
||||||
self.assertEqual(result['D2']['definition_formal'], r'S2\S1\X4')
|
|
|
@ -482,3 +482,172 @@ class TestRSFormViewset(EndpointTester):
|
||||||
self.assertEqual(len(items), 2)
|
self.assertEqual(len(items), 2)
|
||||||
self.assertEqual(items[0]['order'], f1.order + 1)
|
self.assertEqual(items[0]['order'], f1.order + 1)
|
||||||
self.assertEqual(items[0]['definition_formal'], '[α∈X1, β∈X1] Pr1(F10[α,β])')
|
self.assertEqual(items[0]['definition_formal'], '[α∈X1, β∈X1] Pr1(F10[α,β])')
|
||||||
|
|
||||||
|
|
||||||
|
class TestConstituentaAPI(EndpointTester):
|
||||||
|
''' Testing Constituenta view. '''
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super().setUp()
|
||||||
|
self.rsform_owned = RSForm.create(title='Test', alias='T1', owner=self.user)
|
||||||
|
self.rsform_unowned = RSForm.create(title='Test2', alias='T2')
|
||||||
|
self.cst1 = Constituenta.objects.create(
|
||||||
|
alias='X1',
|
||||||
|
cst_type=CstType.BASE,
|
||||||
|
schema=self.rsform_owned.model,
|
||||||
|
order=1,
|
||||||
|
convention='Test',
|
||||||
|
term_raw='Test1',
|
||||||
|
term_resolved='Test1R',
|
||||||
|
term_forms=[{'text': 'form1', 'tags': 'sing,datv'}])
|
||||||
|
self.cst2 = Constituenta.objects.create(
|
||||||
|
alias='X2',
|
||||||
|
cst_type=CstType.BASE,
|
||||||
|
schema=self.rsform_unowned.model,
|
||||||
|
order=1,
|
||||||
|
convention='Test1',
|
||||||
|
term_raw='Test2',
|
||||||
|
term_resolved='Test2R'
|
||||||
|
)
|
||||||
|
self.cst3 = Constituenta.objects.create(
|
||||||
|
alias='X3',
|
||||||
|
schema=self.rsform_owned.model,
|
||||||
|
order=2,
|
||||||
|
term_raw='Test3',
|
||||||
|
term_resolved='Test3',
|
||||||
|
definition_raw='Test1',
|
||||||
|
definition_resolved='Test2'
|
||||||
|
)
|
||||||
|
self.invalid_cst = self.cst3.pk + 1337
|
||||||
|
|
||||||
|
@decl_endpoint('/api/rsforms/{schema}/update-cst', method='patch')
|
||||||
|
def test_partial_update(self):
|
||||||
|
data = {'id': self.cst1.pk, 'convention': 'tt'}
|
||||||
|
self.executeForbidden(data=data, schema=self.rsform_unowned.model.pk)
|
||||||
|
|
||||||
|
self.logout()
|
||||||
|
self.executeForbidden(data=data, schema=self.rsform_owned.model.pk)
|
||||||
|
|
||||||
|
self.login()
|
||||||
|
response = self.executeOK(data=data, schema=self.rsform_owned.model.pk)
|
||||||
|
self.cst1.refresh_from_db()
|
||||||
|
self.assertEqual(response.data['convention'], 'tt')
|
||||||
|
self.assertEqual(self.cst1.convention, 'tt')
|
||||||
|
|
||||||
|
self.executeOK(data=data, schema=self.rsform_owned.model.pk)
|
||||||
|
|
||||||
|
|
||||||
|
@decl_endpoint('/api/rsforms/{schema}/update-cst', method='patch')
|
||||||
|
def test_update_resolved_no_refs(self):
|
||||||
|
data = {
|
||||||
|
'id': self.cst3.pk,
|
||||||
|
'term_raw': 'New term',
|
||||||
|
'definition_raw': 'New def'
|
||||||
|
}
|
||||||
|
response = self.executeOK(data=data, schema=self.rsform_owned.model.pk)
|
||||||
|
self.cst3.refresh_from_db()
|
||||||
|
self.assertEqual(response.data['term_resolved'], 'New term')
|
||||||
|
self.assertEqual(self.cst3.term_resolved, 'New term')
|
||||||
|
self.assertEqual(response.data['definition_resolved'], 'New def')
|
||||||
|
self.assertEqual(self.cst3.definition_resolved, 'New def')
|
||||||
|
|
||||||
|
|
||||||
|
@decl_endpoint('/api/rsforms/{schema}/update-cst', method='patch')
|
||||||
|
def test_update_resolved_refs(self):
|
||||||
|
data = {
|
||||||
|
'id': self.cst3.pk,
|
||||||
|
'term_raw': '@{X1|nomn,sing}',
|
||||||
|
'definition_raw': '@{X1|nomn,sing} @{X1|sing,datv}'
|
||||||
|
}
|
||||||
|
response = self.executeOK(data=data, schema=self.rsform_owned.model.pk)
|
||||||
|
self.cst3.refresh_from_db()
|
||||||
|
self.assertEqual(self.cst3.term_resolved, self.cst1.term_resolved)
|
||||||
|
self.assertEqual(response.data['term_resolved'], self.cst1.term_resolved)
|
||||||
|
self.assertEqual(self.cst3.definition_resolved, f'{self.cst1.term_resolved} form1')
|
||||||
|
self.assertEqual(response.data['definition_resolved'], f'{self.cst1.term_resolved} form1')
|
||||||
|
|
||||||
|
|
||||||
|
@decl_endpoint('/api/rsforms/{schema}/update-cst', method='patch')
|
||||||
|
def test_readonly_cst_fields(self):
|
||||||
|
data = {
|
||||||
|
'id': self.cst1.pk,
|
||||||
|
'alias': 'X33',
|
||||||
|
'order': 10
|
||||||
|
}
|
||||||
|
response = self.executeOK(data=data, schema=self.rsform_owned.model.pk)
|
||||||
|
self.assertEqual(response.data['alias'], 'X1')
|
||||||
|
self.assertEqual(response.data['alias'], self.cst1.alias)
|
||||||
|
self.assertEqual(response.data['order'], self.cst1.order)
|
||||||
|
|
||||||
|
|
||||||
|
class TestInlineSynthesis(EndpointTester):
|
||||||
|
''' Testing Operations endpoints. '''
|
||||||
|
|
||||||
|
|
||||||
|
@decl_endpoint('/api/rsforms/inline-synthesis', method='patch')
|
||||||
|
def setUp(self):
|
||||||
|
super().setUp()
|
||||||
|
self.schema1 = RSForm.create(title='Test1', alias='T1', owner=self.user)
|
||||||
|
self.schema2 = RSForm.create(title='Test2', alias='T2', owner=self.user)
|
||||||
|
self.unowned = RSForm.create(title='Test3', alias='T3')
|
||||||
|
|
||||||
|
|
||||||
|
def test_inline_synthesis_inputs(self):
|
||||||
|
invalid_id = 1338
|
||||||
|
data = {
|
||||||
|
'receiver': self.unowned.model.pk,
|
||||||
|
'source': self.schema1.model.pk,
|
||||||
|
'items': [],
|
||||||
|
'substitutions': []
|
||||||
|
}
|
||||||
|
self.executeForbidden(data=data)
|
||||||
|
|
||||||
|
data['receiver'] = invalid_id
|
||||||
|
self.executeBadData(data=data)
|
||||||
|
|
||||||
|
data['receiver'] = self.schema1.model.pk
|
||||||
|
data['source'] = invalid_id
|
||||||
|
self.executeBadData(data=data)
|
||||||
|
|
||||||
|
data['source'] = self.schema1.model.pk
|
||||||
|
self.executeOK(data=data)
|
||||||
|
|
||||||
|
data['items'] = [invalid_id]
|
||||||
|
self.executeBadData(data=data)
|
||||||
|
|
||||||
|
|
||||||
|
def test_inline_synthesis(self):
|
||||||
|
ks1_x1 = self.schema1.insert_new('X1', term_raw='KS1X1') # -> delete
|
||||||
|
ks1_x2 = self.schema1.insert_new('X2', term_raw='KS1X2') # -> X2
|
||||||
|
ks1_s1 = self.schema1.insert_new('S1', definition_formal='X2', term_raw='KS1S1') # -> S1
|
||||||
|
ks1_d1 = self.schema1.insert_new('D1', definition_formal=r'S1\X1\X2') # -> D1
|
||||||
|
ks2_x1 = self.schema2.insert_new('X1', term_raw='KS2X1') # -> delete
|
||||||
|
ks2_x2 = self.schema2.insert_new('X2', term_raw='KS2X2') # -> X4
|
||||||
|
ks2_s1 = self.schema2.insert_new('S1', definition_formal='X2×X2', term_raw='KS2S1') # -> S2
|
||||||
|
ks2_d1 = self.schema2.insert_new('D1', definition_formal=r'S1\X1\X2') # -> D2
|
||||||
|
ks2_a1 = self.schema2.insert_new('A1', definition_formal='1=1') # -> not included in items
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'receiver': self.schema1.model.pk,
|
||||||
|
'source': self.schema2.model.pk,
|
||||||
|
'items': [ks2_x1.pk, ks2_x2.pk, ks2_s1.pk, ks2_d1.pk],
|
||||||
|
'substitutions': [
|
||||||
|
{
|
||||||
|
'original': ks1_x1.pk,
|
||||||
|
'substitution': ks2_s1.pk
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'original': ks2_x1.pk,
|
||||||
|
'substitution': ks1_s1.pk
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
response = self.executeOK(data=data)
|
||||||
|
result = {item['alias']: item for item in response.data['items']}
|
||||||
|
self.assertEqual(len(result), 6)
|
||||||
|
self.assertEqual(result['X2']['order'], 1)
|
||||||
|
self.assertEqual(result['X4']['order'], 2)
|
||||||
|
self.assertEqual(result['S1']['definition_formal'], 'X2')
|
||||||
|
self.assertEqual(result['S2']['definition_formal'], 'X4×X4')
|
||||||
|
self.assertEqual(result['D1']['definition_formal'], r'S1\S2\X2')
|
||||||
|
self.assertEqual(result['D2']['definition_formal'], r'S2\S1\X4')
|
||||||
|
|
|
@ -9,11 +9,9 @@ library_router.register('rsforms', views.RSFormViewSet, 'RSForm')
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('constituents/<int:pk>', views.ConstituentAPIView.as_view(), name='constituenta-detail'),
|
|
||||||
path('rsforms/import-trs', views.TrsImportView.as_view()),
|
path('rsforms/import-trs', views.TrsImportView.as_view()),
|
||||||
path('rsforms/create-detailed', views.create_rsform),
|
path('rsforms/create-detailed', views.create_rsform),
|
||||||
|
path('rsforms/inline-synthesis', views.inline_synthesis),
|
||||||
path('operations/inline-synthesis', views.inline_synthesis),
|
|
||||||
|
|
||||||
path('rslang/parse-expression', views.parse_expression),
|
path('rslang/parse-expression', views.parse_expression),
|
||||||
path('rslang/to-ascii', views.convert_to_ascii),
|
path('rslang/to-ascii', views.convert_to_ascii),
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
''' REST API: Endpoint processors. '''
|
''' REST API: Endpoint processors. '''
|
||||||
from .cctext import generate_lexeme, inflect, parse_text
|
from .cctext import generate_lexeme, inflect, parse_text
|
||||||
from .constituents import ConstituentAPIView
|
from .rsforms import RSFormViewSet, TrsImportView, create_rsform, inline_synthesis
|
||||||
from .operations import inline_synthesis
|
|
||||||
from .rsforms import RSFormViewSet, TrsImportView, create_rsform
|
|
||||||
from .rslang import convert_to_ascii, convert_to_math, parse_expression
|
from .rslang import convert_to_ascii, convert_to_math, parse_expression
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
''' Endpoints for Constituenta. '''
|
|
||||||
from drf_spectacular.utils import extend_schema, extend_schema_view
|
|
||||||
from rest_framework import generics
|
|
||||||
|
|
||||||
from shared import permissions
|
|
||||||
|
|
||||||
from .. import models as m
|
|
||||||
from .. import serializers as s
|
|
||||||
|
|
||||||
|
|
||||||
@extend_schema(tags=['Constituenta'])
|
|
||||||
@extend_schema_view()
|
|
||||||
class ConstituentAPIView(generics.RetrieveUpdateAPIView, permissions.EditorMixin):
|
|
||||||
''' Endpoint: Get / Update Constituenta. '''
|
|
||||||
queryset = m.Constituenta.objects.all()
|
|
||||||
serializer_class = s.CstSerializer
|
|
|
@ -1,50 +0,0 @@
|
||||||
''' Endpoints for RSForm. '''
|
|
||||||
from typing import cast
|
|
||||||
|
|
||||||
from django.db import transaction
|
|
||||||
from drf_spectacular.utils import extend_schema
|
|
||||||
from rest_framework import status as c
|
|
||||||
from rest_framework.decorators import api_view
|
|
||||||
from rest_framework.request import Request
|
|
||||||
from rest_framework.response import Response
|
|
||||||
|
|
||||||
from .. import models as m
|
|
||||||
from .. import serializers as s
|
|
||||||
|
|
||||||
|
|
||||||
@extend_schema(
|
|
||||||
summary='Inline synthesis: merge one schema into another',
|
|
||||||
tags=['Operations'],
|
|
||||||
request=s.InlineSynthesisSerializer,
|
|
||||||
responses={c.HTTP_200_OK: s.RSFormParseSerializer}
|
|
||||||
)
|
|
||||||
@api_view(['PATCH'])
|
|
||||||
def inline_synthesis(request: Request):
|
|
||||||
''' Endpoint: Inline synthesis. '''
|
|
||||||
serializer = s.InlineSynthesisSerializer(
|
|
||||||
data=request.data,
|
|
||||||
context={'user': request.user}
|
|
||||||
)
|
|
||||||
serializer.is_valid(raise_exception=True)
|
|
||||||
|
|
||||||
receiver = m.RSForm(serializer.validated_data['receiver'])
|
|
||||||
items = cast(list[m.Constituenta], serializer.validated_data['items'])
|
|
||||||
|
|
||||||
with transaction.atomic():
|
|
||||||
new_items = receiver.insert_copy(items)
|
|
||||||
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 == original)
|
|
||||||
original = new_items[index]
|
|
||||||
else:
|
|
||||||
index = next(i for (i, cst) in enumerate(items) if cst == replacement)
|
|
||||||
replacement = new_items[index]
|
|
||||||
receiver.substitute(original, replacement)
|
|
||||||
receiver.restore_order()
|
|
||||||
|
|
||||||
return Response(
|
|
||||||
status=c.HTTP_200_OK,
|
|
||||||
data=s.RSFormParseSerializer(receiver.model).data
|
|
||||||
)
|
|
|
@ -12,6 +12,7 @@ from rest_framework import views, viewsets
|
||||||
from rest_framework.decorators import action, api_view
|
from rest_framework.decorators import action, api_view
|
||||||
from rest_framework.request import Request
|
from rest_framework.request import Request
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
|
from rest_framework.serializers import ValidationError
|
||||||
|
|
||||||
from apps.library.models import AccessPolicy, LibraryItem, LibraryItemType, LocationHead
|
from apps.library.models import AccessPolicy, LibraryItem, LibraryItemType, LocationHead
|
||||||
from apps.library.serializers import LibraryItemSerializer
|
from apps.library.serializers import LibraryItemSerializer
|
||||||
|
@ -45,7 +46,8 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
|
||||||
'substitute',
|
'substitute',
|
||||||
'restore_order',
|
'restore_order',
|
||||||
'reset_aliases',
|
'reset_aliases',
|
||||||
'produce_structure'
|
'produce_structure',
|
||||||
|
'update_cst'
|
||||||
]:
|
]:
|
||||||
permission_list = [permissions.ItemEditor]
|
permission_list = [permissions.ItemEditor]
|
||||||
elif self.action in [
|
elif self.action in [
|
||||||
|
@ -88,15 +90,43 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
|
||||||
new_cst = m.RSForm(schema).create_cst(data, insert_after)
|
new_cst = m.RSForm(schema).create_cst(data, insert_after)
|
||||||
|
|
||||||
schema.refresh_from_db()
|
schema.refresh_from_db()
|
||||||
response = Response(
|
return Response(
|
||||||
status=c.HTTP_201_CREATED,
|
status=c.HTTP_201_CREATED,
|
||||||
data={
|
data={
|
||||||
'new_cst': s.CstSerializer(new_cst).data,
|
'new_cst': s.CstSerializer(new_cst).data,
|
||||||
'schema': s.RSFormParseSerializer(schema).data
|
'schema': s.RSFormParseSerializer(schema).data
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
response['Location'] = new_cst.get_absolute_url()
|
|
||||||
return response
|
@extend_schema(
|
||||||
|
summary='update persistent attributes of a given constituenta',
|
||||||
|
tags=['RSForm'],
|
||||||
|
request=s.CstSerializer,
|
||||||
|
responses={
|
||||||
|
c.HTTP_200_OK: s.CstSerializer,
|
||||||
|
c.HTTP_400_BAD_REQUEST: None,
|
||||||
|
c.HTTP_403_FORBIDDEN: None,
|
||||||
|
c.HTTP_404_NOT_FOUND: None
|
||||||
|
}
|
||||||
|
)
|
||||||
|
@action(detail=True, methods=['patch'], url_path='update-cst')
|
||||||
|
def update_cst(self, request: Request, pk):
|
||||||
|
''' Update persistent attributes of a given constituenta. '''
|
||||||
|
schema = self._get_item()
|
||||||
|
serializer = s.CstSerializer(data=request.data, partial=True)
|
||||||
|
serializer.is_valid(raise_exception=True)
|
||||||
|
|
||||||
|
cst = m.Constituenta.objects.get(pk=request.data['id'])
|
||||||
|
if cst.schema != schema:
|
||||||
|
raise ValidationError({
|
||||||
|
f'schema': msg.constituentaNotInRSform(schema.title)
|
||||||
|
})
|
||||||
|
serializer.update(instance=cst, validated_data=serializer.validated_data)
|
||||||
|
|
||||||
|
return Response(
|
||||||
|
status=c.HTTP_200_OK,
|
||||||
|
data=s.CstSerializer(cst).data
|
||||||
|
)
|
||||||
|
|
||||||
@extend_schema(
|
@extend_schema(
|
||||||
summary='produce the structure of a given constituenta',
|
summary='produce the structure of a given constituenta',
|
||||||
|
@ -521,3 +551,41 @@ def _prepare_rsform_data(data: dict, request: Request, owner: Union[User, None])
|
||||||
|
|
||||||
data['access_policy'] = request.data.get('access_policy', AccessPolicy.PUBLIC)
|
data['access_policy'] = request.data.get('access_policy', AccessPolicy.PUBLIC)
|
||||||
data['location'] = request.data.get('location', LocationHead.USER)
|
data['location'] = request.data.get('location', LocationHead.USER)
|
||||||
|
|
||||||
|
|
||||||
|
@extend_schema(
|
||||||
|
summary='Inline synthesis: merge one schema into another',
|
||||||
|
tags=['Operations'],
|
||||||
|
request=s.InlineSynthesisSerializer,
|
||||||
|
responses={c.HTTP_200_OK: s.RSFormParseSerializer}
|
||||||
|
)
|
||||||
|
@api_view(['PATCH'])
|
||||||
|
def inline_synthesis(request: Request):
|
||||||
|
''' Endpoint: Inline synthesis. '''
|
||||||
|
serializer = s.InlineSynthesisSerializer(
|
||||||
|
data=request.data,
|
||||||
|
context={'user': request.user}
|
||||||
|
)
|
||||||
|
serializer.is_valid(raise_exception=True)
|
||||||
|
|
||||||
|
receiver = m.RSForm(serializer.validated_data['receiver'])
|
||||||
|
items = cast(list[m.Constituenta], serializer.validated_data['items'])
|
||||||
|
|
||||||
|
with transaction.atomic():
|
||||||
|
new_items = receiver.insert_copy(items)
|
||||||
|
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 == original)
|
||||||
|
original = new_items[index]
|
||||||
|
else:
|
||||||
|
index = next(i for (i, cst) in enumerate(items) if cst == replacement)
|
||||||
|
replacement = new_items[index]
|
||||||
|
receiver.substitute(original, replacement)
|
||||||
|
receiver.restore_order()
|
||||||
|
|
||||||
|
return Response(
|
||||||
|
status=c.HTTP_200_OK,
|
||||||
|
data=s.RSFormParseSerializer(receiver.model).data
|
||||||
|
)
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
/**
|
|
||||||
* Endpoints: constituents.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { IConstituentaMeta, ICstUpdateData } from '@/models/rsform';
|
|
||||||
|
|
||||||
import { AxiosPatch, FrontExchange } from './apiTransport';
|
|
||||||
|
|
||||||
export function patchConstituenta(target: string, request: FrontExchange<ICstUpdateData, IConstituentaMeta>) {
|
|
||||||
AxiosPatch({
|
|
||||||
endpoint: `/api/constituents/${target}`,
|
|
||||||
request: request
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
/**
|
|
||||||
* Endpoints: operations.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { IInlineSynthesisData, IRSFormData } from '@/models/rsform';
|
|
||||||
|
|
||||||
import { AxiosPatch, FrontExchange } from './apiTransport';
|
|
||||||
|
|
||||||
export function patchInlineSynthesis(request: FrontExchange<IInlineSynthesisData, IRSFormData>) {
|
|
||||||
AxiosPatch({
|
|
||||||
endpoint: `/api/operations/inline-synthesis`,
|
|
||||||
request: request
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -6,10 +6,13 @@ import { ILibraryCreateData, ILibraryItem } from '@/models/library';
|
||||||
import { ICstSubstituteData } from '@/models/oss';
|
import { ICstSubstituteData } from '@/models/oss';
|
||||||
import {
|
import {
|
||||||
IConstituentaList,
|
IConstituentaList,
|
||||||
|
IConstituentaMeta,
|
||||||
ICstCreateData,
|
ICstCreateData,
|
||||||
ICstCreatedResponse,
|
ICstCreatedResponse,
|
||||||
ICstMovetoData,
|
ICstMovetoData,
|
||||||
ICstRenameData,
|
ICstRenameData,
|
||||||
|
ICstUpdateData,
|
||||||
|
IInlineSynthesisData,
|
||||||
IProduceStructureResponse,
|
IProduceStructureResponse,
|
||||||
IRSFormData,
|
IRSFormData,
|
||||||
IRSFormUploadData,
|
IRSFormUploadData,
|
||||||
|
@ -68,6 +71,13 @@ export function postCreateConstituenta(schema: string, request: FrontExchange<IC
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function patchUpdateConstituenta(schema: string, request: FrontExchange<ICstUpdateData, IConstituentaMeta>) {
|
||||||
|
AxiosPatch({
|
||||||
|
endpoint: `/api/rsforms/${schema}/update-cst`,
|
||||||
|
request: request
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function patchDeleteConstituenta(schema: string, request: FrontExchange<IConstituentaList, IRSFormData>) {
|
export function patchDeleteConstituenta(schema: string, request: FrontExchange<IConstituentaList, IRSFormData>) {
|
||||||
AxiosPatch({
|
AxiosPatch({
|
||||||
endpoint: `/api/rsforms/${schema}/delete-multiple-cst`,
|
endpoint: `/api/rsforms/${schema}/delete-multiple-cst`,
|
||||||
|
@ -135,3 +145,10 @@ export function patchUploadTRS(target: string, request: FrontExchange<IRSFormUpl
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function patchInlineSynthesis(request: FrontExchange<IInlineSynthesisData, IRSFormData>) {
|
||||||
|
AxiosPatch({
|
||||||
|
endpoint: `/api/rsforms/inline-synthesis`,
|
||||||
|
request: request
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
import { createContext, useCallback, useContext, useMemo, useState } from 'react';
|
import { createContext, useCallback, useContext, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import { DataCallback } from '@/backend/apiTransport';
|
import { DataCallback } from '@/backend/apiTransport';
|
||||||
import { patchConstituenta } from '@/backend/constituents';
|
|
||||||
import {
|
import {
|
||||||
deleteUnsubscribe,
|
deleteUnsubscribe,
|
||||||
patchLibraryItem,
|
patchLibraryItem,
|
||||||
|
@ -14,16 +13,17 @@ import {
|
||||||
postCreateVersion,
|
postCreateVersion,
|
||||||
postSubscribe
|
postSubscribe
|
||||||
} from '@/backend/library';
|
} from '@/backend/library';
|
||||||
import { patchInlineSynthesis } from '@/backend/operations';
|
|
||||||
import {
|
import {
|
||||||
getTRSFile,
|
getTRSFile,
|
||||||
patchDeleteConstituenta,
|
patchDeleteConstituenta,
|
||||||
|
patchInlineSynthesis,
|
||||||
patchMoveConstituenta,
|
patchMoveConstituenta,
|
||||||
patchProduceStructure,
|
patchProduceStructure,
|
||||||
patchRenameConstituenta,
|
patchRenameConstituenta,
|
||||||
patchResetAliases,
|
patchResetAliases,
|
||||||
patchRestoreOrder,
|
patchRestoreOrder,
|
||||||
patchSubstituteConstituents,
|
patchSubstituteConstituents,
|
||||||
|
patchUpdateConstituenta,
|
||||||
patchUploadTRS,
|
patchUploadTRS,
|
||||||
postCreateConstituenta
|
postCreateConstituenta
|
||||||
} from '@/backend/rsforms';
|
} from '@/backend/rsforms';
|
||||||
|
@ -439,7 +439,7 @@ export const RSFormState = ({ itemID, versionID, children }: RSFormStateProps) =
|
||||||
const cstUpdate = useCallback(
|
const cstUpdate = useCallback(
|
||||||
(data: ICstUpdateData, callback?: DataCallback<IConstituentaMeta>) => {
|
(data: ICstUpdateData, callback?: DataCallback<IConstituentaMeta>) => {
|
||||||
setProcessingError(undefined);
|
setProcessingError(undefined);
|
||||||
patchConstituenta(String(data.id), {
|
patchUpdateConstituenta(itemID, {
|
||||||
data: data,
|
data: data,
|
||||||
showError: true,
|
showError: true,
|
||||||
setLoading: setProcessing,
|
setLoading: setProcessing,
|
||||||
|
|
Loading…
Reference in New Issue
Block a user