Skip to content

feat: Screenshot upload and map details #21

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions kirovy/constants/api_codes.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ class UploadApiCodes(enum.StrEnum):
EMPTY_UPLOAD = "where-file"
DUPLICATE_MAP = "duplicate-map"
FILE_EXTENSION_NOT_SUPPORTED = "file-extension-not-supported"
INVALID = "invalid-data-upload"


class LegacyUploadApiCodes(enum.StrEnum):
Expand Down
46 changes: 46 additions & 0 deletions kirovy/models/cnc_map.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,3 +227,49 @@ def generate_upload_to(instance: "CncMapFile", filename: str) -> pathlib.Path:

# e.g. "yr/maps/CNC_NET_MAP_ID_HEX/ra2_CNC_NET_MAP_ID_HEX_v1.map
return pathlib.Path(instance.cnc_map.get_map_directory_path(), final_file_name)


class CncMapImageFile(file_base.CncNetFileBaseModel):
"""Represents an image file to display on the website for a map.

.. warning::

``name`` is auto-generated for this file subclass.
"""

objects = CncMapFileManager()

width = models.IntegerField()
height = models.IntegerField()
version = models.IntegerField(editable=False)

cnc_map = models.ForeignKey(CncMap, on_delete=models.CASCADE, null=False)

ALLOWED_EXTENSION_TYPES = {game_models.CncFileExtension.ExtensionTypes.IMAGE.value}

UPLOAD_TYPE = settings.CNC_MAP_DIRECTORY

def save(self, *args, **kwargs):
super().save(*args, **kwargs)

@staticmethod
def generate_upload_to(instance: "CncMapFile", filename: str) -> pathlib.Path:
"""Generate the path to upload map files to.

Gets called by :func:`kirovy.models.file_base._generate_upload_to` when ``CncMapImageFile.save`` is called.
See [the django docs for file fields](https://docs.djangoproject.com/en/5.0/ref/models/fields/#filefield).
``upload_to`` is set in :attr:`kirovy.models.file_base.CncNetFileBaseModel.file`, which calls
``_generate_upload_to``, which calls this function.

:param instance:
Acts as ``self``. The image file object that we are creating an upload path for.
:param filename:
The filename of the uploaded image file.
:return:
Path to upload map to relative to :attr:`~kirovy.settings.base.MEDIA_ROOT`.
"""
filename = pathlib.Path(filename)
final_file_name = f"{instance.name}{filename.suffix}"

# e.g. "yr/maps/CNC_NET_MAP_ID_HEX/screenshot_of_map.jpg
return pathlib.Path(instance.cnc_map.get_map_directory_path(), final_file_name)
2 changes: 1 addition & 1 deletion kirovy/serializers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def get_fields(self):
"""
fields = super().get_fields()
request: t.Optional[KirovyRequest] = self.context.get("request")
if not all([request, request.user.is_authenticated, request.user.is_staff]):
if not (request and request.user.is_authenticated and request.user.is_staff):
fields.pop("last_modified_by_id", None)
return fields

Expand Down
28 changes: 22 additions & 6 deletions kirovy/serializers/cnc_map_serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ class Meta:
hash_sha512 = serializers.CharField(required=True, allow_blank=False)
hash_sha1 = serializers.CharField(required=True, allow_blank=False)

def create(self, validated_data: t) -> cnc_map.CncMapFile:
def create(self, validated_data: t.DictStrAny) -> cnc_map.CncMapFile:
map_file = cnc_map.CncMapFile(**validated_data)
map_file.save()
return map_file
Expand All @@ -106,7 +106,7 @@ class CncMapBaseSerializer(CncNetUserOwnedModelSerializer):
description = serializers.CharField(
required=True,
allow_null=False,
allow_blank=False,
allow_blank=True,
trim_whitespace=True,
min_length=10,
)
Expand All @@ -117,11 +117,9 @@ class CncMapBaseSerializer(CncNetUserOwnedModelSerializer):
)
category_ids = serializers.PrimaryKeyRelatedField(
source="categories",
queryset=cnc_map.MapCategory.objects.all(),
pk_field=serializers.UUIDField(),
many=True,
allow_null=False,
allow_empty=False,
read_only=True, # Set it manually.
)
is_published = serializers.BooleanField(
default=False,
Expand All @@ -139,9 +137,27 @@ class CncMapBaseSerializer(CncNetUserOwnedModelSerializer):
legacy_upload_date = serializers.DateTimeField(
read_only=True,
)
incomplete_upload = serializers.BooleanField(
default=False,
)

parent_id = serializers.PrimaryKeyRelatedField(
source="parent",
queryset=cnc_map.CncMap.objects.all(),
pk_field=serializers.UUIDField(),
many=False,
allow_null=True,
allow_empty=False,
default=None,
)

class Meta:
model = cnc_map.CncMap
# We return the ID instead of the whole object.
exclude = ["cnc_game", "categories"]
exclude = ["cnc_game", "categories", "parent"]
fields = "__all__"

def create(self, validated_data: t.DictStrAny) -> cnc_map.CncMap:
cnc_map_instance = cnc_map.CncMap(**validated_data)
cnc_map_instance.save()
return cnc_map_instance
4 changes: 4 additions & 0 deletions kirovy/settings/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,10 @@
"""
str: The directory inside of :attr:`~kirovy.settings._base.STATIC_URL` where we store game-specific and mod-specific
logos and backgrounds. So a Red Alert 2 icon would be in e.g. ``URL/static/game_images/ra2/icons/allies.png``

.. warning::

This is **not** where we store user-uploaded images. Do not store them here.
"""


Expand Down
26 changes: 18 additions & 8 deletions kirovy/views/map_upload_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from kirovy.request import KirovyRequest
from kirovy.response import KirovyResponse
from kirovy.serializers import cnc_map_serializers
from kirovy.serializers.cnc_map_serializers import CncMapBaseSerializer
from kirovy.services import legacy_upload
from kirovy.services.cnc_gen_2_services import CncGen2MapParser, CncGen2MapSections
from kirovy.utils import file_utils
Expand Down Expand Up @@ -67,15 +68,24 @@ def post(self, request: KirovyRequest, format=None) -> KirovyResponse:
parent_map = self.get_map_parent(map_parser)

# Make the map that we will attach the map file too.
new_map = cnc_map.CncMap(
map_name=map_parser.ini.map_name,
cnc_game_id=game.id,
is_published=False,
incomplete_upload=True,
cnc_user=request.user,
parent=parent_map,
map_serializer = CncMapBaseSerializer(
data=dict(
map_name=map_parser.ini.map_name,
description="",
cnc_game_id=game.id,
is_published=False,
incomplete_upload=True,
cnc_user_id=request.user.id,
parent_id=parent_map.id if parent_map else None,
),
context={"request": self.request},
)
new_map.save()
if not map_serializer.is_valid():
raise KirovyValidationError(
"Map failed validation", code=UploadApiCodes.INVALID, additional=map_serializer.errors
)

new_map = map_serializer.save()

# Set the cncnet map ID in the map file ini.
cnc_net_ini = {constants.CNCNET_INI_MAP_ID_KEY: str(new_map.id)}
Expand Down
Loading