F: Rework constituenta editing endpoints
This commit is contained in:
parent
087ec8cf56
commit
d46fa536e6
|
@ -112,18 +112,6 @@ class TestChangeConstituents(EndpointTester):
|
||||||
self.assertEqual(inherited_cst.definition_formal, 'X1 = X2')
|
self.assertEqual(inherited_cst.definition_formal, 'X1 = X2')
|
||||||
|
|
||||||
|
|
||||||
@decl_endpoint('/api/rsforms/{schema}/rename-cst', method='patch')
|
|
||||||
def test_rename_constituenta(self):
|
|
||||||
data = {'target': self.ks1X1.pk, 'alias': 'D21', 'cst_type': CstType.TERM}
|
|
||||||
response = self.executeOK(data=data, schema=self.ks1.model.pk)
|
|
||||||
self.ks1X1.refresh_from_db()
|
|
||||||
inherited_cst = Constituenta.objects.get(as_child__parent_id=self.ks1X1.pk)
|
|
||||||
self.assertEqual(self.ks1X1.alias, data['alias'])
|
|
||||||
self.assertEqual(self.ks1X1.cst_type, data['cst_type'])
|
|
||||||
self.assertEqual(inherited_cst.alias, 'D2')
|
|
||||||
self.assertEqual(inherited_cst.cst_type, data['cst_type'])
|
|
||||||
|
|
||||||
|
|
||||||
@decl_endpoint('/api/rsforms/{schema}/update-cst', method='patch')
|
@decl_endpoint('/api/rsforms/{schema}/update-cst', method='patch')
|
||||||
def test_update_constituenta(self):
|
def test_update_constituenta(self):
|
||||||
d2 = self.ks3.insert_new('D2', cst_type=CstType.TERM, definition_raw='@{X1|sing,nomn}')
|
d2 = self.ks3.insert_new('D2', cst_type=CstType.TERM, definition_raw='@{X1|sing,nomn}')
|
||||||
|
|
|
@ -16,7 +16,6 @@ from .data_access import (
|
||||||
CstInfoSerializer,
|
CstInfoSerializer,
|
||||||
CstListSerializer,
|
CstListSerializer,
|
||||||
CstMoveSerializer,
|
CstMoveSerializer,
|
||||||
CstRenameSerializer,
|
|
||||||
CstSubstituteSerializer,
|
CstSubstituteSerializer,
|
||||||
CstTargetSerializer,
|
CstTargetSerializer,
|
||||||
CstUpdateSerializer,
|
CstUpdateSerializer,
|
||||||
|
|
|
@ -45,11 +45,12 @@ class CstUpdateSerializer(serializers.Serializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
''' serializer metadata. '''
|
''' serializer metadata. '''
|
||||||
model = Constituenta
|
model = Constituenta
|
||||||
fields = 'convention', 'definition_formal', 'definition_raw', 'term_raw', 'term_forms'
|
fields = 'alias', 'cst_type', 'convention', 'definition_formal', 'definition_raw', 'term_raw', 'term_forms'
|
||||||
|
|
||||||
target = PKField(
|
target = PKField(
|
||||||
many=False,
|
many=False,
|
||||||
queryset=Constituenta.objects.all().only('convention', 'definition_formal', 'definition_raw', 'term_raw')
|
queryset=Constituenta.objects.all().only(
|
||||||
|
'alias', 'cst_type', 'convention', 'definition_formal', 'definition_raw', 'term_raw')
|
||||||
)
|
)
|
||||||
item_data = ConstituentaUpdateData()
|
item_data = ConstituentaUpdateData()
|
||||||
|
|
||||||
|
@ -60,6 +61,12 @@ class CstUpdateSerializer(serializers.Serializer):
|
||||||
raise serializers.ValidationError({
|
raise serializers.ValidationError({
|
||||||
f'{cst.pk}': msg.constituentaNotInRSform(schema.title)
|
f'{cst.pk}': msg.constituentaNotInRSform(schema.title)
|
||||||
})
|
})
|
||||||
|
if 'alias' in attrs['item_data']:
|
||||||
|
new_alias = attrs['item_data']['alias']
|
||||||
|
if cst.alias != new_alias and RSForm(schema).constituents().filter(alias=new_alias).exists():
|
||||||
|
raise serializers.ValidationError({
|
||||||
|
'alias': msg.aliasTaken(new_alias)
|
||||||
|
})
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
|
|
||||||
|
@ -258,32 +265,6 @@ class CstTargetSerializer(serializers.Serializer):
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
|
|
||||||
class CstRenameSerializer(serializers.Serializer):
|
|
||||||
''' Serializer: Constituenta renaming. '''
|
|
||||||
target = PKField(many=False, queryset=Constituenta.objects.only('alias', 'cst_type', 'schema'))
|
|
||||||
alias = serializers.CharField()
|
|
||||||
cst_type = serializers.CharField()
|
|
||||||
|
|
||||||
def validate(self, attrs):
|
|
||||||
attrs = super().validate(attrs)
|
|
||||||
schema = cast(LibraryItem, self.context['schema'])
|
|
||||||
cst = cast(Constituenta, attrs['target'])
|
|
||||||
if cst.schema_id != schema.pk:
|
|
||||||
raise serializers.ValidationError({
|
|
||||||
f'{cst.pk}': msg.constituentaNotInRSform(schema.title)
|
|
||||||
})
|
|
||||||
new_alias = self.initial_data['alias']
|
|
||||||
if cst.alias == new_alias:
|
|
||||||
raise serializers.ValidationError({
|
|
||||||
'alias': msg.renameTrivial(new_alias)
|
|
||||||
})
|
|
||||||
if RSForm(schema).constituents().filter(alias=new_alias).exists():
|
|
||||||
raise serializers.ValidationError({
|
|
||||||
'alias': msg.aliasTaken(new_alias)
|
|
||||||
})
|
|
||||||
return attrs
|
|
||||||
|
|
||||||
|
|
||||||
class CstListSerializer(serializers.Serializer):
|
class CstListSerializer(serializers.Serializer):
|
||||||
''' Serializer: List of constituents from one origin. '''
|
''' Serializer: List of constituents from one origin. '''
|
||||||
items = PKField(many=True, queryset=Constituenta.objects.all().only('schema_id'))
|
items = PKField(many=True, queryset=Constituenta.objects.all().only('schema_id'))
|
||||||
|
|
|
@ -244,56 +244,6 @@ class TestRSFormViewset(EndpointTester):
|
||||||
self.assertEqual(response.data['new_cst']['alias'], data['alias'])
|
self.assertEqual(response.data['new_cst']['alias'], data['alias'])
|
||||||
|
|
||||||
|
|
||||||
@decl_endpoint('/api/rsforms/{item}/rename-cst', method='patch')
|
|
||||||
def test_rename_constituenta(self):
|
|
||||||
x1 = self.owned.insert_new(
|
|
||||||
alias='X1',
|
|
||||||
convention='Test',
|
|
||||||
term_raw='Test1',
|
|
||||||
term_resolved='Test1',
|
|
||||||
term_forms=[{'text': 'form1', 'tags': 'sing,datv'}]
|
|
||||||
)
|
|
||||||
x2_2 = self.unowned.insert_new('X2')
|
|
||||||
x3 = self.owned.insert_new(
|
|
||||||
alias='X3',
|
|
||||||
term_raw='Test3',
|
|
||||||
term_resolved='Test3',
|
|
||||||
definition_raw='Test1',
|
|
||||||
definition_resolved='Test2'
|
|
||||||
)
|
|
||||||
|
|
||||||
data = {'target': x2_2.pk, 'alias': 'D2', 'cst_type': CstType.TERM}
|
|
||||||
self.executeForbidden(data=data, item=self.unowned_id)
|
|
||||||
self.executeBadData(data=data, item=self.owned_id)
|
|
||||||
|
|
||||||
data = {'target': x1.pk, 'alias': x1.alias, 'cst_type': CstType.TERM}
|
|
||||||
self.executeBadData(data=data, item=self.owned_id)
|
|
||||||
|
|
||||||
data = {'target': x1.pk, 'alias': x3.alias}
|
|
||||||
self.executeBadData(data=data, item=self.owned_id)
|
|
||||||
|
|
||||||
d1 = self.owned.insert_new(
|
|
||||||
alias='D1',
|
|
||||||
term_raw='@{X1|plur}',
|
|
||||||
definition_formal='X1'
|
|
||||||
)
|
|
||||||
self.assertEqual(x1.order, 0)
|
|
||||||
self.assertEqual(x1.alias, 'X1')
|
|
||||||
self.assertEqual(x1.cst_type, CstType.BASE)
|
|
||||||
|
|
||||||
data = {'target': x1.pk, 'alias': 'D2', 'cst_type': CstType.TERM}
|
|
||||||
response = self.executeOK(data=data, item=self.owned_id)
|
|
||||||
self.assertEqual(response.data['new_cst']['alias'], 'D2')
|
|
||||||
self.assertEqual(response.data['new_cst']['cst_type'], CstType.TERM)
|
|
||||||
d1.refresh_from_db()
|
|
||||||
x1.refresh_from_db()
|
|
||||||
self.assertEqual(d1.term_resolved, '')
|
|
||||||
self.assertEqual(d1.term_raw, '@{D2|plur}')
|
|
||||||
self.assertEqual(x1.order, 0)
|
|
||||||
self.assertEqual(x1.alias, 'D2')
|
|
||||||
self.assertEqual(x1.cst_type, CstType.TERM)
|
|
||||||
|
|
||||||
|
|
||||||
@decl_endpoint('/api/rsforms/{item}/substitute', method='patch')
|
@decl_endpoint('/api/rsforms/{item}/substitute', method='patch')
|
||||||
def test_substitute_multiple(self):
|
def test_substitute_multiple(self):
|
||||||
self.set_params(item=self.owned_id)
|
self.set_params(item=self.owned_id)
|
||||||
|
@ -507,12 +457,14 @@ class TestConstituentaAPI(EndpointTester):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super().setUp()
|
super().setUp()
|
||||||
self.rsform_owned = RSForm.create(title='Test', alias='T1', owner=self.user)
|
self.owned = RSForm.create(title='Test', alias='T1', owner=self.user)
|
||||||
self.rsform_unowned = RSForm.create(title='Test2', alias='T2')
|
self.owned_id = self.owned.model.pk
|
||||||
|
self.unowned = RSForm.create(title='Test2', alias='T2')
|
||||||
|
self.unowned_id = self.unowned.model.pk
|
||||||
self.cst1 = Constituenta.objects.create(
|
self.cst1 = Constituenta.objects.create(
|
||||||
alias='X1',
|
alias='X1',
|
||||||
cst_type=CstType.BASE,
|
cst_type=CstType.BASE,
|
||||||
schema=self.rsform_owned.model,
|
schema=self.owned.model,
|
||||||
order=0,
|
order=0,
|
||||||
convention='Test',
|
convention='Test',
|
||||||
term_raw='Test1',
|
term_raw='Test1',
|
||||||
|
@ -521,7 +473,7 @@ class TestConstituentaAPI(EndpointTester):
|
||||||
self.cst2 = Constituenta.objects.create(
|
self.cst2 = Constituenta.objects.create(
|
||||||
alias='X2',
|
alias='X2',
|
||||||
cst_type=CstType.BASE,
|
cst_type=CstType.BASE,
|
||||||
schema=self.rsform_unowned.model,
|
schema=self.unowned.model,
|
||||||
order=0,
|
order=0,
|
||||||
convention='Test1',
|
convention='Test1',
|
||||||
term_raw='Test2',
|
term_raw='Test2',
|
||||||
|
@ -529,7 +481,7 @@ class TestConstituentaAPI(EndpointTester):
|
||||||
)
|
)
|
||||||
self.cst3 = Constituenta.objects.create(
|
self.cst3 = Constituenta.objects.create(
|
||||||
alias='X3',
|
alias='X3',
|
||||||
schema=self.rsform_owned.model,
|
schema=self.owned.model,
|
||||||
order=1,
|
order=1,
|
||||||
term_raw='Test3',
|
term_raw='Test3',
|
||||||
term_resolved='Test3',
|
term_resolved='Test3',
|
||||||
|
@ -541,18 +493,42 @@ class TestConstituentaAPI(EndpointTester):
|
||||||
@decl_endpoint('/api/rsforms/{schema}/update-cst', method='patch')
|
@decl_endpoint('/api/rsforms/{schema}/update-cst', method='patch')
|
||||||
def test_partial_update(self):
|
def test_partial_update(self):
|
||||||
data = {'target': self.cst1.pk, 'item_data': {'convention': 'tt'}}
|
data = {'target': self.cst1.pk, 'item_data': {'convention': 'tt'}}
|
||||||
self.executeForbidden(data=data, schema=self.rsform_unowned.model.pk)
|
self.executeForbidden(data=data, schema=self.unowned_id)
|
||||||
|
|
||||||
self.logout()
|
self.logout()
|
||||||
self.executeForbidden(data=data, schema=self.rsform_owned.model.pk)
|
self.executeForbidden(data=data, schema=self.owned_id)
|
||||||
|
|
||||||
self.login()
|
self.login()
|
||||||
response = self.executeOK(data=data, schema=self.rsform_owned.model.pk)
|
self.executeOK(data=data, schema=self.owned_id)
|
||||||
self.cst1.refresh_from_db()
|
self.cst1.refresh_from_db()
|
||||||
self.assertEqual(response.data['convention'], 'tt')
|
|
||||||
self.assertEqual(self.cst1.convention, 'tt')
|
self.assertEqual(self.cst1.convention, 'tt')
|
||||||
|
|
||||||
self.executeOK(data=data, schema=self.rsform_owned.model.pk)
|
self.executeOK(data=data, schema=self.owned_id)
|
||||||
|
|
||||||
|
|
||||||
|
@decl_endpoint('/api/rsforms/{schema}/update-cst', method='patch')
|
||||||
|
def test_partial_update_rename(self):
|
||||||
|
data = {'target': self.cst1.pk, 'item_data': {'alias': self.cst3.alias}}
|
||||||
|
self.executeBadData(data=data, schema=self.owned_id)
|
||||||
|
|
||||||
|
d1 = self.owned.insert_new(
|
||||||
|
alias='D1',
|
||||||
|
term_raw='@{X1|plur}',
|
||||||
|
definition_formal='X1'
|
||||||
|
)
|
||||||
|
self.assertEqual(self.cst1.order, 0)
|
||||||
|
self.assertEqual(self.cst1.alias, 'X1')
|
||||||
|
self.assertEqual(self.cst1.cst_type, CstType.BASE)
|
||||||
|
|
||||||
|
data = {'target': self.cst1.pk, 'item_data': {'alias': 'D2', 'cst_type': CstType.TERM}}
|
||||||
|
self.executeOK(data=data, schema=self.owned_id)
|
||||||
|
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, 0)
|
||||||
|
self.assertEqual(self.cst1.alias, 'D2')
|
||||||
|
self.assertEqual(self.cst1.cst_type, CstType.TERM)
|
||||||
|
|
||||||
|
|
||||||
@decl_endpoint('/api/rsforms/{schema}/update-cst', method='patch')
|
@decl_endpoint('/api/rsforms/{schema}/update-cst', method='patch')
|
||||||
|
@ -564,11 +540,9 @@ class TestConstituentaAPI(EndpointTester):
|
||||||
'definition_raw': 'New def'
|
'definition_raw': 'New def'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
response = self.executeOK(data=data, schema=self.rsform_owned.model.pk)
|
self.executeOK(data=data, schema=self.owned_id)
|
||||||
self.cst3.refresh_from_db()
|
self.cst3.refresh_from_db()
|
||||||
self.assertEqual(response.data['term_resolved'], 'New term')
|
|
||||||
self.assertEqual(self.cst3.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')
|
self.assertEqual(self.cst3.definition_resolved, 'New def')
|
||||||
|
|
||||||
|
|
||||||
|
@ -581,12 +555,10 @@ class TestConstituentaAPI(EndpointTester):
|
||||||
'definition_raw': '@{X1|nomn,sing} @{X1|sing,datv}'
|
'definition_raw': '@{X1|nomn,sing} @{X1|sing,datv}'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
response = self.executeOK(data=data, schema=self.rsform_owned.model.pk)
|
self.executeOK(data=data, schema=self.owned_id)
|
||||||
self.cst3.refresh_from_db()
|
self.cst3.refresh_from_db()
|
||||||
self.assertEqual(self.cst3.term_resolved, self.cst1.term_resolved)
|
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(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')
|
@decl_endpoint('/api/rsforms/{schema}/update-cst', method='patch')
|
||||||
def test_update_term_forms(self):
|
def test_update_term_forms(self):
|
||||||
|
@ -597,25 +569,10 @@ class TestConstituentaAPI(EndpointTester):
|
||||||
'term_forms': [{'text': 'form1', 'tags': 'sing,datv'}]
|
'term_forms': [{'text': 'form1', 'tags': 'sing,datv'}]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
response = self.executeOK(data=data, schema=self.rsform_owned.model.pk)
|
self.executeOK(data=data, schema=self.owned_id)
|
||||||
self.cst3.refresh_from_db()
|
self.cst3.refresh_from_db()
|
||||||
self.assertEqual(self.cst3.definition_resolved, 'form1')
|
self.assertEqual(self.cst3.definition_resolved, 'form1')
|
||||||
self.assertEqual(response.data['definition_resolved'], 'form1')
|
|
||||||
self.assertEqual(self.cst3.term_forms, data['item_data']['term_forms'])
|
self.assertEqual(self.cst3.term_forms, data['item_data']['term_forms'])
|
||||||
self.assertEqual(response.data['term_forms'], data['item_data']['term_forms'])
|
|
||||||
|
|
||||||
|
|
||||||
@decl_endpoint('/api/rsforms/{schema}/update-cst', method='patch')
|
|
||||||
def test_readonly_cst_fields(self):
|
|
||||||
data = {
|
|
||||||
'target': self.cst1.pk,
|
|
||||||
'item_data': {
|
|
||||||
'alias': 'X33'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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)
|
|
||||||
|
|
||||||
|
|
||||||
class TestInlineSynthesis(EndpointTester):
|
class TestInlineSynthesis(EndpointTester):
|
||||||
|
|
|
@ -41,7 +41,6 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
|
||||||
if self.action in [
|
if self.action in [
|
||||||
'load_trs',
|
'load_trs',
|
||||||
'create_cst',
|
'create_cst',
|
||||||
'rename_cst',
|
|
||||||
'update_cst',
|
'update_cst',
|
||||||
'move_cst',
|
'move_cst',
|
||||||
'delete_multiple_cst',
|
'delete_multiple_cst',
|
||||||
|
@ -102,7 +101,7 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
|
||||||
tags=['RSForm'],
|
tags=['RSForm'],
|
||||||
request=s.CstUpdateSerializer,
|
request=s.CstUpdateSerializer,
|
||||||
responses={
|
responses={
|
||||||
c.HTTP_200_OK: s.CstInfoSerializer,
|
c.HTTP_200_OK: s.RSFormParseSerializer,
|
||||||
c.HTTP_400_BAD_REQUEST: None,
|
c.HTTP_400_BAD_REQUEST: None,
|
||||||
c.HTTP_403_FORBIDDEN: None,
|
c.HTTP_403_FORBIDDEN: None,
|
||||||
c.HTTP_404_NOT_FOUND: None
|
c.HTTP_404_NOT_FOUND: None
|
||||||
|
@ -120,9 +119,22 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
old_data = schema.update_cst(cst, data)
|
old_data = schema.update_cst(cst, data)
|
||||||
PropagationFacade.after_update_cst(schema, cst, data, old_data)
|
PropagationFacade.after_update_cst(schema, cst, data, old_data)
|
||||||
|
if 'alias' in data and data['alias'] != cst.alias:
|
||||||
|
cst.refresh_from_db()
|
||||||
|
changed_type = 'cst_type' in data and cst.cst_type != data['cst_type']
|
||||||
|
mapping = {cst.alias: data['alias']}
|
||||||
|
cst.alias = data['alias']
|
||||||
|
if changed_type:
|
||||||
|
cst.cst_type = data['cst_type']
|
||||||
|
cst.save()
|
||||||
|
schema.apply_mapping(mapping=mapping, change_aliases=False)
|
||||||
|
schema.save()
|
||||||
|
cst.refresh_from_db()
|
||||||
|
if changed_type:
|
||||||
|
PropagationFacade.after_change_cst_type(schema, cst)
|
||||||
return Response(
|
return Response(
|
||||||
status=c.HTTP_200_OK,
|
status=c.HTTP_200_OK,
|
||||||
data=s.CstInfoSerializer(m.Constituenta.objects.get(pk=request.data['target'])).data
|
data=s.RSFormParseSerializer(schema.model).data
|
||||||
)
|
)
|
||||||
|
|
||||||
@extend_schema(
|
@extend_schema(
|
||||||
|
@ -169,43 +181,6 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@extend_schema(
|
|
||||||
summary='rename constituenta',
|
|
||||||
tags=['Constituenta'],
|
|
||||||
request=s.CstRenameSerializer,
|
|
||||||
responses={
|
|
||||||
c.HTTP_200_OK: s.NewCstResponse,
|
|
||||||
c.HTTP_400_BAD_REQUEST: None,
|
|
||||||
c.HTTP_403_FORBIDDEN: None,
|
|
||||||
c.HTTP_404_NOT_FOUND: None
|
|
||||||
}
|
|
||||||
)
|
|
||||||
@action(detail=True, methods=['patch'], url_path='rename-cst')
|
|
||||||
def rename_cst(self, request: Request, pk) -> HttpResponse:
|
|
||||||
''' Rename constituenta possibly changing type. '''
|
|
||||||
model = self._get_item()
|
|
||||||
serializer = s.CstRenameSerializer(data=request.data, context={'schema': model})
|
|
||||||
serializer.is_valid(raise_exception=True)
|
|
||||||
cst = cast(m.Constituenta, serializer.validated_data['target'])
|
|
||||||
changed_type = cst.cst_type != serializer.validated_data['cst_type']
|
|
||||||
mapping = {cst.alias: serializer.validated_data['alias']}
|
|
||||||
schema = m.RSForm(model)
|
|
||||||
with transaction.atomic():
|
|
||||||
cst.alias = serializer.validated_data['alias']
|
|
||||||
cst.cst_type = serializer.validated_data['cst_type']
|
|
||||||
cst.save()
|
|
||||||
schema.apply_mapping(mapping=mapping, change_aliases=False)
|
|
||||||
schema.save()
|
|
||||||
cst.refresh_from_db()
|
|
||||||
if changed_type:
|
|
||||||
PropagationFacade.after_change_cst_type(schema, cst)
|
|
||||||
return Response(
|
|
||||||
status=c.HTTP_200_OK,
|
|
||||||
data={
|
|
||||||
'new_cst': s.CstInfoSerializer(cst).data,
|
|
||||||
'schema': s.RSFormParseSerializer(schema.model).data
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
@extend_schema(
|
@extend_schema(
|
||||||
summary='execute substitutions',
|
summary='execute substitutions',
|
||||||
|
|
|
@ -27,7 +27,7 @@ export function ApplicationLayout() {
|
||||||
<NavigationState>
|
<NavigationState>
|
||||||
<div className='min-w-80 antialiased h-full max-w-480 mx-auto'>
|
<div className='min-w-80 antialiased h-full max-w-480 mx-auto'>
|
||||||
<ToasterThemed
|
<ToasterThemed
|
||||||
className={clsx('sm:text-[14px]/[20px] text-[12px]/[16px]', noNavigationAnimation ? 'mt-6' : 'mt-14')}
|
className={clsx('sm:text-[14px]/[20px] text-[12px]/[16px]', noNavigationAnimation ? 'mt-10' : 'mt-18')}
|
||||||
aria-label='Оповещения'
|
aria-label='Оповещения'
|
||||||
autoClose={3000}
|
autoClose={3000}
|
||||||
draggable={false}
|
draggable={false}
|
||||||
|
|
|
@ -8,7 +8,15 @@ import { generateAlias } from '@/features/rsform/models/rsform-api';
|
||||||
import { useCstSearchStore } from '@/features/rsform/stores/cst-search';
|
import { useCstSearchStore } from '@/features/rsform/stores/cst-search';
|
||||||
|
|
||||||
import { MiniButton } from '@/components/control';
|
import { MiniButton } from '@/components/control';
|
||||||
import { IconClone, IconDestroy, IconMoveDown, IconMoveUp, IconNewItem, IconRSForm } from '@/components/icons';
|
import {
|
||||||
|
IconClone,
|
||||||
|
IconDestroy,
|
||||||
|
IconEdit2,
|
||||||
|
IconMoveDown,
|
||||||
|
IconMoveUp,
|
||||||
|
IconNewItem,
|
||||||
|
IconRSForm
|
||||||
|
} from '@/components/icons';
|
||||||
import { cn } from '@/components/utils';
|
import { cn } from '@/components/utils';
|
||||||
import { useDialogsStore } from '@/stores/dialogs';
|
import { useDialogsStore } from '@/stores/dialogs';
|
||||||
import { PARAMETER, prefixes } from '@/utils/constants';
|
import { PARAMETER, prefixes } from '@/utils/constants';
|
||||||
|
@ -20,6 +28,7 @@ interface ToolbarConstituentsProps {
|
||||||
activeCst: IConstituenta | null;
|
activeCst: IConstituenta | null;
|
||||||
setActive: (cstID: number) => void;
|
setActive: (cstID: number) => void;
|
||||||
resetActive: () => void;
|
resetActive: () => void;
|
||||||
|
onEditActive: () => void;
|
||||||
className?: string;
|
className?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,6 +37,7 @@ export function ToolbarConstituents({
|
||||||
activeCst,
|
activeCst,
|
||||||
setActive,
|
setActive,
|
||||||
resetActive,
|
resetActive,
|
||||||
|
onEditActive,
|
||||||
isMutable,
|
isMutable,
|
||||||
className
|
className
|
||||||
}: ToolbarConstituentsProps) {
|
}: ToolbarConstituentsProps) {
|
||||||
|
@ -160,6 +170,13 @@ export function ToolbarConstituents({
|
||||||
onClick={navigateRSForm}
|
onClick={navigateRSForm}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<MiniButton
|
||||||
|
title='Редактировать конституенту'
|
||||||
|
icon={<IconEdit2 size='1rem' className='icon-primary' />}
|
||||||
|
onClick={onEditActive}
|
||||||
|
disabled={!isMutable || isProcessing || !activeCst}
|
||||||
|
/>
|
||||||
|
|
||||||
<MiniButton
|
<MiniButton
|
||||||
title='Создать конституенту'
|
title='Создать конституенту'
|
||||||
icon={<IconNewItem size='1rem' className='icon-green' />}
|
icon={<IconNewItem size='1rem' className='icon-green' />}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { RSFormStats } from '@/features/rsform/components/rsform-stats';
|
||||||
import { ViewConstituents } from '@/features/rsform/components/view-constituents';
|
import { ViewConstituents } from '@/features/rsform/components/view-constituents';
|
||||||
|
|
||||||
import { useFitHeight } from '@/stores/app-layout';
|
import { useFitHeight } from '@/stores/app-layout';
|
||||||
|
import { notImplemented } from '@/utils/utils';
|
||||||
|
|
||||||
import { ToolbarConstituents } from './toolbar-constituents';
|
import { ToolbarConstituents } from './toolbar-constituents';
|
||||||
|
|
||||||
|
@ -27,6 +28,7 @@ export function ViewSchema({ schemaID, isMutable }: ViewSchemaProps) {
|
||||||
schema={schema}
|
schema={schema}
|
||||||
activeCst={activeCst}
|
activeCst={activeCst}
|
||||||
isMutable={isMutable}
|
isMutable={isMutable}
|
||||||
|
onEditActive={notImplemented}
|
||||||
setActive={setActiveID}
|
setActive={setActiveID}
|
||||||
resetActive={() => setActiveID(null)}
|
resetActive={() => setActiveID(null)}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -6,7 +6,6 @@ import { infoMsg } from '@/utils/labels';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
type ICheckConstituentaDTO,
|
type ICheckConstituentaDTO,
|
||||||
type IConstituentaBasicsDTO,
|
|
||||||
type IConstituentaCreatedResponse,
|
type IConstituentaCreatedResponse,
|
||||||
type IConstituentaList,
|
type IConstituentaList,
|
||||||
type ICreateConstituentaDTO,
|
type ICreateConstituentaDTO,
|
||||||
|
@ -14,12 +13,10 @@ import {
|
||||||
type IInlineSynthesisDTO,
|
type IInlineSynthesisDTO,
|
||||||
type IMoveConstituentsDTO,
|
type IMoveConstituentsDTO,
|
||||||
type IProduceStructureResponse,
|
type IProduceStructureResponse,
|
||||||
type IRenameConstituentaDTO,
|
|
||||||
type IRSFormDTO,
|
type IRSFormDTO,
|
||||||
type IRSFormUploadDTO,
|
type IRSFormUploadDTO,
|
||||||
type ISubstitutionsDTO,
|
type ISubstitutionsDTO,
|
||||||
type IUpdateConstituentaDTO,
|
type IUpdateConstituentaDTO,
|
||||||
schemaConstituentaBasics,
|
|
||||||
schemaConstituentaCreatedResponse,
|
schemaConstituentaCreatedResponse,
|
||||||
schemaExpressionParse,
|
schemaExpressionParse,
|
||||||
schemaProduceStructureResponse,
|
schemaProduceStructureResponse,
|
||||||
|
@ -74,8 +71,8 @@ export const rsformsApi = {
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
updateConstituenta: ({ itemID, data }: { itemID: number; data: IUpdateConstituentaDTO }) =>
|
updateConstituenta: ({ itemID, data }: { itemID: number; data: IUpdateConstituentaDTO }) =>
|
||||||
axiosPatch<IUpdateConstituentaDTO, IConstituentaBasicsDTO>({
|
axiosPatch<IUpdateConstituentaDTO, IRSFormDTO>({
|
||||||
schema: schemaConstituentaBasics,
|
schema: schemaRSForm,
|
||||||
endpoint: `/api/rsforms/${itemID}/update-cst`,
|
endpoint: `/api/rsforms/${itemID}/update-cst`,
|
||||||
request: {
|
request: {
|
||||||
data: data,
|
data: data,
|
||||||
|
@ -91,15 +88,6 @@ export const rsformsApi = {
|
||||||
successMessage: infoMsg.constituentsDestroyed(data.items.length)
|
successMessage: infoMsg.constituentsDestroyed(data.items.length)
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
renameConstituenta: ({ itemID, data }: { itemID: number; data: IRenameConstituentaDTO }) =>
|
|
||||||
axiosPatch<IRenameConstituentaDTO, IConstituentaCreatedResponse>({
|
|
||||||
schema: schemaConstituentaCreatedResponse,
|
|
||||||
endpoint: `/api/rsforms/${itemID}/rename-cst`,
|
|
||||||
request: {
|
|
||||||
data: data,
|
|
||||||
successMessage: infoMsg.changesSaved
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
substituteConstituents: ({ itemID, data }: { itemID: number; data: ISubstitutionsDTO }) =>
|
substituteConstituents: ({ itemID, data }: { itemID: number; data: ISubstitutionsDTO }) =>
|
||||||
axiosPatch<ISubstitutionsDTO, IRSFormDTO>({
|
axiosPatch<ISubstitutionsDTO, IRSFormDTO>({
|
||||||
schema: schemaRSForm,
|
schema: schemaRSForm,
|
||||||
|
|
|
@ -64,9 +64,6 @@ export type IConstituentaCreatedResponse = z.infer<typeof schemaConstituentaCrea
|
||||||
/** Represents data, used in updating persistent attributes in {@link IConstituenta}. */
|
/** Represents data, used in updating persistent attributes in {@link IConstituenta}. */
|
||||||
export type IUpdateConstituentaDTO = z.infer<typeof schemaUpdateConstituenta>;
|
export type IUpdateConstituentaDTO = z.infer<typeof schemaUpdateConstituenta>;
|
||||||
|
|
||||||
/** Represents data, used in renaming {@link IConstituenta}. */
|
|
||||||
export type IRenameConstituentaDTO = z.infer<typeof schemaRenameConstituenta>;
|
|
||||||
|
|
||||||
/** Represents data, used in ordering a list of {@link IConstituenta}. */
|
/** Represents data, used in ordering a list of {@link IConstituenta}. */
|
||||||
export interface IMoveConstituentsDTO {
|
export interface IMoveConstituentsDTO {
|
||||||
items: number[];
|
items: number[];
|
||||||
|
@ -344,6 +341,8 @@ export const schemaConstituentaCreatedResponse = z.strictObject({
|
||||||
export const schemaUpdateConstituenta = z.strictObject({
|
export const schemaUpdateConstituenta = z.strictObject({
|
||||||
target: z.number(),
|
target: z.number(),
|
||||||
item_data: z.strictObject({
|
item_data: z.strictObject({
|
||||||
|
alias: z.string().optional(),
|
||||||
|
cst_type: schemaCstType.optional(),
|
||||||
convention: z.string().optional(),
|
convention: z.string().optional(),
|
||||||
definition_formal: z.string().optional(),
|
definition_formal: z.string().optional(),
|
||||||
definition_raw: z.string().optional(),
|
definition_raw: z.string().optional(),
|
||||||
|
@ -352,12 +351,6 @@ export const schemaUpdateConstituenta = z.strictObject({
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
export const schemaRenameConstituenta = z.strictObject({
|
|
||||||
target: z.number(),
|
|
||||||
alias: z.string(),
|
|
||||||
cst_type: schemaCstType
|
|
||||||
});
|
|
||||||
|
|
||||||
export const schemaProduceStructureResponse = z.strictObject({
|
export const schemaProduceStructureResponse = z.strictObject({
|
||||||
cst_list: z.array(z.number()),
|
cst_list: z.array(z.number()),
|
||||||
schema: schemaRSForm
|
schema: schemaRSForm
|
||||||
|
|
|
@ -1,33 +0,0 @@
|
||||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
|
||||||
|
|
||||||
import { useUpdateTimestamp } from '@/features/library/backend/use-update-timestamp';
|
|
||||||
|
|
||||||
import { KEYS } from '@/backend/configuration';
|
|
||||||
|
|
||||||
import { rsformsApi } from './api';
|
|
||||||
import { type IRenameConstituentaDTO } from './types';
|
|
||||||
|
|
||||||
export const useRenameConstituenta = () => {
|
|
||||||
const client = useQueryClient();
|
|
||||||
const { updateTimestamp } = useUpdateTimestamp();
|
|
||||||
const mutation = useMutation({
|
|
||||||
mutationKey: [KEYS.global_mutation, rsformsApi.baseKey, 'rename-constituenta'],
|
|
||||||
mutationFn: rsformsApi.renameConstituenta,
|
|
||||||
onSuccess: async data => {
|
|
||||||
client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.schema.id }).queryKey, data.schema);
|
|
||||||
updateTimestamp(data.schema.id);
|
|
||||||
|
|
||||||
await Promise.allSettled([
|
|
||||||
client.invalidateQueries({ queryKey: [KEYS.oss] }),
|
|
||||||
client.invalidateQueries({
|
|
||||||
queryKey: [rsformsApi.baseKey],
|
|
||||||
predicate: query => query.queryKey.length > 2 && query.queryKey[2] !== String(data.schema.id)
|
|
||||||
})
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
onError: () => client.invalidateQueries()
|
|
||||||
});
|
|
||||||
return {
|
|
||||||
renameConstituenta: (data: { itemID: number; data: IRenameConstituentaDTO }) => mutation.mutateAsync(data)
|
|
||||||
};
|
|
||||||
};
|
|
|
@ -13,12 +13,16 @@ export const useUpdateConstituenta = () => {
|
||||||
const mutation = useMutation({
|
const mutation = useMutation({
|
||||||
mutationKey: [KEYS.global_mutation, rsformsApi.baseKey, 'update-constituenta'],
|
mutationKey: [KEYS.global_mutation, rsformsApi.baseKey, 'update-constituenta'],
|
||||||
mutationFn: rsformsApi.updateConstituenta,
|
mutationFn: rsformsApi.updateConstituenta,
|
||||||
onSuccess: async (_, variables) => {
|
onSuccess: async (data, _) => {
|
||||||
updateTimestamp(variables.itemID);
|
client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey, data);
|
||||||
|
updateTimestamp(data.id);
|
||||||
|
|
||||||
await Promise.allSettled([
|
await Promise.allSettled([
|
||||||
client.invalidateQueries({ queryKey: [KEYS.oss] }),
|
client.invalidateQueries({ queryKey: [KEYS.oss] }),
|
||||||
client.invalidateQueries({ queryKey: [rsformsApi.baseKey] })
|
client.invalidateQueries({
|
||||||
|
queryKey: [rsformsApi.baseKey],
|
||||||
|
predicate: query => query.queryKey.length > 2 && query.queryKey[2] !== String(data.id)
|
||||||
|
})
|
||||||
]);
|
]);
|
||||||
},
|
},
|
||||||
onError: () => client.invalidateQueries()
|
onError: () => client.invalidateQueries()
|
||||||
|
|
|
@ -9,8 +9,8 @@ import { TextInput } from '@/components/input';
|
||||||
import { ModalForm } from '@/components/modal';
|
import { ModalForm } from '@/components/modal';
|
||||||
import { useDialogsStore } from '@/stores/dialogs';
|
import { useDialogsStore } from '@/stores/dialogs';
|
||||||
|
|
||||||
import { type CstType, type IRenameConstituentaDTO, schemaRenameConstituenta } from '../backend/types';
|
import { type CstType, type IUpdateConstituentaDTO, schemaUpdateConstituenta } from '../backend/types';
|
||||||
import { useRenameConstituenta } from '../backend/use-rename-constituenta';
|
import { useUpdateConstituenta } from '../backend/use-update-constituenta';
|
||||||
import { SelectCstType } from '../components/select-cst-type';
|
import { SelectCstType } from '../components/select-cst-type';
|
||||||
import { type IConstituenta, type IRSForm } from '../models/rsform';
|
import { type IConstituenta, type IRSForm } from '../models/rsform';
|
||||||
import { generateAlias, validateNewAlias } from '../models/rsform-api';
|
import { generateAlias, validateNewAlias } from '../models/rsform-api';
|
||||||
|
@ -22,27 +22,29 @@ export interface DlgRenameCstProps {
|
||||||
|
|
||||||
export function DlgRenameCst() {
|
export function DlgRenameCst() {
|
||||||
const { schema, target } = useDialogsStore(state => state.props as DlgRenameCstProps);
|
const { schema, target } = useDialogsStore(state => state.props as DlgRenameCstProps);
|
||||||
const { renameConstituenta: cstRename } = useRenameConstituenta();
|
const { updateConstituenta: cstUpdate } = useUpdateConstituenta();
|
||||||
|
|
||||||
const { register, setValue, handleSubmit, control } = useForm<IRenameConstituentaDTO>({
|
const { register, setValue, handleSubmit, control } = useForm<IUpdateConstituentaDTO>({
|
||||||
resolver: zodResolver(schemaRenameConstituenta),
|
resolver: zodResolver(schemaUpdateConstituenta),
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
target: target.id,
|
target: target.id,
|
||||||
alias: target.alias,
|
item_data: {
|
||||||
cst_type: target.cst_type
|
alias: target.alias,
|
||||||
|
cst_type: target.cst_type
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const alias = useWatch({ control, name: 'alias' });
|
const alias = useWatch({ control, name: 'item_data.alias' })!;
|
||||||
const cst_type = useWatch({ control, name: 'cst_type' });
|
const cst_type = useWatch({ control, name: 'item_data.cst_type' })!;
|
||||||
const isValid = alias !== target.alias && validateNewAlias(alias, cst_type, schema);
|
const isValid = alias !== target.alias && validateNewAlias(alias, cst_type, schema);
|
||||||
|
|
||||||
function onSubmit(data: IRenameConstituentaDTO) {
|
function onSubmit(data: IUpdateConstituentaDTO) {
|
||||||
return cstRename({ itemID: schema.id, data: data });
|
return cstUpdate({ itemID: schema.id, data: data });
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleChangeType(newType: CstType) {
|
function handleChangeType(newType: CstType) {
|
||||||
setValue('cst_type', newType);
|
setValue('item_data.cst_type', newType);
|
||||||
setValue('alias', generateAlias(newType, schema), { shouldValidate: true });
|
setValue('item_data.alias', generateAlias(newType, schema), { shouldValidate: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -63,7 +65,7 @@ export function DlgRenameCst() {
|
||||||
/>
|
/>
|
||||||
<TextInput
|
<TextInput
|
||||||
id='dlg_cst_alias' //
|
id='dlg_cst_alias' //
|
||||||
{...register('alias')}
|
{...register('item_data.alias')}
|
||||||
dense
|
dense
|
||||||
label='Имя'
|
label='Имя'
|
||||||
className='w-28'
|
className='w-28'
|
||||||
|
|
Loading…
Reference in New Issue
Block a user