mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-25 20:40:36 +03:00
Refactoring: improving backend
Some checks are pending
Backend CI / build (3.12) (push) Waiting to run
Some checks are pending
Backend CI / build (3.12) (push) Waiting to run
This commit is contained in:
parent
cdb2f6cb79
commit
8697ee6175
|
@ -47,6 +47,7 @@ cover/
|
|||
# Django
|
||||
rsconcept/frontend/static
|
||||
rsconcept/frontend/media
|
||||
visualizeDB.dot
|
||||
*.log
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -42,6 +42,7 @@ cover/
|
|||
*.log
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
visualizeDB.dot
|
||||
|
||||
|
||||
# React
|
||||
|
|
17
.vscode/launch.json
vendored
17
.vscode/launch.json
vendored
|
@ -5,6 +5,7 @@
|
|||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
// Run Frontend + Backend with current Database
|
||||
"name": "Run",
|
||||
"type": "PowerShell",
|
||||
"request": "launch",
|
||||
|
@ -12,6 +13,7 @@
|
|||
"args": []
|
||||
},
|
||||
{
|
||||
// Run Linters
|
||||
"name": "Lint",
|
||||
"type": "PowerShell",
|
||||
"request": "launch",
|
||||
|
@ -19,6 +21,7 @@
|
|||
"args": []
|
||||
},
|
||||
{
|
||||
// Run Tests
|
||||
"name": "Test",
|
||||
"type": "PowerShell",
|
||||
"request": "launch",
|
||||
|
@ -26,6 +29,7 @@
|
|||
"args": []
|
||||
},
|
||||
{
|
||||
// Run Tests for backend for current file in Debug mode
|
||||
"name": "BE-DebugTestFile",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
|
@ -35,6 +39,7 @@
|
|||
"django": true
|
||||
},
|
||||
{
|
||||
// Run Tests for frontned in Debug mode
|
||||
"name": "FE-DebugTestAll",
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
|
@ -55,6 +60,7 @@
|
|||
}
|
||||
},
|
||||
{
|
||||
// Run Browser in Debug mode (Backend should be running)
|
||||
"name": "FE-Debug",
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
|
@ -62,6 +68,7 @@
|
|||
"webRoot": "${workspaceFolder}/rsconcept/frontend"
|
||||
},
|
||||
{
|
||||
// Run Backend in Debug mode
|
||||
"name": "BE-Debug",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
|
@ -70,6 +77,7 @@
|
|||
"django": true
|
||||
},
|
||||
{
|
||||
// Run Backend test coverage
|
||||
"name": "BE-Coverage",
|
||||
"type": "PowerShell",
|
||||
"request": "launch",
|
||||
|
@ -77,11 +85,20 @@
|
|||
"args": []
|
||||
},
|
||||
{
|
||||
// Recreate database, fill with initial data and Run Backend + Frontend
|
||||
"name": "Restart",
|
||||
"type": "PowerShell",
|
||||
"request": "launch",
|
||||
"script": "${workspaceFolder}/scripts/dev/RunServer.ps1",
|
||||
"args": ["-freshStart"]
|
||||
},
|
||||
{
|
||||
// Create DOT file for visualizing database
|
||||
"name": "BE-GraphDB",
|
||||
"type": "PowerShell",
|
||||
"request": "launch",
|
||||
"script": "${workspaceFolder}/scripts/dev/GraphDB.ps1",
|
||||
"args": []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -7,8 +7,24 @@ from . import models
|
|||
class OperationAdmin(admin.ModelAdmin):
|
||||
''' Admin model: Operation. '''
|
||||
ordering = ['oss']
|
||||
list_display = ['oss', 'operation_type', 'result', 'alias', 'title', 'comment', 'position_x', 'position_y']
|
||||
search_fields = ['operation_type', 'title', 'alias']
|
||||
list_display = ['id', 'oss', 'operation_type', 'result', 'alias', 'title', 'comment', 'position_x', 'position_y']
|
||||
search_fields = ['id', 'operation_type', 'title', 'alias']
|
||||
|
||||
|
||||
class ArgumentAdmin(admin.ModelAdmin):
|
||||
''' Admin model: Operation arguments. '''
|
||||
ordering = ['operation']
|
||||
list_display = ['id', 'operation', 'argument']
|
||||
search_fields = ['id', 'operation', 'argument']
|
||||
|
||||
|
||||
class SynthesisSubstitutionAdmin(admin.ModelAdmin):
|
||||
''' Admin model: Substitutions as part of Synthesis operation. '''
|
||||
ordering = ['operation']
|
||||
list_display = ['id', 'operation', 'original', 'substitution', 'transfer_term']
|
||||
search_fields = ['id', 'operation', 'original', 'substitution']
|
||||
|
||||
|
||||
admin.site.register(models.Operation, OperationAdmin)
|
||||
admin.site.register(models.Argument, ArgumentAdmin)
|
||||
admin.site.register(models.SynthesisSubstitution, SynthesisSubstitutionAdmin)
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
# Generated by Django 5.0.7 on 2024-07-22 13:23
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('oss', '0001_initial'),
|
||||
('rsform', '0008_alter_libraryitem_item_type'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='OperationSchema',
|
||||
fields=[
|
||||
],
|
||||
options={
|
||||
'proxy': True,
|
||||
'indexes': [],
|
||||
'constraints': [],
|
||||
},
|
||||
bases=('rsform.libraryitem',),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='operation',
|
||||
name='oss',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='items', to='oss.operationschema', verbose_name='Схема синтеза'),
|
||||
),
|
||||
]
|
|
@ -24,4 +24,4 @@ class Argument(Model):
|
|||
unique_together = [['operation', 'argument']]
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f'{self.argument.pk} -> {self.operation.pk}'
|
||||
return f'{self.argument} -> {self.operation}'
|
||||
|
|
|
@ -21,7 +21,7 @@ class Operation(Model):
|
|||
''' Operational schema Unit.'''
|
||||
oss: ForeignKey = ForeignKey(
|
||||
verbose_name='Схема синтеза',
|
||||
to='rsform.LibraryItem',
|
||||
to='oss.OperationSchema',
|
||||
on_delete=CASCADE,
|
||||
related_name='items'
|
||||
)
|
||||
|
|
|
@ -3,7 +3,7 @@ from typing import Optional
|
|||
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db import transaction
|
||||
from django.db.models import QuerySet
|
||||
from django.db.models import Manager, QuerySet
|
||||
|
||||
from apps.rsform.models import LibraryItem, LibraryItemType
|
||||
from shared import messages as msg
|
||||
|
@ -13,30 +13,37 @@ from .Operation import Operation
|
|||
from .SynthesisSubstitution import SynthesisSubstitution
|
||||
|
||||
|
||||
class OperationSchema:
|
||||
class OperationSchema(LibraryItem):
|
||||
''' Operations schema API. '''
|
||||
|
||||
def __init__(self, item: LibraryItem):
|
||||
if item.item_type != LibraryItemType.OPERATION_SCHEMA:
|
||||
raise ValueError(msg.libraryTypeUnexpected())
|
||||
self.item = item
|
||||
class Meta:
|
||||
''' Model metadata. '''
|
||||
proxy = True
|
||||
|
||||
@staticmethod
|
||||
def create(**kwargs) -> 'OperationSchema':
|
||||
item = LibraryItem.objects.create(item_type=LibraryItemType.OPERATION_SCHEMA, **kwargs)
|
||||
return OperationSchema(item=item)
|
||||
class InternalManager(Manager):
|
||||
''' Object manager. '''
|
||||
|
||||
def get_queryset(self) -> QuerySet:
|
||||
return super().get_queryset().filter(item_type=LibraryItemType.OPERATION_SCHEMA)
|
||||
|
||||
def create(self, **kwargs):
|
||||
kwargs.update({'item_type': LibraryItemType.OPERATION_SCHEMA})
|
||||
return super().create(**kwargs)
|
||||
|
||||
# Legit overriding object manager
|
||||
objects = InternalManager() # type: ignore[misc]
|
||||
|
||||
def operations(self) -> QuerySet[Operation]:
|
||||
''' Get QuerySet containing all operations of current OSS. '''
|
||||
return Operation.objects.filter(oss=self.item)
|
||||
return Operation.objects.filter(oss=self)
|
||||
|
||||
def arguments(self) -> QuerySet[Argument]:
|
||||
''' Operation arguments. '''
|
||||
return Argument.objects.filter(operation__oss=self.item)
|
||||
return Argument.objects.filter(operation__oss=self)
|
||||
|
||||
def substitutions(self) -> QuerySet[SynthesisSubstitution]:
|
||||
''' Operation substitutions. '''
|
||||
return SynthesisSubstitution.objects.filter(operation__oss=self.item)
|
||||
return SynthesisSubstitution.objects.filter(operation__oss=self)
|
||||
|
||||
def update_positions(self, data: list[dict]):
|
||||
''' Update positions. '''
|
||||
|
@ -53,11 +60,8 @@ class OperationSchema:
|
|||
''' Insert new operation. '''
|
||||
if kwargs['alias'] != '' and self.operations().filter(alias=kwargs['alias']).exists():
|
||||
raise ValidationError(msg.aliasTaken(kwargs['alias']))
|
||||
result = Operation.objects.create(
|
||||
oss=self.item,
|
||||
**kwargs
|
||||
)
|
||||
self.item.save()
|
||||
result = Operation.objects.create(oss=self, **kwargs)
|
||||
self.save()
|
||||
result.refresh_from_db()
|
||||
return result
|
||||
|
||||
|
@ -69,7 +73,7 @@ class OperationSchema:
|
|||
# deal with attached schema
|
||||
# trigger on_change effects
|
||||
|
||||
self.item.save()
|
||||
self.save()
|
||||
|
||||
@transaction.atomic
|
||||
def set_input(self, target: Operation, schema: Optional[LibraryItem]):
|
||||
|
@ -87,7 +91,7 @@ class OperationSchema:
|
|||
|
||||
# trigger on_change effects
|
||||
|
||||
self.item.save()
|
||||
self.save()
|
||||
|
||||
@transaction.atomic
|
||||
def add_argument(self, operation: Operation, argument: Operation) -> Optional[Argument]:
|
||||
|
@ -95,7 +99,7 @@ class OperationSchema:
|
|||
if Argument.objects.filter(operation=operation, argument=argument).exists():
|
||||
return None
|
||||
result = Argument.objects.create(operation=operation, argument=argument)
|
||||
self.item.save()
|
||||
self.save()
|
||||
return result
|
||||
|
||||
@transaction.atomic
|
||||
|
@ -109,7 +113,7 @@ class OperationSchema:
|
|||
|
||||
# trigger on_change effects
|
||||
|
||||
self.item.save()
|
||||
self.save()
|
||||
|
||||
@transaction.atomic
|
||||
def set_substitutions(self, target: Operation, substitutes: list[dict]):
|
||||
|
@ -125,4 +129,4 @@ class OperationSchema:
|
|||
|
||||
# trigger on_change effects
|
||||
|
||||
self.item.save()
|
||||
self.save()
|
|
@ -33,4 +33,4 @@ class SynthesisSubstitution(Model):
|
|||
verbose_name_plural = 'Таблицы отождествлений'
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f'{self.original.pk} -> {self.substitution.pk}'
|
||||
return f'{self.original} -> {self.substitution}'
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
from apps.rsform.models import LibraryItem, LibraryItemType
|
||||
|
||||
from .api_OSS import OperationSchema
|
||||
from .Argument import Argument
|
||||
from .Operation import Operation, OperationType
|
||||
from .OperationSchema import OperationSchema
|
||||
from .SynthesisSubstitution import SynthesisSubstitution
|
||||
|
|
|
@ -85,20 +85,19 @@ class OperationSchemaSerializer(serializers.ModelSerializer):
|
|||
|
||||
class Meta:
|
||||
''' serializer metadata. '''
|
||||
model = LibraryItem
|
||||
model = OperationSchema
|
||||
fields = '__all__'
|
||||
|
||||
def to_representation(self, instance: LibraryItem):
|
||||
def to_representation(self, instance: OperationSchema):
|
||||
result = LibraryItemDetailsSerializer(instance).data
|
||||
oss = OperationSchema(instance)
|
||||
result['items'] = []
|
||||
for operation in oss.operations():
|
||||
for operation in instance.operations():
|
||||
result['items'].append(OperationSerializer(operation).data)
|
||||
result['arguments'] = []
|
||||
for argument in oss.arguments():
|
||||
for argument in instance.arguments():
|
||||
result['arguments'].append(ArgumentSerializer(argument).data)
|
||||
result['substitutions'] = []
|
||||
for substitution in oss.substitutions().values(
|
||||
for substitution in instance.substitutions().values(
|
||||
'operation',
|
||||
'original',
|
||||
'substitution',
|
||||
|
|
|
@ -1 +1,4 @@
|
|||
''' Tests for Django Models. '''
|
||||
from .t_Argument import *
|
||||
from .t_Operation import *
|
||||
from .t_SynthesisSubstitution import *
|
||||
|
|
36
rsconcept/backend/apps/oss/tests/s_models/t_Argument.py
Normal file
36
rsconcept/backend/apps/oss/tests/s_models/t_Argument.py
Normal file
|
@ -0,0 +1,36 @@
|
|||
''' Testing models: Argument. '''
|
||||
from django.test import TestCase
|
||||
|
||||
from apps.oss.models import Argument, Operation, OperationSchema, OperationType
|
||||
|
||||
|
||||
class TestArgument(TestCase):
|
||||
''' Testing Argument model. '''
|
||||
|
||||
def setUp(self):
|
||||
self.oss = OperationSchema.objects.create(alias='T1')
|
||||
|
||||
self.operation1 = Operation.objects.create(oss=self.oss, alias='KS1', operation_type=OperationType.INPUT)
|
||||
self.operation2 = Operation.objects.create(oss=self.oss, alias='KS2', operation_type=OperationType.SYNTHESIS)
|
||||
self.operation3 = Operation.objects.create(oss=self.oss, alias='KS3', operation_type=OperationType.INPUT)
|
||||
self.argument = Argument.objects.create(
|
||||
operation=self.operation2,
|
||||
argument=self.operation1
|
||||
)
|
||||
|
||||
|
||||
def test_str(self):
|
||||
testStr = f'{self.operation1} -> {self.operation2}'
|
||||
self.assertEqual(str(self.argument), testStr)
|
||||
|
||||
|
||||
def test_cascade_delete_operation(self):
|
||||
self.assertEqual(Argument.objects.count(), 1)
|
||||
self.operation2.delete()
|
||||
self.assertEqual(Argument.objects.count(), 0)
|
||||
|
||||
|
||||
def test_cascade_delete_argument(self):
|
||||
self.assertEqual(Argument.objects.count(), 1)
|
||||
self.operation1.delete()
|
||||
self.assertEqual(Argument.objects.count(), 0)
|
31
rsconcept/backend/apps/oss/tests/s_models/t_Operation.py
Normal file
31
rsconcept/backend/apps/oss/tests/s_models/t_Operation.py
Normal file
|
@ -0,0 +1,31 @@
|
|||
''' Testing models: Operation. '''
|
||||
from django.test import TestCase
|
||||
|
||||
from apps.oss.models import Operation, OperationSchema, OperationType
|
||||
|
||||
|
||||
class TestOperation(TestCase):
|
||||
''' Testing Operation model. '''
|
||||
|
||||
def setUp(self):
|
||||
self.oss = OperationSchema.objects.create(alias='T1')
|
||||
self.operation = Operation.objects.create(
|
||||
oss=self.oss,
|
||||
alias='KS1'
|
||||
)
|
||||
|
||||
|
||||
def test_str(self):
|
||||
testStr = 'Операция KS1'
|
||||
self.assertEqual(str(self.operation), testStr)
|
||||
|
||||
|
||||
def test_create_default(self):
|
||||
self.assertEqual(self.operation.oss, self.oss)
|
||||
self.assertEqual(self.operation.operation_type, OperationType.INPUT)
|
||||
self.assertEqual(self.operation.result, None)
|
||||
self.assertEqual(self.operation.alias, 'KS1')
|
||||
self.assertEqual(self.operation.title, '')
|
||||
self.assertEqual(self.operation.comment, '')
|
||||
self.assertEqual(self.operation.position_x, 0)
|
||||
self.assertEqual(self.operation.position_y, 0)
|
|
@ -0,0 +1,75 @@
|
|||
''' Testing models: SynthesisSubstitution. '''
|
||||
from unittest import result
|
||||
|
||||
from django.test import TestCase
|
||||
|
||||
from apps.oss.models import (
|
||||
Argument,
|
||||
Operation,
|
||||
OperationSchema,
|
||||
OperationType,
|
||||
SynthesisSubstitution
|
||||
)
|
||||
from apps.rsform.models import RSForm
|
||||
|
||||
|
||||
class TestSynthesisSubstitution(TestCase):
|
||||
''' Testing SynthesisSubstitution model. '''
|
||||
|
||||
def setUp(self):
|
||||
self.oss = OperationSchema.objects.create(alias='T1')
|
||||
|
||||
self.ks1 = RSForm.objects.create(alias='KS1', title='Test1')
|
||||
self.ks1x1 = self.ks1.insert_new('X1', term_resolved='X1_1')
|
||||
self.ks2 = RSForm.objects.create(alias='KS2', title='Test2')
|
||||
self.ks2x1 = self.ks2.insert_new('X2', term_resolved='X1_2')
|
||||
|
||||
self.operation1 = Operation.objects.create(
|
||||
oss=self.oss,
|
||||
alias='KS1',
|
||||
operation_type=OperationType.INPUT,
|
||||
result=self.ks1)
|
||||
self.operation2 = Operation.objects.create(
|
||||
oss=self.oss,
|
||||
alias='KS2',
|
||||
operation_type=OperationType.INPUT,
|
||||
result=self.ks1)
|
||||
self.operation3 = Operation.objects.create(oss=self.oss, alias='KS3', operation_type=OperationType.SYNTHESIS)
|
||||
Argument.objects.create(
|
||||
operation=self.operation3,
|
||||
argument=self.operation1
|
||||
)
|
||||
Argument.objects.create(
|
||||
operation=self.operation3,
|
||||
argument=self.operation2
|
||||
)
|
||||
|
||||
self.substitution = SynthesisSubstitution.objects.create(
|
||||
operation=self.operation3,
|
||||
original=self.ks1x1,
|
||||
substitution=self.ks2x1,
|
||||
transfer_term=False
|
||||
)
|
||||
|
||||
|
||||
def test_str(self):
|
||||
testStr = f'{self.ks1x1} -> {self.ks2x1}'
|
||||
self.assertEqual(str(self.substitution), testStr)
|
||||
|
||||
|
||||
def test_cascade_delete_operation(self):
|
||||
self.assertEqual(SynthesisSubstitution.objects.count(), 1)
|
||||
self.operation3.delete()
|
||||
self.assertEqual(SynthesisSubstitution.objects.count(), 0)
|
||||
|
||||
|
||||
def test_cascade_delete_original(self):
|
||||
self.assertEqual(SynthesisSubstitution.objects.count(), 1)
|
||||
self.ks1x1.delete()
|
||||
self.assertEqual(SynthesisSubstitution.objects.count(), 0)
|
||||
|
||||
|
||||
def test_cascade_delete_substitution(self):
|
||||
self.assertEqual(SynthesisSubstitution.objects.count(), 1)
|
||||
self.ks2x1.delete()
|
||||
self.assertEqual(SynthesisSubstitution.objects.count(), 0)
|
|
@ -12,29 +12,29 @@ class TestOssViewset(EndpointTester):
|
|||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.owned = OperationSchema.create(title='Test', alias='T1', owner=self.user)
|
||||
self.owned_id = self.owned.item.pk
|
||||
self.unowned = OperationSchema.create(title='Test2', alias='T2')
|
||||
self.unowned_id = self.unowned.item.pk
|
||||
self.private = OperationSchema.create(title='Test2', alias='T2', access_policy=AccessPolicy.PRIVATE)
|
||||
self.private_id = self.private.item.pk
|
||||
self.invalid_id = self.private.item.pk + 1337
|
||||
self.owned = OperationSchema.objects.create(title='Test', alias='T1', owner=self.user)
|
||||
self.owned_id = self.owned.pk
|
||||
self.unowned = OperationSchema.objects.create(title='Test2', alias='T2')
|
||||
self.unowned_id = self.unowned.pk
|
||||
self.private = OperationSchema.objects.create(title='Test2', alias='T2', access_policy=AccessPolicy.PRIVATE)
|
||||
self.private_id = self.private.pk
|
||||
self.invalid_id = self.private.pk + 1337
|
||||
|
||||
|
||||
def populateData(self):
|
||||
self.ks1 = RSForm.create(alias='KS1', title='Test1')
|
||||
self.ks1 = RSForm.objects.create(alias='KS1', title='Test1')
|
||||
self.ks1x1 = self.ks1.insert_new('X1', term_resolved='X1_1')
|
||||
self.ks2 = RSForm.create(alias='KS2', title='Test2')
|
||||
self.ks2 = RSForm.objects.create(alias='KS2', title='Test2')
|
||||
self.ks2x1 = self.ks2.insert_new('X2', term_resolved='X1_2')
|
||||
self.operation1 = self.owned.create_operation(
|
||||
alias='1',
|
||||
operation_type=OperationType.INPUT,
|
||||
result=self.ks1.item
|
||||
result=self.ks1
|
||||
)
|
||||
self.operation2 = self.owned.create_operation(
|
||||
alias='2',
|
||||
operation_type=OperationType.INPUT,
|
||||
result=self.ks2.item
|
||||
result=self.ks2
|
||||
)
|
||||
self.operation3 = self.owned.create_operation(
|
||||
alias='3',
|
||||
|
@ -53,12 +53,12 @@ class TestOssViewset(EndpointTester):
|
|||
self.populateData()
|
||||
|
||||
response = self.executeOK(item=self.owned_id)
|
||||
self.assertEqual(response.data['owner'], self.owned.item.owner.pk)
|
||||
self.assertEqual(response.data['title'], self.owned.item.title)
|
||||
self.assertEqual(response.data['alias'], self.owned.item.alias)
|
||||
self.assertEqual(response.data['location'], self.owned.item.location)
|
||||
self.assertEqual(response.data['access_policy'], self.owned.item.access_policy)
|
||||
self.assertEqual(response.data['visible'], self.owned.item.visible)
|
||||
self.assertEqual(response.data['owner'], self.owned.owner.pk)
|
||||
self.assertEqual(response.data['title'], self.owned.title)
|
||||
self.assertEqual(response.data['alias'], self.owned.alias)
|
||||
self.assertEqual(response.data['location'], self.owned.location)
|
||||
self.assertEqual(response.data['access_policy'], self.owned.access_policy)
|
||||
self.assertEqual(response.data['visible'], self.owned.visible)
|
||||
|
||||
self.assertEqual(response.data['item_type'], LibraryItemType.OPERATION_SCHEMA)
|
||||
|
||||
|
@ -179,7 +179,7 @@ class TestOssViewset(EndpointTester):
|
|||
'arguments': [self.operation1.pk, self.operation3.pk]
|
||||
}
|
||||
response = self.executeCreated(data=data, item=self.owned_id)
|
||||
self.owned.item.refresh_from_db()
|
||||
self.owned.refresh_from_db()
|
||||
new_operation = response.data['new_operation']
|
||||
arguments = self.owned.arguments()
|
||||
self.assertTrue(arguments.filter(operation__id=new_operation['id'], argument=self.operation1))
|
||||
|
@ -193,14 +193,14 @@ class TestOssViewset(EndpointTester):
|
|||
'item_data': {
|
||||
'alias': 'Test4',
|
||||
'operation_type': OperationType.INPUT,
|
||||
'result': self.ks1.item.pk
|
||||
'result': self.ks1.pk
|
||||
},
|
||||
'positions': [],
|
||||
}
|
||||
response = self.executeCreated(data=data, item=self.owned_id)
|
||||
self.owned.item.refresh_from_db()
|
||||
self.owned.refresh_from_db()
|
||||
new_operation = response.data['new_operation']
|
||||
self.assertEqual(new_operation['result'], self.ks1.item.pk)
|
||||
self.assertEqual(new_operation['result'], self.ks1.pk)
|
||||
|
||||
|
||||
@decl_endpoint('/api/oss/{item}/delete-operation', method='patch')
|
||||
|
|
|
@ -20,11 +20,11 @@ from .. import serializers as s
|
|||
@extend_schema_view()
|
||||
class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.RetrieveAPIView):
|
||||
''' Endpoint: OperationSchema. '''
|
||||
queryset = m.LibraryItem.objects.filter(item_type=m.LibraryItemType.OPERATION_SCHEMA)
|
||||
queryset = m.OperationSchema.objects.all()
|
||||
serializer_class = s.LibraryItemSerializer
|
||||
|
||||
def _get_schema(self) -> m.OperationSchema:
|
||||
return m.OperationSchema(cast(m.LibraryItem, self.get_object()))
|
||||
return cast(m.OperationSchema, self.get_object())
|
||||
|
||||
def get_permissions(self):
|
||||
''' Determine permission class. '''
|
||||
|
@ -52,7 +52,7 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
|
|||
@action(detail=True, methods=['get'], url_path='details')
|
||||
def details(self, request: Request, pk):
|
||||
''' Endpoint: Detailed OSS data. '''
|
||||
serializer = s.OperationSchemaSerializer(cast(m.LibraryItem, self.get_object()))
|
||||
serializer = s.OperationSchemaSerializer(self._get_schema())
|
||||
return Response(
|
||||
status=c.HTTP_200_OK,
|
||||
data=serializer.data
|
||||
|
@ -101,13 +101,13 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
|
|||
if new_operation.operation_type != m.OperationType.INPUT and 'arguments' in serializer.validated_data:
|
||||
for argument in serializer.validated_data['arguments']:
|
||||
schema.add_argument(operation=new_operation, argument=argument)
|
||||
schema.item.refresh_from_db()
|
||||
schema.refresh_from_db()
|
||||
|
||||
response = Response(
|
||||
status=c.HTTP_201_CREATED,
|
||||
data={
|
||||
'new_operation': s.OperationSerializer(new_operation).data,
|
||||
'oss': s.OperationSchemaSerializer(schema.item).data
|
||||
'oss': s.OperationSchemaSerializer(schema).data
|
||||
}
|
||||
)
|
||||
return response
|
||||
|
@ -129,16 +129,16 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
|
|||
schema = self._get_schema()
|
||||
serializer = s.OperationDeleteSerializer(
|
||||
data=request.data,
|
||||
context={'oss': schema.item}
|
||||
context={'oss': schema}
|
||||
)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
|
||||
with transaction.atomic():
|
||||
schema.update_positions(serializer.validated_data['positions'])
|
||||
schema.delete_operation(serializer.validated_data['target'])
|
||||
schema.item.refresh_from_db()
|
||||
schema.refresh_from_db()
|
||||
|
||||
return Response(
|
||||
status=c.HTTP_200_OK,
|
||||
data=s.OperationSchemaSerializer(schema.item).data
|
||||
data=s.OperationSchemaSerializer(schema).data
|
||||
)
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
# Generated by Django 5.0.7 on 2024-07-22 14:21
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('rsform', '0008_alter_libraryitem_item_type'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='RSForm',
|
||||
fields=[
|
||||
],
|
||||
options={
|
||||
'proxy': True,
|
||||
'indexes': [],
|
||||
'constraints': [],
|
||||
},
|
||||
bases=('rsform.libraryitem',),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='constituenta',
|
||||
name='schema',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='rsform.rsform', verbose_name='Концептуальная схема'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='librarytemplate',
|
||||
name='lib_source',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='rsform.rsform', verbose_name='Источник'),
|
||||
),
|
||||
]
|
|
@ -40,7 +40,7 @@ class Constituenta(Model):
|
|||
''' Constituenta is the base unit for every conceptual schema. '''
|
||||
schema: ForeignKey = ForeignKey(
|
||||
verbose_name='Концептуальная схема',
|
||||
to='rsform.LibraryItem',
|
||||
to='rsform.RSForm',
|
||||
on_delete=CASCADE
|
||||
)
|
||||
order: PositiveIntegerField = PositiveIntegerField(
|
||||
|
|
|
@ -6,7 +6,7 @@ class LibraryTemplate(Model):
|
|||
''' Template for library items and constituents. '''
|
||||
lib_source: ForeignKey = ForeignKey(
|
||||
verbose_name='Источник',
|
||||
to='rsform.LibraryItem',
|
||||
to='rsform.RSForm',
|
||||
on_delete=CASCADE,
|
||||
null=True
|
||||
)
|
||||
|
|
|
@ -5,7 +5,7 @@ from typing import Optional, cast
|
|||
from cctext import Entity, Resolver, TermForm, extract_entities, split_grams
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db import transaction
|
||||
from django.db.models import QuerySet
|
||||
from django.db.models import Manager, QuerySet
|
||||
|
||||
from shared import messages as msg
|
||||
|
||||
|
@ -28,21 +28,29 @@ from .Version import Version
|
|||
_INSERT_LAST: int = -1
|
||||
|
||||
|
||||
class RSForm:
|
||||
class RSForm(LibraryItem):
|
||||
''' RSForm is math form of conceptual schema. '''
|
||||
|
||||
def __init__(self, item: LibraryItem):
|
||||
if item.item_type != LibraryItemType.RSFORM:
|
||||
raise ValueError(msg.libraryTypeUnexpected())
|
||||
self.item = item
|
||||
class Meta:
|
||||
''' Model metadata. '''
|
||||
proxy = True
|
||||
|
||||
@staticmethod
|
||||
def create(**kwargs) -> 'RSForm':
|
||||
return RSForm(LibraryItem.objects.create(item_type=LibraryItemType.RSFORM, **kwargs))
|
||||
class InternalManager(Manager):
|
||||
''' Object manager. '''
|
||||
|
||||
def get_queryset(self) -> QuerySet:
|
||||
return super().get_queryset().filter(item_type=LibraryItemType.RSFORM)
|
||||
|
||||
def create(self, **kwargs):
|
||||
kwargs.update({'item_type': LibraryItemType.RSFORM})
|
||||
return super().create(**kwargs)
|
||||
|
||||
# Legit overriding object manager
|
||||
objects = InternalManager() # type: ignore[misc]
|
||||
|
||||
def constituents(self) -> QuerySet[Constituenta]:
|
||||
''' Get QuerySet containing all constituents of current RSForm. '''
|
||||
return Constituenta.objects.filter(schema=self.item)
|
||||
return Constituenta.objects.filter(schema=self.pk)
|
||||
|
||||
def resolver(self) -> Resolver:
|
||||
''' Create resolver for text references based on schema terms. '''
|
||||
|
@ -98,7 +106,7 @@ class RSForm:
|
|||
''' Get maximum alias index for specific CstType. '''
|
||||
result: int = 0
|
||||
items = Constituenta.objects \
|
||||
.filter(schema=self.item, cst_type=cst_type) \
|
||||
.filter(schema=self, cst_type=cst_type) \
|
||||
.order_by('-alias') \
|
||||
.values_list('alias', flat=True)
|
||||
for alias in items:
|
||||
|
@ -150,13 +158,13 @@ class RSForm:
|
|||
cst_type = guess_type(alias)
|
||||
self._shift_positions(position, 1)
|
||||
result = Constituenta.objects.create(
|
||||
schema=self.item,
|
||||
schema=self,
|
||||
order=position,
|
||||
alias=alias,
|
||||
cst_type=cst_type,
|
||||
**kwargs
|
||||
)
|
||||
self.item.save()
|
||||
self.save()
|
||||
result.refresh_from_db()
|
||||
return result
|
||||
|
||||
|
@ -183,13 +191,13 @@ class RSForm:
|
|||
result = deepcopy(items)
|
||||
for cst in result:
|
||||
cst.pk = None
|
||||
cst.schema = self.item
|
||||
cst.schema = self
|
||||
cst.order = position
|
||||
cst.alias = mapping[cst.alias]
|
||||
cst.apply_mapping(mapping)
|
||||
cst.save()
|
||||
position = position + 1
|
||||
self.item.save()
|
||||
self.save()
|
||||
return result
|
||||
|
||||
@transaction.atomic
|
||||
|
@ -213,7 +221,7 @@ class RSForm:
|
|||
count_moved += 1
|
||||
update_list.append(cst)
|
||||
Constituenta.objects.bulk_update(update_list, ['order'])
|
||||
self.item.save()
|
||||
self.save()
|
||||
|
||||
@transaction.atomic
|
||||
def delete_cst(self, listCst):
|
||||
|
@ -222,7 +230,7 @@ class RSForm:
|
|||
cst.delete()
|
||||
self._reset_order()
|
||||
self.resolve_all_text()
|
||||
self.item.save()
|
||||
self.save()
|
||||
|
||||
@transaction.atomic
|
||||
def substitute(
|
||||
|
@ -296,7 +304,7 @@ class RSForm:
|
|||
def create_version(self, version: str, description: str, data) -> Version:
|
||||
''' Creates version for current state. '''
|
||||
return Version.objects.create(
|
||||
item=self.item,
|
||||
item=self,
|
||||
version=version,
|
||||
description=description,
|
||||
data=data
|
||||
|
@ -322,7 +330,7 @@ class RSForm:
|
|||
prefix = get_type_prefix(cst_type)
|
||||
for text in expressions:
|
||||
new_item = Constituenta.objects.create(
|
||||
schema=self.item,
|
||||
schema=self,
|
||||
order=position,
|
||||
alias=f'{prefix}{free_index}',
|
||||
definition_formal=text,
|
||||
|
@ -332,7 +340,7 @@ class RSForm:
|
|||
free_index = free_index + 1
|
||||
position = position + 1
|
||||
|
||||
self.item.save()
|
||||
self.save()
|
||||
return result
|
||||
|
||||
def _shift_positions(self, start: int, shift: int):
|
||||
|
@ -341,7 +349,7 @@ class RSForm:
|
|||
update_list = \
|
||||
Constituenta.objects \
|
||||
.only('id', 'order', 'schema') \
|
||||
.filter(schema=self.item, order__gte=start)
|
||||
.filter(schema=self.pk, order__gte=start)
|
||||
for cst in update_list:
|
||||
cst.order += shift
|
||||
Constituenta.objects.bulk_update(update_list, ['order'])
|
|
@ -1,6 +1,6 @@
|
|||
''' Django: Models. '''
|
||||
|
||||
from .api_RSForm import RSForm
|
||||
from .RSForm import RSForm
|
||||
from .Constituenta import Constituenta, CstType, _empty_forms
|
||||
from .Editor import Editor
|
||||
from .LibraryItem import (
|
||||
|
|
|
@ -109,22 +109,21 @@ class CstSerializer(serializers.ModelSerializer):
|
|||
|
||||
def update(self, instance: Constituenta, validated_data) -> Constituenta:
|
||||
data = validated_data # Note: use alias for better code readability
|
||||
schema = RSForm(instance.schema)
|
||||
definition: Optional[str] = data['definition_raw'] if 'definition_raw' in data else None
|
||||
term: Optional[str] = data['term_raw'] if 'term_raw' in data else None
|
||||
term_changed = 'term_forms' in data
|
||||
if definition is not None and definition != instance.definition_raw:
|
||||
data['definition_resolved'] = schema.resolver().resolve(definition)
|
||||
data['definition_resolved'] = instance.schema.resolver().resolve(definition)
|
||||
if term is not None and term != instance.term_raw:
|
||||
data['term_resolved'] = schema.resolver().resolve(term)
|
||||
data['term_resolved'] = instance.schema.resolver().resolve(term)
|
||||
if data['term_resolved'] != instance.term_resolved and 'term_forms' not in data:
|
||||
data['term_forms'] = []
|
||||
term_changed = data['term_resolved'] != instance.term_resolved
|
||||
result: Constituenta = super().update(instance, data)
|
||||
if term_changed:
|
||||
schema.on_term_change([result.id])
|
||||
instance.schema.on_term_change([result.id])
|
||||
result.refresh_from_db()
|
||||
schema.item.save()
|
||||
instance.schema.save()
|
||||
return result
|
||||
|
||||
|
||||
|
@ -170,17 +169,16 @@ class RSFormSerializer(serializers.ModelSerializer):
|
|||
model = LibraryItem
|
||||
fields = '__all__'
|
||||
|
||||
def to_representation(self, instance: LibraryItem) -> dict:
|
||||
def to_representation(self, instance: RSForm) -> dict:
|
||||
result = LibraryItemDetailsSerializer(instance).data
|
||||
schema = RSForm(instance)
|
||||
result['items'] = []
|
||||
for cst in schema.constituents().order_by('order'):
|
||||
for cst in instance.constituents().order_by('order'):
|
||||
result['items'].append(CstSerializer(cst).data)
|
||||
return result
|
||||
|
||||
def to_versioned_data(self) -> dict:
|
||||
''' Create serializable version representation without redundant data. '''
|
||||
result = self.to_representation(cast(LibraryItem, self.instance))
|
||||
result = self.to_representation(cast(RSForm, self.instance))
|
||||
del result['versions']
|
||||
del result['subscribers']
|
||||
del result['editors']
|
||||
|
@ -197,14 +195,14 @@ class RSFormSerializer(serializers.ModelSerializer):
|
|||
|
||||
def from_versioned_data(self, version: int, data: dict) -> dict:
|
||||
''' Load data from version. '''
|
||||
result = self.to_representation(cast(LibraryItem, self.instance))
|
||||
result = self.to_representation(cast(RSForm, self.instance))
|
||||
result['version'] = version
|
||||
return result | data
|
||||
|
||||
@transaction.atomic
|
||||
def restore_from_version(self, data: dict):
|
||||
''' Load data from version. '''
|
||||
schema = RSForm(cast(LibraryItem, self.instance))
|
||||
schema = cast(RSForm, self.instance)
|
||||
items: list[dict] = data['items']
|
||||
ids: list[int] = [item['id'] for item in items]
|
||||
processed: list[int] = []
|
||||
|
@ -258,13 +256,13 @@ class RSFormParseSerializer(serializers.ModelSerializer):
|
|||
model = LibraryItem
|
||||
fields = '__all__'
|
||||
|
||||
def to_representation(self, instance: LibraryItem):
|
||||
def to_representation(self, instance: RSForm):
|
||||
result = RSFormSerializer(instance).data
|
||||
return self._parse_data(result)
|
||||
|
||||
def from_versioned_data(self, version: int, data: dict) -> dict:
|
||||
''' Load data from version and parse. '''
|
||||
item = cast(LibraryItem, self.instance)
|
||||
item = cast(RSForm, self.instance)
|
||||
result = RSFormSerializer(item).from_versioned_data(version, data)
|
||||
return self._parse_data(result)
|
||||
|
||||
|
@ -283,7 +281,7 @@ class CstTargetSerializer(serializers.Serializer):
|
|||
target = PKField(many=False, queryset=Constituenta.objects.all())
|
||||
|
||||
def validate(self, attrs):
|
||||
schema = cast(LibraryItem, self.context['schema'])
|
||||
schema = cast(RSForm, self.context['schema'])
|
||||
cst = cast(Constituenta, attrs['target'])
|
||||
if schema and cst.schema != schema:
|
||||
raise serializers.ValidationError({
|
||||
|
@ -315,7 +313,7 @@ class CstRenameSerializer(serializers.Serializer):
|
|||
|
||||
def validate(self, attrs):
|
||||
attrs = super().validate(attrs)
|
||||
schema = cast(LibraryItem, self.context['schema'])
|
||||
schema = cast(RSForm, self.context['schema'])
|
||||
cst = cast(Constituenta, attrs['target'])
|
||||
if cst.schema != schema:
|
||||
raise serializers.ValidationError({
|
||||
|
@ -326,7 +324,7 @@ class CstRenameSerializer(serializers.Serializer):
|
|||
raise serializers.ValidationError({
|
||||
'alias': msg.renameTrivial(new_alias)
|
||||
})
|
||||
if RSForm(schema).constituents().filter(alias=new_alias).exists():
|
||||
if schema.constituents().filter(alias=new_alias).exists():
|
||||
raise serializers.ValidationError({
|
||||
'alias': msg.aliasTaken(new_alias)
|
||||
})
|
||||
|
@ -338,7 +336,7 @@ class CstListSerializer(serializers.Serializer):
|
|||
items = PKField(many=True, queryset=Constituenta.objects.all())
|
||||
|
||||
def validate(self, attrs):
|
||||
schema = cast(LibraryItem, self.context['schema'])
|
||||
schema = cast(RSForm, self.context['schema'])
|
||||
if not schema:
|
||||
return attrs
|
||||
|
||||
|
@ -370,7 +368,7 @@ class CstSubstituteSerializer(serializers.Serializer):
|
|||
)
|
||||
|
||||
def validate(self, attrs):
|
||||
schema = cast(LibraryItem, self.context['schema'])
|
||||
schema = cast(RSForm, self.context['schema'])
|
||||
deleted = set()
|
||||
for item in attrs['substitutions']:
|
||||
original_cst = cast(Constituenta, item['original'])
|
||||
|
@ -397,8 +395,8 @@ class CstSubstituteSerializer(serializers.Serializer):
|
|||
|
||||
class InlineSynthesisSerializer(serializers.Serializer):
|
||||
''' Serializer: Inline synthesis operation input. '''
|
||||
receiver = PKField(many=False, queryset=LibraryItem.objects.all())
|
||||
source = PKField(many=False, queryset=LibraryItem.objects.all()) # type: ignore
|
||||
receiver = PKField(many=False, queryset=RSForm.objects.all())
|
||||
source = PKField(many=False, queryset=RSForm.objects.all()) # type: ignore
|
||||
items = PKField(many=True, queryset=Constituenta.objects.all())
|
||||
substitutions = serializers.ListField(
|
||||
child=CstSubstituteSerializerBase()
|
||||
|
@ -406,8 +404,8 @@ class InlineSynthesisSerializer(serializers.Serializer):
|
|||
|
||||
def validate(self, attrs):
|
||||
user = cast(User, self.context['user'])
|
||||
schema_in = cast(LibraryItem, attrs['source'])
|
||||
schema_out = cast(LibraryItem, attrs['receiver'])
|
||||
schema_in = cast(RSForm, attrs['source'])
|
||||
schema_out = cast(RSForm, attrs['receiver'])
|
||||
if user.is_anonymous or (schema_out.owner != user and not user.is_staff):
|
||||
raise PermissionDenied({
|
||||
'message': msg.schemaNotOwned(),
|
||||
|
|
|
@ -39,9 +39,9 @@ class RSFormTRSSerializer(serializers.Serializer):
|
|||
def _prepare_json_rsform(schema: RSForm) -> dict:
|
||||
return {
|
||||
'type': _TRS_TYPE,
|
||||
'title': schema.item.title,
|
||||
'alias': schema.item.alias,
|
||||
'comment': schema.item.comment,
|
||||
'title': schema.title,
|
||||
'alias': schema.alias,
|
||||
'comment': schema.comment,
|
||||
'items': [],
|
||||
'claimed': False,
|
||||
'selection': [],
|
||||
|
@ -125,7 +125,7 @@ class RSFormTRSSerializer(serializers.Serializer):
|
|||
result['comment'] = data.get('comment', '')
|
||||
if 'id' in data:
|
||||
result['id'] = data['id']
|
||||
self.instance = RSForm(LibraryItem.objects.get(pk=result['id']))
|
||||
self.instance = RSForm.objects.get(pk=result['id'])
|
||||
return result
|
||||
|
||||
def validate(self, attrs: dict):
|
||||
|
@ -139,7 +139,7 @@ class RSFormTRSSerializer(serializers.Serializer):
|
|||
|
||||
@transaction.atomic
|
||||
def create(self, validated_data: dict) -> RSForm:
|
||||
self.instance: RSForm = RSForm.create(
|
||||
self.instance: RSForm = RSForm.objects.create(
|
||||
owner=validated_data.get('owner', None),
|
||||
alias=validated_data['alias'],
|
||||
title=validated_data['title'],
|
||||
|
@ -149,12 +149,12 @@ class RSFormTRSSerializer(serializers.Serializer):
|
|||
access_policy=validated_data['access_policy'],
|
||||
location=validated_data['location']
|
||||
)
|
||||
self.instance.item.save()
|
||||
self.instance.save()
|
||||
order = 1
|
||||
for cst_data in validated_data['items']:
|
||||
cst = Constituenta(
|
||||
alias=cst_data['alias'],
|
||||
schema=self.instance.item,
|
||||
schema=self.instance,
|
||||
order=order,
|
||||
cst_type=cst_data['cstType'],
|
||||
)
|
||||
|
@ -167,11 +167,11 @@ class RSFormTRSSerializer(serializers.Serializer):
|
|||
@transaction.atomic
|
||||
def update(self, instance: RSForm, validated_data) -> RSForm:
|
||||
if 'alias' in validated_data:
|
||||
instance.item.alias = validated_data['alias']
|
||||
instance.alias = validated_data['alias']
|
||||
if 'title' in validated_data:
|
||||
instance.item.title = validated_data['title']
|
||||
instance.title = validated_data['title']
|
||||
if 'comment' in validated_data:
|
||||
instance.item.comment = validated_data['comment']
|
||||
instance.comment = validated_data['comment']
|
||||
|
||||
order = 1
|
||||
prev_constituents = instance.constituents()
|
||||
|
@ -188,7 +188,7 @@ class RSFormTRSSerializer(serializers.Serializer):
|
|||
else:
|
||||
cst = Constituenta(
|
||||
alias=cst_data['alias'],
|
||||
schema=instance.item,
|
||||
schema=instance,
|
||||
order=order,
|
||||
cst_type=cst_data['cstType'],
|
||||
)
|
||||
|
@ -202,7 +202,7 @@ class RSFormTRSSerializer(serializers.Serializer):
|
|||
prev_cst.delete()
|
||||
|
||||
instance.resolve_all_text()
|
||||
instance.item.save()
|
||||
instance.save()
|
||||
return instance
|
||||
|
||||
@staticmethod
|
||||
|
|
|
@ -3,15 +3,15 @@ from django.db.utils import IntegrityError
|
|||
from django.forms import ValidationError
|
||||
from django.test import TestCase
|
||||
|
||||
from apps.rsform.models import Constituenta, CstType, LibraryItem, LibraryItemType
|
||||
from apps.rsform.models import Constituenta, CstType, LibraryItemType, RSForm
|
||||
|
||||
|
||||
class TestConstituenta(TestCase):
|
||||
''' Testing Constituenta model. '''
|
||||
|
||||
def setUp(self):
|
||||
self.schema1 = LibraryItem.objects.create(item_type=LibraryItemType.RSFORM, title='Test1')
|
||||
self.schema2 = LibraryItem.objects.create(item_type=LibraryItemType.RSFORM, title='Test2')
|
||||
self.schema1 = RSForm.objects.create(title='Test1')
|
||||
self.schema2 = RSForm.objects.create(title='Test2')
|
||||
|
||||
|
||||
def test_str(self):
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
''' Testing models: Editor. '''
|
||||
from django.test import TestCase
|
||||
|
||||
from apps.rsform.models import Editor, LibraryItem, LibraryItemType, User
|
||||
from apps.rsform.models import Editor, LibraryItemType, RSForm, User
|
||||
|
||||
|
||||
class TestEditor(TestCase):
|
||||
|
@ -10,8 +10,7 @@ class TestEditor(TestCase):
|
|||
def setUp(self):
|
||||
self.user1 = User.objects.create(username='User1')
|
||||
self.user2 = User.objects.create(username='User2')
|
||||
self.item = LibraryItem.objects.create(
|
||||
item_type=LibraryItemType.RSFORM,
|
||||
self.item = RSForm.objects.create(
|
||||
title='Test',
|
||||
alias='КС1',
|
||||
owner=self.user1
|
||||
|
|
|
@ -11,49 +11,49 @@ class TestRSForm(TestCase):
|
|||
def setUp(self):
|
||||
self.user1 = User.objects.create(username='User1')
|
||||
self.user2 = User.objects.create(username='User2')
|
||||
self.schema = RSForm.create(title='Test')
|
||||
self.schema = RSForm.objects.create(title='Test')
|
||||
self.assertNotEqual(self.user1, self.user2)
|
||||
|
||||
|
||||
def test_constituents(self):
|
||||
schema1 = RSForm.create(title='Test1')
|
||||
schema2 = RSForm.create(title='Test2')
|
||||
schema1 = RSForm.objects.create(title='Test1')
|
||||
schema2 = RSForm.objects.create(title='Test2')
|
||||
self.assertFalse(schema1.constituents().exists())
|
||||
self.assertFalse(schema2.constituents().exists())
|
||||
|
||||
Constituenta.objects.create(alias='X1', schema=schema1.item, order=1)
|
||||
Constituenta.objects.create(alias='X2', schema=schema1.item, order=2)
|
||||
Constituenta.objects.create(alias='X1', schema=schema1, order=1)
|
||||
Constituenta.objects.create(alias='X2', schema=schema1, order=2)
|
||||
self.assertTrue(schema1.constituents().exists())
|
||||
self.assertFalse(schema2.constituents().exists())
|
||||
self.assertEqual(schema1.constituents().count(), 2)
|
||||
|
||||
|
||||
def test_get_max_index(self):
|
||||
schema1 = RSForm.create(title='Test1')
|
||||
Constituenta.objects.create(alias='X1', schema=schema1.item, order=1)
|
||||
Constituenta.objects.create(alias='D2', cst_type=CstType.TERM, schema=schema1.item, order=2)
|
||||
schema1 = RSForm.objects.create(title='Test1')
|
||||
Constituenta.objects.create(alias='X1', schema=schema1, order=1)
|
||||
Constituenta.objects.create(alias='D2', cst_type=CstType.TERM, schema=schema1, order=2)
|
||||
self.assertEqual(schema1.get_max_index(CstType.BASE), 1)
|
||||
self.assertEqual(schema1.get_max_index(CstType.TERM), 2)
|
||||
self.assertEqual(schema1.get_max_index(CstType.AXIOM), 0)
|
||||
|
||||
|
||||
def test_insert_at(self):
|
||||
schema = RSForm.create(title='Test')
|
||||
schema = RSForm.objects.create(title='Test')
|
||||
x1 = schema.insert_new('X1')
|
||||
self.assertEqual(x1.order, 1)
|
||||
self.assertEqual(x1.schema, schema.item)
|
||||
self.assertEqual(x1.schema, schema)
|
||||
|
||||
x2 = schema.insert_new('X2', position=1)
|
||||
x1.refresh_from_db()
|
||||
self.assertEqual(x2.order, 1)
|
||||
self.assertEqual(x2.schema, schema.item)
|
||||
self.assertEqual(x2.schema, schema)
|
||||
self.assertEqual(x1.order, 2)
|
||||
|
||||
x3 = schema.insert_new('X3', position=4)
|
||||
x2.refresh_from_db()
|
||||
x1.refresh_from_db()
|
||||
self.assertEqual(x3.order, 3)
|
||||
self.assertEqual(x3.schema, schema.item)
|
||||
self.assertEqual(x3.schema, schema)
|
||||
self.assertEqual(x2.order, 1)
|
||||
self.assertEqual(x1.order, 2)
|
||||
|
||||
|
@ -62,7 +62,7 @@ class TestRSForm(TestCase):
|
|||
x2.refresh_from_db()
|
||||
x1.refresh_from_db()
|
||||
self.assertEqual(x4.order, 3)
|
||||
self.assertEqual(x4.schema, schema.item)
|
||||
self.assertEqual(x4.schema, schema)
|
||||
self.assertEqual(x3.order, 4)
|
||||
self.assertEqual(x2.order, 1)
|
||||
self.assertEqual(x1.order, 2)
|
||||
|
@ -94,11 +94,11 @@ class TestRSForm(TestCase):
|
|||
def test_insert_last(self):
|
||||
x1 = self.schema.insert_new('X1')
|
||||
self.assertEqual(x1.order, 1)
|
||||
self.assertEqual(x1.schema, self.schema.item)
|
||||
self.assertEqual(x1.schema, self.schema)
|
||||
|
||||
x2 = self.schema.insert_new('X2')
|
||||
self.assertEqual(x2.order, 2)
|
||||
self.assertEqual(x2.schema, self.schema.item)
|
||||
self.assertEqual(x2.schema, self.schema)
|
||||
self.assertEqual(x1.order, 1)
|
||||
|
||||
def test_create_cst(self):
|
||||
|
|
|
@ -8,12 +8,12 @@ class TestConstituentaAPI(EndpointTester):
|
|||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.rsform_owned = RSForm.create(title='Test', alias='T1', owner=self.user)
|
||||
self.rsform_unowned = RSForm.create(title='Test2', alias='T2')
|
||||
self.rsform_owned = RSForm.objects.create(title='Test', alias='T1', owner=self.user)
|
||||
self.rsform_unowned = RSForm.objects.create(title='Test2', alias='T2')
|
||||
self.cst1 = Constituenta.objects.create(
|
||||
alias='X1',
|
||||
cst_type=CstType.BASE,
|
||||
schema=self.rsform_owned.item,
|
||||
schema=self.rsform_owned,
|
||||
order=1,
|
||||
convention='Test',
|
||||
term_raw='Test1',
|
||||
|
@ -22,7 +22,7 @@ class TestConstituentaAPI(EndpointTester):
|
|||
self.cst2 = Constituenta.objects.create(
|
||||
alias='X2',
|
||||
cst_type=CstType.BASE,
|
||||
schema=self.rsform_unowned.item,
|
||||
schema=self.rsform_unowned,
|
||||
order=1,
|
||||
convention='Test1',
|
||||
term_raw='Test2',
|
||||
|
@ -30,7 +30,7 @@ class TestConstituentaAPI(EndpointTester):
|
|||
)
|
||||
self.cst3 = Constituenta.objects.create(
|
||||
alias='X3',
|
||||
schema=self.rsform_owned.item,
|
||||
schema=self.rsform_owned,
|
||||
order=2,
|
||||
term_raw='Test3',
|
||||
term_resolved='Test3',
|
||||
|
|
|
@ -20,20 +20,16 @@ class TestLibraryViewset(EndpointTester):
|
|||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.owned = LibraryItem.objects.create(
|
||||
item_type=LibraryItemType.RSFORM,
|
||||
self.owned = RSForm.objects.create(
|
||||
title='Test',
|
||||
alias='T1',
|
||||
owner=self.user
|
||||
)
|
||||
self.schema = RSForm(self.owned)
|
||||
self.unowned = LibraryItem.objects.create(
|
||||
item_type=LibraryItemType.RSFORM,
|
||||
self.unowned = RSForm.objects.create(
|
||||
title='Test2',
|
||||
alias='T2'
|
||||
)
|
||||
self.common = LibraryItem.objects.create(
|
||||
item_type=LibraryItemType.RSFORM,
|
||||
self.common = RSForm.objects.create(
|
||||
title='Test3',
|
||||
alias='T3',
|
||||
location=LocationHead.COMMON
|
||||
|
@ -363,12 +359,12 @@ class TestLibraryViewset(EndpointTester):
|
|||
|
||||
@decl_endpoint('/api/library/{item}/clone', method='post')
|
||||
def test_clone_rsform(self):
|
||||
x12 = self.schema.insert_new(
|
||||
x12 = self.owned.insert_new(
|
||||
alias='X12',
|
||||
term_raw='человек',
|
||||
term_resolved='человек'
|
||||
)
|
||||
d2 = self.schema.insert_new(
|
||||
d2 = self.owned.insert_new(
|
||||
alias='D2',
|
||||
term_raw='@{X12|plur}',
|
||||
term_resolved='люди'
|
||||
|
|
|
@ -10,16 +10,16 @@ class TestInlineSynthesis(EndpointTester):
|
|||
@decl_endpoint('/api/operations/inline-synthesis', method='patch')
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.schema1 = RSForm.create(title='Test1', alias='T1', owner=self.user)
|
||||
self.schema2 = RSForm.create(title='Test2', alias='T2', owner=self.user)
|
||||
self.unowned = RSForm.create(title='Test3', alias='T3')
|
||||
self.schema1 = RSForm.objects.create(title='Test1', alias='T1', owner=self.user)
|
||||
self.schema2 = RSForm.objects.create(title='Test2', alias='T2', owner=self.user)
|
||||
self.unowned = RSForm.objects.create(title='Test3', alias='T3')
|
||||
|
||||
|
||||
def test_inline_synthesis_inputs(self):
|
||||
invalid_id = 1338
|
||||
data = {
|
||||
'receiver': self.unowned.item.pk,
|
||||
'source': self.schema1.item.pk,
|
||||
'receiver': self.unowned.pk,
|
||||
'source': self.schema1.pk,
|
||||
'items': [],
|
||||
'substitutions': []
|
||||
}
|
||||
|
@ -28,11 +28,11 @@ class TestInlineSynthesis(EndpointTester):
|
|||
data['receiver'] = invalid_id
|
||||
self.executeBadData(data=data)
|
||||
|
||||
data['receiver'] = self.schema1.item.pk
|
||||
data['receiver'] = self.schema1.pk
|
||||
data['source'] = invalid_id
|
||||
self.executeBadData(data=data)
|
||||
|
||||
data['source'] = self.schema1.item.pk
|
||||
data['source'] = self.schema1.pk
|
||||
self.executeOK(data=data)
|
||||
|
||||
data['items'] = [invalid_id]
|
||||
|
@ -51,8 +51,8 @@ class TestInlineSynthesis(EndpointTester):
|
|||
ks2_a1 = self.schema2.insert_new('A1', definition_formal='1=1') # -> not included in items
|
||||
|
||||
data = {
|
||||
'receiver': self.schema1.item.pk,
|
||||
'source': self.schema2.item.pk,
|
||||
'receiver': self.schema1.pk,
|
||||
'source': self.schema2.pk,
|
||||
'items': [ks2_x1.pk, ks2_x2.pk, ks2_s1.pk, ks2_d1.pk],
|
||||
'substitutions': [
|
||||
{
|
||||
|
|
|
@ -24,12 +24,12 @@ class TestRSFormViewset(EndpointTester):
|
|||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.owned = RSForm.create(title='Test', alias='T1', owner=self.user)
|
||||
self.owned_id = self.owned.item.pk
|
||||
self.unowned = RSForm.create(title='Test2', alias='T2')
|
||||
self.unowned_id = self.unowned.item.pk
|
||||
self.private = RSForm.create(title='Test2', alias='T2', access_policy=AccessPolicy.PRIVATE)
|
||||
self.private_id = self.private.item.pk
|
||||
self.owned = RSForm.objects.create(title='Test', alias='T1', owner=self.user)
|
||||
self.owned_id = self.owned.pk
|
||||
self.unowned = RSForm.objects.create(title='Test2', alias='T2')
|
||||
self.unowned_id = self.unowned.pk
|
||||
self.private = RSForm.objects.create(title='Test2', alias='T2', access_policy=AccessPolicy.PRIVATE)
|
||||
self.private_id = self.private.pk
|
||||
|
||||
|
||||
@decl_endpoint('/api/rsforms/create-detailed', method='post')
|
||||
|
@ -63,19 +63,19 @@ class TestRSFormViewset(EndpointTester):
|
|||
)
|
||||
response = self.executeOK()
|
||||
self.assertFalse(response_contains(response, non_schema))
|
||||
self.assertTrue(response_contains(response, self.unowned.item))
|
||||
self.assertTrue(response_contains(response, self.owned.item))
|
||||
self.assertTrue(response_contains(response, self.unowned))
|
||||
self.assertTrue(response_contains(response, self.owned))
|
||||
|
||||
|
||||
@decl_endpoint('/api/rsforms/{item}/contents', method='get')
|
||||
def test_contents(self):
|
||||
response = self.executeOK(item=self.owned_id)
|
||||
self.assertEqual(response.data['owner'], self.owned.item.owner.pk)
|
||||
self.assertEqual(response.data['title'], self.owned.item.title)
|
||||
self.assertEqual(response.data['alias'], self.owned.item.alias)
|
||||
self.assertEqual(response.data['location'], self.owned.item.location)
|
||||
self.assertEqual(response.data['access_policy'], self.owned.item.access_policy)
|
||||
self.assertEqual(response.data['visible'], self.owned.item.visible)
|
||||
self.assertEqual(response.data['owner'], self.owned.owner.pk)
|
||||
self.assertEqual(response.data['title'], self.owned.title)
|
||||
self.assertEqual(response.data['alias'], self.owned.alias)
|
||||
self.assertEqual(response.data['location'], self.owned.location)
|
||||
self.assertEqual(response.data['access_policy'], self.owned.access_policy)
|
||||
self.assertEqual(response.data['visible'], self.owned.visible)
|
||||
|
||||
|
||||
@decl_endpoint('/api/rsforms/{item}/details', method='get')
|
||||
|
@ -92,12 +92,12 @@ class TestRSFormViewset(EndpointTester):
|
|||
)
|
||||
|
||||
response = self.executeOK(item=self.owned_id)
|
||||
self.assertEqual(response.data['owner'], self.owned.item.owner.pk)
|
||||
self.assertEqual(response.data['title'], self.owned.item.title)
|
||||
self.assertEqual(response.data['alias'], self.owned.item.alias)
|
||||
self.assertEqual(response.data['location'], self.owned.item.location)
|
||||
self.assertEqual(response.data['access_policy'], self.owned.item.access_policy)
|
||||
self.assertEqual(response.data['visible'], self.owned.item.visible)
|
||||
self.assertEqual(response.data['owner'], self.owned.owner.pk)
|
||||
self.assertEqual(response.data['title'], self.owned.title)
|
||||
self.assertEqual(response.data['alias'], self.owned.alias)
|
||||
self.assertEqual(response.data['location'], self.owned.location)
|
||||
self.assertEqual(response.data['access_policy'], self.owned.access_policy)
|
||||
self.assertEqual(response.data['visible'], self.owned.visible)
|
||||
|
||||
self.assertEqual(len(response.data['items']), 2)
|
||||
self.assertEqual(response.data['items'][0]['id'], x1.pk)
|
||||
|
@ -176,9 +176,9 @@ class TestRSFormViewset(EndpointTester):
|
|||
|
||||
@decl_endpoint('/api/rsforms/{item}/export-trs', method='get')
|
||||
def test_export_trs(self):
|
||||
schema = RSForm.create(title='Test')
|
||||
schema = RSForm.objects.create(title='Test')
|
||||
schema.insert_new('X1')
|
||||
response = self.executeOK(item=schema.item.pk)
|
||||
response = self.executeOK(item=schema.pk)
|
||||
self.assertEqual(response.headers['Content-Disposition'], 'attachment; filename=Schema.trs')
|
||||
with io.BytesIO(response.content) as stream:
|
||||
with ZipFile(stream, 'r') as zipped_file:
|
||||
|
@ -387,7 +387,7 @@ class TestRSFormViewset(EndpointTester):
|
|||
data = {'items': [x1.pk]}
|
||||
response = self.executeOK(data=data)
|
||||
x2.refresh_from_db()
|
||||
self.owned.item.refresh_from_db()
|
||||
self.owned.refresh_from_db()
|
||||
self.assertEqual(len(response.data['items']), 1)
|
||||
self.assertEqual(self.owned.constituents().count(), 1)
|
||||
self.assertEqual(x2.alias, 'X2')
|
||||
|
@ -449,16 +449,16 @@ class TestRSFormViewset(EndpointTester):
|
|||
@decl_endpoint('/api/rsforms/{item}/load-trs', method='patch')
|
||||
def test_load_trs(self):
|
||||
self.set_params(item=self.owned_id)
|
||||
self.owned.item.title = 'Test11'
|
||||
self.owned.item.save()
|
||||
self.owned.title = 'Test11'
|
||||
self.owned.save()
|
||||
x1 = self.owned.insert_new('X1')
|
||||
work_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
with open(f'{work_dir}/data/sample-rsform.trs', 'rb') as file:
|
||||
data = {'file': file, 'load_metadata': False}
|
||||
response = self.client.patch(self.endpoint, data=data, format='multipart')
|
||||
self.owned.item.refresh_from_db()
|
||||
self.owned.refresh_from_db()
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(self.owned.item.title, 'Test11')
|
||||
self.assertEqual(self.owned.title, 'Test11')
|
||||
self.assertEqual(len(response.data['items']), 25)
|
||||
self.assertEqual(self.owned.constituents().count(), 25)
|
||||
self.assertFalse(Constituenta.objects.filter(pk=x1.pk).exists())
|
||||
|
|
|
@ -15,10 +15,9 @@ class TestVersionViews(EndpointTester):
|
|||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.owned = RSForm.create(title='Test', alias='T1', owner=self.user).item
|
||||
self.schema = RSForm(self.owned)
|
||||
self.unowned = RSForm.create(title='Test2', alias='T2').item
|
||||
self.x1 = self.schema.insert_new(
|
||||
self.owned = RSForm.objects.create(title='Test', alias='T1', owner=self.user)
|
||||
self.unowned = RSForm.objects.create(title='Test2', alias='T2')
|
||||
self.x1 = self.owned.insert_new(
|
||||
alias='X1',
|
||||
convention='testStart'
|
||||
)
|
||||
|
@ -135,14 +134,14 @@ class TestVersionViews(EndpointTester):
|
|||
@decl_endpoint('/api/versions/{version}/restore', method='patch')
|
||||
def test_restore_version(self):
|
||||
x1 = self.x1
|
||||
x2 = self.schema.insert_new('X2')
|
||||
d1 = self.schema.insert_new('D1', term_raw='TestTerm')
|
||||
x2 = self.owned.insert_new('X2')
|
||||
d1 = self.owned.insert_new('D1', term_raw='TestTerm')
|
||||
data = {'version': '1.0.0', 'description': 'test'}
|
||||
version_id = self._create_version(data=data)
|
||||
invalid_id = version_id + 1337
|
||||
|
||||
d1.delete()
|
||||
x3 = self.schema.insert_new('X3')
|
||||
x3 = self.owned.insert_new('X3')
|
||||
x1.order = x3.order
|
||||
x1.convention = 'Test2'
|
||||
x1.term_raw = 'Test'
|
||||
|
|
|
@ -85,7 +85,11 @@ class LibraryViewSet(viewsets.ModelViewSet):
|
|||
serializer = s.LibraryItemCloneSerializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
item = self._get_item()
|
||||
clone = deepcopy(item)
|
||||
if item.item_type != m.LibraryItemType.RSFORM:
|
||||
return Response(status=c.HTTP_400_BAD_REQUEST)
|
||||
|
||||
schema = m.RSForm.objects.get(pk=item.pk)
|
||||
clone = deepcopy(schema)
|
||||
clone.pk = None
|
||||
clone.owner = self.request.user
|
||||
clone.title = serializer.validated_data['title']
|
||||
|
@ -98,9 +102,8 @@ class LibraryViewSet(viewsets.ModelViewSet):
|
|||
|
||||
with transaction.atomic():
|
||||
clone.save()
|
||||
if clone.item_type == m.LibraryItemType.RSFORM:
|
||||
need_filter = 'items' in request.data
|
||||
for cst in m.RSForm(item).constituents():
|
||||
for cst in schema.constituents():
|
||||
if not need_filter or cst.pk in request.data['items']:
|
||||
cst.pk = None
|
||||
cst.schema = clone
|
||||
|
@ -109,7 +112,6 @@ class LibraryViewSet(viewsets.ModelViewSet):
|
|||
status=c.HTTP_201_CREATED,
|
||||
data=s.RSFormParseSerializer(clone).data
|
||||
)
|
||||
return Response(status=c.HTTP_400_BAD_REQUEST)
|
||||
|
||||
@extend_schema(
|
||||
summary='subscribe to item',
|
||||
|
|
|
@ -27,7 +27,7 @@ def inline_synthesis(request: Request):
|
|||
)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
|
||||
schema = m.RSForm(serializer.validated_data['receiver'])
|
||||
schema = cast(m.RSForm, serializer.validated_data['receiver'])
|
||||
items = cast(list[m.Constituenta], serializer.validated_data['items'])
|
||||
|
||||
with transaction.atomic():
|
||||
|
@ -46,5 +46,5 @@ def inline_synthesis(request: Request):
|
|||
|
||||
return Response(
|
||||
status=c.HTTP_200_OK,
|
||||
data=s.RSFormParseSerializer(schema.item).data
|
||||
data=s.RSFormParseSerializer(schema).data
|
||||
)
|
||||
|
|
|
@ -25,11 +25,11 @@ from .. import utils
|
|||
@extend_schema_view()
|
||||
class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.RetrieveAPIView):
|
||||
''' Endpoint: RSForm operations. '''
|
||||
queryset = m.LibraryItem.objects.filter(item_type=m.LibraryItemType.RSFORM)
|
||||
queryset = m.RSForm.objects.all()
|
||||
serializer_class = s.LibraryItemSerializer
|
||||
|
||||
def _get_schema(self) -> m.RSForm:
|
||||
return m.RSForm(cast(m.LibraryItem, self.get_object()))
|
||||
return cast(m.RSForm, self.get_object())
|
||||
|
||||
def get_permissions(self):
|
||||
''' Determine permission class. '''
|
||||
|
@ -81,12 +81,12 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
|
|||
insert_after = None
|
||||
new_cst = schema.create_cst(data, insert_after)
|
||||
|
||||
schema.item.refresh_from_db()
|
||||
schema.refresh_from_db()
|
||||
response = Response(
|
||||
status=c.HTTP_201_CREATED,
|
||||
data={
|
||||
'new_cst': s.CstSerializer(new_cst).data,
|
||||
'schema': s.RSFormParseSerializer(schema.item).data
|
||||
'schema': s.RSFormParseSerializer(schema).data
|
||||
}
|
||||
)
|
||||
response['Location'] = new_cst.get_absolute_url()
|
||||
|
@ -108,11 +108,11 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
|
|||
''' Produce a term for every element of the target constituenta typification. '''
|
||||
schema = self._get_schema()
|
||||
|
||||
serializer = s.CstTargetSerializer(data=request.data, context={'schema': schema.item})
|
||||
serializer = s.CstTargetSerializer(data=request.data, context={'schema': schema})
|
||||
serializer.is_valid(raise_exception=True)
|
||||
cst = cast(m.Constituenta, serializer.validated_data['target'])
|
||||
|
||||
schema_details = s.RSFormParseSerializer(schema.item).data['items']
|
||||
schema_details = s.RSFormParseSerializer(schema).data['items']
|
||||
cst_parse = next(item for item in schema_details if item['id'] == cst.id)['parse']
|
||||
if not cst_parse['typification']:
|
||||
return Response(
|
||||
|
@ -125,7 +125,7 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
|
|||
status=c.HTTP_200_OK,
|
||||
data={
|
||||
'cst_list': result,
|
||||
'schema': s.RSFormParseSerializer(schema.item).data
|
||||
'schema': s.RSFormParseSerializer(schema).data
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -144,7 +144,7 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
|
|||
def cst_rename(self, request: Request, pk):
|
||||
''' Rename constituenta possibly changing type. '''
|
||||
schema = self._get_schema()
|
||||
serializer = s.CstRenameSerializer(data=request.data, context={'schema': schema.item})
|
||||
serializer = s.CstRenameSerializer(data=request.data, context={'schema': schema})
|
||||
serializer.is_valid(raise_exception=True)
|
||||
|
||||
cst = cast(m.Constituenta, serializer.validated_data['target'])
|
||||
|
@ -156,14 +156,14 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
|
|||
with transaction.atomic():
|
||||
cst.save()
|
||||
schema.apply_mapping(mapping={old_alias: cst.alias}, change_aliases=False)
|
||||
schema.item.refresh_from_db()
|
||||
schema.refresh_from_db()
|
||||
cst.refresh_from_db()
|
||||
|
||||
return Response(
|
||||
status=c.HTTP_200_OK,
|
||||
data={
|
||||
'new_cst': s.CstSerializer(cst).data,
|
||||
'schema': s.RSFormParseSerializer(schema.item).data
|
||||
'schema': s.RSFormParseSerializer(schema).data
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -184,7 +184,7 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
|
|||
schema = self._get_schema()
|
||||
serializer = s.CstSubstituteSerializer(
|
||||
data=request.data,
|
||||
context={'schema': schema.item}
|
||||
context={'schema': schema}
|
||||
)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
|
||||
|
@ -193,10 +193,10 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
|
|||
original = cast(m.Constituenta, substitution['original'])
|
||||
replacement = cast(m.Constituenta, substitution['substitution'])
|
||||
schema.substitute(original, replacement, substitution['transfer_term'])
|
||||
schema.item.refresh_from_db()
|
||||
schema.refresh_from_db()
|
||||
return Response(
|
||||
status=c.HTTP_200_OK,
|
||||
data=s.RSFormParseSerializer(schema.item).data
|
||||
data=s.RSFormParseSerializer(schema).data
|
||||
)
|
||||
|
||||
@extend_schema(
|
||||
|
@ -216,14 +216,14 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
|
|||
schema = self._get_schema()
|
||||
serializer = s.CstListSerializer(
|
||||
data=request.data,
|
||||
context={'schema': schema.item}
|
||||
context={'schema': schema}
|
||||
)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
schema.delete_cst(serializer.validated_data['items'])
|
||||
schema.item.refresh_from_db()
|
||||
schema.refresh_from_db()
|
||||
return Response(
|
||||
status=c.HTTP_200_OK,
|
||||
data=s.RSFormParseSerializer(schema.item).data
|
||||
data=s.RSFormParseSerializer(schema).data
|
||||
)
|
||||
|
||||
@extend_schema(
|
||||
|
@ -243,7 +243,7 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
|
|||
schema = self._get_schema()
|
||||
serializer = s.CstMoveSerializer(
|
||||
data=request.data,
|
||||
context={'schema': schema.item}
|
||||
context={'schema': schema}
|
||||
)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
schema.move_cst(
|
||||
|
@ -252,7 +252,7 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
|
|||
)
|
||||
return Response(
|
||||
status=c.HTTP_200_OK,
|
||||
data=s.RSFormParseSerializer(schema.item).data
|
||||
data=s.RSFormParseSerializer(schema).data
|
||||
)
|
||||
|
||||
@extend_schema(
|
||||
|
@ -272,7 +272,7 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
|
|||
schema.reset_aliases()
|
||||
return Response(
|
||||
status=c.HTTP_200_OK,
|
||||
data=s.RSFormParseSerializer(schema.item).data
|
||||
data=s.RSFormParseSerializer(schema).data
|
||||
)
|
||||
|
||||
@extend_schema(
|
||||
|
@ -292,7 +292,7 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
|
|||
schema.restore_order()
|
||||
return Response(
|
||||
status=c.HTTP_200_OK,
|
||||
data=s.RSFormParseSerializer(schema.item).data
|
||||
data=s.RSFormParseSerializer(schema).data
|
||||
)
|
||||
|
||||
@extend_schema(
|
||||
|
@ -314,7 +314,7 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
|
|||
schema = self._get_schema()
|
||||
load_metadata = input_serializer.validated_data['load_metadata']
|
||||
data = utils.read_zipped_json(request.FILES['file'].file, utils.EXTEOR_INNER_FILENAME)
|
||||
data['id'] = schema.item.pk
|
||||
data['id'] = schema.pk
|
||||
|
||||
serializer = s.RSFormTRSSerializer(
|
||||
data=data,
|
||||
|
@ -324,7 +324,7 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
|
|||
result = serializer.save()
|
||||
return Response(
|
||||
status=c.HTTP_200_OK,
|
||||
data=s.RSFormParseSerializer(result.item).data
|
||||
data=s.RSFormParseSerializer(result).data
|
||||
)
|
||||
|
||||
@extend_schema(
|
||||
|
@ -357,7 +357,7 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
|
|||
@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()))
|
||||
serializer = s.RSFormParseSerializer(self._get_schema())
|
||||
return Response(
|
||||
status=c.HTTP_200_OK,
|
||||
data=serializer.data
|
||||
|
@ -421,7 +421,7 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
|
|||
''' Endpoint: Download Exteor compatible file. '''
|
||||
data = s.RSFormTRSSerializer(self._get_schema()).data
|
||||
file = utils.write_zipped_json(data, utils.EXTEOR_INNER_FILENAME)
|
||||
filename = utils.filename_for_schema(self._get_schema().item.alias)
|
||||
filename = utils.filename_for_schema(self._get_schema().alias)
|
||||
response = HttpResponse(file, content_type='application/zip')
|
||||
response['Content-Disposition'] = f'attachment; filename={filename}'
|
||||
return response
|
||||
|
@ -451,7 +451,7 @@ class TrsImportView(views.APIView):
|
|||
)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
schema = serializer.save()
|
||||
result = s.LibraryItemSerializer(schema.item)
|
||||
result = s.LibraryItemSerializer(schema)
|
||||
return Response(
|
||||
status=c.HTTP_201_CREATED,
|
||||
data=result.data
|
||||
|
@ -483,7 +483,7 @@ def create_rsform(request: Request):
|
|||
serializer_rsform = s.RSFormTRSSerializer(data=data, context={'load_meta': True})
|
||||
serializer_rsform.is_valid(raise_exception=True)
|
||||
schema = serializer_rsform.save()
|
||||
result = s.LibraryItemSerializer(schema.item)
|
||||
result = s.LibraryItemSerializer(schema)
|
||||
return Response(
|
||||
status=c.HTTP_201_CREATED,
|
||||
data=result.data
|
||||
|
|
|
@ -42,10 +42,11 @@ class VersionViewset(
|
|||
''' Restore version data into current item. '''
|
||||
version = cast(m.Version, self.get_object())
|
||||
item = cast(m.LibraryItem, version.item)
|
||||
s.RSFormSerializer(item).restore_from_version(version.data)
|
||||
schema = m.RSForm.objects.get(pk=item.pk)
|
||||
s.RSFormSerializer(schema).restore_from_version(version.data)
|
||||
return Response(
|
||||
status=c.HTTP_200_OK,
|
||||
data=s.RSFormParseSerializer(item).data
|
||||
data=s.RSFormParseSerializer(schema).data
|
||||
)
|
||||
|
||||
|
||||
|
@ -65,7 +66,7 @@ class VersionViewset(
|
|||
def create_version(request: Request, pk_item: int):
|
||||
''' Endpoint: Create new version for RSForm copying current content. '''
|
||||
try:
|
||||
item = m.LibraryItem.objects.get(pk=pk_item)
|
||||
item = m.RSForm.objects.get(pk=pk_item)
|
||||
except m.LibraryItem.DoesNotExist:
|
||||
return Response(status=c.HTTP_404_NOT_FOUND)
|
||||
creator = request.user
|
||||
|
@ -75,7 +76,7 @@ def create_version(request: Request, pk_item: int):
|
|||
version_input = s.VersionCreateSerializer(data=request.data)
|
||||
version_input.is_valid(raise_exception=True)
|
||||
data = s.RSFormSerializer(item).to_versioned_data()
|
||||
result = m.RSForm(item).create_version(
|
||||
result = item.create_version(
|
||||
version=version_input.validated_data['version'],
|
||||
description=version_input.validated_data['description'],
|
||||
data=data
|
||||
|
@ -102,8 +103,8 @@ def create_version(request: Request, pk_item: int):
|
|||
def retrieve_version(request: Request, pk_item: int, pk_version: int):
|
||||
''' Endpoint: Retrieve version for RSForm. '''
|
||||
try:
|
||||
item = m.LibraryItem.objects.get(pk=pk_item)
|
||||
except m.LibraryItem.DoesNotExist:
|
||||
item = m.RSForm.objects.get(pk=pk_item)
|
||||
except m.RSForm.DoesNotExist:
|
||||
return Response(status=c.HTTP_404_NOT_FOUND)
|
||||
try:
|
||||
version = m.Version.objects.get(pk=pk_version)
|
||||
|
@ -135,7 +136,8 @@ def export_file(request: Request, pk: int):
|
|||
version = m.Version.objects.get(pk=pk)
|
||||
except m.Version.DoesNotExist:
|
||||
return Response(status=c.HTTP_404_NOT_FOUND)
|
||||
data = s.RSFormTRSSerializer(m.RSForm(version.item)).from_versioned_data(version.data)
|
||||
schema = m.RSForm.objects.get(pk=version.item.pk)
|
||||
data = s.RSFormTRSSerializer(schema).from_versioned_data(version.data)
|
||||
file = utils.write_zipped_json(data, utils.EXTEOR_INNER_FILENAME)
|
||||
filename = utils.filename_for_schema(data['alias'])
|
||||
response = HttpResponse(file, content_type='application/zip')
|
||||
|
|
|
@ -79,6 +79,8 @@ INSTALLED_APPS = [
|
|||
'drf_spectacular',
|
||||
'drf_spectacular_sidecar',
|
||||
]
|
||||
if DEBUG:
|
||||
INSTALLED_APPS.append('django_extensions')
|
||||
|
||||
|
||||
REST_FRAMEWORK = {
|
||||
|
@ -128,7 +130,6 @@ APPEND_SLASH = False
|
|||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/4.1/howto/static-files/
|
||||
|
||||
STATIC_ROOT = os.environ.get('STATIC_ROOT', os.path.join(BASE_DIR, 'static'))
|
||||
STATIC_URL = 'static/'
|
||||
MEDIA_ROOT = os.environ.get('MEDIA_ROOT', os.path.join(BASE_DIR, 'media'))
|
||||
|
@ -156,7 +157,6 @@ WSGI_APPLICATION = 'project.wsgi.application'
|
|||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/4.1/ref/settings/#databases
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': os.environ.get('DB_ENGINE', 'django.db.backends.sqlite3'),
|
||||
|
@ -198,7 +198,6 @@ SPECTACULAR_SETTINGS = {
|
|||
|
||||
# Password validation
|
||||
# https://docs.djangoproject.com/en/4.1/ref/settings/#auth-password-validators
|
||||
|
||||
AUTH_PASSWORD_VALIDATORS: list[str] = [
|
||||
# NOTE: Password validators disabled
|
||||
# {
|
||||
|
@ -231,6 +230,14 @@ USE_TZ = True
|
|||
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
||||
|
||||
|
||||
# Graph model settings for visualization
|
||||
# https://django-extensions.readthedocs.io/en/latest/graph_models.html
|
||||
GRAPH_MODELS = {
|
||||
'all_applications': True,
|
||||
'group_models': True,
|
||||
}
|
||||
|
||||
|
||||
LOGGING = {
|
||||
'version': 1,
|
||||
'disable_existing_loggers': False,
|
||||
|
|
|
@ -14,6 +14,7 @@ psycopg2-binary
|
|||
gunicorn
|
||||
|
||||
djangorestframework-stubs[compatible-mypy]
|
||||
django-extensions
|
||||
mypy
|
||||
pylint
|
||||
coverage
|
|
@ -50,10 +50,6 @@ def typificationInvalidStr():
|
|||
return 'Invalid typification string'
|
||||
|
||||
|
||||
def libraryTypeUnexpected():
|
||||
return 'Attempting to use invalid adaptor for non-RSForm item'
|
||||
|
||||
|
||||
def exteorFileVersionNotSupported():
|
||||
return 'Некорректный формат файла Экстеор. Сохраните файл в новой версии'
|
||||
|
||||
|
|
16
scripts/dev/GraphDB.ps1
Normal file
16
scripts/dev/GraphDB.ps1
Normal file
|
@ -0,0 +1,16 @@
|
|||
# Generate DOT file for DB structure
|
||||
$backend = Resolve-Path -Path "${PSScriptRoot}\..\..\rsconcept\backend"
|
||||
|
||||
function GenerateDOT() {
|
||||
Set-Location $backend
|
||||
|
||||
$python = "${backend}\venv\Scripts\python.exe"
|
||||
$djangoSrc = "${backend}\manage.py"
|
||||
|
||||
& $python $djangoSrc graph_models -o visualizeDB.dot
|
||||
|
||||
notepad.exe "${backend}\visualizeDB.dot"
|
||||
Start-Process "https://dreampuf.github.io/GraphvizOnline"
|
||||
}
|
||||
|
||||
GenerateDOT
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
$backend = Resolve-Path -Path "$PSScriptRoot\..\..\rsconcept\backend"
|
||||
|
||||
function RunLinters() {
|
||||
function RunCoverage() {
|
||||
BackendCoverage
|
||||
}
|
||||
|
||||
|
@ -20,4 +20,4 @@ function BackendCoverage() {
|
|||
Start-Process "$backend\htmlcov\index.html"
|
||||
}
|
||||
|
||||
RunLinters
|
||||
RunCoverage
|
Loading…
Reference in New Issue
Block a user