Skip to content

Commit c56a030

Browse files
committed
- Add map image serializer
- Add map images to the map serializer. - Test map images return
1 parent cd7b0ee commit c56a030

File tree

6 files changed

+107
-13
lines changed

6 files changed

+107
-13
lines changed

kirovy/models/cnc_map.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -238,8 +238,6 @@ class CncMapImageFile(file_base.CncNetFileBaseModel):
238238
``name`` is auto-generated for this file subclass.
239239
"""
240240

241-
objects = CncMapFileManager()
242-
243241
width = models.IntegerField()
244242
height = models.IntegerField()
245243

@@ -267,7 +265,8 @@ class Meta:
267265
indexes = [models.Index(fields=["cnc_map"])]
268266

269267
def save(self, *args, **kwargs):
270-
self.name = self.cnc_map.map_name
268+
if not self.name:
269+
self.name = self.cnc_map.map_name
271270
self.cnc_game = self.cnc_map.cnc_game
272271
super().save(*args, **kwargs)
273272

kirovy/serializers/cnc_map_serializers.py

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,76 @@ def update(self, instance: cnc_map.CncMapFile, validated_data: t.DictStrAny) ->
9595
)
9696

9797

98+
class CncMapImageFileSerializer(KirovySerializer):
99+
100+
class Meta:
101+
model = cnc_map.CncMapImageFile
102+
# We return the ID instead of the whole object.
103+
exclude = ["cnc_game", "cnc_map", "file_extension", "hash_md5", "hash_sha512", "hash_sha1"]
104+
fields = "__all__"
105+
106+
width = serializers.IntegerField()
107+
"""attr: The map height.
108+
109+
Extracted using :class:`kirovy.services.cnc_gen_2_services.CncGen2MapParser`
110+
"""
111+
112+
height = serializers.IntegerField()
113+
"""attr: The map height.
114+
115+
Extracted using :class:`kirovy.services.cnc_gen_2_services.CncGen2MapParser`
116+
"""
117+
118+
is_extracted = serializers.BooleanField()
119+
"""attr: Whether the image was automatically extracted from the map file.
120+
121+
Extracted using :class:`kirovy.services.cnc_gen_2_services.CncGen2MapParser`
122+
"""
123+
124+
cnc_map_id = serializers.PrimaryKeyRelatedField(
125+
source="cnc_map",
126+
queryset=cnc_map.CncMap.objects.all(),
127+
pk_field=serializers.UUIDField(),
128+
)
129+
130+
name = serializers.CharField(max_length=100, allow_null=True)
131+
"""attr: The filename.
132+
133+
If blank, uses map name in :func:`kirovy.models.cnc_map.CncMapImageFile.save`.
134+
"""
135+
136+
image_order = serializers.IntegerField(min_value=0)
137+
"""attr: The order in which the image will appear.
138+
139+
If there are collsions, we fallback to the created date.
140+
"""
141+
142+
file = serializers.FileField(use_url=True)
143+
144+
file_extension_id = serializers.PrimaryKeyRelatedField(
145+
source="file_extension",
146+
queryset=CncFileExtension.objects.filter(extension_type__in=cnc_map.CncMapImageFile.ALLOWED_EXTENSION_TYPES),
147+
pk_field=serializers.UUIDField(),
148+
)
149+
150+
cnc_game_id = serializers.PrimaryKeyRelatedField(
151+
source="cnc_game",
152+
queryset=CncGame.objects.all(),
153+
pk_field=serializers.UUIDField(),
154+
)
155+
156+
def create(self, validated_data: t.DictStrAny) -> cnc_map.CncMapImageFile:
157+
image_file = cnc_map.CncMapImageFile(**validated_data)
158+
image_file.save()
159+
return image_file
160+
161+
def update(self, instance: cnc_map.CncMapImageFile, validated_data: t.DictStrAny) -> cnc_map.CncMapImageFile:
162+
instance.name = validated_data.get("name", instance.name)
163+
instance.image_order = validated_data.get("image_order", instance.image_order)
164+
instance.save(update_fields=["name", "image_order"])
165+
return instance
166+
167+
98168
class CncMapBaseSerializer(CncNetUserOwnedModelSerializer):
99169
map_name = serializers.CharField(
100170
required=True,
@@ -151,6 +221,9 @@ class CncMapBaseSerializer(CncNetUserOwnedModelSerializer):
151221
default=None,
152222
)
153223

224+
files = CncMapFileSerializer(many=True, read_only=True, source="cncmapfile_set")
225+
images = CncMapImageFileSerializer(many=True, read_only=True, source="cncmapimagefile_set")
226+
154227
# TODO: These serializer method fields really ought to be sub serializers
155228
# TODO: Make sure queries are optimized in the views for listing maps.
156229
latest_map_file_hash = serializers.SerializerMethodField()
@@ -160,7 +233,7 @@ class CncMapBaseSerializer(CncNetUserOwnedModelSerializer):
160233
class Meta:
161234
model = cnc_map.CncMap
162235
# We return the ID instead of the whole object.
163-
exclude = ["cnc_game", "categories", "parent"]
236+
exclude = ["cnc_game", "categories", "parent", "cnc_map_files"]
164237
fields = "__all__"
165238

166239
def get_latest_map_file_hash(self, obj: cnc_map.CncMap) -> t.Optional[str]:

kirovy/views/cnc_map_views.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ def get_queryset(self):
155155
# Prefetch the categories because they're displayed like tags.
156156
# TODO: Since the category list is going to be somewhat small,
157157
# maybe the UI should just cache them and I return IDs instead of objects?
158-
.prefetch_related("categories", "cncmapfile_set")
158+
.prefetch_related("categories", "cncmapfile_set", "cncmapimagefile_set")
159159
)
160160
return base_query
161161

kirovy/views/map_upload_views.py

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -166,14 +166,22 @@ def extract_preview(self, new_map_file: cnc_map.CncMapFile, map_parser: CncGen2M
166166
image_extension = CncFileExtension.objects.get(extension="jpg")
167167
extracted_image.save(image_io, format="JPEG", quality=95)
168168
django_image = InMemoryUploadedFile(image_io, None, "temp.jpg", "image/jpeg", image_io.tell(), None)
169-
new_map_preview = map_preview.MapPreview(
170-
is_extracted=True,
171-
cnc_map_file=new_map_file,
172-
file=django_image,
173-
file_extension=image_extension,
169+
image_serializer = cnc_map_serializers.CncMapImageFileSerializer(
170+
data=dict(
171+
name=None, # will default to map name.
172+
width=extracted_image.width,
173+
height=extracted_image.height,
174+
is_extracted=True,
175+
cnc_map_id=new_map_file.cnc_map_id,
176+
cnc_game_id=new_map_file.cnc_game_id,
177+
file=django_image,
178+
file_extension_id=image_extension.id,
179+
image_order=999,
180+
)
174181
)
175-
new_map_preview.save()
176-
extracted_image_url = new_map_preview.file.url
182+
image_serializer.is_valid(raise_exception=True)
183+
image_serializer.save()
184+
extracted_image_url = image_serializer.instance.file.url
177185

178186
return extracted_image_url
179187

tests/models/test_cnc_map.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ def test_cnc_map_generate_upload_to(game_yuri, extension_map, file_map_desert, c
1818
1919
This test will fail if you alter the initial migrations.
2020
"""
21-
settings.CNC_MAP_DIRECTORY = "worlds" # Change default to check that the settings control the upload path.
2221
expected_path = pathlib.Path(
2322
settings.MEDIA_ROOT,
2423
"yr",
@@ -35,6 +34,7 @@ def test_cnc_map_generate_upload_to(game_yuri, extension_map, file_map_desert, c
3534
cnc_game=game_yuri,
3635
name=cnc_map.generate_versioned_name_for_file(),
3736
)
37+
CncMapFile.UPLOAD_TYPE = "worlds"
3838
saved_map.save()
3939
saved_map.refresh_from_db()
4040

tests/test_views/test_map_upload.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
from kirovy import settings, typing as t
99
from kirovy.constants.api_codes import UploadApiCodes
10+
from kirovy.models.cnc_map import CncMapImageFile
1011
from kirovy.utils import file_utils
1112
from kirovy.models import CncMap, CncMapFile, MapCategory
1213
from kirovy.response import KirovyResponse
@@ -49,6 +50,7 @@ def test_map_file_upload_happy_path(client_user, file_map_desert, game_yuri, ext
4950

5051
map_object = CncMap.objects.get(id=response.data["result"]["cnc_map_id"])
5152
file_object = CncMapFile.objects.get(cnc_map_id=map_object.id)
53+
image_object = CncMapImageFile.objects.get(cnc_map_id=map_object.id)
5254

5355
assert map_object
5456

@@ -76,6 +78,18 @@ def test_map_file_upload_happy_path(client_user, file_map_desert, game_yuri, ext
7678
assert response_map["legacy_upload_date"] is None, "Non legacy maps should never have this field."
7779
assert response_map["id"] == str(map_object.id)
7880

81+
# Check the get endpoint returns the files.
82+
assert len(response_map["files"]) == 1
83+
assert response_map["files"][0]["id"] == str(file_object.id)
84+
assert response_map["files"][0]["file"].endswith(uploaded_file_url)
85+
86+
# Check that the image was included
87+
assert len(response_map["images"]) == 1
88+
assert response_map["images"][0]["id"] == str(image_object.id)
89+
assert response_map["images"][0]["name"] == map_object.map_name
90+
assert response_map["images"][0]["file"].endswith(uploaded_image_url)
91+
assert response_map["images"][0]["is_extracted"] is True
92+
7993

8094
def test_map_file_upload_banned_user(file_map_desert, game_yuri, client_banned):
8195
"""Test that a banned user cannot upload a new map."""

0 commit comments

Comments
 (0)