diff --git a/rsconcept/backend/apps/rsform/serializers.py b/rsconcept/backend/apps/rsform/serializers.py index f48f3b16..abfce0fb 100644 --- a/rsconcept/backend/apps/rsform/serializers.py +++ b/rsconcept/backend/apps/rsform/serializers.py @@ -3,6 +3,7 @@ from typing import Optional from rest_framework import serializers from django.db import transaction +from .utils import fix_old_references from .models import Constituenta, RSForm _CST_TYPE = 'constituenta' @@ -197,11 +198,11 @@ class RSFormTRSSerializer(serializers.Serializer): if 'definition' in data: cst.definition_formal = data['definition'].get('formal', '') if 'text' in data['definition']: - cst.definition_raw = data['definition']['text'].get('raw', '') + cst.definition_raw = fix_old_references(data['definition']['text'].get('raw', '')) else: cst.definition_raw = '' if 'term' in data: - cst.term_raw = data['term'].get('raw', '') + cst.term_raw = fix_old_references(data['term'].get('raw', '')) cst.term_forms = data['term'].get('forms', []) else: cst.term_raw = '' diff --git a/rsconcept/backend/apps/rsform/tests/t_utils.py b/rsconcept/backend/apps/rsform/tests/t_utils.py index 808ad85f..21fa63c6 100644 --- a/rsconcept/backend/apps/rsform/tests/t_utils.py +++ b/rsconcept/backend/apps/rsform/tests/t_utils.py @@ -2,7 +2,7 @@ import unittest import re -from apps.rsform.utils import apply_mapping_pattern +from apps.rsform.utils import apply_mapping_pattern, fix_old_references class TestUtils(unittest.TestCase): @@ -14,3 +14,10 @@ class TestUtils(unittest.TestCase): self.assertEqual(apply_mapping_pattern('X20', mapping, pattern), 'X20') self.assertEqual(apply_mapping_pattern('X101', mapping, pattern), 'X20') self.assertEqual(apply_mapping_pattern('asdf X101 asdf', mapping, pattern), 'asdf X20 asdf') + + def test_fix_old_references(self): + self.assertEqual(fix_old_references(''), '') + self.assertEqual(fix_old_references('X20'), 'X20') + self.assertEqual(fix_old_references('@{X1|nomn,sing}'), '@{X1|nomn,sing}') + self.assertEqual(fix_old_references('@{X1|sing,ablt} @{X1|sing,ablt}'), '@{X1|sing,ablt} @{X1|sing,ablt}') + self.assertEqual(fix_old_references('@{X1|nomn|sing}'), '@{X1|nomn,sing}') diff --git a/rsconcept/backend/apps/rsform/tests/t_views.py b/rsconcept/backend/apps/rsform/tests/t_views.py index 3ad65cbb..356ac659 100644 --- a/rsconcept/backend/apps/rsform/tests/t_views.py +++ b/rsconcept/backend/apps/rsform/tests/t_views.py @@ -72,7 +72,9 @@ class TestConstituentaAPI(APITestCase): response = self.client.patch(f'/api/constituents/{self.cst3.id}/', data, content_type='application/json') self.assertEqual(response.status_code, 200) self.cst3.refresh_from_db() + self.assertEqual(response.data['term_resolved'], 'New term') self.assertEqual(self.cst3.term_resolved, 'New term') + self.assertEqual(response.data['definition_resolved'], 'New def') self.assertEqual(self.cst3.definition_resolved, 'New def') def test_update_resolved_refs(self): @@ -84,7 +86,9 @@ class TestConstituentaAPI(APITestCase): self.assertEqual(response.status_code, 200) self.cst3.refresh_from_db() self.assertEqual(self.cst3.term_resolved, self.cst1.term_resolved) + self.assertEqual(response.data['term_resolved'], self.cst1.term_resolved) self.assertEqual(self.cst3.definition_resolved, f'{self.cst1.term_resolved} form1') + self.assertEqual(response.data['definition_resolved'], f'{self.cst1.term_resolved} form1') def test_readonly_cst_fields(self): data = json.dumps({'alias': 'X33', 'order': 10}) @@ -151,14 +155,27 @@ class TestRSFormViewset(APITestCase): def test_details(self): schema = RSForm.objects.create(title='Test') - cst = schema.insert_at(1, 'X1', CstType.BASE) - schema.insert_at(2, 'X2', CstType.BASE) + x1 = schema.insert_at(1, 'X1', CstType.BASE) + x2 = schema.insert_at(2, 'X2', CstType.BASE) + x1.term_raw = 'человек' + x1.term_resolved = 'человек' + x2.term_raw = '@{X1|plur}' + x2.term_resolved = 'люди' + x1.save() + x2.save() + response = self.client.get(f'/api/rsforms/{schema.id}/details/') + self.assertEqual(response.status_code, 200) self.assertEqual(response.data['title'], 'Test') self.assertEqual(len(response.data['items']), 2) + self.assertEqual(response.data['items'][0]['id'], x1.id) self.assertEqual(response.data['items'][0]['parse']['status'], 'verified') - self.assertEqual(response.data['items'][0]['id'], cst.id) + 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'][1]['id'], x2.id) + self.assertEqual(response.data['items'][1]['term']['raw'], x2.term_raw) + self.assertEqual(response.data['items'][1]['term']['resolved'], x2.term_resolved) def test_check(self): schema = RSForm.objects.create(title='Test') @@ -347,11 +364,24 @@ class TestRSFormViewset(APITestCase): schema.title = 'Testt11' schema.save() x1 = Constituenta.objects.create(schema=schema, alias='X12', cst_type='basic', order=1) + d1 = Constituenta.objects.create(schema=schema, alias='D2', cst_type='term', order=1) + x1.term_raw = 'человек' + x1.term_resolved = 'человек' + d1.term_raw = '@{X12|plur}' + d1.term_resolved = 'люди' + x1.save() + d1.save() + data = json.dumps({'title': 'Title'}) response = self.client.post(f'/api/rsforms/{schema.id}/clone/', data=data, content_type='application/json') + self.assertEqual(response.status_code, 201) self.assertEqual(response.data['title'], 'Title') 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']['resolved'], x1.term_resolved) + self.assertEqual(response.data['items'][1]['term']['raw'], d1.term_raw) + self.assertEqual(response.data['items'][1]['term']['resolved'], d1.term_resolved) class TestFunctionalViews(APITestCase): diff --git a/rsconcept/backend/apps/rsform/utils.py b/rsconcept/backend/apps/rsform/utils.py index 4f36b491..e59af10f 100644 --- a/rsconcept/backend/apps/rsform/utils.py +++ b/rsconcept/backend/apps/rsform/utils.py @@ -57,3 +57,18 @@ def apply_mapping_pattern(text: str, mapping: dict[str, str], pattern: re.Patter pos_input = segment.end(0) output += text[pos_input : len(text)] return output + +_REF_OLD_PATTERN = re.compile(r'@{([^0-9\-][^\}\|\{]*?)\|([^\}\|\{]*?)\|([^\}\|\{]*?)}') + +def fix_old_references(text: str) -> str: + ''' Fix reference format: @{X1|nomn|sing} -> {X1|nomn,sing} ''' + if text == '': + return text + pos_input: int = 0 + output: str = '' + for segment in re.finditer(_REF_OLD_PATTERN, text): + output += text[pos_input : segment.start(0)] + output += f'@{{{segment.group(1)}|{segment.group(2)},{segment.group(3)}}}' + pos_input = segment.end(0) + output += text[pos_input : len(text)] + return output diff --git a/rsconcept/backend/cctext/resolver.py b/rsconcept/backend/cctext/resolver.py index 1fe18180..528d191d 100644 --- a/rsconcept/backend/cctext/resolver.py +++ b/rsconcept/backend/cctext/resolver.py @@ -7,7 +7,7 @@ from .conceptapi import inflect_dependant from .context import TermContext from .reference import EntityReference, SyntacticReference, parse_reference, Reference -_REF_ENTITY_PATTERN = re.compile(r'@{([^0-9\-].*?)\|.*?}') +_REF_ENTITY_PATTERN = re.compile(r'@{([^0-9\-][^\}\|\{]*?)\|([^\}\|\{]*?)}') def extract_entities(text: str) -> list[str]: ''' Extract list of entities that are referenced. ''' @@ -76,7 +76,7 @@ class ResolvedReference: class Resolver: ''' Text reference resolver ''' - REFERENCE_PATTERN = re.compile(r'@{.*?}') + REFERENCE_PATTERN = re.compile(r'@{[^\}\{]*?}') def __init__(self, context: TermContext): self.context = context diff --git a/rsconcept/backend/cctext/tests/t_resolver.py b/rsconcept/backend/cctext/tests/t_resolver.py index 6033cab4..e0d88b6d 100644 --- a/rsconcept/backend/cctext/tests/t_resolver.py +++ b/rsconcept/backend/cctext/tests/t_resolver.py @@ -17,6 +17,7 @@ class TestUtils(unittest.TestCase): self.assertEqual(extract_entities('@{X1|nomn}'), ['X1']) self.assertEqual(extract_entities('@{X1|datv}'), ['X1']) self.assertEqual(extract_entities('@{X1|datv} @{X1|datv} @{X2|datv}'), ['X1', 'X2']) + self.assertEqual(extract_entities('@{X1} | @{X1} | @{X2}'), []) class TestResolver(unittest.TestCase):