Refactor constituenta API and remove move restriction

This commit is contained in:
IRBorisov 2023-08-29 15:17:16 +03:00
parent 31cbb6f05a
commit 03fb9e3fd0
16 changed files with 235 additions and 242 deletions

View File

@ -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']
})

View File

@ -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

View File

@ -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')

View File

@ -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):

View File

@ -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']

View File

@ -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>
); );
} }

View File

@ -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>
); );

View File

@ -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) {

View File

@ -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);

View File

@ -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='Отображать только владеемые схемы'

View File

@ -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)}

View File

@ -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',

View File

@ -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>}

View File

@ -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,

View File

@ -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;
} }

View File

@ -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,