|
3 | 3 | import os
|
4 | 4 | import time
|
5 | 5 | import logging
|
| 6 | +import re |
6 | 7 | from binascii import hexlify
|
7 | 8 | from rest_framework import viewsets, mixins
|
| 9 | +from rest_framework.views import APIView |
8 | 10 | from rest_framework.permissions import IsAuthenticated
|
9 | 11 | from rest_framework.decorators import action
|
10 | 12 | from rest_framework.response import Response
|
11 | 13 | from rest_framework.exceptions import ValidationError
|
12 | 14 | from rest_framework.parsers import MultiPartParser, FormParser, JSONParser
|
| 15 | +from rest_framework import authentication |
13 | 16 | from rest_framework import status
|
| 17 | +from rest_framework.authtoken.models import Token |
14 | 18 | from django.utils import timezone
|
15 | 19 | from django.conf import settings
|
16 | 20 | from django.http import HttpRequest
|
@@ -870,3 +874,99 @@ def list(self, request):
|
870 | 874 |
|
871 | 875 | serializer = self.serializer_class(results, many=True)
|
872 | 876 | return Response(serializer.data)
|
| 877 | + |
| 878 | + |
| 879 | +class GetApiKeyView(APIView): |
| 880 | + permission_classes = (IsAuthenticated,) |
| 881 | + authentication_classes = (CsrfExemptSessionAuthentication,) |
| 882 | + |
| 883 | + def get(self, request): |
| 884 | + token, created = Token.objects.get_or_create(user=request.user) |
| 885 | + return Response({'api_key': token.key }) |
| 886 | + |
| 887 | + |
| 888 | +class SlicerApiVersionView(APIView): |
| 889 | + authentication_classes = [authentication.TokenAuthentication] |
| 890 | + permission_classes = (IsAuthenticated,) |
| 891 | + |
| 892 | + def get(self, request, format=None): |
| 893 | + return Response({"text": "Obico"}) |
| 894 | + |
| 895 | + |
| 896 | +class SlicerApiPrinterListView(APIView): |
| 897 | + authentication_classes = [authentication.TokenAuthentication] |
| 898 | + permission_classes = (IsAuthenticated,) |
| 899 | + |
| 900 | + def get(self, request, format=None): |
| 901 | + return Response({"printers": [{"id": str(p.id), "name": p.name} for p in request.user.printer_set.all()]}) |
| 902 | + |
| 903 | + |
| 904 | +class SlicerApiUploadView(APIView): |
| 905 | + authentication_classes = [authentication.TokenAuthentication] |
| 906 | + permission_classes = (IsAuthenticated,) |
| 907 | + |
| 908 | + def post(self, request, format=None): |
| 909 | + data = dict(**request.data) |
| 910 | + file = request.data.get('file') |
| 911 | + if file: |
| 912 | + data['filename'] = file.name |
| 913 | + |
| 914 | + serializer = GCodeFileDeSerializer(data=data, context={'request': request}) |
| 915 | + serializer.is_valid(raise_exception=True) |
| 916 | + validated_data = serializer.validated_data |
| 917 | + |
| 918 | + start_print = request.data.get('print') == 'true' |
| 919 | + |
| 920 | + printer = None |
| 921 | + if start_print: |
| 922 | + raw_printer_id = request.data.get('printer_id') |
| 923 | + if raw_printer_id: |
| 924 | + try: |
| 925 | + printer_id = int(re.match(r".*\[(\d+)\]", raw_printer_id).groups()[0]) |
| 926 | + except (ValueError, TypeError, IndexError): |
| 927 | + raise ValidationError({'printer_id': 'invalid value'}) |
| 928 | + |
| 929 | + printer = request.user.printer_set.filter(id=printer_id).first() |
| 930 | + if printer is None: |
| 931 | + raise ValidationError({'printer_id': 'could not find printer'}) |
| 932 | + |
| 933 | + if 'file' in request.FILES: |
| 934 | + file_size_limit = 500 * 1024 * 1024 if request.user.is_pro else 50 * 1024 * 1024 |
| 935 | + num_bytes=request.FILES['file'].size |
| 936 | + if num_bytes > file_size_limit: |
| 937 | + return Response({'error': 'File size too large'}, status=413) |
| 938 | + |
| 939 | + gcode_file = GCodeFile.objects.create(**validated_data) |
| 940 | + |
| 941 | + self.set_metadata(gcode_file, *gcode_metadata.parse(request.FILES['file'], num_bytes, request.encoding or settings.DEFAULT_CHARSET)) |
| 942 | + |
| 943 | + request.FILES['file'].seek(0) |
| 944 | + _, ext_url = save_file_obj(self.path_in_storage(gcode_file), request.FILES['file'], settings.GCODE_CONTAINER) |
| 945 | + gcode_file.url = ext_url |
| 946 | + gcode_file.num_bytes = num_bytes |
| 947 | + gcode_file.save() |
| 948 | + |
| 949 | + if start_print: |
| 950 | + # FIXME |
| 951 | + return Response({"message": "print queued"}) |
| 952 | + |
| 953 | + |
| 954 | + return Response({"message": "uploaded successfully"}) |
| 955 | + |
| 956 | + raise ValidationError({"file": "Missing file body"}) |
| 957 | + |
| 958 | + def set_metadata(self, gcode_file, metadata, thumbnails): |
| 959 | + gcode_file.metadata_json = json.dumps(metadata) |
| 960 | + for key in ['estimated_time', 'filament_total']: |
| 961 | + setattr(gcode_file, key, metadata.get(key)) |
| 962 | + |
| 963 | + thumb_num = 0 |
| 964 | + for thumb in sorted(thumbnails, key=lambda x: x.getbuffer().nbytes, reverse=True): |
| 965 | + thumb_num += 1 |
| 966 | + if thumb_num > 3: |
| 967 | + continue |
| 968 | + _, ext_url = save_file_obj(f'gcode_thumbnails/{gcode_file.user.id}/{gcode_file.id}/{thumb_num}.png', thumb, settings.TIMELAPSE_CONTAINER) |
| 969 | + setattr(gcode_file, f'thumbnail{thumb_num}_url', ext_url) |
| 970 | + |
| 971 | + def path_in_storage(self, gcode_file): |
| 972 | + return f'{gcode_file.user.id}/{gcode_file.id}' |
0 commit comments