Refactoring: preparing backend for oss pt1
This commit is contained in:
parent
aed899ba70
commit
da7113ce7e
4
.vscode/settings.json
vendored
4
.vscode/settings.json
vendored
|
@ -11,7 +11,9 @@
|
||||||
"--multi-line",
|
"--multi-line",
|
||||||
"3",
|
"3",
|
||||||
"--project",
|
"--project",
|
||||||
"apps"
|
"apps",
|
||||||
|
"--project",
|
||||||
|
"shared"
|
||||||
],
|
],
|
||||||
"autopep8.args": [
|
"autopep8.args": [
|
||||||
"--max-line-length",
|
"--max-line-length",
|
||||||
|
|
|
@ -69,6 +69,7 @@ This readme file is used mostly to document project dependencies
|
||||||
- Backticks
|
- Backticks
|
||||||
- Svg Preview
|
- Svg Preview
|
||||||
- TODO Highlight v2
|
- TODO Highlight v2
|
||||||
|
- Prettier
|
||||||
</pre>
|
</pre>
|
||||||
</details>
|
</details>
|
||||||
<details>
|
<details>
|
||||||
|
@ -114,8 +115,9 @@ This readme file is used mostly to document project dependencies
|
||||||
<pre>
|
<pre>
|
||||||
- Pylance
|
- Pylance
|
||||||
- Pylint
|
- Pylint
|
||||||
- Django
|
|
||||||
- autopep8
|
- autopep8
|
||||||
|
- isort
|
||||||
|
- Django
|
||||||
- SQLite
|
- SQLite
|
||||||
</pre>
|
</pre>
|
||||||
</details>
|
</details>
|
||||||
|
|
0
rsconcept/backend/apps/oss/__init__.py
Normal file
0
rsconcept/backend/apps/oss/__init__.py
Normal file
14
rsconcept/backend/apps/oss/admin.py
Normal file
14
rsconcept/backend/apps/oss/admin.py
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
''' Admin view: OperationSchema. '''
|
||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
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']
|
||||||
|
|
||||||
|
|
||||||
|
admin.site.register(models.Operation, OperationAdmin)
|
8
rsconcept/backend/apps/oss/apps.py
Normal file
8
rsconcept/backend/apps/oss/apps.py
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
''' Application: Operation Schema. '''
|
||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class RsformConfig(AppConfig):
|
||||||
|
''' Application config. '''
|
||||||
|
default_auto_field = 'django.db.models.BigAutoField'
|
||||||
|
name = 'apps.oss'
|
69
rsconcept/backend/apps/oss/migrations/0001_initial.py
Normal file
69
rsconcept/backend/apps/oss/migrations/0001_initial.py
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
# Generated by Django 5.0.7 on 2024-07-17 09:51
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('rsform', '0008_alter_libraryitem_item_type'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Operation',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('operation_type', models.CharField(choices=[
|
||||||
|
('input', 'Input'), ('synthesis', 'Synthesis')], default='input', max_length=10, verbose_name='Тип')),
|
||||||
|
('alias', models.CharField(blank=True, max_length=255, verbose_name='Шифр')),
|
||||||
|
('title', models.TextField(blank=True, verbose_name='Название')),
|
||||||
|
('comment', models.TextField(blank=True, verbose_name='Комментарий')),
|
||||||
|
('position_x', models.FloatField(default=0, verbose_name='Положение по горизонтали')),
|
||||||
|
('position_y', models.FloatField(default=0, verbose_name='Положение по вертикали')),
|
||||||
|
('oss', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
related_name='items', to='rsform.libraryitem', verbose_name='Схема синтеза')),
|
||||||
|
('result', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL,
|
||||||
|
related_name='producer', to='rsform.libraryitem', verbose_name='Связанная КС')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'Операция',
|
||||||
|
'verbose_name_plural': 'Операции',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='SynthesisSubstitution',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('transfer_term', models.BooleanField(default=False, verbose_name='Перенос термина')),
|
||||||
|
('operation', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
to='oss.operation', verbose_name='Операция')),
|
||||||
|
('original', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
related_name='as_original', to='rsform.constituenta', verbose_name='Удаляемая конституента')),
|
||||||
|
('substitution', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
related_name='as_substitute', to='rsform.constituenta', verbose_name='Замещающая конституента')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'Отождествление синтеза',
|
||||||
|
'verbose_name_plural': 'Таблицы отождествлений',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Argument',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('argument', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
related_name='descendants', to='oss.operation', verbose_name='Аргумент')),
|
||||||
|
('operation', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
related_name='arguments', to='oss.operation', verbose_name='Операция')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'Аргумент',
|
||||||
|
'verbose_name_plural': 'Аргументы операций',
|
||||||
|
'unique_together': {('operation', 'argument')},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
0
rsconcept/backend/apps/oss/migrations/__init__.py
Normal file
0
rsconcept/backend/apps/oss/migrations/__init__.py
Normal file
27
rsconcept/backend/apps/oss/models/Argument.py
Normal file
27
rsconcept/backend/apps/oss/models/Argument.py
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
''' Models: Operation Argument in OSS. '''
|
||||||
|
from django.db.models import CASCADE, ForeignKey, Model
|
||||||
|
|
||||||
|
|
||||||
|
class Argument(Model):
|
||||||
|
''' Operation Argument.'''
|
||||||
|
operation: ForeignKey = ForeignKey(
|
||||||
|
verbose_name='Операция',
|
||||||
|
to='oss.Operation',
|
||||||
|
on_delete=CASCADE,
|
||||||
|
related_name='arguments'
|
||||||
|
)
|
||||||
|
argument: ForeignKey = ForeignKey(
|
||||||
|
verbose_name='Аргумент',
|
||||||
|
to='oss.Operation',
|
||||||
|
on_delete=CASCADE,
|
||||||
|
related_name='descendants'
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
''' Model metadata. '''
|
||||||
|
verbose_name = 'Аргумент'
|
||||||
|
verbose_name_plural = 'Аргументы операций'
|
||||||
|
unique_together = [['operation', 'argument']]
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return f'{self.argument.pk} -> {self.operation.pk}'
|
71
rsconcept/backend/apps/oss/models/Operation.py
Normal file
71
rsconcept/backend/apps/oss/models/Operation.py
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
''' Models: Operation in OSS. '''
|
||||||
|
from django.db.models import (
|
||||||
|
CASCADE,
|
||||||
|
SET_NULL,
|
||||||
|
CharField,
|
||||||
|
FloatField,
|
||||||
|
ForeignKey,
|
||||||
|
Model,
|
||||||
|
TextChoices,
|
||||||
|
TextField
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class OperationType(TextChoices):
|
||||||
|
''' Type of operation. '''
|
||||||
|
INPUT = 'input'
|
||||||
|
SYNTHESIS = 'synthesis'
|
||||||
|
|
||||||
|
|
||||||
|
class Operation(Model):
|
||||||
|
''' Operational schema Unit.'''
|
||||||
|
oss: ForeignKey = ForeignKey(
|
||||||
|
verbose_name='Схема синтеза',
|
||||||
|
to='rsform.LibraryItem',
|
||||||
|
on_delete=CASCADE,
|
||||||
|
related_name='items'
|
||||||
|
)
|
||||||
|
operation_type: CharField = CharField(
|
||||||
|
verbose_name='Тип',
|
||||||
|
max_length=10,
|
||||||
|
choices=OperationType.choices,
|
||||||
|
default=OperationType.INPUT
|
||||||
|
)
|
||||||
|
result: ForeignKey = ForeignKey(
|
||||||
|
verbose_name='Связанная КС',
|
||||||
|
to='rsform.LibraryItem',
|
||||||
|
null=True,
|
||||||
|
on_delete=SET_NULL,
|
||||||
|
related_name='producer'
|
||||||
|
)
|
||||||
|
|
||||||
|
alias: CharField = CharField(
|
||||||
|
verbose_name='Шифр',
|
||||||
|
max_length=255,
|
||||||
|
blank=True
|
||||||
|
)
|
||||||
|
title: TextField = TextField(
|
||||||
|
verbose_name='Название',
|
||||||
|
blank=True
|
||||||
|
)
|
||||||
|
comment: TextField = TextField(
|
||||||
|
verbose_name='Комментарий',
|
||||||
|
blank=True
|
||||||
|
)
|
||||||
|
|
||||||
|
position_x: FloatField = FloatField(
|
||||||
|
verbose_name='Положение по горизонтали',
|
||||||
|
default=0
|
||||||
|
)
|
||||||
|
position_y: FloatField = FloatField(
|
||||||
|
verbose_name='Положение по вертикали',
|
||||||
|
default=0
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
''' Model metadata. '''
|
||||||
|
verbose_name = 'Операция'
|
||||||
|
verbose_name_plural = 'Операции'
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return f'Операция {self.alias}'
|
36
rsconcept/backend/apps/oss/models/SynthesisSubstitution.py
Normal file
36
rsconcept/backend/apps/oss/models/SynthesisSubstitution.py
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
''' Models: SynthesisSubstitution. '''
|
||||||
|
from django.db.models import CASCADE, BooleanField, ForeignKey, Model
|
||||||
|
|
||||||
|
|
||||||
|
class SynthesisSubstitution(Model):
|
||||||
|
''' Substitutions as part of Synthesis operation in OSS.'''
|
||||||
|
operation: ForeignKey = ForeignKey(
|
||||||
|
verbose_name='Операция',
|
||||||
|
to='oss.Operation',
|
||||||
|
on_delete=CASCADE
|
||||||
|
)
|
||||||
|
|
||||||
|
original: ForeignKey = ForeignKey(
|
||||||
|
verbose_name='Удаляемая конституента',
|
||||||
|
to='rsform.Constituenta',
|
||||||
|
on_delete=CASCADE,
|
||||||
|
related_name='as_original'
|
||||||
|
)
|
||||||
|
substitution: ForeignKey = ForeignKey(
|
||||||
|
verbose_name='Замещающая конституента',
|
||||||
|
to='rsform.Constituenta',
|
||||||
|
on_delete=CASCADE,
|
||||||
|
related_name='as_substitute'
|
||||||
|
)
|
||||||
|
transfer_term: BooleanField = BooleanField(
|
||||||
|
verbose_name='Перенос термина',
|
||||||
|
default=False
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
''' Model metadata. '''
|
||||||
|
verbose_name = 'Отождествление синтеза'
|
||||||
|
verbose_name_plural = 'Таблицы отождествлений'
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return f'{self.original.pk} -> {self.substitution.pk}'
|
8
rsconcept/backend/apps/oss/models/__init__.py
Normal file
8
rsconcept/backend/apps/oss/models/__init__.py
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
''' Django: Models. '''
|
||||||
|
|
||||||
|
from apps.rsform.models import LibraryItem, LibraryItemType
|
||||||
|
|
||||||
|
from .api_OSS import OperationSchema
|
||||||
|
from .Argument import Argument
|
||||||
|
from .Operation import Operation
|
||||||
|
from .SynthesisSubstitution import SynthesisSubstitution
|
58
rsconcept/backend/apps/oss/models/api_OSS.py
Normal file
58
rsconcept/backend/apps/oss/models/api_OSS.py
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
''' Models: OSS API. '''
|
||||||
|
from django.core.exceptions import ValidationError
|
||||||
|
from django.db import transaction
|
||||||
|
from django.db.models import QuerySet
|
||||||
|
|
||||||
|
from apps.rsform.models import LibraryItem, LibraryItemType
|
||||||
|
from shared import messages as msg
|
||||||
|
|
||||||
|
from .Argument import Argument
|
||||||
|
from .Operation import Operation, OperationType
|
||||||
|
from .SynthesisSubstitution import SynthesisSubstitution
|
||||||
|
|
||||||
|
|
||||||
|
class OperationSchema:
|
||||||
|
''' Operations schema API. '''
|
||||||
|
|
||||||
|
def __init__(self, item: LibraryItem):
|
||||||
|
if item.item_type != LibraryItemType.OPERATION_SCHEMA:
|
||||||
|
raise ValueError(msg.libraryTypeUnexpected())
|
||||||
|
self.item = item
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def create(**kwargs) -> 'OperationSchema':
|
||||||
|
item = LibraryItem.objects.create(item_type=LibraryItemType.OPERATION_SCHEMA, **kwargs)
|
||||||
|
return OperationSchema(item=item)
|
||||||
|
|
||||||
|
def operations(self) -> QuerySet[Operation]:
|
||||||
|
''' Get QuerySet containing all operations of current OSS. '''
|
||||||
|
return Operation.objects.filter(oss=self.item)
|
||||||
|
|
||||||
|
def arguments(self) -> QuerySet[Argument]:
|
||||||
|
''' Operation arguments. '''
|
||||||
|
return Argument.objects.filter(operation__oss=self.item)
|
||||||
|
|
||||||
|
def substitutions(self) -> QuerySet[SynthesisSubstitution]:
|
||||||
|
''' Operation arguments. '''
|
||||||
|
return SynthesisSubstitution.objects.filter(operation__oss=self.item)
|
||||||
|
|
||||||
|
@transaction.atomic
|
||||||
|
def create_operation(self, operation_type: OperationType, alias: str, **kwargs) -> Operation:
|
||||||
|
''' Insert new operation. '''
|
||||||
|
if self.operations().filter(alias=alias).exists():
|
||||||
|
raise ValidationError(msg.aliasTaken(alias))
|
||||||
|
result = Operation.objects.create(
|
||||||
|
oss=self.item,
|
||||||
|
alias=alias,
|
||||||
|
operation_type=operation_type,
|
||||||
|
**kwargs
|
||||||
|
)
|
||||||
|
self.item.save()
|
||||||
|
result.refresh_from_db()
|
||||||
|
return result
|
||||||
|
|
||||||
|
@transaction.atomic
|
||||||
|
def delete_operation(self, operation: Operation):
|
||||||
|
''' Delete operation. '''
|
||||||
|
operation.delete()
|
||||||
|
self.item.save()
|
11
rsconcept/backend/apps/oss/serializers/__init__.py
Normal file
11
rsconcept/backend/apps/oss/serializers/__init__.py
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
''' REST API: Serializers. '''
|
||||||
|
|
||||||
|
from apps.rsform.serializers import LibraryItemSerializer
|
||||||
|
|
||||||
|
from .data_access import (
|
||||||
|
ArgumentSerializer,
|
||||||
|
OperationCreateSerializer,
|
||||||
|
OperationSchemaSerializer,
|
||||||
|
OperationSerializer
|
||||||
|
)
|
||||||
|
from .schema_typing import NewOperationResponse
|
97
rsconcept/backend/apps/oss/serializers/data_access.py
Normal file
97
rsconcept/backend/apps/oss/serializers/data_access.py
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
''' Serializers for persistent data manipulation. '''
|
||||||
|
from typing import cast
|
||||||
|
|
||||||
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
from apps.rsform.models import LibraryItem
|
||||||
|
from apps.rsform.serializers import LibraryItemDetailsSerializer
|
||||||
|
from shared import messages as msg
|
||||||
|
|
||||||
|
from ..models import Argument, Operation, OperationSchema
|
||||||
|
|
||||||
|
|
||||||
|
class OperationSerializer(serializers.ModelSerializer):
|
||||||
|
''' Serializer: Operation data. '''
|
||||||
|
class Meta:
|
||||||
|
''' serializer metadata. '''
|
||||||
|
model = Operation
|
||||||
|
fields = '__all__'
|
||||||
|
read_only_fields = ('id', 'oss')
|
||||||
|
|
||||||
|
|
||||||
|
class ArgumentSerializer(serializers.ModelSerializer):
|
||||||
|
''' Serializer: Operation data. '''
|
||||||
|
class Meta:
|
||||||
|
''' serializer metadata. '''
|
||||||
|
model = Argument
|
||||||
|
fields = ('operation', 'argument')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class OperationPositionSerializer(serializers.ModelSerializer):
|
||||||
|
''' Serializer: Positions of a single operations in OSS. '''
|
||||||
|
class Meta:
|
||||||
|
''' serializer metadata. '''
|
||||||
|
model = Operation
|
||||||
|
fields = 'id', 'position_x', 'position_y'
|
||||||
|
|
||||||
|
def validate(self, attrs):
|
||||||
|
oss = cast(LibraryItem, self.context['oss'])
|
||||||
|
operation = cast(Operation, self.instance)
|
||||||
|
if operation.oss != oss:
|
||||||
|
raise serializers.ValidationError({
|
||||||
|
'id': msg.operationNotOwned(oss.title)
|
||||||
|
})
|
||||||
|
return attrs
|
||||||
|
|
||||||
|
|
||||||
|
class OperationCreateSerializer(serializers.ModelSerializer):
|
||||||
|
''' Serializer: Constituenta creation. '''
|
||||||
|
positions = serializers.ListField(
|
||||||
|
child=OperationPositionSerializer(),
|
||||||
|
default=[]
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
''' serializer metadata. '''
|
||||||
|
model = Operation
|
||||||
|
fields = \
|
||||||
|
'alias', 'operation_type', 'title', \
|
||||||
|
'comment', 'position_x', 'position_y'
|
||||||
|
|
||||||
|
|
||||||
|
class OperationSchemaSerializer(serializers.ModelSerializer):
|
||||||
|
''' Serializer: Detailed data for OSS. '''
|
||||||
|
items = serializers.ListField(
|
||||||
|
child=OperationSerializer()
|
||||||
|
)
|
||||||
|
graph = serializers.ListField(
|
||||||
|
child=ArgumentSerializer()
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
''' serializer metadata. '''
|
||||||
|
model = LibraryItem
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
def to_representation(self, instance: LibraryItem):
|
||||||
|
result = LibraryItemDetailsSerializer(instance).data
|
||||||
|
oss = OperationSchema(instance)
|
||||||
|
result['items'] = []
|
||||||
|
for operation in oss.operations():
|
||||||
|
result['items'].append(OperationSerializer(operation).data)
|
||||||
|
result['graph'] = []
|
||||||
|
for argument in oss.arguments():
|
||||||
|
result['graph'].append(ArgumentSerializer(argument).data)
|
||||||
|
result['substitutions'] = []
|
||||||
|
for substitution in oss.substitutions().values(
|
||||||
|
'original',
|
||||||
|
'original__alias',
|
||||||
|
'original__term_resolved',
|
||||||
|
'substitution',
|
||||||
|
'substitution__alias',
|
||||||
|
'substitution__term_resolved',
|
||||||
|
'transfer_term'
|
||||||
|
):
|
||||||
|
result['substitutions'].append(substitution)
|
||||||
|
return result
|
10
rsconcept/backend/apps/oss/serializers/schema_typing.py
Normal file
10
rsconcept/backend/apps/oss/serializers/schema_typing.py
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
''' Utility serializers for REST API schema - SHOULD NOT BE ACCESSED DIRECTLY. '''
|
||||||
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
from .data_access import OperationSchemaSerializer, OperationSerializer
|
||||||
|
|
||||||
|
|
||||||
|
class NewOperationResponse(serializers.Serializer):
|
||||||
|
''' Serializer: Create operation response. '''
|
||||||
|
new_operation = OperationSerializer()
|
||||||
|
oss = OperationSchemaSerializer()
|
12
rsconcept/backend/apps/oss/urls.py
Normal file
12
rsconcept/backend/apps/oss/urls.py
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
''' Routing: Operation Schema. '''
|
||||||
|
from django.urls import include, path
|
||||||
|
from rest_framework import routers
|
||||||
|
|
||||||
|
from . import views
|
||||||
|
|
||||||
|
library_router = routers.SimpleRouter(trailing_slash=False)
|
||||||
|
library_router.register('rsforms', views.OssViewSet, 'RSForm')
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path('', include(library_router.urls)),
|
||||||
|
]
|
2
rsconcept/backend/apps/oss/views/__init__.py
Normal file
2
rsconcept/backend/apps/oss/views/__init__.py
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
''' REST API: Endpoint processors. '''
|
||||||
|
from .oss import OssViewSet
|
110
rsconcept/backend/apps/oss/views/oss.py
Normal file
110
rsconcept/backend/apps/oss/views/oss.py
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
''' Endpoints for OSS. '''
|
||||||
|
from typing import cast
|
||||||
|
|
||||||
|
from drf_spectacular.utils import extend_schema, extend_schema_view
|
||||||
|
from rest_framework import generics
|
||||||
|
from rest_framework import status as c
|
||||||
|
from rest_framework import viewsets
|
||||||
|
from rest_framework.decorators import action
|
||||||
|
from rest_framework.request import Request
|
||||||
|
from rest_framework.response import Response
|
||||||
|
|
||||||
|
from shared import permissions
|
||||||
|
|
||||||
|
from .. import models as m
|
||||||
|
from .. import serializers as s
|
||||||
|
|
||||||
|
|
||||||
|
@extend_schema(tags=['OSS'])
|
||||||
|
@extend_schema_view()
|
||||||
|
class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.RetrieveAPIView):
|
||||||
|
''' Endpoint: OperationSchema. '''
|
||||||
|
queryset = m.LibraryItem.objects.filter(item_type=m.LibraryItemType.OPERATION_SCHEMA)
|
||||||
|
serializer_class = s.LibraryItemSerializer
|
||||||
|
|
||||||
|
def _get_schema(self) -> m.OperationSchema:
|
||||||
|
return m.OperationSchema(cast(m.LibraryItem, self.get_object()))
|
||||||
|
|
||||||
|
def get_permissions(self):
|
||||||
|
''' Determine permission class. '''
|
||||||
|
if self.action in [
|
||||||
|
'operation_create',
|
||||||
|
'operation_delete'
|
||||||
|
]:
|
||||||
|
permission_list = [permissions.ItemEditor]
|
||||||
|
elif self.action in ['details']:
|
||||||
|
permission_list = [permissions.ItemAnyone]
|
||||||
|
else:
|
||||||
|
permission_list = [permissions.Anyone]
|
||||||
|
return [permission() for permission in permission_list]
|
||||||
|
|
||||||
|
@extend_schema(
|
||||||
|
summary='get operations data',
|
||||||
|
tags=['OSS'],
|
||||||
|
request=None,
|
||||||
|
responses={
|
||||||
|
c.HTTP_200_OK: s.OperationSchemaSerializer,
|
||||||
|
c.HTTP_404_NOT_FOUND: None
|
||||||
|
}
|
||||||
|
)
|
||||||
|
@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()))
|
||||||
|
return Response(
|
||||||
|
status=c.HTTP_200_OK,
|
||||||
|
data=serializer.data
|
||||||
|
)
|
||||||
|
|
||||||
|
@extend_schema(
|
||||||
|
summary='create operation',
|
||||||
|
tags=['OSS'],
|
||||||
|
request=s.OperationCreateSerializer(),
|
||||||
|
responses={
|
||||||
|
c.HTTP_201_CREATED: s.NewOperationResponse,
|
||||||
|
c.HTTP_403_FORBIDDEN: None
|
||||||
|
}
|
||||||
|
)
|
||||||
|
@action(detail=True, methods=['post'], url_path='operation-create')
|
||||||
|
def operation_create(self, request: Request, pk):
|
||||||
|
''' Create new operation. '''
|
||||||
|
schema = self._get_schema()
|
||||||
|
serializer = s.OperationCreateSerializer(data=request.data)
|
||||||
|
serializer.is_valid(raise_exception=True)
|
||||||
|
data = serializer.validated_data
|
||||||
|
new_operation = schema.create_operation(*data)
|
||||||
|
schema.item.refresh_from_db()
|
||||||
|
response = Response(
|
||||||
|
status=c.HTTP_201_CREATED,
|
||||||
|
data={
|
||||||
|
'new_operation': s.OperationSerializer(new_operation).data,
|
||||||
|
'schema': s.OperationSchemaSerializer(schema.item).data
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return response
|
||||||
|
|
||||||
|
# @extend_schema(
|
||||||
|
# summary='delete operation',
|
||||||
|
# tags=['RSForm'],
|
||||||
|
# request=s.CstListSerializer,
|
||||||
|
# responses={
|
||||||
|
# c.HTTP_200_OK: s.RSFormParseSerializer,
|
||||||
|
# c.HTTP_403_FORBIDDEN: None,
|
||||||
|
# c.HTTP_404_NOT_FOUND: None
|
||||||
|
# }
|
||||||
|
# )
|
||||||
|
# @action(detail=True, methods=['patch'], url_path='operation-delete')
|
||||||
|
# def operation_delete(self, request: Request, pk):
|
||||||
|
# ''' Endpoint: Delete operation. '''
|
||||||
|
# schema = self._get_schema()
|
||||||
|
# serializer = s.CstListSerializer(
|
||||||
|
# data=request.data,
|
||||||
|
# context={'schema': schema.item}
|
||||||
|
# )
|
||||||
|
# serializer.is_valid(raise_exception=True)
|
||||||
|
# schema.delete_cst(serializer.validated_data['items'])
|
||||||
|
# schema.item.refresh_from_db()
|
||||||
|
# return Response(
|
||||||
|
# status=c.HTTP_200_OK,
|
||||||
|
# data=s.RSFormParseSerializer(schema.item).data
|
||||||
|
# )
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 5.0.7 on 2024-07-17 09:51
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('rsform', '0007_location_and_flags'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='libraryitem',
|
||||||
|
name='item_type',
|
||||||
|
field=models.CharField(choices=[('rsform', 'Rsform'), ('oss', 'Operation Schema')], max_length=50, verbose_name='Тип'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -9,6 +9,7 @@ from django.db.models import (
|
||||||
DateTimeField,
|
DateTimeField,
|
||||||
ForeignKey,
|
ForeignKey,
|
||||||
Model,
|
Model,
|
||||||
|
QuerySet,
|
||||||
TextChoices,
|
TextChoices,
|
||||||
TextField
|
TextField
|
||||||
)
|
)
|
||||||
|
@ -113,18 +114,18 @@ class LibraryItem(Model):
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return f'/api/library/{self.pk}'
|
return f'/api/library/{self.pk}'
|
||||||
|
|
||||||
def subscribers(self) -> list[Subscription]:
|
def subscribers(self) -> list[User]:
|
||||||
''' Get all subscribers for this item. '''
|
''' Get all subscribers for this item. '''
|
||||||
return [subscription.user for subscription in Subscription.objects.filter(item=self.pk).only('user')]
|
return [subscription.user for subscription in Subscription.objects.filter(item=self.pk).only('user')]
|
||||||
|
|
||||||
def versions(self) -> list[Version]:
|
def editors(self) -> list[User]:
|
||||||
''' Get all Versions of this item. '''
|
|
||||||
return list(Version.objects.filter(item=self.pk).order_by('-time_create'))
|
|
||||||
|
|
||||||
def editors(self) -> list[Editor]:
|
|
||||||
''' Get all Editors of this item. '''
|
''' Get all Editors of this item. '''
|
||||||
return [item.editor for item in Editor.objects.filter(item=self.pk).only('editor')]
|
return [item.editor for item in Editor.objects.filter(item=self.pk).only('editor')]
|
||||||
|
|
||||||
|
def versions(self) -> QuerySet[Version]:
|
||||||
|
''' Get all Versions of this item. '''
|
||||||
|
return Version.objects.filter(item=self.pk).order_by('-time_create')
|
||||||
|
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
subscribe = not self.pk and self.owner
|
subscribe = not self.pk and self.owner
|
||||||
|
|
|
@ -7,7 +7,8 @@ from django.core.exceptions import ValidationError
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.db.models import QuerySet
|
from django.db.models import QuerySet
|
||||||
|
|
||||||
from .. import messages as msg
|
from shared import messages as msg
|
||||||
|
|
||||||
from ..graph import Graph
|
from ..graph import Graph
|
||||||
from .api_RSLanguage import (
|
from .api_RSLanguage import (
|
||||||
extract_globals,
|
extract_globals,
|
||||||
|
|
|
@ -6,7 +6,8 @@ from typing import Set, Tuple, cast
|
||||||
|
|
||||||
import pyconcept
|
import pyconcept
|
||||||
|
|
||||||
from .. import messages as msg
|
from shared import messages as msg
|
||||||
|
|
||||||
from .Constituenta import CstType
|
from .Constituenta import CstType
|
||||||
|
|
||||||
_RE_GLOBALS = r'[XCSADFPT]\d+' # cspell:disable-line
|
_RE_GLOBALS = r'[XCSADFPT]\d+' # cspell:disable-line
|
||||||
|
|
|
@ -22,6 +22,7 @@ from .data_access import (
|
||||||
InlineSynthesisSerializer,
|
InlineSynthesisSerializer,
|
||||||
LibraryItemBaseSerializer,
|
LibraryItemBaseSerializer,
|
||||||
LibraryItemCloneSerializer,
|
LibraryItemCloneSerializer,
|
||||||
|
LibraryItemDetailsSerializer,
|
||||||
LibraryItemSerializer,
|
LibraryItemSerializer,
|
||||||
RSFormParseSerializer,
|
RSFormParseSerializer,
|
||||||
RSFormSerializer,
|
RSFormSerializer,
|
||||||
|
|
|
@ -4,7 +4,8 @@ from typing import cast
|
||||||
from cctext import EntityReference, Reference, ReferenceType, Resolver, SyntacticReference
|
from cctext import EntityReference, Reference, ReferenceType, Resolver, SyntacticReference
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
from .. import messages as msg
|
from shared import messages as msg
|
||||||
|
|
||||||
from ..models import AccessPolicy, validate_location
|
from ..models import AccessPolicy, validate_location
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,8 @@ from django.db import transaction
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from rest_framework.serializers import PrimaryKeyRelatedField as PKField
|
from rest_framework.serializers import PrimaryKeyRelatedField as PKField
|
||||||
|
|
||||||
from .. import messages as msg
|
from shared import messages as msg
|
||||||
|
|
||||||
from ..models import Constituenta, CstType, LibraryItem, RSForm, Version
|
from ..models import Constituenta, CstType, LibraryItem, RSForm, Version
|
||||||
from .basics import CstParseSerializer
|
from .basics import CstParseSerializer
|
||||||
from .io_pyconcept import PyConceptAdapter
|
from .io_pyconcept import PyConceptAdapter
|
||||||
|
|
|
@ -2,7 +2,8 @@
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
from .. import messages as msg
|
from shared import messages as msg
|
||||||
|
|
||||||
from ..models import Constituenta, LibraryItem, RSForm
|
from ..models import Constituenta, LibraryItem, RSForm
|
||||||
from ..utils import fix_old_references
|
from ..utils import fix_old_references
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,8 @@ from typing import Optional, Union, cast
|
||||||
|
|
||||||
import pyconcept
|
import pyconcept
|
||||||
|
|
||||||
from .. import messages as msg
|
from shared import messages as msg
|
||||||
|
|
||||||
from ..models import RSForm
|
from ..models import RSForm
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
''' Testing views '''
|
''' Testing views '''
|
||||||
from cctext import split_grams
|
from cctext import split_grams
|
||||||
|
|
||||||
from ..EndpointTester import EndpointTester, decl_endpoint
|
from shared.EndpointTester import EndpointTester, decl_endpoint
|
||||||
|
|
||||||
|
|
||||||
class TestNaturalLanguageViews(EndpointTester):
|
class TestNaturalLanguageViews(EndpointTester):
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
''' Testing API: Constituents. '''
|
''' Testing API: Constituents. '''
|
||||||
from apps.rsform.models import Constituenta, CstType, RSForm
|
from apps.rsform.models import Constituenta, CstType, RSForm
|
||||||
|
from shared.EndpointTester import EndpointTester, decl_endpoint
|
||||||
from ..EndpointTester import EndpointTester, decl_endpoint
|
|
||||||
|
|
||||||
|
|
||||||
class TestConstituentaAPI(EndpointTester):
|
class TestConstituentaAPI(EndpointTester):
|
||||||
|
|
|
@ -11,9 +11,8 @@ from apps.rsform.models import (
|
||||||
RSForm,
|
RSForm,
|
||||||
Subscription
|
Subscription
|
||||||
)
|
)
|
||||||
|
from shared.EndpointTester import EndpointTester, decl_endpoint
|
||||||
from ..EndpointTester import EndpointTester, decl_endpoint
|
from shared.testing_utils import response_contains
|
||||||
from ..testing_utils import response_contains
|
|
||||||
|
|
||||||
|
|
||||||
class TestLibraryViewset(EndpointTester):
|
class TestLibraryViewset(EndpointTester):
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
''' Testing API: Operations. '''
|
''' Testing API: Operations. '''
|
||||||
from apps.rsform.models import Constituenta, CstType, RSForm
|
from apps.rsform.models import Constituenta, CstType, RSForm
|
||||||
|
from shared.EndpointTester import EndpointTester, decl_endpoint
|
||||||
from ..EndpointTester import EndpointTester, decl_endpoint
|
|
||||||
|
|
||||||
|
|
||||||
class TestInlineSynthesis(EndpointTester):
|
class TestInlineSynthesis(EndpointTester):
|
||||||
|
|
|
@ -15,9 +15,8 @@ from apps.rsform.models import (
|
||||||
LocationHead,
|
LocationHead,
|
||||||
RSForm
|
RSForm
|
||||||
)
|
)
|
||||||
|
from shared.EndpointTester import EndpointTester, decl_endpoint
|
||||||
from ..EndpointTester import EndpointTester, decl_endpoint
|
from shared.testing_utils import response_contains
|
||||||
from ..testing_utils import response_contains
|
|
||||||
|
|
||||||
|
|
||||||
class TestRSFormViewset(EndpointTester):
|
class TestRSFormViewset(EndpointTester):
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
''' Testing views '''
|
''' Testing views '''
|
||||||
from ..EndpointTester import EndpointTester, decl_endpoint
|
from shared.EndpointTester import EndpointTester, decl_endpoint
|
||||||
|
|
||||||
|
|
||||||
class TestRSLanguageViews(EndpointTester):
|
class TestRSLanguageViews(EndpointTester):
|
||||||
|
|
|
@ -7,8 +7,7 @@ from zipfile import ZipFile
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
|
|
||||||
from apps.rsform.models import Constituenta, RSForm
|
from apps.rsform.models import Constituenta, RSForm
|
||||||
|
from shared.EndpointTester import EndpointTester, decl_endpoint
|
||||||
from ..EndpointTester import EndpointTester, decl_endpoint
|
|
||||||
|
|
||||||
|
|
||||||
class TestVersionViews(EndpointTester):
|
class TestVersionViews(EndpointTester):
|
||||||
|
|
|
@ -2,8 +2,9 @@
|
||||||
from drf_spectacular.utils import extend_schema, extend_schema_view
|
from drf_spectacular.utils import extend_schema, extend_schema_view
|
||||||
from rest_framework import generics
|
from rest_framework import generics
|
||||||
|
|
||||||
|
from shared import permissions
|
||||||
|
|
||||||
from .. import models as m
|
from .. import models as m
|
||||||
from .. import permissions
|
|
||||||
from .. import serializers as s
|
from .. import serializers as s
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -13,8 +13,9 @@ from rest_framework.decorators import action
|
||||||
from rest_framework.request import Request
|
from rest_framework.request import Request
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
|
|
||||||
|
from shared import permissions
|
||||||
|
|
||||||
from .. import models as m
|
from .. import models as m
|
||||||
from .. import permissions
|
|
||||||
from .. import serializers as s
|
from .. import serializers as s
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -13,9 +13,10 @@ from rest_framework.decorators import action, api_view
|
||||||
from rest_framework.request import Request
|
from rest_framework.request import Request
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
|
|
||||||
from .. import messages as msg
|
from shared import messages as msg
|
||||||
|
from shared import permissions
|
||||||
|
|
||||||
from .. import models as m
|
from .. import models as m
|
||||||
from .. import permissions
|
|
||||||
from .. import serializers as s
|
from .. import serializers as s
|
||||||
from .. import utils
|
from .. import utils
|
||||||
|
|
||||||
|
|
|
@ -10,8 +10,9 @@ from rest_framework.decorators import action, api_view, permission_classes
|
||||||
from rest_framework.request import Request
|
from rest_framework.request import Request
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
|
|
||||||
|
from shared import permissions
|
||||||
|
|
||||||
from .. import models as m
|
from .. import models as m
|
||||||
from .. import permissions
|
|
||||||
from .. import serializers as s
|
from .. import serializers as s
|
||||||
from .. import utils
|
from .. import utils
|
||||||
|
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
''' Utility: Text messages. '''
|
|
||||||
# pylint: skip-file
|
|
||||||
|
|
||||||
|
|
||||||
def passwordAuthFailed():
|
|
||||||
return 'Неизвестное сочетание имени пользователя (email) и пароля'
|
|
||||||
|
|
||||||
|
|
||||||
def passwordsNotMatch():
|
|
||||||
return 'Введенные пароли не совпадают'
|
|
||||||
|
|
||||||
|
|
||||||
def emailAlreadyTaken():
|
|
||||||
return 'Пользователь с данным email уже существует'
|
|
|
@ -4,8 +4,8 @@ from django.contrib.auth.password_validation import validate_password
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
from apps.rsform.models import Editor, Subscription
|
from apps.rsform.models import Editor, Subscription
|
||||||
|
from shared import messages as msg
|
||||||
|
|
||||||
from . import messages as msg
|
|
||||||
from . import models
|
from . import models
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
''' Testing API: users. '''
|
''' Testing API: users. '''
|
||||||
from rest_framework.test import APIClient, APITestCase
|
from rest_framework.test import APIClient, APITestCase
|
||||||
|
|
||||||
from apps.rsform.tests.EndpointTester import EndpointTester, decl_endpoint
|
|
||||||
from apps.users.models import User
|
from apps.users.models import User
|
||||||
|
from shared.EndpointTester import EndpointTester, decl_endpoint
|
||||||
|
|
||||||
|
|
||||||
class TestUserAPIViews(EndpointTester):
|
class TestUserAPIViews(EndpointTester):
|
||||||
|
|
|
@ -74,6 +74,7 @@ INSTALLED_APPS = [
|
||||||
|
|
||||||
'apps.users',
|
'apps.users',
|
||||||
'apps.rsform',
|
'apps.rsform',
|
||||||
|
'apps.oss',
|
||||||
|
|
||||||
'drf_spectacular',
|
'drf_spectacular',
|
||||||
'drf_spectacular_sidecar',
|
'drf_spectacular_sidecar',
|
||||||
|
|
1
rsconcept/backend/shared/__init__.py
Normal file
1
rsconcept/backend/shared/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
''' Utilities shared between applications. '''
|
|
@ -6,6 +6,10 @@ def constituentaNotOwned(title: str):
|
||||||
return f'Конституента не принадлежит схеме: {title}'
|
return f'Конституента не принадлежит схеме: {title}'
|
||||||
|
|
||||||
|
|
||||||
|
def operationNotOwned(title: str):
|
||||||
|
return f'Операция не принадлежит схеме: {title}'
|
||||||
|
|
||||||
|
|
||||||
def substitutionNotInList():
|
def substitutionNotInList():
|
||||||
return 'Отождествляемая конституента отсутствует в списке'
|
return 'Отождествляемая конституента отсутствует в списке'
|
||||||
|
|
||||||
|
@ -64,3 +68,15 @@ def constituentaNoStructure():
|
||||||
|
|
||||||
def missingFile():
|
def missingFile():
|
||||||
return 'Отсутствует прикрепленный файл'
|
return 'Отсутствует прикрепленный файл'
|
||||||
|
|
||||||
|
|
||||||
|
def passwordAuthFailed():
|
||||||
|
return 'Неизвестное сочетание имени пользователя (email) и пароля'
|
||||||
|
|
||||||
|
|
||||||
|
def passwordsNotMatch():
|
||||||
|
return 'Введенные пароли не совпадают'
|
||||||
|
|
||||||
|
|
||||||
|
def emailAlreadyTaken():
|
||||||
|
return 'Пользователь с данным email уже существует'
|
|
@ -11,16 +11,24 @@ from rest_framework.permissions import \
|
||||||
from rest_framework.request import Request
|
from rest_framework.request import Request
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
|
|
||||||
from . import models as m
|
from apps.rsform.models import (
|
||||||
|
AccessPolicy,
|
||||||
|
Constituenta,
|
||||||
|
Editor,
|
||||||
|
LibraryItem,
|
||||||
|
Subscription,
|
||||||
|
Version
|
||||||
|
)
|
||||||
|
from apps.users.models import User
|
||||||
|
|
||||||
|
|
||||||
def _extract_item(obj: Any) -> m.LibraryItem:
|
def _extract_item(obj: Any) -> LibraryItem:
|
||||||
if isinstance(obj, m.LibraryItem):
|
if isinstance(obj, LibraryItem):
|
||||||
return obj
|
return obj
|
||||||
elif isinstance(obj, m.Constituenta):
|
elif isinstance(obj, Constituenta):
|
||||||
return cast(m.LibraryItem, obj.schema)
|
return cast(LibraryItem, obj.schema)
|
||||||
elif isinstance(obj, (m.Version, m.Subscription, m.Editor)):
|
elif isinstance(obj, (Version, Subscription, Editor)):
|
||||||
return cast(m.LibraryItem, obj.item)
|
return cast(LibraryItem, obj.item)
|
||||||
raise PermissionDenied({
|
raise PermissionDenied({
|
||||||
'message': 'Invalid type error. Please contact developers',
|
'message': 'Invalid type error. Please contact developers',
|
||||||
'object_id': obj.id
|
'object_id': obj.id
|
||||||
|
@ -60,10 +68,10 @@ class ItemEditor(ItemOwner):
|
||||||
if request.user.is_anonymous:
|
if request.user.is_anonymous:
|
||||||
return False
|
return False
|
||||||
item = _extract_item(obj)
|
item = _extract_item(obj)
|
||||||
if m.Editor.objects.filter(
|
if Editor.objects.filter(
|
||||||
item=item,
|
item=item,
|
||||||
editor=cast(m.User, request.user)
|
editor=cast(User, request.user)
|
||||||
).exists() and item.access_policy != m.AccessPolicy.PRIVATE:
|
).exists() and item.access_policy != AccessPolicy.PRIVATE:
|
||||||
return True
|
return True
|
||||||
return super().has_object_permission(request, view, obj)
|
return super().has_object_permission(request, view, obj)
|
||||||
|
|
||||||
|
@ -76,7 +84,7 @@ class ItemAnyone(ItemEditor):
|
||||||
|
|
||||||
def has_object_permission(self, request: Request, view: APIView, obj: Any) -> bool:
|
def has_object_permission(self, request: Request, view: APIView, obj: Any) -> bool:
|
||||||
item = _extract_item(obj)
|
item = _extract_item(obj)
|
||||||
if item.access_policy == m.AccessPolicy.PUBLIC:
|
if item.access_policy == AccessPolicy.PUBLIC:
|
||||||
return True
|
return True
|
||||||
return super().has_object_permission(request, view, obj)
|
return super().has_object_permission(request, view, obj)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user