Skip to content
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

feature: type name icons #1116

Merged
merged 5 commits into from
Aug 8, 2024
Merged
Show file tree
Hide file tree
Changes from 4 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
39 changes: 39 additions & 0 deletions data/v2/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -629,6 +629,45 @@ def csv_record_to_objects(info):

build_generic((TypeEfficacyPast,), "type_efficacy_past.csv", csv_record_to_objects)

def csv_record_to_objects(info):
game_map = {
"generation-iii": [
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi!, I'm not too fond of the hardcoded map here. Can't we fetch it dynamically?

Why do we need this map anyway?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I may be misunderstanding, but the pokemon sprites looks just as hardcoded as the type sprites I've added. The structure of the sprites object is written out. I just used a map so that I wouldn't have to actually write the full structure and can make the code more compact with some for loops. The only thing I see about the pokemon sprites that could be considered more "dynamic" is the try_image_names function, but that just seems to be accounting for inconsistencies in the pokemon sprite file names/extensions, which isn't a problem with the type sprite files since there are much fewer of them and should be easier to keep the same format, IMO.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oopsie, you're absolutely right! Maybe we could formalize that object into a single structure that can be used for pokemon, forms, items and types. But that's for the future. I only now realize that the map is basically representing this folder: https://github.com/PokeAPI/sprites/tree/master/sprites/types

Ok, sorry and thanks for pointing it out :)

"colosseum",
"emerald",
"firered-leafgreen",
"ruby-saphire",
"xd",
],
"generation-iv": ["diamond-pearl", "heartgold-soulsilver", "platinum"],
"generation-v": ["black-2-white-2", "black-white"],
"generation-vi": ["omega-ruby-alpha-sapphire", "x-y"],
"generation-vii": [
"lets-go-pikachu-lets-go-eevee",
"sun-moon",
"ultra-sun-ultra-moon",
],
"generation-viii": [
"brilliant-diamond-and-shining-pearl",
"legends-arceus",
"sword-shield",
],
"generation-ix": ["scarlet-violet"],
}
sprites = {}
for generation in game_map.keys():
for game in game_map[generation]:
if generation not in sprites:
sprites[generation] = {}
sprites[generation][game] = {
"name_icon": file_path_or_none(
f"types/{generation}/{game}/{info[0]}.png"
)
}

yield TypeSprites(type_id=int(info[0]), sprites=sprites)

build_generic((TypeSprites,), "types.csv", csv_record_to_objects)


#############
# CONTEST #
Expand Down
2 changes: 1 addition & 1 deletion data/v2/sprites
Submodule sprites updated 902 files
41 changes: 41 additions & 0 deletions pokemon_v2/migrations/0016_typesprites.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Generated by Django 3.2.23 on 2024-07-29 02:09

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):
dependencies = [
("pokemon_v2", "0015_pokemoncries"),
]

operations = [
migrations.CreateModel(
name="TypeSprites",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("sprites", models.JSONField()),
(
"type",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="typesprites",
to="pokemon_v2.type",
),
),
],
options={
"abstract": False,
},
),
]
4 changes: 4 additions & 0 deletions pokemon_v2/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -773,6 +773,10 @@ class TypeEfficacyPast(HasTypeEfficacy, HasGeneration):
pass


class TypeSprites(HasType):
sprites = models.JSONField()


#################
# STAT MODELS #
#################
Expand Down
12 changes: 12 additions & 0 deletions pokemon_v2/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2340,6 +2340,12 @@ class Meta:
fields = ("name", "language")


class TypeSpriteSerializer(serializers.ModelSerializer):
class Meta:
model = TypeSprites
fields = ("sprites",)


