2023-07-15 17:46:19 +03:00
|
|
|
import json
|
|
|
|
from django.http import HttpResponse
|
|
|
|
from django_filters.rest_framework import DjangoFilterBackend
|
2023-08-01 20:14:03 +03:00
|
|
|
from django.db.models import Q
|
2023-07-18 14:55:40 +03:00
|
|
|
from rest_framework import views, viewsets, filters, generics, permissions
|
2023-07-15 17:46:19 +03:00
|
|
|
from rest_framework.decorators import action
|
|
|
|
from rest_framework.response import Response
|
|
|
|
from rest_framework.decorators import api_view
|
|
|
|
|
|
|
|
import pyconcept
|
|
|
|
from . import models
|
|
|
|
from . import serializers
|
|
|
|
from . import utils
|
|
|
|
|
|
|
|
|
2023-08-01 20:14:03 +03:00
|
|
|
class LibraryView(generics.ListAPIView):
|
|
|
|
'''
|
|
|
|
Get list of rsforms available for active user.
|
|
|
|
'''
|
|
|
|
permission_classes = (permissions.AllowAny,)
|
|
|
|
serializer_class = serializers.RSFormSerializer
|
|
|
|
|
|
|
|
def get_queryset(self):
|
|
|
|
user = self.request.user
|
|
|
|
if not user.is_anonymous:
|
|
|
|
return models.RSForm.objects.filter(Q(is_common=True) | Q(owner=user))
|
|
|
|
else:
|
|
|
|
return models.RSForm.objects.filter(is_common=True)
|
|
|
|
|
|
|
|
|
2023-07-18 14:55:40 +03:00
|
|
|
class ConstituentAPIView(generics.RetrieveUpdateAPIView):
|
|
|
|
queryset = models.Constituenta.objects.all()
|
|
|
|
serializer_class = serializers.ConstituentaSerializer
|
|
|
|
|
|
|
|
def get_permissions(self):
|
|
|
|
result = super().get_permissions()
|
|
|
|
if self.request.method.lower() == 'get':
|
|
|
|
result.append(permissions.AllowAny())
|
|
|
|
else:
|
|
|
|
result.append(utils.SchemaOwnerOrAdmin())
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
2023-07-15 17:46:19 +03:00
|
|
|
class RSFormViewSet(viewsets.ModelViewSet):
|
|
|
|
queryset = models.RSForm.objects.all()
|
|
|
|
serializer_class = serializers.RSFormSerializer
|
|
|
|
|
|
|
|
filter_backends = (DjangoFilterBackend, filters.OrderingFilter)
|
|
|
|
filterset_fields = ['owner', 'is_common']
|
|
|
|
ordering_fields = ('owner', 'title', 'time_update')
|
|
|
|
ordering = ('-time_update')
|
|
|
|
|
2023-07-27 22:04:25 +03:00
|
|
|
def _get_schema(self) -> models.RSForm:
|
|
|
|
return self.get_object()
|
|
|
|
|
2023-07-15 17:46:19 +03:00
|
|
|
def perform_create(self, serializer):
|
|
|
|
if not self.request.user.is_anonymous and 'owner' not in self.request.POST:
|
|
|
|
return serializer.save(owner=self.request.user)
|
|
|
|
else:
|
|
|
|
return serializer.save()
|
|
|
|
|
|
|
|
def get_permissions(self):
|
2023-07-27 22:04:25 +03:00
|
|
|
if self.action in ['update', 'destroy', 'partial_update', 'load_trs',
|
|
|
|
'cst_create', 'cst_multidelete', 'reset_aliases']:
|
2023-07-15 17:46:19 +03:00
|
|
|
permission_classes = [utils.ObjectOwnerOrAdmin]
|
2023-07-27 22:04:25 +03:00
|
|
|
elif self.action in ['create', 'claim', 'clone']:
|
2023-07-15 17:46:19 +03:00
|
|
|
permission_classes = [permissions.IsAuthenticated]
|
|
|
|
else:
|
|
|
|
permission_classes = [permissions.AllowAny]
|
|
|
|
return [permission() for permission in permission_classes]
|
|
|
|
|
2023-07-23 21:38:04 +03:00
|
|
|
@action(detail=True, methods=['post'], url_path='cst-create')
|
|
|
|
def cst_create(self, request, pk):
|
|
|
|
''' Create new constituenta '''
|
2023-07-27 22:04:25 +03:00
|
|
|
schema = self._get_schema()
|
2023-07-24 22:34:03 +03:00
|
|
|
serializer = serializers.CstCreateSerializer(data=request.data)
|
2023-07-23 15:23:01 +03:00
|
|
|
serializer.is_valid(raise_exception=True)
|
2023-07-26 23:11:00 +03:00
|
|
|
if ('insert_after' in serializer.validated_data and serializer.validated_data['insert_after'] is not None):
|
2023-07-23 15:23:01 +03:00
|
|
|
cstafter = models.Constituenta.objects.get(pk=serializer.validated_data['insert_after'])
|
|
|
|
constituenta = schema.insert_at(cstafter.order + 1,
|
|
|
|
serializer.validated_data['alias'],
|
2023-07-26 23:11:00 +03:00
|
|
|
serializer.validated_data['cst_type'])
|
2023-07-23 15:23:01 +03:00
|
|
|
else:
|
2023-07-26 23:11:00 +03:00
|
|
|
constituenta = schema.insert_last(serializer.validated_data['alias'], serializer.validated_data['cst_type'])
|
2023-08-16 18:32:37 +03:00
|
|
|
|
|
|
|
constituenta.convention = serializer.validated_data.get('convention', '')
|
|
|
|
constituenta.term_raw = serializer.validated_data.get('term_raw', '')
|
|
|
|
constituenta.term_resolved = serializer.validated_data.get('term_resolved', '')
|
|
|
|
constituenta.definition_formal = serializer.validated_data.get('definition_formal', '')
|
|
|
|
constituenta.definition_raw = serializer.validated_data.get('definition_raw', '')
|
|
|
|
constituenta.definition_resolved = serializer.validated_data.get('definition_resolved', '')
|
|
|
|
constituenta.save()
|
2023-07-24 22:34:03 +03:00
|
|
|
schema.refresh_from_db()
|
|
|
|
outSerializer = serializers.RSFormDetailsSerlializer(schema)
|
2023-07-26 23:11:00 +03:00
|
|
|
response = Response(status=201, data={
|
|
|
|
'new_cst': serializers.ConstituentaSerializer(constituenta).data,
|
|
|
|
'schema': outSerializer.data})
|
2023-07-24 22:34:03 +03:00
|
|
|
response['Location'] = constituenta.get_absolute_url()
|
|
|
|
return response
|
2023-07-23 15:23:01 +03:00
|
|
|
|
2023-07-25 00:20:37 +03:00
|
|
|
@action(detail=True, methods=['patch'], url_path='cst-multidelete')
|
2023-07-23 21:38:04 +03:00
|
|
|
def cst_multidelete(self, request, pk):
|
|
|
|
''' Delete multiple constituents '''
|
2023-07-27 22:04:25 +03:00
|
|
|
schema = self._get_schema()
|
2023-07-24 22:34:03 +03:00
|
|
|
serializer = serializers.CstListSerlializer(data=request.data, context={'schema': schema})
|
2023-07-23 21:38:04 +03:00
|
|
|
serializer.is_valid(raise_exception=True)
|
2023-07-24 22:34:03 +03:00
|
|
|
schema.delete_cst(serializer.validated_data['constituents'])
|
2023-07-25 00:20:37 +03:00
|
|
|
schema.refresh_from_db()
|
|
|
|
outSerializer = serializers.RSFormDetailsSerlializer(schema)
|
|
|
|
return Response(status=202, data=outSerializer.data)
|
2023-07-23 21:38:04 +03:00
|
|
|
|
2023-07-24 22:34:03 +03:00
|
|
|
@action(detail=True, methods=['patch'], url_path='cst-moveto')
|
|
|
|
def cst_moveto(self, request, pk):
|
|
|
|
''' Delete multiple constituents '''
|
2023-07-27 22:04:25 +03:00
|
|
|
schema: models.RSForm = self._get_schema()
|
2023-07-24 22:34:03 +03:00
|
|
|
serializer = serializers.CstMoveSerlializer(data=request.data, context={'schema': schema})
|
|
|
|
serializer.is_valid(raise_exception=True)
|
|
|
|
schema.move_cst(serializer.validated_data['constituents'], serializer.validated_data['move_to'])
|
|
|
|
schema.refresh_from_db()
|
|
|
|
outSerializer = serializers.RSFormDetailsSerlializer(schema)
|
|
|
|
return Response(status=200, data=outSerializer.data)
|
|
|
|
|
2023-07-27 22:04:25 +03:00
|
|
|
@action(detail=True, methods=['patch'], url_path='reset-aliases')
|
|
|
|
def reset_aliases(self, request, pk):
|
|
|
|
''' Recreate all aliases based on order '''
|
|
|
|
schema = self._get_schema()
|
|
|
|
result = json.loads(pyconcept.reset_aliases(json.dumps(schema.to_trs())))
|
|
|
|
schema.load_trs(data=result, sync_metadata=False, skip_update=True)
|
|
|
|
outSerializer = serializers.RSFormDetailsSerlializer(schema)
|
|
|
|
return Response(status=200, data=outSerializer.data)
|
|
|
|
|
|
|
|
@action(detail=True, methods=['patch'], url_path='load-trs')
|
|
|
|
def load_trs(self, request, pk):
|
|
|
|
''' Load data from file and replace current schema '''
|
|
|
|
serializer = serializers.RSFormUploadSerializer(data=request.data)
|
|
|
|
serializer.is_valid(raise_exception=True)
|
|
|
|
schema = self._get_schema()
|
|
|
|
load_metadata = serializer.validated_data['load_metadata']
|
|
|
|
data = utils.read_trs(request.FILES['file'].file)
|
|
|
|
schema.load_trs(data, load_metadata, skip_update=False)
|
|
|
|
outSerializer = serializers.RSFormDetailsSerlializer(schema)
|
|
|
|
return Response(status=200, data=outSerializer.data)
|
|
|
|
|
|
|
|
@action(detail=True, methods=['post'], url_path='clone')
|
|
|
|
def clone(self, request, pk):
|
|
|
|
''' Clone RSForm constituents and create new schema using new metadata '''
|
|
|
|
serializer = serializers.RSFormSerializer(data=request.data)
|
|
|
|
serializer.is_valid(raise_exception=True)
|
|
|
|
new_schema = models.RSForm.objects.create(
|
|
|
|
title=serializer.validated_data['title'],
|
|
|
|
owner=self.request.user,
|
|
|
|
alias=serializer.validated_data.get('alias', ''),
|
|
|
|
comment=serializer.validated_data.get('comment', ''),
|
|
|
|
is_common=serializer.validated_data.get('is_common', False),
|
|
|
|
)
|
|
|
|
new_schema.load_trs(data=self._get_schema().to_trs(), sync_metadata=False, skip_update=True)
|
|
|
|
outSerializer = serializers.RSFormDetailsSerlializer(new_schema)
|
|
|
|
return Response(status=201, data=outSerializer.data)
|
|
|
|
|
2023-07-15 17:46:19 +03:00
|
|
|
@action(detail=True, methods=['post'])
|
|
|
|
def claim(self, request, pk=None):
|
2023-07-27 22:04:25 +03:00
|
|
|
schema = self._get_schema()
|
2023-07-15 17:46:19 +03:00
|
|
|
if schema.owner == self.request.user:
|
|
|
|
return Response(status=304)
|
|
|
|
else:
|
|
|
|
schema.owner = self.request.user
|
|
|
|
schema.save()
|
2023-07-24 22:34:03 +03:00
|
|
|
return Response(status=200, data=serializers.RSFormSerializer(schema).data)
|
2023-07-15 17:46:19 +03:00
|
|
|
|
|
|
|
@action(detail=True, methods=['get'])
|
|
|
|
def contents(self, request, pk):
|
2023-07-27 22:04:25 +03:00
|
|
|
''' View schema db contents (including constituents) '''
|
|
|
|
schema = serializers.RSFormContentsSerializer(self._get_schema()).data
|
2023-07-15 17:46:19 +03:00
|
|
|
return Response(schema)
|
|
|
|
|
|
|
|
@action(detail=True, methods=['get'])
|
|
|
|
def details(self, request, pk):
|
|
|
|
''' Detailed schema view including statuses '''
|
2023-07-27 22:04:25 +03:00
|
|
|
schema = self._get_schema()
|
2023-07-24 22:34:03 +03:00
|
|
|
serializer = serializers.RSFormDetailsSerlializer(schema)
|
|
|
|
return Response(serializer.data)
|
2023-07-15 17:46:19 +03:00
|
|
|
|
|
|
|
@action(detail=True, methods=['post'])
|
|
|
|
def check(self, request, pk):
|
|
|
|
''' Check RS expression against schema context '''
|
2023-07-27 22:04:25 +03:00
|
|
|
schema = self._get_schema().to_trs()
|
2023-07-15 17:46:19 +03:00
|
|
|
serializer = serializers.ExpressionSerializer(data=request.data)
|
|
|
|
serializer.is_valid(raise_exception=True)
|
|
|
|
expression = serializer.validated_data['expression']
|
|
|
|
result = pyconcept.check_expression(json.dumps(schema), expression)
|
|
|
|
return Response(json.loads(result))
|
|
|
|
|
|
|
|
@action(detail=True, methods=['get'], url_path='export-trs')
|
|
|
|
def export_trs(self, request, pk):
|
|
|
|
''' Download Exteor compatible file '''
|
2023-07-27 22:04:25 +03:00
|
|
|
schema = self._get_schema().to_trs()
|
2023-07-15 17:46:19 +03:00
|
|
|
trs = utils.write_trs(schema)
|
2023-07-27 22:04:25 +03:00
|
|
|
filename = self._get_schema().alias
|
2023-07-15 17:46:19 +03:00
|
|
|
if filename == '' or not filename.isascii():
|
|
|
|
# Note: non-ascii symbols in Content-Disposition
|
|
|
|
# are not supported by some browsers
|
|
|
|
filename = 'Schema'
|
|
|
|
filename += '.trs'
|
|
|
|
|
|
|
|
response = HttpResponse(trs, content_type='application/zip')
|
|
|
|
response['Content-Disposition'] = f'attachment; filename={filename}'
|
|
|
|
return response
|
|
|
|
|
|
|
|
|
|
|
|
class TrsImportView(views.APIView):
|
|
|
|
''' Upload RS form in Exteor format '''
|
|
|
|
serializer_class = serializers.FileSerializer
|
|
|
|
|
|
|
|
def post(self, request, format=None):
|
|
|
|
data = utils.read_trs(request.FILES['file'].file)
|
|
|
|
owner = self.request.user
|
|
|
|
if owner.is_anonymous:
|
|
|
|
owner = None
|
2023-07-27 22:04:25 +03:00
|
|
|
schema = models.RSForm.create_from_trs(owner, data)
|
2023-07-15 17:46:19 +03:00
|
|
|
result = serializers.RSFormSerializer(schema)
|
|
|
|
return Response(status=201, data=result.data)
|
|
|
|
|
|
|
|
|
|
|
|
@api_view(['POST'])
|
|
|
|
def create_rsform(request):
|
|
|
|
''' Create RSForm from user input and/or trs file '''
|
|
|
|
owner = request.user
|
|
|
|
if owner.is_anonymous:
|
|
|
|
owner = None
|
|
|
|
if ('file' not in request.FILES):
|
|
|
|
serializer = serializers.RSFormSerializer(data=request.data)
|
|
|
|
serializer.is_valid(raise_exception=True)
|
|
|
|
schema = models.RSForm.objects.create(
|
2023-07-27 22:04:25 +03:00
|
|
|
title=serializer.validated_data['title'],
|
2023-07-15 17:46:19 +03:00
|
|
|
owner=owner,
|
2023-07-27 22:04:25 +03:00
|
|
|
alias=serializer.validated_data.get('alias', ''),
|
|
|
|
comment=serializer.validated_data.get('comment', ''),
|
|
|
|
is_common=serializer.validated_data.get('is_common', False),
|
2023-07-15 17:46:19 +03:00
|
|
|
)
|
|
|
|
else:
|
|
|
|
data = utils.read_trs(request.FILES['file'].file)
|
|
|
|
if ('title' in request.data and request.data['title'] != ''):
|
|
|
|
data['title'] = request.data['title']
|
2023-07-26 23:11:00 +03:00
|
|
|
if data['title'] == '':
|
|
|
|
data['title'] = 'Без названия ' + request.FILES['file'].fileName
|
2023-07-15 17:46:19 +03:00
|
|
|
if ('alias' in request.data and request.data['alias'] != ''):
|
|
|
|
data['alias'] = request.data['alias']
|
|
|
|
if ('comment' in request.data and request.data['comment'] != ''):
|
|
|
|
data['comment'] = request.data['comment']
|
|
|
|
is_common = True
|
|
|
|
if ('is_common' in request.data):
|
2023-07-26 23:11:00 +03:00
|
|
|
is_common = request.data['is_common'] == 'true'
|
2023-07-27 22:04:25 +03:00
|
|
|
schema = models.RSForm.create_from_trs(owner, data, is_common)
|
2023-07-15 17:46:19 +03:00
|
|
|
result = serializers.RSFormSerializer(schema)
|
|
|
|
return Response(status=201, data=result.data)
|
|
|
|
|
|
|
|
|
|
|
|
@api_view(['POST'])
|
|
|
|
def parse_expression(request):
|
|
|
|
'''Parse RS expression '''
|
|
|
|
serializer = serializers.ExpressionSerializer(data=request.data)
|
|
|
|
serializer.is_valid(raise_exception=True)
|
|
|
|
expression = serializer.validated_data['expression']
|
|
|
|
result = pyconcept.parse_expression(expression)
|
|
|
|
return Response(json.loads(result))
|
|
|
|
|
|
|
|
|
|
|
|
@api_view(['POST'])
|
|
|
|
def convert_to_ascii(request):
|
|
|
|
''' Convert to ASCII syntax '''
|
|
|
|
serializer = serializers.ExpressionSerializer(data=request.data)
|
|
|
|
serializer.is_valid(raise_exception=True)
|
|
|
|
expression = serializer.validated_data['expression']
|
|
|
|
result = pyconcept.convert_to_ascii(expression)
|
|
|
|
return Response({'result': result})
|
|
|
|
|
|
|
|
|
|
|
|
@api_view(['POST'])
|
|
|
|
def convert_to_math(request):
|
|
|
|
'''Convert to MATH syntax '''
|
|
|
|
serializer = serializers.ExpressionSerializer(data=request.data)
|
|
|
|
serializer.is_valid(raise_exception=True)
|
|
|
|
expression = serializer.validated_data['expression']
|
|
|
|
result = pyconcept.convert_to_math(expression)
|
|
|
|
return Response({'result': result})
|