mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 13:00:39 +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_plural = 'Конституенты'
|
||||
|
||||
def get_absolute_url(self):
|
||||
''' URL access. '''
|
||||
return reverse('constituenta-detail', kwargs={'pk': self.pk})
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f'{self.alias}'
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ class CstSerializer(serializers.ModelSerializer):
|
|||
''' serializer metadata. '''
|
||||
model = Constituenta
|
||||
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:
|
||||
data = validated_data # Note: use alias for better code readability
|
||||
|
|
|
@ -20,12 +20,6 @@ class TestConstituenta(TestCase):
|
|||
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):
|
||||
with self.assertRaises(IntegrityError):
|
||||
Constituenta.objects.create(alias='X1', schema=self.schema1.model)
|
||||
|
|
|
@ -208,11 +208,11 @@ class TestRSForm(TestCase):
|
|||
definition_formal=x1.alias
|
||||
)
|
||||
|
||||
self.schema.substitute(x1, x2, True)
|
||||
self.schema.substitute(x1, x2)
|
||||
x2.refresh_from_db()
|
||||
d1.refresh_from_db()
|
||||
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)
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
''' Tests for REST API. '''
|
||||
from .t_cctext import *
|
||||
from .t_constituents import *
|
||||
from .t_operations import *
|
||||
from .t_rsforms 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(items[0]['order'], f1.order + 1)
|
||||
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 = [
|
||||
path('constituents/<int:pk>', views.ConstituentAPIView.as_view(), name='constituenta-detail'),
|
||||
path('rsforms/import-trs', views.TrsImportView.as_view()),
|
||||
path('rsforms/create-detailed', views.create_rsform),
|
||||
|
||||
path('operations/inline-synthesis', views.inline_synthesis),
|
||||
path('rsforms/inline-synthesis', views.inline_synthesis),
|
||||
|
||||
path('rslang/parse-expression', views.parse_expression),
|
||||
path('rslang/to-ascii', views.convert_to_ascii),
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
''' REST API: Endpoint processors. '''
|
||||
from .cctext import generate_lexeme, inflect, parse_text
|
||||
from .constituents import ConstituentAPIView
|
||||
from .operations import inline_synthesis
|
||||
from .rsforms import RSFormViewSet, TrsImportView, create_rsform
|
||||
from .rsforms import RSFormViewSet, TrsImportView, create_rsform, inline_synthesis
|
||||
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.request import Request
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.serializers import ValidationError
|
||||
|
||||
from apps.library.models import AccessPolicy, LibraryItem, LibraryItemType, LocationHead
|
||||
from apps.library.serializers import LibraryItemSerializer
|
||||
|
@ -45,7 +46,8 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
|
|||
'substitute',
|
||||
'restore_order',
|
||||
'reset_aliases',
|
||||
'produce_structure'
|
||||
'produce_structure',
|
||||
'update_cst'
|
||||
]:
|
||||
permission_list = [permissions.ItemEditor]
|
||||
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)
|
||||
|
||||
schema.refresh_from_db()
|
||||
response = Response(
|
||||
return Response(
|
||||
status=c.HTTP_201_CREATED,
|
||||
data={
|
||||
'new_cst': s.CstSerializer(new_cst).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(
|
||||
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['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 {
|
||||
IConstituentaList,
|
||||
IConstituentaMeta,
|
||||
ICstCreateData,
|
||||
ICstCreatedResponse,
|
||||
ICstMovetoData,
|
||||
ICstRenameData,
|
||||
ICstUpdateData,
|
||||
IInlineSynthesisData,
|
||||
IProduceStructureResponse,
|
||||
IRSFormData,
|
||||
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>) {
|
||||
AxiosPatch({
|
||||
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 { DataCallback } from '@/backend/apiTransport';
|
||||
import { patchConstituenta } from '@/backend/constituents';
|
||||
import {
|
||||
deleteUnsubscribe,
|
||||
patchLibraryItem,
|
||||
|
@ -14,16 +13,17 @@ import {
|
|||
postCreateVersion,
|
||||
postSubscribe
|
||||
} from '@/backend/library';
|
||||
import { patchInlineSynthesis } from '@/backend/operations';
|
||||
import {
|
||||
getTRSFile,
|
||||
patchDeleteConstituenta,
|
||||
patchInlineSynthesis,
|
||||
patchMoveConstituenta,
|
||||
patchProduceStructure,
|
||||
patchRenameConstituenta,
|
||||
patchResetAliases,
|
||||
patchRestoreOrder,
|
||||
patchSubstituteConstituents,
|
||||
patchUpdateConstituenta,
|
||||
patchUploadTRS,
|
||||
postCreateConstituenta
|
||||
} from '@/backend/rsforms';
|
||||
|
@ -439,7 +439,7 @@ export const RSFormState = ({ itemID, versionID, children }: RSFormStateProps) =
|
|||
const cstUpdate = useCallback(
|
||||
(data: ICstUpdateData, callback?: DataCallback<IConstituentaMeta>) => {
|
||||
setProcessingError(undefined);
|
||||
patchConstituenta(String(data.id), {
|
||||
patchUpdateConstituenta(itemID, {
|
||||
data: data,
|
||||
showError: true,
|
||||
setLoading: setProcessing,
|
||||
|
|
Loading…
Reference in New Issue
Block a user