class TypeDetailSerializer(serializers.ModelSerializer):
"""
Serializer for the Type resource
Expand All @@ -2357,6 +2363,7 @@ class TypeDetailSerializer(serializers.ModelSerializer):
)
pokemon = serializers.SerializerMethodField("get_type_pokemon")
moves = MoveSummarySerializer(many=True, read_only=True, source="move")
sprites = serializers.SerializerMethodField("get_type_sprites")

class Meta:
model = Type
Expand All @@ -2371,8 +2378,13 @@ class Meta:
"names",
"pokemon",
"moves",
"sprites",
)

def get_type_sprites(self, obj):
sprites_object = TypeSprites.objects.get(type_id=obj)
return sprites_object.sprites

# adds an entry for the given type with the given damage
# factor in the given direction to the set of relations

Expand Down
108 changes: 86 additions & 22 deletions pokemon_v2/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ def setup_item_category_name_data(cls, item_category, name="itm ctgry nm"):

@classmethod
def setup_item_sprites_data(cls, item, default=True):
sprite_path = "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/%s.png"
sprite_path = "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/items/%s.png"

sprites = {
"default": sprite_path % item.id if default else None,
Expand Down Expand Up @@ -692,6 +692,54 @@ def setup_type_game_index_data(cls, type, game_index=0):

return type_game_index

def setup_type_sprites_data(cls, type):
game_map = {
"generation-iii": [
"colosseum",
"emerald",
"firered-leafgreen",
"ruby-saphire",
"xd",
],
"generation-iv": ["diamond-pearl", "heartgold-soulsilver", "platinum"],
"generation-v": ["black-2-white-2", "black-white"],
"generation-vi": ["omega-ruby-alpha-sapphire", "x-y"],
"generation-vii": [
"lets-go-pikachu-lets-go-eevee",
"sun-moon",
"ultra-sun-ultra-moon",
],
"generation-viii": [
"brilliant-diamond-and-shining-pearl",
"legends-arceus",
"sword-shield",
],
"generation-ix": ["scarlet-violet"],
}
sprites = {}
for generation in game_map.keys():
for game in game_map[generation]:
if generation not in sprites:
sprites[generation] = {}

if type.id == 18 and generation.endswith(("-iii", "-iv", "-v")):
sprites[generation][game] = None
elif type.id == 19 and generation.endswith(
("-iii", "-iv", "-v", "-vi", "-vii", "-viii")
):
sprites[generation][game] = None
else:
sprites[generation][game] = {
"name_icon": f"https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/types/{generation}/{game}/{type.id}.png"
}

type_sprites = TypeSprites.objects.create(
type=type, sprites=json.dumps(sprites)
)
type_sprites.save()

return type_sprites, game_map

# Move Data
@classmethod
def setup_move_ailment_data(cls, name="mv almnt"):
Expand Down Expand Up @@ -2753,9 +2801,14 @@ def test_item_api(self):
"{}{}/evolution-chain/{}/".format(TEST_HOST, API_V2, evolution_chain.pk),
)

sprites_data = json.loads(response.data["sprites"])
sprites_data = json.loads(item_sprites.sprites)
response_sprites_data = json.loads(response.data["sprites"])

# sprites
self.assertEqual(
sprites_data["default"],
response_sprites_data["default"],
)
self.assertEqual(
sprites_data["default"],
"{}".format(sprites_data["default"]),
Expand Down Expand Up @@ -3313,12 +3366,12 @@ def test_super_contest_effect_api(self):

# Type Tests
def test_type_api(self):
type = self.setup_type_data(name="base tp")
type_name = self.setup_type_name_data(type, name="base tp nm")
type_game_index = self.setup_type_game_index_data(type, game_index=10)
move = self.setup_move_data(name="mv for base tp", type=type)
type_obj = self.setup_type_data(name="base tp")
type_name = self.setup_type_name_data(type_obj, name="base tp nm")
type_game_index = self.setup_type_game_index_data(type_obj, game_index=10)
move = self.setup_move_data(name="mv for base tp", type=type_obj)
pokemon = self.setup_pokemon_data(name="pkmn for base tp")
pokemon_type = self.setup_pokemon_type_data(pokemon=pokemon, type=type)
pokemon_type = self.setup_pokemon_type_data(pokemon=pokemon, type=type_obj)

generation = self.setup_generation_data(name="past gen")

Expand All @@ -3345,60 +3398,62 @@ def test_type_api(self):

newer_type = self.setup_type_data(name="newer tp", generation=newer_generation)

type_sprites, game_map = self.setup_type_sprites_data(type=type_obj)

# type relations
no_damage_to_relation = TypeEfficacy(
damage_type=type, target_type=no_damage_to, damage_factor=0
damage_type=type_obj, target_type=no_damage_to, damage_factor=0
)
no_damage_to_relation.save()

half_damage_to_type_relation = TypeEfficacy(
damage_type=type, target_type=half_damage_to, damage_factor=50
damage_type=type_obj, target_type=half_damage_to, damage_factor=50
)
half_damage_to_type_relation.save()

double_damage_to_type_relation = TypeEfficacy(
damage_type=type, target_type=double_damage_to, damage_factor=200
damage_type=type_obj, target_type=double_damage_to, damage_factor=200
)
double_damage_to_type_relation.save()

no_damage_from_relation = TypeEfficacy(
damage_type=no_damage_from, target_type=type, damage_factor=0
damage_type=no_damage_from, target_type=type_obj, damage_factor=0
)
no_damage_from_relation.save()

half_damage_from_type_relation = TypeEfficacy(
damage_type=half_damage_from, target_type=type, damage_factor=50
damage_type=half_damage_from, target_type=type_obj, damage_factor=50
)
half_damage_from_type_relation.save()

double_damage_from_type_relation = TypeEfficacy(
damage_type=double_damage_from, target_type=type, damage_factor=200
damage_type=double_damage_from, target_type=type_obj, damage_factor=200
)
double_damage_from_type_relation.save()

double_damage_from_newer_type_relation = TypeEfficacy(
damage_type=newer_type, target_type=type, damage_factor=200
damage_type=newer_type, target_type=type_obj, damage_factor=200
)
double_damage_from_newer_type_relation.save()

# past type relations

# type used to deal half damage rather than no damage
past_no_damage_to_relation = TypeEfficacyPast(
damage_type=type,
damage_type=type_obj,
target_type=no_damage_to,
damage_factor=50,
generation=generation,
)
past_no_damage_to_relation.save()

response = self.client.get("{}/type/{}/".format(API_V2, type.pk))
response = self.client.get("{}/type/{}/".format(API_V2, type_obj.pk))

self.assertEqual(response.status_code, status.HTTP_200_OK)

# base params
self.assertEqual(response.data["id"], type.pk)
self.assertEqual(response.data["name"], type.name)
self.assertEqual(response.data["id"], type_obj.pk)
self.assertEqual(response.data["name"], type_obj.name)
# name params
self.assertEqual(response.data["names"][0]["name"], type_name.name)
self.assertEqual(
Expand All @@ -3409,19 +3464,19 @@ def test_type_api(self):
"{}{}/language/{}/".format(TEST_HOST, API_V2, type_name.language.pk),
)
# generation params
self.assertEqual(response.data["generation"]["name"], type.generation.name)
self.assertEqual(response.data["generation"]["name"], type_obj.generation.name)
self.assertEqual(
response.data["generation"]["url"],
"{}{}/generation/{}/".format(TEST_HOST, API_V2, type.generation.pk),
"{}{}/generation/{}/".format(TEST_HOST, API_V2, type_obj.generation.pk),
)
# damage class params
self.assertEqual(
response.data["move_damage_class"]["name"], type.move_damage_class.name
response.data["move_damage_class"]["name"], type_obj.move_damage_class.name
)
self.assertEqual(
response.data["move_damage_class"]["url"],
"{}{}/move-damage-class/{}/".format(
TEST_HOST, API_V2, type.move_damage_class.pk
TEST_HOST, API_V2, type_obj.move_damage_class.pk
),
)
# move params
Expand Down Expand Up @@ -3570,6 +3625,15 @@ def test_type_api(self):
),
)

sprites_data = json.loads(type_sprites.sprites)

for generation in game_map.keys():
for game in game_map[generation]:
self.assertEqual(
json.loads(response.data["sprites"])[generation][game]["name_icon"],
sprites_data[generation][game]["name_icon"],
)

# Pokedex Tests
def test_pokedex_api(self):
pokedex = self.setup_pokedex_data(name="base pkdx")
Expand Down
Loading