diff --git a/kirovy/models/cnc_game.py b/kirovy/models/cnc_game.py index 3d81107..239bea7 100644 --- a/kirovy/models/cnc_game.py +++ b/kirovy/models/cnc_game.py @@ -128,3 +128,6 @@ def icon_url(self) -> str: @cached_property def logo_url(self) -> str: return f"{self.images_relative_url}{self.slug}/logo.png" + + def __repr__(self) -> str: + return f"<{type(self).__name__} Object: ({self.slug}) '{self.full_name}' [{self.id}]>" diff --git a/kirovy/services/cnc_gen_2_services.py b/kirovy/services/cnc_gen_2_services.py index e50a5ac..1d394ba 100644 --- a/kirovy/services/cnc_gen_2_services.py +++ b/kirovy/services/cnc_gen_2_services.py @@ -10,6 +10,7 @@ import lzo from django.core.files import File +from django.core.files.uploadedfile import UploadedFile from rest_framework import status from kirovy import typing as t, exceptions @@ -41,7 +42,7 @@ class CncGen2MapParser: Parses maps, validates them, and extracts previews if necessary. """ - file: File + file: UploadedFile """:attr: The uploaded file that we're parsing.""" parser: configparser.ConfigParser """:attr: The parser object where the ini will be parsed into.""" @@ -64,7 +65,7 @@ class ErrorMsg(enum.StrEnum): CORRUPT_MAP = _("Could not parse map file.") MISSING_INI = _("Missing necessary INI sections.") - def __init__(self, uploaded_file: File): + def __init__(self, uploaded_file: UploadedFile): self.validate_file_type(uploaded_file) self.file = uploaded_file self.parser = configparser.ConfigParser() @@ -84,7 +85,9 @@ def _parse_file(self) -> None: self.file.open("r") try: - self.parser.read_file(self.file) + # We can't use read_file because parser expects the file to be read as a string, + # but django uploaded files are read as bytes. So we need to convert to string first. + self.parser.read_string(self.file.read().decode()) except configparser.ParsingError as e: raise exceptions.InvalidMapFile( self.ErrorMsg.CORRUPT_MAP, @@ -144,6 +147,7 @@ def is_text(cls, uploaded_file: File) -> bool: magic_parser = magic.Magic(mime=True) uploaded_file.seek(0) mr_mime = magic_parser.from_buffer(uploaded_file.read()) + uploaded_file.seek(0) return mr_mime == "text/plain" def extract_preview(self) -> t.Optional[Image.Image]: diff --git a/kirovy/urls.py b/kirovy/urls.py index c174ec3..5f8077e 100644 --- a/kirovy/urls.py +++ b/kirovy/urls.py @@ -36,7 +36,7 @@ def _get_url_patterns() -> t.List[path]: map_patterns = [ path("categories/", cnc_map_views.MapCategoryListCreateView.as_view()), - path("upload//", cnc_map_views.MapFileUploadView.as_view()), + path("upload/", cnc_map_views.MapFileUploadView.as_view()), ] diff --git a/kirovy/views/cnc_map_views.py b/kirovy/views/cnc_map_views.py index 9912cd3..8290c6d 100644 --- a/kirovy/views/cnc_map_views.py +++ b/kirovy/views/cnc_map_views.py @@ -1,14 +1,14 @@ import logging +import pathlib from django.conf import settings from django.core.files.uploadedfile import UploadedFile from rest_framework import status -from rest_framework.parsers import FileUploadParser, MultiPartParser -from rest_framework.response import Response +from rest_framework.parsers import MultiPartParser from rest_framework.views import APIView -from kirovy import permissions, typing as t, exceptions -from kirovy.models import MapCategory +from kirovy import permissions, typing as t, exceptions, constants +from kirovy.models import MapCategory, cnc_map, CncGame, CncFileExtension from kirovy.request import KirovyRequest from kirovy.response import KirovyResponse from kirovy.serializers import cnc_map_serializers @@ -44,11 +44,12 @@ class MapFileUploadView(APIView): parser_classes = [MultiPartParser] permission_classes = [permissions.CanUpload] - def post( - self, request: KirovyRequest, filename: str, format=None - ) -> KirovyResponse: - + def post(self, request: KirovyRequest, format=None) -> KirovyResponse: + game = CncGame.objects.get(id=request.data["game_id"]) uploaded_file: UploadedFile = request.data["file"] + extension = CncFileExtension.objects.get( + extension=pathlib.Path(uploaded_file.name).suffix.lstrip(".") + ) max_size = file_utils.ByteSized(mega=25) uploaded_size = file_utils.ByteSized(uploaded_file.size) @@ -73,6 +74,27 @@ def post( status=status.HTTP_400_BAD_REQUEST, ) + new_map = cnc_map.CncMap( + map_name=map_parser.parser.get(map_parser.map_sections.BASIC, "Name"), + cnc_game=game, + is_published=False, + incomplete_upload=True, + cnc_user=request.user, + ) + new_map.save() + # TODO: Map categories + # TODO: Save the in memory file object. + new_map_file = cnc_map.CncMapFile( + width=map_parser.parser.get(map_parser.map_sections.HEADER, "Width"), + height=map_parser.parser.get(map_parser.map_sections.HEADER, "Height"), + name="", # TODO: Make filename + cnc_map=new_map, + file=uploaded_file, + file_extension=extension, + cnc_game=new_map.cnc_game, + ) + new_map_file.save() + return KirovyResponse( t.ResponseData(message="File uploaded successfully"), status=status.HTTP_201_CREATED, diff --git a/tests/test_views/test_map_upload.py b/tests/test_views/test_map_upload.py index d8d7409..58cc2fb 100644 --- a/tests/test_views/test_map_upload.py +++ b/tests/test_views/test_map_upload.py @@ -6,14 +6,16 @@ _UPLOAD_URL = "/maps/upload/" -def test_map_file_upload_happy_path(client_user, file_map_desert): +def test_map_file_upload_happy_path( + client_user, file_map_desert, game_yuri, extension_map +): # TODO: Finish the tests. file_name = parse.quote_plus(pathlib.Path(file_map_desert.name).name) response = client_user.post( - f"{_UPLOAD_URL}{file_name}/", - {"file": file_map_desert}, + _UPLOAD_URL, + {"file": file_map_desert, "game_id": str(game_yuri.id)}, format="multipart", content_type=None, ) - assert response.status_code == status.HTTP_200_OK + assert response.status_code == status.HTTP_201_CREATED