diff --git a/README.md b/README.md index ea99477d..a6b96af3 100644 --- a/README.md +++ b/README.md @@ -116,6 +116,7 @@ This readme file is used mostly to document project dependencies - Pylint - Django - autopep8 + - SQLite diff --git a/rsconcept/backend/apps/rsform/models/LibraryItem.py b/rsconcept/backend/apps/rsform/models/LibraryItem.py index 56813796..70489f9c 100644 --- a/rsconcept/backend/apps/rsform/models/LibraryItem.py +++ b/rsconcept/backend/apps/rsform/models/LibraryItem.py @@ -23,7 +23,7 @@ from .Version import Version class LibraryItemType(TextChoices): ''' Type of library items ''' RSFORM = 'rsform' - OPERATIONS_SCHEMA = 'oss' + OPERATION_SCHEMA = 'oss' class AccessPolicy(TextChoices): @@ -115,7 +115,7 @@ class LibraryItem(Model): def subscribers(self) -> list[Subscription]: ''' Get all subscribers for this item. ''' - return [subscription.user for subscription in Subscription.objects.filter(item=self.pk)] + return [subscription.user for subscription in Subscription.objects.filter(item=self.pk).only('user')] def versions(self) -> list[Version]: ''' Get all Versions of this item. ''' @@ -123,7 +123,7 @@ class LibraryItem(Model): def editors(self) -> list[Editor]: ''' Get all Editors of this item. ''' - return [item.editor for item in Editor.objects.filter(item=self.pk)] + return [item.editor for item in Editor.objects.filter(item=self.pk).only('editor')] @transaction.atomic def save(self, *args, **kwargs): diff --git a/rsconcept/backend/apps/rsform/models/api_RSForm.py b/rsconcept/backend/apps/rsform/models/api_RSForm.py index e0c03e12..dee2f7e6 100644 --- a/rsconcept/backend/apps/rsform/models/api_RSForm.py +++ b/rsconcept/backend/apps/rsform/models/api_RSForm.py @@ -1,6 +1,6 @@ ''' Models: RSForm API. ''' from copy import deepcopy -from typing import Optional, Union, cast +from typing import Optional, cast from cctext import Entity, Resolver, TermForm, extract_entities, split_grams from django.core.exceptions import ValidationError @@ -39,7 +39,7 @@ class RSForm: def create(**kwargs) -> 'RSForm': return RSForm(LibraryItem.objects.create(item_type=LibraryItemType.RSFORM, **kwargs)) - def constituents(self) -> QuerySet['Constituenta']: + def constituents(self) -> QuerySet[Constituenta]: ''' Get QuerySet containing all constituents of current RSForm. ''' return Constituenta.objects.filter(schema=self.item) @@ -104,11 +104,39 @@ class RSForm: result = max(result, int(alias[1:])) return result + @transaction.atomic + def create_cst(self, data: dict, insert_after: Optional[Constituenta] = None) -> Constituenta: + ''' Create new cst from data. ''' + if insert_after is None: + position = _INSERT_LAST + else: + position = insert_after.order + 1 + result = self.insert_new(data['alias'], data['cst_type'], position) + result.convention = data.get('convention', '') + result.definition_formal = data.get('definition_formal', '') + result.term_forms = data.get('term_forms', []) + result.term_raw = data.get('term_raw', '') + result.definition_raw = data.get('definition_raw', '') + + if result.term_raw != '' or result.definition_raw != '': + resolver = self.resolver() + if result.term_raw != '': + resolved = resolver.resolve(result.term_raw) + result.term_resolved = resolved + resolver.context[result.alias] = Entity(result.alias, resolved) + if result.definition_raw != '': + result.definition_resolved = resolver.resolve(result.definition_raw) + + result.save() + self.on_term_change([result.id]) + result.refresh_from_db() + return result + @transaction.atomic def insert_new( self, alias: str, - cst_type: Union[CstType, None] = None, + cst_type: Optional[CstType] = None, position: int = _INSERT_LAST, **kwargs ) -> Constituenta: @@ -195,27 +223,6 @@ class RSForm: self.resolve_all_text() self.item.save() - @transaction.atomic - def create_cst(self, data: dict, insert_after: Optional[str] = None) -> Constituenta: - ''' Create new cst from data. ''' - resolver = self.resolver() - cst = self._insert_new(data, insert_after) - cst.convention = data.get('convention', '') - cst.definition_formal = data.get('definition_formal', '') - cst.term_forms = data.get('term_forms', []) - cst.term_raw = data.get('term_raw', '') - if cst.term_raw != '': - resolved = resolver.resolve(cst.term_raw) - cst.term_resolved = resolved - resolver.context[cst.alias] = Entity(cst.alias, resolved) - cst.definition_raw = data.get('definition_raw', '') - if cst.definition_raw != '': - cst.definition_resolved = resolver.resolve(cst.definition_raw) - cst.save() - self.on_term_change([cst.id]) - cst.refresh_from_db() - return cst - @transaction.atomic def substitute( self, @@ -363,13 +370,6 @@ class RSForm: cst.save() order += 1 - def _insert_new(self, data: dict, insert_after: Optional[str] = None) -> Constituenta: - if insert_after is not None: - cst_after = Constituenta.objects.get(pk=insert_after) - return self.insert_new(data['alias'], data['cst_type'], cst_after.order + 1) - else: - return self.insert_new(data['alias'], data['cst_type']) - def _graph_formal(self) -> Graph[int]: ''' Graph based on formal definitions. ''' result: Graph[int] = Graph() diff --git a/rsconcept/backend/apps/rsform/tests/EndpointTester.py b/rsconcept/backend/apps/rsform/tests/EndpointTester.py index e2436cc4..0f0a6b63 100644 --- a/rsconcept/backend/apps/rsform/tests/EndpointTester.py +++ b/rsconcept/backend/apps/rsform/tests/EndpointTester.py @@ -26,6 +26,21 @@ class EndpointTester(APITestCase): ''' Abstract base class for Testing endpoints. ''' def setUp(self): + self.factory = APIRequestFactory() + self.user = User.objects.create( + username='UserTest', + email='blank@test.com', + password='password' + ) + self.user2 = User.objects.create( + username='UserTest2', + email='another@test.com', + password='password' + ) + self.client = APIClient() + self.client.force_authenticate(user=self.user) + + def setUpFullUsers(self): self.factory = APIRequestFactory() self.user = User.objects.create_user( username='UserTest', diff --git a/rsconcept/backend/apps/rsform/tests/s_models/t_RSForm.py b/rsconcept/backend/apps/rsform/tests/s_models/t_RSForm.py index 7881f2b9..195bb080 100644 --- a/rsconcept/backend/apps/rsform/tests/s_models/t_RSForm.py +++ b/rsconcept/backend/apps/rsform/tests/s_models/t_RSForm.py @@ -101,6 +101,26 @@ class TestRSForm(TestCase): self.assertEqual(x2.schema, self.schema.item) self.assertEqual(x1.order, 1) + def test_create_cst(self): + data = { + 'alias': 'X3', + 'cst_type': CstType.BASE, + 'term_raw': 'слон', + 'definition_raw': 'test', + 'convention': 'convention' + } + + x1 = self.schema.insert_new('X1') + x2 = self.schema.insert_new('X2') + x3 = self.schema.create_cst(data=data, insert_after=x1) + x2.refresh_from_db() + + self.assertEqual(x3.alias, data['alias']) + self.assertEqual(x3.term_raw, data['term_raw']) + self.assertEqual(x3.definition_raw, data['definition_raw']) + self.assertEqual(x2.order, 3) + self.assertEqual(x3.order, 2) + def test_create_cst_resolve(self): x1 = self.schema.insert_new( diff --git a/rsconcept/backend/apps/rsform/tests/s_views/t_library.py b/rsconcept/backend/apps/rsform/tests/s_views/t_library.py index 3c53d80e..fcd92781 100644 --- a/rsconcept/backend/apps/rsform/tests/s_views/t_library.py +++ b/rsconcept/backend/apps/rsform/tests/s_views/t_library.py @@ -52,7 +52,7 @@ class TestLibraryViewset(EndpointTester): self.executeBadData(data) data = { - 'item_type': LibraryItemType.OPERATIONS_SCHEMA, + 'item_type': LibraryItemType.OPERATION_SCHEMA, 'title': 'Title', 'alias': 'alias', 'access_policy': AccessPolicy.PROTECTED, @@ -294,7 +294,7 @@ class TestLibraryViewset(EndpointTester): @decl_endpoint('/api/library', method='get') def test_library_get(self): non_schema = LibraryItem.objects.create( - item_type=LibraryItemType.OPERATIONS_SCHEMA, + item_type=LibraryItemType.OPERATION_SCHEMA, title='Test4' ) response = self.executeOK() diff --git a/rsconcept/backend/apps/rsform/tests/s_views/t_rsforms.py b/rsconcept/backend/apps/rsform/tests/s_views/t_rsforms.py index dc735490..ac385761 100644 --- a/rsconcept/backend/apps/rsform/tests/s_views/t_rsforms.py +++ b/rsconcept/backend/apps/rsform/tests/s_views/t_rsforms.py @@ -59,7 +59,7 @@ class TestRSFormViewset(EndpointTester): @decl_endpoint('/api/rsforms', method='get') def test_list_rsforms(self): non_schema = LibraryItem.objects.create( - item_type=LibraryItemType.OPERATIONS_SCHEMA, + item_type=LibraryItemType.OPERATION_SCHEMA, title='Test3' ) response = self.executeOK() diff --git a/rsconcept/backend/apps/rsform/views/rsforms.py b/rsconcept/backend/apps/rsform/views/rsforms.py index 1af946f1..65136f53 100644 --- a/rsconcept/backend/apps/rsform/views/rsforms.py +++ b/rsconcept/backend/apps/rsform/views/rsforms.py @@ -60,10 +60,15 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr serializer = s.CstCreateSerializer(data=request.data) serializer.is_valid(raise_exception=True) data = serializer.validated_data - new_cst = schema.create_cst( - data=data, - insert_after=data['insert_after'] if 'insert_after' in data else None - ) + if 'insert_after' in data: + try: + insert_after = m.Constituenta.objects.get(pk=data['insert_after']) + except m.LibraryItem.DoesNotExist: + return Response(status=c.HTTP_404_NOT_FOUND) + else: + insert_after = None + new_cst = schema.create_cst(data, insert_after) + schema.item.refresh_from_db() response = Response( status=c.HTTP_201_CREATED, @@ -313,7 +318,7 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr c.HTTP_404_NOT_FOUND: None } ) - @action(detail=True, methods=['get']) + @action(detail=True, methods=['get'], url_path='contents') def contents(self, request: Request, pk): ''' Endpoint: View schema db contents (including constituents). ''' schema = s.RSFormSerializer(self.get_object()) @@ -331,7 +336,7 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr c.HTTP_404_NOT_FOUND: None } ) - @action(detail=True, methods=['get']) + @action(detail=True, methods=['get'], url_path='details') def details(self, request: Request, pk): ''' Endpoint: Detailed schema view including statuses and parse. ''' serializer = s.RSFormParseSerializer(cast(m.LibraryItem, self.get_object())) @@ -349,7 +354,7 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr c.HTTP_404_NOT_FOUND: None }, ) - @action(detail=True, methods=['post']) + @action(detail=True, methods=['post'], url_path='check') def check(self, request: Request, pk): ''' Endpoint: Check RSLang expression against schema context. ''' serializer = s.ExpressionSerializer(data=request.data) @@ -371,7 +376,7 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr c.HTTP_404_NOT_FOUND: None } ) - @action(detail=True, methods=['post']) + @action(detail=True, methods=['post'], url_path='resolve') def resolve(self, request: Request, pk): ''' Endpoint: Resolve references in text against schema terms context. ''' serializer = s.TextSerializer(data=request.data) diff --git a/rsconcept/backend/apps/users/tests/t_views.py b/rsconcept/backend/apps/users/tests/t_views.py index 44551a5c..c003a385 100644 --- a/rsconcept/backend/apps/users/tests/t_views.py +++ b/rsconcept/backend/apps/users/tests/t_views.py @@ -9,7 +9,7 @@ class TestUserAPIViews(EndpointTester): ''' Testing Authentication views. ''' def setUp(self): - super().setUp() + super().setUpFullUsers() @decl_endpoint('/users/api/login', method='post') @@ -59,7 +59,7 @@ class TestUserUserProfileAPIView(EndpointTester): ''' Testing User profile views. ''' def setUp(self): - super().setUp() + super().setUpFullUsers() self.user.first_name = 'John' self.user.second_name = 'Smith' self.user.save() diff --git a/rsconcept/backend/fixtures/InitialData.json b/rsconcept/backend/fixtures/InitialData.json index ee9dd19c..cfb7982c 100644 --- a/rsconcept/backend/fixtures/InitialData.json +++ b/rsconcept/backend/fixtures/InitialData.json @@ -1,4 +1,22 @@ [ +{ + "model": "auth.user", + "pk": 1, + "fields": { + "password": "pbkdf2_sha256$720000$gFZkaBswurtL0naQKiUnW7$3b6SUN3fY2Xl1H7erAszVpQl2LpoKusan+yJP7Bp3JA=", + "last_login": "2024-06-03T20:57:02.522Z", + "is_superuser": true, + "username": "admin", + "first_name": "Администратор", + "last_name": "", + "email": "admin@mail.ru", + "is_staff": true, + "is_active": true, + "date_joined": "2024-05-27T18:31:48.913Z", + "groups": [], + "user_permissions": [] + } +}, { "model": "auth.user", "pk": 3,