Skip to content

Commit

Permalink
Map Upload WIP
Browse files Browse the repository at this point in the history
- Fix the map parser service to use django's `UploadedFile` correctly
- Start figuring out the serialization and creation of the map model objects

TODO:

- Map category fetching.
- Actually save the map file
- Generate a filename
- check map hashes and cncnetid. Exclude cncnet id section from hashing
- Add the cncnetID
- Actually verify saved files in test.
- Move everything to a DRF serializer for more safety.
  • Loading branch information
alexlambson committed Apr 24, 2024
1 parent e63d235 commit 97c476d
Show file tree
Hide file tree
Showing 5 changed files with 47 additions and 16 deletions.
3 changes: 3 additions & 0 deletions kirovy/models/cnc_game.py
Original file line number Diff line number Diff line change
Expand Up @@ -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}]>"
10 changes: 7 additions & 3 deletions kirovy/services/cnc_gen_2_services.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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."""
Expand All @@ -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()
Expand All @@ -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,
Expand Down Expand Up @@ -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]:
Expand Down
2 changes: 1 addition & 1 deletion kirovy/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def _get_url_patterns() -> t.List[path]:

map_patterns = [
path("categories/", cnc_map_views.MapCategoryListCreateView.as_view()),
path("upload/<filename>/", cnc_map_views.MapFileUploadView.as_view()),
path("upload/", cnc_map_views.MapFileUploadView.as_view()),
]


Expand Down
38 changes: 30 additions & 8 deletions kirovy/views/cnc_map_views.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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)

Expand All @@ -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,
Expand Down
10 changes: 6 additions & 4 deletions tests/test_views/test_map_upload.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

0 comments on commit 97c476d

Please sign in to comment.