Implement CstRename

This commit is contained in:
IRBorisov 2023-08-23 12:15:16 +03:00
parent 24da53bf40
commit 375bac2a52
6 changed files with 104 additions and 8 deletions

View File

@ -241,7 +241,7 @@ class RSForm(Model):
def reset_aliases(self):
''' Recreate all aliases based on cst order. '''
mapping = self._create_reset_mapping()
self._apply_mapping(mapping)
self.apply_mapping(mapping, change_aliases=True)
def _create_reset_mapping(self) -> dict[str, str]:
bases = cast(dict[str, int], {})
@ -257,11 +257,12 @@ class RSForm(Model):
return mapping
@transaction.atomic
def _apply_mapping(self, mapping: dict[str, str]):
def apply_mapping(self, mapping: dict[str, str], change_aliases: bool = False):
''' Apply rename mapping. '''
cst_list = self.constituents().order_by('order')
for cst in cst_list:
modified = False
if cst.alias in mapping:
if change_aliases and cst.alias in mapping:
modified = True
cst.alias = mapping[cst.alias]
expression = apply_mapping_pattern(cst.definition_formal, mapping, _GLOBAL_ID_PATTERN)

View File

@ -1,5 +1,5 @@
''' Serializers for conceptual schema API. '''
from typing import Optional
from typing import Optional, cast
from rest_framework import serializers
from django.db import transaction
@ -264,6 +264,30 @@ class CstCreateSerializer(serializers.ModelSerializer):
fields = 'alias', 'cst_type', 'convention', 'term_raw', 'definition_raw', 'definition_formal', 'insert_after'
class CstRenameSerializer(serializers.ModelSerializer):
''' Serializer: Constituenta renaming. '''
class Meta:
''' serializer metadata. '''
model = Constituenta
fields = 'id', 'alias', 'cst_type'
def validate(self, attrs):
schema = cast(RSForm, self.context['schema'])
old_cst = Constituenta.objects.get(pk=self.initial_data['id'])
if old_cst.schema != schema:
raise serializers.ValidationError({
'id': f'Изменяемая конституента должна относиться к изменяемой схеме: {schema.title}'
})
if old_cst.alias == self.initial_data['alias']:
raise serializers.ValidationError({
'alias': f'Имя конституенты должно отличаться от текущего: {self.initial_data["alias"]}'
})
self.instance = old_cst
attrs['schema'] = schema
attrs['id'] = self.initial_data['id']
return attrs
class CstListSerlializer(serializers.Serializer):
''' Serializer: List of constituents from one origin. '''
items = serializers.ListField(

View File

@ -246,6 +246,56 @@ class TestRSFormViewset(APITestCase):
x4 = Constituenta.objects.get(alias=response.data['new_cst']['alias'])
self.assertEqual(x4.order, 3)
def test_rename_constituenta(self):
self.cst1 = Constituenta.objects.create(
alias='X1', schema=self.rsform_owned, order=1, convention='Test',
term_raw='Test1', term_resolved='Test1',
term_forms=[{'text':'form1', 'tags':'sing,datv'}])
self.cst2 = Constituenta.objects.create(
alias='X2', schema=self.rsform_unowned, order=1, convention='Test1',
term_raw='Test2', term_resolved='Test2')
self.cst3 = Constituenta.objects.create(
alias='X3', schema=self.rsform_owned, order=2,
term_raw='Test3', term_resolved='Test3',
definition_raw='Test1', definition_resolved='Test2')
data = json.dumps({'alias': 'D2', 'cst_type': 'term', 'id': self.cst2.pk})
response = self.client.patch(f'/api/rsforms/{self.rsform_unowned.id}/cst-rename/',
data=data, content_type='application/json')
self.assertEqual(response.status_code, 403)
response = self.client.patch(f'/api/rsforms/{self.rsform_owned.id}/cst-rename/',
data=data, content_type='application/json')
self.assertEqual(response.status_code, 400)
data = json.dumps({'alias': self.cst1.alias, 'cst_type': 'term', 'id': self.cst1.pk})
response = self.client.patch(f'/api/rsforms/{self.rsform_owned.id}/cst-rename/',
data=data, content_type='application/json')
self.assertEqual(response.status_code, 400)
data = json.dumps({'alias': 'D2', 'cst_type': 'term', 'id': self.cst1.pk})
schema = self.rsform_owned
d1 = Constituenta.objects.create(schema=schema, alias='D1', cst_type='term', order=4)
d1.term_raw = '@{X1|plur}'
d1.definition_formal = 'X1'
d1.save()
self.assertEqual(self.cst1.order, 1)
self.assertEqual(self.cst1.alias, 'X1')
self.assertEqual(self.cst1.cst_type, CstType.BASE)
response = self.client.patch(f'/api/rsforms/{schema.id}/cst-rename/',
data=data, content_type='application/json')
self.assertEqual(response.status_code, 200)
self.assertEqual(response.data['new_cst']['alias'], 'D2')
self.assertEqual(response.data['new_cst']['cst_type'], 'term')
d1.refresh_from_db()
self.cst1.refresh_from_db()
self.assertEqual(d1.term_resolved, '')
self.assertEqual(d1.term_raw, '@{D2|plur}')
self.assertEqual(self.cst1.order, 2)
self.assertEqual(self.cst1.alias, 'D2')
self.assertEqual(self.cst1.cst_type, CstType.TERM)
def test_create_constituenta_data(self):
data = json.dumps({
'alias': 'X3',

View File

@ -1,5 +1,6 @@
''' REST API: RSForms for conceptual schemas. '''
import json
from django.db import transaction
from django.http import HttpResponse
from django_filters.rest_framework import DjangoFilterBackend
from django.db.models import Q
@ -63,7 +64,7 @@ class RSFormViewSet(viewsets.ModelViewSet):
def get_permissions(self):
if self.action in ['update', 'destroy', 'partial_update', 'load_trs',
'cst_create', 'cst_multidelete', 'reset_aliases']:
'cst_create', 'cst_multidelete', 'reset_aliases', 'cst_rename']:
permission_classes = [utils.ObjectOwnerOrAdmin]
elif self.action in ['create', 'claim', 'clone']:
permission_classes = [permissions.IsAuthenticated]
@ -87,6 +88,25 @@ class RSFormViewSet(viewsets.ModelViewSet):
response['Location'] = new_cst.get_absolute_url()
return response
@transaction.atomic
@action(detail=True, methods=['patch'], url_path='cst-rename')
def cst_rename(self, request, pk):
''' Rename constituenta possibly changing type. '''
schema = self._get_schema()
serializer = serializers.CstRenameSerializer(data=request.data, context={'schema': schema})
serializer.is_valid(raise_exception=True)
old_alias = models.Constituenta.objects.get(pk=request.data['id']).alias
serializer.save()
mapping = { old_alias: serializer.validated_data['alias'] }
schema.apply_mapping(mapping, change_aliases=False)
schema.update_order()
schema.refresh_from_db()
cst = models.Constituenta.objects.get(pk=serializer.validated_data['id'])
return Response(status=200, data={
'new_cst': serializers.ConstituentaSerializer(cst).data,
'schema': models.PyConceptAdapter(schema).full()
})
@action(detail=True, methods=['patch'], url_path='cst-multidelete')
def cst_multidelete(self, request, pk):
''' Endpoint: Delete multiple constituents. '''

View File

@ -227,10 +227,11 @@ export const RSFormState = ({ schemaID, children }: RSFormStateProps) => {
setLoading: setProcessing,
onError: error => setError(error),
onSuccess: newData => {
reload(setProcessing, () => { if (callback) callback(newData); })
setSchema(newData.schema);
if (callback) callback(newData.new_cst);
}
});
}, [setError, reload, schemaID]);
}, [setError, setSchema, schemaID]);
const cstMoveTo = useCallback(
(data: ICstMovetoData, callback?: () => void) => {

View File

@ -210,7 +210,7 @@ export function patchConstituenta(target: string, request: FrontExchange<ICstUpd
});
}
export function patchRenameConstituenta(schema: string, request: FrontExchange<ICstRenameData, IConstituentaMeta>) {
export function patchRenameConstituenta(schema: string, request: FrontExchange<ICstRenameData, ICstCreatedResponse>) {
AxiosPatch({
title: `Renaming constituenta id=${request.data.id} for schema id=${schema}`,
endpoint: `/api/rsforms/${schema}/cst-rename/`,