mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 13:00:39 +03:00
Refactor constituenta API and remove move restriction
This commit is contained in:
parent
31cbb6f05a
commit
03fb9e3fd0
|
@ -1,6 +1,4 @@
|
||||||
''' Models: RSForms for conceptual schemas. '''
|
''' Models: RSForms for conceptual schemas. '''
|
||||||
import json
|
|
||||||
from copy import deepcopy
|
|
||||||
import re
|
import re
|
||||||
from typing import Iterable, Optional, cast
|
from typing import Iterable, Optional, cast
|
||||||
|
|
||||||
|
@ -13,7 +11,6 @@ from django.core.validators import MinValueValidator
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
|
||||||
import pyconcept
|
|
||||||
from apps.users.models import User
|
from apps.users.models import User
|
||||||
from cctext import Resolver, Entity, extract_entities
|
from cctext import Resolver, Entity, extract_entities
|
||||||
from .graph import Graph
|
from .graph import Graph
|
||||||
|
@ -315,6 +312,8 @@ class RSForm:
|
||||||
''' Insert new constituenta at given position. All following constituents order is shifted by 1 position '''
|
''' Insert new constituenta at given position. All following constituents order is shifted by 1 position '''
|
||||||
if position <= 0:
|
if position <= 0:
|
||||||
raise ValidationError('Invalid position: should be positive integer')
|
raise ValidationError('Invalid position: should be positive integer')
|
||||||
|
currentSize = self.constituents().count()
|
||||||
|
position = max(1, min(position, currentSize + 1))
|
||||||
update_list = Constituenta.objects.only('id', 'order', 'schema').filter(schema=self.item, order__gte=position)
|
update_list = Constituenta.objects.only('id', 'order', 'schema').filter(schema=self.item, order__gte=position)
|
||||||
for cst in update_list:
|
for cst in update_list:
|
||||||
cst.order += 1
|
cst.order += 1
|
||||||
|
@ -326,7 +325,6 @@ class RSForm:
|
||||||
alias=alias,
|
alias=alias,
|
||||||
cst_type=insert_type
|
cst_type=insert_type
|
||||||
)
|
)
|
||||||
self.update_order()
|
|
||||||
self.item.save()
|
self.item.save()
|
||||||
result.refresh_from_db()
|
result.refresh_from_db()
|
||||||
return result
|
return result
|
||||||
|
@ -343,7 +341,6 @@ class RSForm:
|
||||||
alias=alias,
|
alias=alias,
|
||||||
cst_type=insert_type
|
cst_type=insert_type
|
||||||
)
|
)
|
||||||
self.update_order()
|
|
||||||
self.item.save()
|
self.item.save()
|
||||||
result.refresh_from_db()
|
result.refresh_from_db()
|
||||||
return result
|
return result
|
||||||
|
@ -369,7 +366,6 @@ class RSForm:
|
||||||
count_moved += 1
|
count_moved += 1
|
||||||
update_list.append(cst)
|
update_list.append(cst)
|
||||||
Constituenta.objects.bulk_update(update_list, ['order'])
|
Constituenta.objects.bulk_update(update_list, ['order'])
|
||||||
self.update_order()
|
|
||||||
self.item.save()
|
self.item.save()
|
||||||
|
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
|
@ -377,7 +373,7 @@ class RSForm:
|
||||||
''' Delete multiple constituents. Do not check if listCst are from this schema '''
|
''' Delete multiple constituents. Do not check if listCst are from this schema '''
|
||||||
for cst in listCst:
|
for cst in listCst:
|
||||||
cst.delete()
|
cst.delete()
|
||||||
self.update_order()
|
self._reset_order()
|
||||||
self.resolve_all_text()
|
self.resolve_all_text()
|
||||||
self.item.save()
|
self.item.save()
|
||||||
|
|
||||||
|
@ -447,23 +443,6 @@ class RSForm:
|
||||||
if modified:
|
if modified:
|
||||||
cst.save()
|
cst.save()
|
||||||
|
|
||||||
@transaction.atomic
|
|
||||||
def update_order(self):
|
|
||||||
''' Update constituents order. '''
|
|
||||||
checked = PyConceptAdapter(self).basic()
|
|
||||||
update_list = self.constituents().only('id', 'order')
|
|
||||||
if len(checked['items']) != update_list.count():
|
|
||||||
raise ValidationError('Invalid constituents count')
|
|
||||||
order = 1
|
|
||||||
for cst in checked['items']:
|
|
||||||
cst_id = cst['id']
|
|
||||||
for oldCst in update_list:
|
|
||||||
if oldCst.pk == cst_id:
|
|
||||||
oldCst.order = order
|
|
||||||
order += 1
|
|
||||||
break
|
|
||||||
Constituenta.objects.bulk_update(update_list, ['order'])
|
|
||||||
|
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
def resolve_all_text(self):
|
def resolve_all_text(self):
|
||||||
''' Trigger reference resolution for all texts. '''
|
''' Trigger reference resolution for all texts. '''
|
||||||
|
@ -482,6 +461,15 @@ class RSForm:
|
||||||
cst.definition_resolved = resolved
|
cst.definition_resolved = resolved
|
||||||
cst.save()
|
cst.save()
|
||||||
|
|
||||||
|
@transaction.atomic
|
||||||
|
def _reset_order(self):
|
||||||
|
order = 1
|
||||||
|
for cst in self.constituents().only('id', 'order').order_by('order'):
|
||||||
|
if cst.order != order:
|
||||||
|
cst.order = order
|
||||||
|
cst.save()
|
||||||
|
order += 1
|
||||||
|
|
||||||
def _insert_new(self, data: dict, insert_after: Optional[str]=None) -> 'Constituenta':
|
def _insert_new(self, data: dict, insert_after: Optional[str]=None) -> 'Constituenta':
|
||||||
if insert_after is not None:
|
if insert_after is not None:
|
||||||
cstafter = Constituenta.objects.get(pk=insert_after)
|
cstafter = Constituenta.objects.get(pk=insert_after)
|
||||||
|
@ -510,87 +498,3 @@ class RSForm:
|
||||||
if result.contains(alias):
|
if result.contains(alias):
|
||||||
result.add_edge(id_from=alias, id_to=cst.alias)
|
result.add_edge(id_from=alias, id_to=cst.alias)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
class PyConceptAdapter:
|
|
||||||
''' RSForm adapter for interacting with pyconcept module. '''
|
|
||||||
def __init__(self, instance: RSForm):
|
|
||||||
self.schema = instance
|
|
||||||
self.data = self._prepare_request()
|
|
||||||
self._checked_data: Optional[dict] = None
|
|
||||||
|
|
||||||
def basic(self) -> dict:
|
|
||||||
''' Check RSForm and return check results.
|
|
||||||
Warning! Does not include texts. '''
|
|
||||||
self._produce_response()
|
|
||||||
if self._checked_data is None:
|
|
||||||
raise ValueError('Invalid data response from pyconcept')
|
|
||||||
return self._checked_data
|
|
||||||
|
|
||||||
def full(self) -> dict:
|
|
||||||
''' Check RSForm and return check results including initial texts. '''
|
|
||||||
self._produce_response()
|
|
||||||
if self._checked_data is None:
|
|
||||||
raise ValueError('Invalid data response from pyconcept')
|
|
||||||
return self._complete_rsform_details(self._checked_data)
|
|
||||||
|
|
||||||
def _complete_rsform_details(self, data: dict) -> dict:
|
|
||||||
result = deepcopy(data)
|
|
||||||
result['id'] = self.schema.item.pk
|
|
||||||
result['alias'] = self.schema.item.alias
|
|
||||||
result['title'] = self.schema.item.title
|
|
||||||
result['comment'] = self.schema.item.comment
|
|
||||||
result['time_update'] = self.schema.item.time_update
|
|
||||||
result['time_create'] = self.schema.item.time_create
|
|
||||||
result['is_common'] = self.schema.item.is_common
|
|
||||||
result['is_canonical'] = self.schema.item.is_canonical
|
|
||||||
result['owner'] = (self.schema.item.owner.pk if self.schema.item.owner is not None else None)
|
|
||||||
for cst_data in result['items']:
|
|
||||||
cst = Constituenta.objects.get(pk=cst_data['id'])
|
|
||||||
cst_data['convention'] = cst.convention
|
|
||||||
cst_data['term'] = {
|
|
||||||
'raw': cst.term_raw,
|
|
||||||
'resolved': cst.term_resolved,
|
|
||||||
'forms': cst.term_forms
|
|
||||||
}
|
|
||||||
cst_data['definition']['text'] = {
|
|
||||||
'raw': cst.definition_raw,
|
|
||||||
'resolved': cst.definition_resolved,
|
|
||||||
}
|
|
||||||
result['subscribers'] = [item.pk for item in self.schema.item.subscribers()]
|
|
||||||
return result
|
|
||||||
|
|
||||||
def _prepare_request(self) -> dict:
|
|
||||||
result: dict = {
|
|
||||||
'items': []
|
|
||||||
}
|
|
||||||
items = self.schema.constituents().order_by('order')
|
|
||||||
for cst in items:
|
|
||||||
result['items'].append({
|
|
||||||
'entityUID': cst.pk,
|
|
||||||
'cstType': cst.cst_type,
|
|
||||||
'alias': cst.alias,
|
|
||||||
'definition': {
|
|
||||||
'formal': cst.definition_formal
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return result
|
|
||||||
|
|
||||||
def _produce_response(self):
|
|
||||||
if self._checked_data is not None:
|
|
||||||
return
|
|
||||||
response = pyconcept.check_schema(json.dumps(self.data))
|
|
||||||
data = json.loads(response)
|
|
||||||
self._checked_data = {
|
|
||||||
'items': []
|
|
||||||
}
|
|
||||||
for cst in data['items']:
|
|
||||||
self._checked_data['items'].append({
|
|
||||||
'id': cst['entityUID'],
|
|
||||||
'cstType': cst['cstType'],
|
|
||||||
'alias': cst['alias'],
|
|
||||||
'definition': {
|
|
||||||
'formal': cst['definition']['formal']
|
|
||||||
},
|
|
||||||
'parse': cst['parse']
|
|
||||||
})
|
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
''' Serializers for conceptual schema API. '''
|
''' Serializers for conceptual schema API. '''
|
||||||
|
import json
|
||||||
from typing import Optional, cast
|
from typing import Optional, cast
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
|
|
||||||
|
import pyconcept
|
||||||
from cctext import Resolver, Reference, ReferenceType, EntityReference, SyntacticReference
|
from cctext import Resolver, Reference, ReferenceType, EntityReference, SyntacticReference
|
||||||
|
|
||||||
from .utils import fix_old_references
|
from .utils import fix_old_references
|
||||||
|
@ -31,7 +33,7 @@ class TextSerializer(serializers.Serializer):
|
||||||
|
|
||||||
|
|
||||||
class LibraryItemSerializer(serializers.ModelSerializer):
|
class LibraryItemSerializer(serializers.ModelSerializer):
|
||||||
''' Serializer: Library item data. '''
|
''' Serializer: LibraryItem entry. '''
|
||||||
class Meta:
|
class Meta:
|
||||||
''' serializer metadata. '''
|
''' serializer metadata. '''
|
||||||
model = LibraryItem
|
model = LibraryItem
|
||||||
|
@ -39,6 +41,71 @@ class LibraryItemSerializer(serializers.ModelSerializer):
|
||||||
read_only_fields = ('owner', 'id', 'item_type')
|
read_only_fields = ('owner', 'id', 'item_type')
|
||||||
|
|
||||||
|
|
||||||
|
class LibraryItemDetailsSerializer(serializers.ModelSerializer):
|
||||||
|
''' Serializer: LibraryItem detailed data. '''
|
||||||
|
class Meta:
|
||||||
|
''' serializer metadata. '''
|
||||||
|
model = LibraryItem
|
||||||
|
fields = '__all__'
|
||||||
|
read_only_fields = ('owner', 'id', 'item_type')
|
||||||
|
|
||||||
|
def to_representation(self, instance: LibraryItem):
|
||||||
|
result = super().to_representation(instance)
|
||||||
|
result['subscribers'] = [item.pk for item in instance.subscribers()]
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
class PyConceptAdapter:
|
||||||
|
''' RSForm adapter for interacting with pyconcept module. '''
|
||||||
|
def __init__(self, instance: RSForm):
|
||||||
|
self.schema = instance
|
||||||
|
self.data = self._prepare_request()
|
||||||
|
self._checked_data: Optional[dict] = None
|
||||||
|
|
||||||
|
def parse(self) -> dict:
|
||||||
|
''' Check RSForm and return check results.
|
||||||
|
Warning! Does not include texts. '''
|
||||||
|
self._produce_response()
|
||||||
|
if self._checked_data is None:
|
||||||
|
raise ValueError('Invalid data response from pyconcept')
|
||||||
|
return self._checked_data
|
||||||
|
|
||||||
|
def _prepare_request(self) -> dict:
|
||||||
|
result: dict = {
|
||||||
|
'items': []
|
||||||
|
}
|
||||||
|
items = self.schema.constituents().order_by('order')
|
||||||
|
for cst in items:
|
||||||
|
result['items'].append({
|
||||||
|
'entityUID': cst.pk,
|
||||||
|
'cstType': cst.cst_type,
|
||||||
|
'alias': cst.alias,
|
||||||
|
'definition': {
|
||||||
|
'formal': cst.definition_formal
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return result
|
||||||
|
|
||||||
|
def _produce_response(self):
|
||||||
|
if self._checked_data is not None:
|
||||||
|
return
|
||||||
|
response = pyconcept.check_schema(json.dumps(self.data))
|
||||||
|
data = json.loads(response)
|
||||||
|
self._checked_data = {
|
||||||
|
'items': []
|
||||||
|
}
|
||||||
|
for cst in data['items']:
|
||||||
|
self._checked_data['items'].append({
|
||||||
|
'id': cst['entityUID'],
|
||||||
|
'cstType': cst['cstType'],
|
||||||
|
'alias': cst['alias'],
|
||||||
|
'definition': {
|
||||||
|
'formal': cst['definition']['formal']
|
||||||
|
},
|
||||||
|
'parse': cst['parse']
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
class RSFormSerializer(serializers.ModelSerializer):
|
class RSFormSerializer(serializers.ModelSerializer):
|
||||||
''' Serializer: Detailed data for RSForm. '''
|
''' Serializer: Detailed data for RSForm. '''
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -46,13 +113,30 @@ class RSFormSerializer(serializers.ModelSerializer):
|
||||||
model = RSForm
|
model = RSForm
|
||||||
|
|
||||||
def to_representation(self, instance: RSForm):
|
def to_representation(self, instance: RSForm):
|
||||||
result = LibraryItemSerializer(instance.item).data
|
result = LibraryItemDetailsSerializer(instance.item).data
|
||||||
result['items'] = []
|
result['items'] = []
|
||||||
for cst in instance.constituents().order_by('order'):
|
for cst in instance.constituents().order_by('order'):
|
||||||
result['items'].append(ConstituentaSerializer(cst).data)
|
result['items'].append(ConstituentaSerializer(cst).data)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
class RSFormParseSerializer(serializers.ModelSerializer):
|
||||||
|
''' Serializer: Detailed data for RSForm including parse. '''
|
||||||
|
class Meta:
|
||||||
|
''' serializer metadata. '''
|
||||||
|
model = RSForm
|
||||||
|
|
||||||
|
def to_representation(self, instance: RSForm):
|
||||||
|
result = RSFormSerializer(instance).data
|
||||||
|
parse = PyConceptAdapter(instance).parse()
|
||||||
|
for cst_data in result['items']:
|
||||||
|
cst_data['parse'] = next(
|
||||||
|
cst['parse'] for cst in parse['items']
|
||||||
|
if cst['id'] == cst_data['id']
|
||||||
|
)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
class RSFormUploadSerializer(serializers.Serializer):
|
class RSFormUploadSerializer(serializers.Serializer):
|
||||||
''' Upload data for RSForm serializer. '''
|
''' Upload data for RSForm serializer. '''
|
||||||
file = serializers.FileField()
|
file = serializers.FileField()
|
||||||
|
@ -193,7 +277,6 @@ class RSFormTRSSerializer(serializers.Serializer):
|
||||||
if prev_cst.pk not in loaded_ids:
|
if prev_cst.pk not in loaded_ids:
|
||||||
prev_cst.delete()
|
prev_cst.delete()
|
||||||
|
|
||||||
instance.update_order()
|
|
||||||
instance.resolve_all_text()
|
instance.resolve_all_text()
|
||||||
instance.item.save()
|
instance.item.save()
|
||||||
return instance
|
return instance
|
||||||
|
|
|
@ -200,10 +200,10 @@ class TestRSForm(TestCase):
|
||||||
d2 = schema.insert_at(1, 'D2', CstType.TERM)
|
d2 = schema.insert_at(1, 'D2', CstType.TERM)
|
||||||
d1.refresh_from_db()
|
d1.refresh_from_db()
|
||||||
self.assertEqual(d1.order, 3)
|
self.assertEqual(d1.order, 3)
|
||||||
self.assertEqual(d2.order, 2)
|
self.assertEqual(d2.order, 1)
|
||||||
|
|
||||||
x2 = schema.insert_at(4, 'X2', CstType.BASE)
|
x2 = schema.insert_at(4, 'X2', CstType.BASE)
|
||||||
self.assertEqual(x2.order, 2)
|
self.assertEqual(x2.order, 4)
|
||||||
|
|
||||||
def test_insert_last(self):
|
def test_insert_last(self):
|
||||||
schema = RSForm.create(title='Test')
|
schema = RSForm.create(title='Test')
|
||||||
|
@ -259,10 +259,10 @@ class TestRSForm(TestCase):
|
||||||
x2.refresh_from_db()
|
x2.refresh_from_db()
|
||||||
d1.refresh_from_db()
|
d1.refresh_from_db()
|
||||||
d2.refresh_from_db()
|
d2.refresh_from_db()
|
||||||
self.assertEqual(x1.order, 2)
|
self.assertEqual(x1.order, 3)
|
||||||
self.assertEqual(x2.order, 1)
|
self.assertEqual(x2.order, 1)
|
||||||
self.assertEqual(d1.order, 4)
|
self.assertEqual(d1.order, 4)
|
||||||
self.assertEqual(d2.order, 3)
|
self.assertEqual(d2.order, 2)
|
||||||
|
|
||||||
def test_move_cst_down(self):
|
def test_move_cst_down(self):
|
||||||
schema = RSForm.create(title='Test')
|
schema = RSForm.create(title='Test')
|
||||||
|
|
|
@ -287,11 +287,11 @@ class TestRSFormViewset(APITestCase):
|
||||||
self.assertEqual(len(response.data['items']), 2)
|
self.assertEqual(len(response.data['items']), 2)
|
||||||
self.assertEqual(response.data['items'][0]['id'], x1.id)
|
self.assertEqual(response.data['items'][0]['id'], x1.id)
|
||||||
self.assertEqual(response.data['items'][0]['parse']['status'], 'verified')
|
self.assertEqual(response.data['items'][0]['parse']['status'], 'verified')
|
||||||
self.assertEqual(response.data['items'][0]['term']['raw'], x1.term_raw)
|
self.assertEqual(response.data['items'][0]['term_raw'], x1.term_raw)
|
||||||
self.assertEqual(response.data['items'][0]['term']['resolved'], x1.term_resolved)
|
self.assertEqual(response.data['items'][0]['term_resolved'], x1.term_resolved)
|
||||||
self.assertEqual(response.data['items'][1]['id'], x2.id)
|
self.assertEqual(response.data['items'][1]['id'], x2.id)
|
||||||
self.assertEqual(response.data['items'][1]['term']['raw'], x2.term_raw)
|
self.assertEqual(response.data['items'][1]['term_raw'], x2.term_raw)
|
||||||
self.assertEqual(response.data['items'][1]['term']['resolved'], x2.term_resolved)
|
self.assertEqual(response.data['items'][1]['term_resolved'], x2.term_resolved)
|
||||||
self.assertEqual(response.data['subscribers'], [self.user.pk])
|
self.assertEqual(response.data['subscribers'], [self.user.pk])
|
||||||
|
|
||||||
def test_check(self):
|
def test_check(self):
|
||||||
|
@ -412,6 +412,7 @@ class TestRSFormViewset(APITestCase):
|
||||||
d1.definition_formal = 'X1'
|
d1.definition_formal = 'X1'
|
||||||
d1.save()
|
d1.save()
|
||||||
|
|
||||||
|
self.assertEqual(d1.order, 4)
|
||||||
self.assertEqual(self.cst1.order, 1)
|
self.assertEqual(self.cst1.order, 1)
|
||||||
self.assertEqual(self.cst1.alias, 'X1')
|
self.assertEqual(self.cst1.alias, 'X1')
|
||||||
self.assertEqual(self.cst1.cst_type, CstType.BASE)
|
self.assertEqual(self.cst1.cst_type, CstType.BASE)
|
||||||
|
@ -422,9 +423,10 @@ class TestRSFormViewset(APITestCase):
|
||||||
self.assertEqual(response.data['new_cst']['cst_type'], 'term')
|
self.assertEqual(response.data['new_cst']['cst_type'], 'term')
|
||||||
d1.refresh_from_db()
|
d1.refresh_from_db()
|
||||||
self.cst1.refresh_from_db()
|
self.cst1.refresh_from_db()
|
||||||
|
self.assertEqual(d1.order, 4)
|
||||||
self.assertEqual(d1.term_resolved, '')
|
self.assertEqual(d1.term_resolved, '')
|
||||||
self.assertEqual(d1.term_raw, '@{D2|plur}')
|
self.assertEqual(d1.term_raw, '@{D2|plur}')
|
||||||
self.assertEqual(self.cst1.order, 2)
|
self.assertEqual(self.cst1.order, 1)
|
||||||
self.assertEqual(self.cst1.alias, 'D2')
|
self.assertEqual(self.cst1.alias, 'D2')
|
||||||
self.assertEqual(self.cst1.cst_type, CstType.TERM)
|
self.assertEqual(self.cst1.cst_type, CstType.TERM)
|
||||||
|
|
||||||
|
@ -560,10 +562,10 @@ class TestRSFormViewset(APITestCase):
|
||||||
self.assertEqual(response.status_code, 201)
|
self.assertEqual(response.status_code, 201)
|
||||||
self.assertEqual(response.data['title'], 'Title')
|
self.assertEqual(response.data['title'], 'Title')
|
||||||
self.assertEqual(response.data['items'][0]['alias'], x1.alias)
|
self.assertEqual(response.data['items'][0]['alias'], x1.alias)
|
||||||
self.assertEqual(response.data['items'][0]['term']['raw'], x1.term_raw)
|
self.assertEqual(response.data['items'][0]['term_raw'], x1.term_raw)
|
||||||
self.assertEqual(response.data['items'][0]['term']['resolved'], x1.term_resolved)
|
self.assertEqual(response.data['items'][0]['term_resolved'], x1.term_resolved)
|
||||||
self.assertEqual(response.data['items'][1]['term']['raw'], d1.term_raw)
|
self.assertEqual(response.data['items'][1]['term_raw'], d1.term_raw)
|
||||||
self.assertEqual(response.data['items'][1]['term']['resolved'], d1.term_resolved)
|
self.assertEqual(response.data['items'][1]['term_resolved'], d1.term_resolved)
|
||||||
|
|
||||||
|
|
||||||
class TestFunctionalViews(APITestCase):
|
class TestFunctionalViews(APITestCase):
|
||||||
|
|
|
@ -94,7 +94,7 @@ class LibraryViewSet(viewsets.ModelViewSet):
|
||||||
clone = s.RSFormTRSSerializer(data=clone_data, context={'load_meta': True})
|
clone = s.RSFormTRSSerializer(data=clone_data, context={'load_meta': True})
|
||||||
clone.is_valid(raise_exception=True)
|
clone.is_valid(raise_exception=True)
|
||||||
new_schema = clone.save()
|
new_schema = clone.save()
|
||||||
return Response(status=201, data=m.PyConceptAdapter(new_schema).full())
|
return Response(status=201, data=s.RSFormParseSerializer(new_schema).data)
|
||||||
return Response(status=404)
|
return Response(status=404)
|
||||||
|
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
|
@ -153,7 +153,7 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
|
||||||
schema.item.refresh_from_db()
|
schema.item.refresh_from_db()
|
||||||
response = Response(status=201, data={
|
response = Response(status=201, data={
|
||||||
'new_cst': s.ConstituentaSerializer(new_cst).data,
|
'new_cst': s.ConstituentaSerializer(new_cst).data,
|
||||||
'schema': m.PyConceptAdapter(schema).full()
|
'schema': s.RSFormParseSerializer(schema).data
|
||||||
})
|
})
|
||||||
response['Location'] = new_cst.get_absolute_url()
|
response['Location'] = new_cst.get_absolute_url()
|
||||||
return response
|
return response
|
||||||
|
@ -169,12 +169,11 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
|
||||||
serializer.save()
|
serializer.save()
|
||||||
mapping = { old_alias: serializer.validated_data['alias'] }
|
mapping = { old_alias: serializer.validated_data['alias'] }
|
||||||
schema.apply_mapping(mapping, change_aliases=False)
|
schema.apply_mapping(mapping, change_aliases=False)
|
||||||
schema.update_order()
|
|
||||||
schema.item.refresh_from_db()
|
schema.item.refresh_from_db()
|
||||||
cst = m.Constituenta.objects.get(pk=serializer.validated_data['id'])
|
cst = m.Constituenta.objects.get(pk=serializer.validated_data['id'])
|
||||||
return Response(status=200, data={
|
return Response(status=200, data={
|
||||||
'new_cst': s.ConstituentaSerializer(cst).data,
|
'new_cst': s.ConstituentaSerializer(cst).data,
|
||||||
'schema': m.PyConceptAdapter(schema).full()
|
'schema': s.RSFormParseSerializer(schema).data
|
||||||
})
|
})
|
||||||
|
|
||||||
@action(detail=True, methods=['patch'], url_path='cst-multidelete')
|
@action(detail=True, methods=['patch'], url_path='cst-multidelete')
|
||||||
|
@ -185,7 +184,7 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
|
||||||
serializer.is_valid(raise_exception=True)
|
serializer.is_valid(raise_exception=True)
|
||||||
schema.delete_cst(serializer.validated_data['constituents'])
|
schema.delete_cst(serializer.validated_data['constituents'])
|
||||||
schema.item.refresh_from_db()
|
schema.item.refresh_from_db()
|
||||||
return Response(status=202, data=m.PyConceptAdapter(schema).full())
|
return Response(status=202, data=s.RSFormParseSerializer(schema).data)
|
||||||
|
|
||||||
@action(detail=True, methods=['patch'], url_path='cst-moveto')
|
@action(detail=True, methods=['patch'], url_path='cst-moveto')
|
||||||
def cst_moveto(self, request, pk):
|
def cst_moveto(self, request, pk):
|
||||||
|
@ -195,14 +194,14 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
|
||||||
serializer.is_valid(raise_exception=True)
|
serializer.is_valid(raise_exception=True)
|
||||||
schema.move_cst(serializer.validated_data['constituents'], serializer.validated_data['move_to'])
|
schema.move_cst(serializer.validated_data['constituents'], serializer.validated_data['move_to'])
|
||||||
schema.item.refresh_from_db()
|
schema.item.refresh_from_db()
|
||||||
return Response(status=200, data=m.PyConceptAdapter(schema).full())
|
return Response(status=200, data=s.RSFormParseSerializer(schema).data)
|
||||||
|
|
||||||
@action(detail=True, methods=['patch'], url_path='reset-aliases')
|
@action(detail=True, methods=['patch'], url_path='reset-aliases')
|
||||||
def reset_aliases(self, request, pk):
|
def reset_aliases(self, request, pk):
|
||||||
''' Endpoint: Recreate all aliases based on order. '''
|
''' Endpoint: Recreate all aliases based on order. '''
|
||||||
schema = self._get_schema()
|
schema = self._get_schema()
|
||||||
schema.reset_aliases()
|
schema.reset_aliases()
|
||||||
return Response(status=200, data=m.PyConceptAdapter(schema).full())
|
return Response(status=200, data=s.RSFormParseSerializer(schema).data)
|
||||||
|
|
||||||
@action(detail=True, methods=['patch'], url_path='load-trs')
|
@action(detail=True, methods=['patch'], url_path='load-trs')
|
||||||
def load_trs(self, request, pk):
|
def load_trs(self, request, pk):
|
||||||
|
@ -217,7 +216,7 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
|
||||||
serializer = s.RSFormTRSSerializer(data=data, context={'load_meta': load_metadata})
|
serializer = s.RSFormTRSSerializer(data=data, context={'load_meta': load_metadata})
|
||||||
serializer.is_valid(raise_exception=True)
|
serializer.is_valid(raise_exception=True)
|
||||||
schema = serializer.save()
|
schema = serializer.save()
|
||||||
return Response(status=200, data=m.PyConceptAdapter(schema).full())
|
return Response(status=200, data=s.RSFormParseSerializer(schema).data)
|
||||||
|
|
||||||
@action(detail=True, methods=['get'])
|
@action(detail=True, methods=['get'])
|
||||||
def contents(self, request, pk):
|
def contents(self, request, pk):
|
||||||
|
@ -229,13 +228,13 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
|
||||||
def details(self, request, pk):
|
def details(self, request, pk):
|
||||||
''' Endpoint: Detailed schema view including statuses and parse. '''
|
''' Endpoint: Detailed schema view including statuses and parse. '''
|
||||||
schema = self._get_schema()
|
schema = self._get_schema()
|
||||||
serializer = m.PyConceptAdapter(schema)
|
serializer = s.RSFormParseSerializer(schema)
|
||||||
return Response(serializer.full())
|
return Response(serializer.data)
|
||||||
|
|
||||||
@action(detail=True, methods=['post'])
|
@action(detail=True, methods=['post'])
|
||||||
def check(self, request, pk):
|
def check(self, request, pk):
|
||||||
''' Endpoint: Check RSLang expression against schema context. '''
|
''' Endpoint: Check RSLang expression against schema context. '''
|
||||||
schema = m.PyConceptAdapter(self._get_schema())
|
schema = s.PyConceptAdapter(self._get_schema())
|
||||||
serializer = s.ExpressionSerializer(data=request.data)
|
serializer = s.ExpressionSerializer(data=request.data)
|
||||||
serializer.is_valid(raise_exception=True)
|
serializer.is_valid(raise_exception=True)
|
||||||
expression = serializer.validated_data['expression']
|
expression = serializer.validated_data['expression']
|
||||||
|
|
|
@ -18,7 +18,7 @@ function Checkbox({ id, required, disabled, tooltip, label, widthClass = 'w-full
|
||||||
|
|
||||||
const cursor = disabled ? 'cursor-not-allowed' : 'cursor-pointer';
|
const cursor = disabled ? 'cursor-not-allowed' : 'cursor-pointer';
|
||||||
|
|
||||||
function handleLabelClick(event: React.MouseEvent<HTMLLabelElement, MouseEvent>): void {
|
function handleClick(event: React.MouseEvent<HTMLButtonElement, MouseEvent>): void {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
if (!disabled) {
|
if (!disabled) {
|
||||||
inputRef.current?.click();
|
inputRef.current?.click();
|
||||||
|
@ -26,7 +26,12 @@ function Checkbox({ id, required, disabled, tooltip, label, widthClass = 'w-full
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={'flex gap-2 [&:not(:first-child)]:mt-3 ' + widthClass} title={tooltip}>
|
<button
|
||||||
|
className={'flex gap-2 [&:not(:first-child)]:mt-3 ' + widthClass}
|
||||||
|
title={tooltip}
|
||||||
|
disabled={disabled}
|
||||||
|
onClick={handleClick}
|
||||||
|
>
|
||||||
<input id={id} type='checkbox' ref={inputRef}
|
<input id={id} type='checkbox' ref={inputRef}
|
||||||
className={`relative peer w-4 h-4 shrink-0 mt-0.5 border rounded-sm appearance-none clr-checkbox ${cursor}`}
|
className={`relative peer w-4 h-4 shrink-0 mt-0.5 border rounded-sm appearance-none clr-checkbox ${cursor}`}
|
||||||
required={required}
|
required={required}
|
||||||
|
@ -40,7 +45,6 @@ function Checkbox({ id, required, disabled, tooltip, label, widthClass = 'w-full
|
||||||
text={label}
|
text={label}
|
||||||
required={required}
|
required={required}
|
||||||
htmlFor={id}
|
htmlFor={id}
|
||||||
onClick={handleLabelClick}
|
|
||||||
/>}
|
/>}
|
||||||
<svg
|
<svg
|
||||||
className='absolute hidden w-3 h-3 mt-1 ml-0.5 text-white pointer-events-none peer-checked:block'
|
className='absolute hidden w-3 h-3 mt-1 ml-0.5 text-white pointer-events-none peer-checked:block'
|
||||||
|
@ -49,7 +53,7 @@ function Checkbox({ id, required, disabled, tooltip, label, widthClass = 'w-full
|
||||||
>
|
>
|
||||||
<path d='M470.6 105.4c12.5 12.5 12.5 32.8 0 45.3l-256 256c-12.5 12.5-32.8 12.5-45.3 0l-128-128c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0L192 338.7l233.4-233.3c12.5-12.5 32.8-12.5 45.3 0z' />
|
<path d='M470.6 105.4c12.5 12.5 12.5 32.8 0 45.3l-256 256c-12.5 12.5-32.8 12.5-45.3 0l-128-128c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0L192 338.7l233.4-233.3c12.5-12.5 32.8-12.5 45.3 0z' />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,9 +11,9 @@ function InfoConstituenta({ data, ...props }: InfoConstituentaProps) {
|
||||||
<div {...props}>
|
<div {...props}>
|
||||||
<h1>Конституента {data.alias}</h1>
|
<h1>Конституента {data.alias}</h1>
|
||||||
<p><b>Типизация: </b>{getCstTypificationLabel(data)}</p>
|
<p><b>Типизация: </b>{getCstTypificationLabel(data)}</p>
|
||||||
<p><b>Термин: </b>{data.term.resolved || data.term.raw}</p>
|
<p><b>Термин: </b>{data.term_resolved || data.term_raw}</p>
|
||||||
{data.definition.formal && <p><b>Выражение: </b>{data.definition.formal}</p>}
|
{data.definition_formal && <p><b>Выражение: </b>{data.definition_formal}</p>}
|
||||||
{data.definition.text.resolved && <p><b>Определение: </b>{data.definition.text.resolved}</p>}
|
{data.definition_resolved && <p><b>Определение: </b>{data.definition_resolved}</p>}
|
||||||
{data.convention && <p><b>Конвенция: </b>{data.convention}</p>}
|
{data.convention && <p><b>Конвенция: </b>{data.convention}</p>}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -11,19 +11,19 @@ function createTooltipFor(cst: IConstituenta) {
|
||||||
alias.className = 'text-sm text-left';
|
alias.className = 'text-sm text-left';
|
||||||
alias.textContent = `${cst.alias}: ${getCstTypificationLabel(cst)}`;
|
alias.textContent = `${cst.alias}: ${getCstTypificationLabel(cst)}`;
|
||||||
dom.appendChild(alias);
|
dom.appendChild(alias);
|
||||||
if (cst.term.resolved) {
|
if (cst.term_resolved) {
|
||||||
const term = document.createElement('p');
|
const term = document.createElement('p');
|
||||||
term.innerHTML = `<b>Термин:</b> ${cst.term.resolved}`;
|
term.innerHTML = `<b>Термин:</b> ${cst.term_resolved}`;
|
||||||
dom.appendChild(term);
|
dom.appendChild(term);
|
||||||
}
|
}
|
||||||
if (cst.definition.formal) {
|
if (cst.definition_formal) {
|
||||||
const expression = document.createElement('p');
|
const expression = document.createElement('p');
|
||||||
expression.innerHTML = `<b>Выражение:</b> ${cst.definition.formal}`;
|
expression.innerHTML = `<b>Выражение:</b> ${cst.definition_formal}`;
|
||||||
dom.appendChild(expression);
|
dom.appendChild(expression);
|
||||||
}
|
}
|
||||||
if (cst.definition.text.resolved) {
|
if (cst.definition_resolved) {
|
||||||
const definition = document.createElement('p');
|
const definition = document.createElement('p');
|
||||||
definition.innerHTML = `<b>Определение:</b> ${cst.definition.text.resolved}`;
|
definition.innerHTML = `<b>Определение:</b> ${cst.definition_resolved}`;
|
||||||
dom.appendChild(definition);
|
dom.appendChild(definition);
|
||||||
}
|
}
|
||||||
if (cst.convention) {
|
if (cst.convention) {
|
||||||
|
|
|
@ -70,7 +70,7 @@ function useCheckExpression({ schema }: { schema?: IRSForm }) {
|
||||||
onError: error => setError(error),
|
onError: error => setError(error),
|
||||||
onSuccess: parse => {
|
onSuccess: parse => {
|
||||||
if (activeCst) {
|
if (activeCst) {
|
||||||
adjustResults(parse, expression === getCstExpressionPrefix(activeCst), activeCst.cstType);
|
adjustResults(parse, expression === getCstExpressionPrefix(activeCst), activeCst.cst_type);
|
||||||
}
|
}
|
||||||
setParseData(parse);
|
setParseData(parse);
|
||||||
if (onSuccess) onSuccess(parse);
|
if (onSuccess) onSuccess(parse);
|
||||||
|
|
|
@ -5,6 +5,7 @@ import Checkbox from '../../components/Common/Checkbox';
|
||||||
import Dropdown from '../../components/Common/Dropdown';
|
import Dropdown from '../../components/Common/Dropdown';
|
||||||
import DropdownButton from '../../components/Common/DropdownButton';
|
import DropdownButton from '../../components/Common/DropdownButton';
|
||||||
import { FilterCogIcon } from '../../components/Icons';
|
import { FilterCogIcon } from '../../components/Icons';
|
||||||
|
import { useAuth } from '../../context/AuthContext';
|
||||||
import useDropdown from '../../hooks/useDropdown';
|
import useDropdown from '../../hooks/useDropdown';
|
||||||
import { LibraryFilterStrategy } from '../../utils/models';
|
import { LibraryFilterStrategy } from '../../utils/models';
|
||||||
|
|
||||||
|
@ -15,6 +16,7 @@ interface PickerStrategyProps {
|
||||||
|
|
||||||
function PickerStrategy({ value, onChange }: PickerStrategyProps) {
|
function PickerStrategy({ value, onChange }: PickerStrategyProps) {
|
||||||
const pickerMenu = useDropdown();
|
const pickerMenu = useDropdown();
|
||||||
|
const { user } = useAuth();
|
||||||
|
|
||||||
const handleChange = useCallback(
|
const handleChange = useCallback(
|
||||||
(newValue: LibraryFilterStrategy) => {
|
(newValue: LibraryFilterStrategy) => {
|
||||||
|
@ -61,6 +63,7 @@ function PickerStrategy({ value, onChange }: PickerStrategyProps) {
|
||||||
<Checkbox
|
<Checkbox
|
||||||
value={value === LibraryFilterStrategy.PERSONAL}
|
value={value === LibraryFilterStrategy.PERSONAL}
|
||||||
label='Личные'
|
label='Личные'
|
||||||
|
disabled={!user}
|
||||||
widthClass='w-fit px-2'
|
widthClass='w-fit px-2'
|
||||||
tooltip='Отображать только подписки и владеемые схемы'
|
tooltip='Отображать только подписки и владеемые схемы'
|
||||||
/>
|
/>
|
||||||
|
@ -69,6 +72,7 @@ function PickerStrategy({ value, onChange }: PickerStrategyProps) {
|
||||||
<Checkbox
|
<Checkbox
|
||||||
value={value === LibraryFilterStrategy.SUBSCRIBE}
|
value={value === LibraryFilterStrategy.SUBSCRIBE}
|
||||||
label='Подписки'
|
label='Подписки'
|
||||||
|
disabled={!user}
|
||||||
widthClass='w-fit px-2'
|
widthClass='w-fit px-2'
|
||||||
tooltip='Отображать только подписки'
|
tooltip='Отображать только подписки'
|
||||||
/>
|
/>
|
||||||
|
@ -76,6 +80,7 @@ function PickerStrategy({ value, onChange }: PickerStrategyProps) {
|
||||||
<DropdownButton onClick={() => handleChange(LibraryFilterStrategy.OWNED)}>
|
<DropdownButton onClick={() => handleChange(LibraryFilterStrategy.OWNED)}>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
value={value === LibraryFilterStrategy.OWNED}
|
value={value === LibraryFilterStrategy.OWNED}
|
||||||
|
disabled={!user}
|
||||||
label='Я - Владелец!'
|
label='Я - Владелец!'
|
||||||
widthClass='w-fit px-2'
|
widthClass='w-fit px-2'
|
||||||
tooltip='Отображать только владеемые схемы'
|
tooltip='Отображать только владеемые схемы'
|
||||||
|
|
|
@ -54,23 +54,23 @@ function EditorConstituenta({ activeID, onShowAST, onCreateCst, onRenameCst, onO
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setIsModified(
|
setIsModified(
|
||||||
activeCst.term.raw !== term ||
|
activeCst.term_raw !== term ||
|
||||||
activeCst.definition.text.raw !== textDefinition ||
|
activeCst.definition_raw !== textDefinition ||
|
||||||
activeCst.convention !== convention ||
|
activeCst.convention !== convention ||
|
||||||
activeCst.definition.formal !== expression
|
activeCst.definition_formal !== expression
|
||||||
);
|
);
|
||||||
}, [activeCst, activeCst?.term, activeCst?.definition.formal,
|
}, [activeCst, activeCst?.term_raw, activeCst?.definition_formal,
|
||||||
activeCst?.definition.text.raw, activeCst?.convention,
|
activeCst?.definition_raw, activeCst?.convention,
|
||||||
term, textDefinition, expression, convention, setIsModified]);
|
term, textDefinition, expression, convention, setIsModified]);
|
||||||
|
|
||||||
useLayoutEffect(
|
useLayoutEffect(
|
||||||
() => {
|
() => {
|
||||||
if (activeCst) {
|
if (activeCst) {
|
||||||
setAlias(activeCst.alias);
|
setAlias(activeCst.alias);
|
||||||
setConvention(activeCst.convention ?? '');
|
setConvention(activeCst.convention || '');
|
||||||
setTerm(activeCst.term?.raw ?? '');
|
setTerm(activeCst.term_raw || '');
|
||||||
setTextDefinition(activeCst.definition?.text?.raw ?? '');
|
setTextDefinition(activeCst.definition_raw || '');
|
||||||
setExpression(activeCst.definition?.formal ?? '');
|
setExpression(activeCst.definition_formal || '');
|
||||||
setTypification(activeCst ? getCstTypificationLabel(activeCst) : 'N/A');
|
setTypification(activeCst ? getCstTypificationLabel(activeCst) : 'N/A');
|
||||||
}
|
}
|
||||||
}, [activeCst, onOpenEdit, schema]);
|
}, [activeCst, onOpenEdit, schema]);
|
||||||
|
@ -106,7 +106,7 @@ function EditorConstituenta({ activeID, onShowAST, onCreateCst, onRenameCst, onO
|
||||||
}
|
}
|
||||||
const data: ICstCreateData = {
|
const data: ICstCreateData = {
|
||||||
insert_after: activeID,
|
insert_after: activeID,
|
||||||
cst_type: activeCst?.cstType ?? CstType.BASE,
|
cst_type: activeCst?.cst_type ?? CstType.BASE,
|
||||||
alias: '',
|
alias: '',
|
||||||
term_raw: '',
|
term_raw: '',
|
||||||
definition_formal: '',
|
definition_formal: '',
|
||||||
|
@ -123,7 +123,7 @@ function EditorConstituenta({ activeID, onShowAST, onCreateCst, onRenameCst, onO
|
||||||
const data: ICstRenameData = {
|
const data: ICstRenameData = {
|
||||||
id: activeID,
|
id: activeID,
|
||||||
alias: activeCst?.alias,
|
alias: activeCst?.alias,
|
||||||
cst_type: activeCst.cstType
|
cst_type: activeCst.cst_type
|
||||||
};
|
};
|
||||||
onRenameCst(data);
|
onRenameCst(data);
|
||||||
}
|
}
|
||||||
|
@ -181,8 +181,8 @@ function EditorConstituenta({ activeID, onShowAST, onCreateCst, onRenameCst, onO
|
||||||
placeholder='Схемный или предметный термин, обозначающий данное понятие или утверждение'
|
placeholder='Схемный или предметный термин, обозначающий данное понятие или утверждение'
|
||||||
rows={2}
|
rows={2}
|
||||||
value={term}
|
value={term}
|
||||||
initialValue={activeCst?.term.raw ?? ''}
|
initialValue={activeCst?.term_raw ?? ''}
|
||||||
resolved={activeCst?.term.resolved ?? ''}
|
resolved={activeCst?.term_resolved ?? ''}
|
||||||
disabled={!isEnabled}
|
disabled={!isEnabled}
|
||||||
spellCheck
|
spellCheck
|
||||||
onChange={event => setTerm(event.target.value)}
|
onChange={event => setTerm(event.target.value)}
|
||||||
|
@ -209,8 +209,8 @@ function EditorConstituenta({ activeID, onShowAST, onCreateCst, onRenameCst, onO
|
||||||
placeholder='Лингвистическая интерпретация формального выражения'
|
placeholder='Лингвистическая интерпретация формального выражения'
|
||||||
rows={4}
|
rows={4}
|
||||||
value={textDefinition}
|
value={textDefinition}
|
||||||
initialValue={activeCst?.definition.text.raw ?? ''}
|
initialValue={activeCst?.definition_raw ?? ''}
|
||||||
resolved={activeCst?.definition.text.resolved ?? ''}
|
resolved={activeCst?.definition_resolved ?? ''}
|
||||||
disabled={!isEnabled}
|
disabled={!isEnabled}
|
||||||
spellCheck
|
spellCheck
|
||||||
onChange={event => setTextDefinition(event.target.value)}
|
onChange={event => setTextDefinition(event.target.value)}
|
||||||
|
|
|
@ -215,7 +215,7 @@ function EditorItems({ onOpenEdit, onCreateCst, onDeleteCst }: EditorItemsProps)
|
||||||
{
|
{
|
||||||
name: 'Термин',
|
name: 'Термин',
|
||||||
id: 'term',
|
id: 'term',
|
||||||
selector: (cst: IConstituenta) => cst.term?.resolved ?? cst.term?.raw ?? '',
|
selector: (cst: IConstituenta) => cst.term_resolved || cst.term_raw || '',
|
||||||
width: '350px',
|
width: '350px',
|
||||||
minWidth: '150px',
|
minWidth: '150px',
|
||||||
maxWidth: '350px',
|
maxWidth: '350px',
|
||||||
|
@ -225,7 +225,7 @@ function EditorItems({ onOpenEdit, onCreateCst, onDeleteCst }: EditorItemsProps)
|
||||||
{
|
{
|
||||||
name: 'Формальное определение',
|
name: 'Формальное определение',
|
||||||
id: 'expression',
|
id: 'expression',
|
||||||
selector: (cst: IConstituenta) => cst.definition?.formal ?? '',
|
selector: (cst: IConstituenta) => cst.definition_formal || '',
|
||||||
minWidth: '300px',
|
minWidth: '300px',
|
||||||
maxWidth: '500px',
|
maxWidth: '500px',
|
||||||
grow: 2,
|
grow: 2,
|
||||||
|
@ -237,7 +237,7 @@ function EditorItems({ onOpenEdit, onCreateCst, onDeleteCst }: EditorItemsProps)
|
||||||
id: 'definition',
|
id: 'definition',
|
||||||
cell: (cst: IConstituenta) => (
|
cell: (cst: IConstituenta) => (
|
||||||
<div style={{ fontSize: 12 }}>
|
<div style={{ fontSize: 12 }}>
|
||||||
{cst.definition?.text.resolved ?? cst.definition?.text.raw ?? ''}
|
{cst.definition_resolved || cst.definition_raw || ''}
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
minWidth: '200px',
|
minWidth: '200px',
|
||||||
|
|
|
@ -31,7 +31,7 @@ const TREE_SIZE_MILESTONE = 50;
|
||||||
|
|
||||||
function getCstNodeColor(cst: IConstituenta, coloringScheme: ColoringScheme, colors: IColorTheme): string {
|
function getCstNodeColor(cst: IConstituenta, coloringScheme: ColoringScheme, colors: IColorTheme): string {
|
||||||
if (coloringScheme === 'type') {
|
if (coloringScheme === 'type') {
|
||||||
return getCstClassColor(cst.cstClass, colors);
|
return getCstClassColor(cst.cst_class, colors);
|
||||||
}
|
}
|
||||||
if (coloringScheme === 'status') {
|
if (coloringScheme === 'status') {
|
||||||
return getCstStatusColor(cst.status, colors);
|
return getCstStatusColor(cst.status, colors);
|
||||||
|
@ -125,14 +125,14 @@ function EditorTermGraph({ onOpenEdit, onCreateCst, onDeleteCst }: EditorTermGra
|
||||||
}
|
}
|
||||||
if (noTemplates) {
|
if (noTemplates) {
|
||||||
schema.items.forEach(cst => {
|
schema.items.forEach(cst => {
|
||||||
if (cst.isTemplate) {
|
if (cst.is_template) {
|
||||||
graph.foldNode(cst.id);
|
graph.foldNode(cst.id);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (allowedTypes.length < Object.values(CstType).length) {
|
if (allowedTypes.length < Object.values(CstType).length) {
|
||||||
schema.items.forEach(cst => {
|
schema.items.forEach(cst => {
|
||||||
if (!allowedTypes.includes(cst.cstType)) {
|
if (!allowedTypes.includes(cst.cst_type)) {
|
||||||
graph.foldNode(cst.id);
|
graph.foldNode(cst.id);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -173,7 +173,7 @@ function EditorTermGraph({ onOpenEdit, onCreateCst, onDeleteCst }: EditorTermGra
|
||||||
result.push({
|
result.push({
|
||||||
id: String(node.id),
|
id: String(node.id),
|
||||||
fill: getCstNodeColor(cst, coloringScheme, colors),
|
fill: getCstNodeColor(cst, coloringScheme, colors),
|
||||||
label: cst.term.resolved && !noTerms ? `${cst.alias}: ${cst.term.resolved}` : cst.alias
|
label: cst.term_resolved && !noTerms ? `${cst.alias}: ${cst.term_resolved}` : cst.alias
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -360,7 +360,7 @@ function EditorTermGraph({ onOpenEdit, onCreateCst, onDeleteCst }: EditorTermGra
|
||||||
<div className='relative'>
|
<div className='relative'>
|
||||||
<InfoConstituenta
|
<InfoConstituenta
|
||||||
data={hoverCst}
|
data={hoverCst}
|
||||||
className='absolute top-0 left-0 z-50 w-[25rem] min-h-[11rem] overflow-y-auto border h-fit clr-app px-3'
|
className='absolute top-[2.2rem] left-[2.6rem] z-50 w-[25rem] min-h-[11rem] overflow-y-auto border h-fit clr-app px-3'
|
||||||
/>
|
/>
|
||||||
</div>}
|
</div>}
|
||||||
|
|
||||||
|
|
|
@ -49,7 +49,16 @@ function ViewSideConstituents({ expression, baseHeight, activeID, onOpenEdit }:
|
||||||
const diff = Array.from(aliases).filter(name => !names.includes(name));
|
const diff = Array.from(aliases).filter(name => !names.includes(name));
|
||||||
if (diff.length > 0) {
|
if (diff.length > 0) {
|
||||||
diff.forEach(
|
diff.forEach(
|
||||||
(alias, index) => filtered.push(getMockConstituenta(-index, alias, CstType.BASE, 'Конституента отсутствует')));
|
(alias, index) => filtered.push(
|
||||||
|
getMockConstituenta(
|
||||||
|
schema.id,
|
||||||
|
-index,
|
||||||
|
alias,
|
||||||
|
CstType.BASE,
|
||||||
|
'Конституента отсутствует'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} else if (!activeID) {
|
} else if (!activeID) {
|
||||||
filtered = schema.items
|
filtered = schema.items
|
||||||
|
@ -133,7 +142,7 @@ function ViewSideConstituents({ expression, baseHeight, activeID, onOpenEdit }:
|
||||||
{
|
{
|
||||||
name: 'Выражение',
|
name: 'Выражение',
|
||||||
id: 'expression',
|
id: 'expression',
|
||||||
selector: (cst: IConstituenta) => cst.definition?.formal ?? '',
|
selector: (cst: IConstituenta) => cst.definition_formal || '',
|
||||||
minWidth: '200px',
|
minWidth: '200px',
|
||||||
hide: 1600,
|
hide: 1600,
|
||||||
grow: 2,
|
grow: 2,
|
||||||
|
|
|
@ -148,33 +148,9 @@ export enum CstClass {
|
||||||
TEMPLATE = 'template'
|
TEMPLATE = 'template'
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IConstituenta {
|
export interface TermForm {
|
||||||
id: number
|
text: string
|
||||||
alias: string
|
tags: string
|
||||||
cstType: CstType
|
|
||||||
convention: string
|
|
||||||
term: {
|
|
||||||
raw: string
|
|
||||||
resolved: string
|
|
||||||
forms: string[]
|
|
||||||
}
|
|
||||||
definition: {
|
|
||||||
formal: string
|
|
||||||
text: {
|
|
||||||
raw: string
|
|
||||||
resolved: string
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cstClass: CstClass
|
|
||||||
status: ExpressionStatus
|
|
||||||
isTemplate: boolean
|
|
||||||
parse: {
|
|
||||||
status: ParsingStatus
|
|
||||||
valueClass: ValueClass
|
|
||||||
typification: string
|
|
||||||
syntaxTree: string
|
|
||||||
args: IFunctionArg[]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IConstituentaMeta {
|
export interface IConstituentaMeta {
|
||||||
|
@ -189,6 +165,21 @@ export interface IConstituentaMeta {
|
||||||
definition_resolved: string
|
definition_resolved: string
|
||||||
term_raw: string
|
term_raw: string
|
||||||
term_resolved: string
|
term_resolved: string
|
||||||
|
term_forms: TermForm[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IConstituenta
|
||||||
|
extends IConstituentaMeta {
|
||||||
|
cst_class: CstClass
|
||||||
|
status: ExpressionStatus
|
||||||
|
is_template: boolean
|
||||||
|
parse: {
|
||||||
|
status: ParsingStatus
|
||||||
|
valueClass: ValueClass
|
||||||
|
typification: string
|
||||||
|
syntaxTree: string
|
||||||
|
args: IFunctionArg[]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IConstituentaID extends Pick<IConstituentaMeta, 'id'>{}
|
export interface IConstituentaID extends Pick<IConstituentaMeta, 'id'>{}
|
||||||
|
@ -431,35 +422,35 @@ export function LoadRSFormData(schema: IRSFormData): IRSForm {
|
||||||
((cst.parse?.status === ParsingStatus.VERIFIED && cst.parse?.valueClass === ValueClass.INVALID) ? 1 : 0) || 0, 0),
|
((cst.parse?.status === ParsingStatus.VERIFIED && cst.parse?.valueClass === ValueClass.INVALID) ? 1 : 0) || 0, 0),
|
||||||
|
|
||||||
count_termin: result.items.reduce(
|
count_termin: result.items.reduce(
|
||||||
(sum, cst) => (sum + (cst.term?.raw ? 1 : 0) || 0), 0),
|
(sum, cst) => (sum + (cst.term_raw ? 1 : 0) || 0), 0),
|
||||||
count_definition: result.items.reduce(
|
count_definition: result.items.reduce(
|
||||||
(sum, cst) => (sum + (cst.definition?.text.raw ? 1 : 0) || 0), 0),
|
(sum, cst) => (sum + (cst.definition_raw ? 1 : 0) || 0), 0),
|
||||||
count_convention: result.items.reduce(
|
count_convention: result.items.reduce(
|
||||||
(sum, cst) => (sum + (cst.convention ? 1 : 0) || 0), 0),
|
(sum, cst) => (sum + (cst.convention ? 1 : 0) || 0), 0),
|
||||||
|
|
||||||
count_base: result.items.reduce(
|
count_base: result.items.reduce(
|
||||||
(sum, cst) => sum + (cst.cstType === CstType.BASE ? 1 : 0), 0),
|
(sum, cst) => sum + (cst.cst_type === CstType.BASE ? 1 : 0), 0),
|
||||||
count_constant: result.items?.reduce(
|
count_constant: result.items?.reduce(
|
||||||
(sum, cst) => sum + (cst.cstType === CstType.CONSTANT ? 1 : 0), 0),
|
(sum, cst) => sum + (cst.cst_type === CstType.CONSTANT ? 1 : 0), 0),
|
||||||
count_structured: result.items?.reduce(
|
count_structured: result.items?.reduce(
|
||||||
(sum, cst) => sum + (cst.cstType === CstType.STRUCTURED ? 1 : 0), 0),
|
(sum, cst) => sum + (cst.cst_type === CstType.STRUCTURED ? 1 : 0), 0),
|
||||||
count_axiom: result.items?.reduce(
|
count_axiom: result.items?.reduce(
|
||||||
(sum, cst) => sum + (cst.cstType === CstType.AXIOM ? 1 : 0), 0),
|
(sum, cst) => sum + (cst.cst_type === CstType.AXIOM ? 1 : 0), 0),
|
||||||
count_term: result.items.reduce(
|
count_term: result.items.reduce(
|
||||||
(sum, cst) => sum + (cst.cstType === CstType.TERM ? 1 : 0), 0),
|
(sum, cst) => sum + (cst.cst_type === CstType.TERM ? 1 : 0), 0),
|
||||||
count_function: result.items.reduce(
|
count_function: result.items.reduce(
|
||||||
(sum, cst) => sum + (cst.cstType === CstType.FUNCTION ? 1 : 0), 0),
|
(sum, cst) => sum + (cst.cst_type === CstType.FUNCTION ? 1 : 0), 0),
|
||||||
count_predicate: result.items.reduce(
|
count_predicate: result.items.reduce(
|
||||||
(sum, cst) => sum + (cst.cstType === CstType.PREDICATE ? 1 : 0), 0),
|
(sum, cst) => sum + (cst.cst_type === CstType.PREDICATE ? 1 : 0), 0),
|
||||||
count_theorem: result.items.reduce(
|
count_theorem: result.items.reduce(
|
||||||
(sum, cst) => sum + (cst.cstType === CstType.THEOREM ? 1 : 0), 0)
|
(sum, cst) => sum + (cst.cst_type === CstType.THEOREM ? 1 : 0), 0)
|
||||||
}
|
}
|
||||||
result.items.forEach(cst => {
|
result.items.forEach(cst => {
|
||||||
cst.status = inferStatus(cst.parse.status, cst.parse.valueClass);
|
cst.status = inferStatus(cst.parse.status, cst.parse.valueClass);
|
||||||
cst.isTemplate = inferTemplate(cst.definition.formal);
|
cst.is_template = inferTemplate(cst.definition_formal);
|
||||||
cst.cstClass = inferClass(cst.cstType, cst.isTemplate);
|
cst.cst_class = inferClass(cst.cst_type, cst.is_template);
|
||||||
result.graph.addNode(cst.id);
|
result.graph.addNode(cst.id);
|
||||||
const dependencies = extractGlobals(cst.definition.formal);
|
const dependencies = extractGlobals(cst.definition_formal);
|
||||||
dependencies.forEach(value => {
|
dependencies.forEach(value => {
|
||||||
const source = schema.items.find(cst => cst.alias === value)
|
const source = schema.items.find(cst => cst.alias === value)
|
||||||
if (source) {
|
if (source) {
|
||||||
|
@ -476,15 +467,15 @@ export function matchConstituenta(query: string, target: IConstituenta, mode: Cs
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if ((mode === CstMatchMode.ALL || mode === CstMatchMode.TERM) &&
|
if ((mode === CstMatchMode.ALL || mode === CstMatchMode.TERM) &&
|
||||||
target.term.resolved.match(query)) {
|
target.term_resolved.match(query)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if ((mode === CstMatchMode.ALL || mode === CstMatchMode.EXPR) &&
|
if ((mode === CstMatchMode.ALL || mode === CstMatchMode.EXPR) &&
|
||||||
target.definition.formal.match(query)) {
|
target.definition_formal.match(query)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if ((mode === CstMatchMode.ALL || mode === CstMatchMode.TEXT)) {
|
if ((mode === CstMatchMode.ALL || mode === CstMatchMode.TEXT)) {
|
||||||
return (target.definition.text.resolved.match(query) || target.convention.match(query));
|
return (target.definition_resolved.match(query) || target.convention.match(query));
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,18 +16,18 @@ export interface IDescriptor {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getCstDescription(cst: IConstituenta): string {
|
export function getCstDescription(cst: IConstituenta): string {
|
||||||
if (cst.cstType === CstType.STRUCTURED) {
|
if (cst.cst_type === CstType.STRUCTURED) {
|
||||||
return (
|
return (
|
||||||
cst.term.resolved || cst.term.raw ||
|
cst.term_resolved || cst.term_raw ||
|
||||||
cst.definition.text.resolved || cst.definition.text.raw ||
|
cst.definition_resolved || cst.definition_raw ||
|
||||||
cst.convention ||
|
cst.convention ||
|
||||||
cst.definition.formal
|
cst.definition_formal
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
cst.term.resolved || cst.term.raw ||
|
cst.term_resolved || cst.term_raw ||
|
||||||
cst.definition.text.resolved || cst.definition.text.raw ||
|
cst.definition_resolved || cst.definition_raw ||
|
||||||
cst.definition.formal ||
|
cst.definition_formal ||
|
||||||
cst.convention
|
cst.convention
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,7 @@ export function getCstTypePrefix(type: CstType) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getCstExpressionPrefix(cst: IConstituenta): string {
|
export function getCstExpressionPrefix(cst: IConstituenta): string {
|
||||||
return cst.alias + (cst.cstType === CstType.STRUCTURED ? '::=' : ':==');
|
return cst.alias + (cst.cst_type === CstType.STRUCTURED ? '::=' : ':==');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getRSButtonData(id: TokenID): IDescriptor {
|
export function getRSButtonData(id: TokenID): IDescriptor {
|
||||||
|
@ -424,7 +424,7 @@ export function createAliasFor(type: CstType, schema: IRSForm): string {
|
||||||
return `${prefix}1`;
|
return `${prefix}1`;
|
||||||
}
|
}
|
||||||
const index = schema.items.reduce((prev, cst, index) => {
|
const index = schema.items.reduce((prev, cst, index) => {
|
||||||
if (cst.cstType !== type) {
|
if (cst.cst_type !== type) {
|
||||||
return prev;
|
return prev;
|
||||||
}
|
}
|
||||||
index = Number(cst.alias.slice(1 - cst.alias.length)) + 1;
|
index = Number(cst.alias.slice(1 - cst.alias.length)) + 1;
|
||||||
|
@ -433,27 +433,23 @@ export function createAliasFor(type: CstType, schema: IRSForm): string {
|
||||||
return `${prefix}${index}`;
|
return `${prefix}${index}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getMockConstituenta(id: number, alias: string, type: CstType, comment: string): IConstituenta {
|
export function getMockConstituenta(schema: number, id: number, alias: string, type: CstType, comment: string): IConstituenta {
|
||||||
return {
|
return {
|
||||||
id: id,
|
id: id,
|
||||||
|
order: -1,
|
||||||
|
schema: schema,
|
||||||
alias: alias,
|
alias: alias,
|
||||||
convention: comment,
|
convention: comment,
|
||||||
cstType: type,
|
cst_type: type,
|
||||||
term: {
|
term_raw: '',
|
||||||
raw: '',
|
term_resolved: '',
|
||||||
resolved: '',
|
term_forms: [],
|
||||||
forms: []
|
definition_formal: '',
|
||||||
},
|
definition_raw: '',
|
||||||
definition: {
|
definition_resolved: '',
|
||||||
formal: '',
|
|
||||||
text: {
|
|
||||||
raw: '',
|
|
||||||
resolved: ''
|
|
||||||
}
|
|
||||||
},
|
|
||||||
status: ExpressionStatus.INCORRECT,
|
status: ExpressionStatus.INCORRECT,
|
||||||
isTemplate: false,
|
is_template: false,
|
||||||
cstClass: CstClass.DERIVED,
|
cst_class: CstClass.DERIVED,
|
||||||
parse: {
|
parse: {
|
||||||
status: ParsingStatus.INCORRECT,
|
status: ParsingStatus.INCORRECT,
|
||||||
valueClass: ValueClass.INVALID,
|
valueClass: ValueClass.INVALID,
|
||||||
|
|
Loading…
Reference in New Issue
Block a user