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

Release: Fixes to spells which were deleted by accident, and some magic items. #231

Merged
merged 31 commits into from
May 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
383f96a
Model added, tests partial.
invalid-email-address May 11, 2023
3d53f51
Adding spell list.
invalid-email-address May 11, 2023
a7ce2e3
Adding Spell List to the API.
invalid-email-address May 11, 2023
5860c22
Spells coming through now.
invalid-email-address May 11, 2023
0c10df4
Working!
invalid-email-address May 12, 2023
c73e44e
Adding tests and spell lists.
invalid-email-address May 12, 2023
99d8779
Adding schema changes.
invalid-email-address May 12, 2023
177b1ac
Spelllist should not actually return entire spells
invalid-email-address May 13, 2023
602acf6
Fixing tests.
invalid-email-address May 13, 2023
af9883a
Adding a spell list to spell.
invalid-email-address May 13, 2023
e098054
Spell_lists is now queryable.
invalid-email-address May 13, 2023
c15a84f
Removing fireball from cleric and warlock list.
invalid-email-address May 13, 2023
6ae220c
Adding a monsterfilter.
invalid-email-address May 13, 2023
63af417
Adding fields to armor.
invalid-email-address May 13, 2023
7cd94d4
Adding an exclusion filter for spell lists.
invalid-email-address May 14, 2023
1bc0ff7
Fixed hellfire armor
mshea May 16, 2023
55c98d4
fix_spell_data_deletion
invalid-email-address May 16, 2023
a041511
Merge pull request #226 from open5e/fix_hellfire_armor
eepMoody May 16, 2023
707417e
Merge pull request #227 from open5e/fix_spell_data_deletion
eepMoody May 16, 2023
3fca3ab
Merge pull request #222 from open5e/spell_class_list
augustjohnson May 16, 2023
4e19dfa
Merge branch 'staging' into 90-less-than-or-equals-in-filter
augustjohnson May 16, 2023
b11c903
Merge pull request #223 from open5e/90-less-than-or-equals-in-filter
augustjohnson May 16, 2023
9cab5e8
Merge pull request #224 from open5e/135_armor_response_lacks_base_ac_…
augustjohnson May 16, 2023
b318e77
Ordering conflicting migrations.
invalid-email-address May 16, 2023
847dafb
Merge pull request #232 from open5e/staging_bugfix_spelllist_migration
eepMoody May 16, 2023
d6b79b3
Removing a single type character in the wish spell
invalid-email-address May 16, 2023
69c1c2a
Merge pull request #235 from open5e/wish_spell_typo
eepMoody May 17, 2023
1d033f6
vault: fix Bag of Bramble Beasts table
Sturlen May 17, 2023
f7da635
Added environment tags to SRD monsters
tbasten May 18, 2023
8914c25
Merge pull request #241 from tbasten/staging
eepMoody May 18, 2023
e243372
Merge pull request #238 from Sturlen/bag-of-bramble-fix
eepMoody May 18, 2023
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
19 changes: 19 additions & 0 deletions api/management/commands/importer.py
Original file line number Diff line number Diff line change
Expand Up @@ -645,6 +645,25 @@ def import_spell(self, spell_json, import_spec) -> ImportResult:
i.save()
return result

def import_spell_list(self, spell_list_json, import_spec) -> ImportResult:
""" Create or update a spell list. Spells must be present before importing the list."""
new = False
exists = False
slug = slugify(spell_list_json["name"])
if models.SpellList.objects.filter(slug=slug).exists():
i = models.SpellList.objects.get(slug=slug)
exists = True
else:
i = models.SpellList(document=self._last_document_imported)
new = True

i.import_from_json_v1(json=spell_list_json)

result = self._determine_import_result(new, exists)
if result is not ImportResult.SKIPPED:
i.save()
return result

def import_weapon(self, weapon_json, import_spec) -> ImportResult:
"""Create or update a single Weapon model from a JSON object."""
new = False
Expand Down
1 change: 1 addition & 0 deletions api/management/commands/populatedb.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ def _populate_from_directory(self, directory: Path) -> None:
importer.import_magic_item,
),
ImportSpec("spells.json", models.Spell, importer.import_spell),
ImportSpec("spelllist.json", models.SpellList, importer.import_spell_list),
ImportSpec("monsters.json", models.Monster, importer.import_monster),
ImportSpec("planes.json", models.Plane, importer.import_plane),
ImportSpec("sections.json", models.Section, importer.import_section),
Expand Down
36 changes: 36 additions & 0 deletions api/migrations/0026_auto_20230511_1741.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Generated by Django 3.2.18 on 2023-05-11 17:41

import django.core.validators
from django.db import migrations, models
import django.db.models.deletion
import uuid


class Migration(migrations.Migration):

dependencies = [
('api', '0025_auto_20230423_0234'),
]

operations = [
migrations.AlterField(
model_name='spell',
name='target_range_sort',
field=models.IntegerField(help_text='Sortable distance ranking to the target. 0 for self, 1 for touch, sight is 9999, unlimited (same plane) is 99990, unlimited any plane is 99999. All other values in feet.', validators=[django.core.validators.MinValueValidator(0)]),
),
migrations.CreateModel(
name='SpellList',
fields=[
('slug', models.CharField(default=uuid.uuid1, help_text='Short name for the game content item.', max_length=255, primary_key=True, serialize=False, unique=True)),
('name', models.TextField(help_text='Name of the game content item.')),
('desc', models.TextField(help_text='Description of the game content item. Markdown.')),
('created_at', models.DateTimeField(auto_now_add=True)),
('page_no', models.IntegerField(null=True)),
('document', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.document')),
('spells', models.ManyToManyField(help_text='The set of spells.', to='api.Spell')),
],
options={
'abstract': False,
},
),
]
44 changes: 44 additions & 0 deletions api/migrations/0026_auto_20230513_1436.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Generated by Django 3.2.18 on 2023-05-13 14:36

import django.core.validators
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('api', '0025_auto_20230423_0234'),
]

operations = [
migrations.AlterField(
model_name='armor',
name='plus_con_mod',
field=models.BooleanField(default=False),
),
migrations.AlterField(
model_name='armor',
name='plus_dex_mod',
field=models.BooleanField(default=False),
),
migrations.AlterField(
model_name='armor',
name='plus_flat_mod',
field=models.IntegerField(default=False),
),
migrations.AlterField(
model_name='armor',
name='plus_max',
field=models.IntegerField(default=0),
),
migrations.AlterField(
model_name='armor',
name='plus_wis_mod',
field=models.BooleanField(default=False),
),
migrations.AlterField(
model_name='spell',
name='target_range_sort',
field=models.IntegerField(help_text='Sortable distance ranking to the target. 0 for self, 1 for touch, sight is 9999, unlimited (same plane) is 99990, unlimited any plane is 99999. All other values in feet.', validators=[django.core.validators.MinValueValidator(0)]),
),
]
18 changes: 18 additions & 0 deletions api/migrations/0027_alter_spelllist_spells.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 3.2.18 on 2023-05-11 17:57

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('api', '0026_auto_20230511_1741'),
]

operations = [
migrations.AlterField(
model_name='spelllist',
name='spells',
field=models.ManyToManyField(help_text='The set of spells.', related_name='spell_lists', to='api.Spell'),
),
]
14 changes: 14 additions & 0 deletions api/migrations/0028_merge_20230516_1753.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Generated by Django 3.2.18 on 2023-05-16 17:53

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
('api', '0026_auto_20230513_1436'),
('api', '0027_alter_spelllist_spells'),
]

operations = [
]
1 change: 1 addition & 0 deletions api/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@
from .models import Weapon
from .models import Armor
from .spell import Spell
from .spell import SpellList
from .monster import Monster
from .monster import MonsterSpell
10 changes: 5 additions & 5 deletions api/models/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -329,11 +329,11 @@ class Armor(GameContent):
stealth_disadvantage = models.BooleanField(
'Boolean representing whether wearing the armor results in stealth disadvantage for the wearer.')
base_ac = models.IntegerField()
plus_dex_mod = models.BooleanField(null=True)
plus_con_mod = models.BooleanField(null=True)
plus_wis_mod = models.BooleanField(null=True)
plus_flat_mod = models.IntegerField(null=True) # Build a shield this way.
plus_max = models.IntegerField(null=True)
plus_dex_mod = models.BooleanField(default=False)
plus_con_mod = models.BooleanField(default=False)
plus_wis_mod = models.BooleanField(default=False)
plus_flat_mod = models.IntegerField(default=False) # Build a shield this way.
plus_max = models.IntegerField(default=0)

def ac_string(self):
ac = str(self.base_ac)
Expand Down
24 changes: 24 additions & 0 deletions api/models/spell.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,3 +223,27 @@ def import_from_json_v1(self, json):
def plural_str() -> str:
"""Return a string specifying the plural name of this model."""
return "Spells"

class SpellList(GameContent):
""" A list of spells to be referenced by classes and subclasses"""

spells = models.ManyToManyField(Spell,
related_name="spell_lists",
help_text='The set of spells.')

def import_from_json_v1(self, json):
"""Log to import a single object from a standard json structure."""
self.name = json["name"]
self.slug = slugify(json["name"])
if "desc" in json:
self.desc = json["desc"]

for spell_slug in json["spell_list"]:
#spell_obj = Spell.objects.filter(slug=spell_slug)
self.spells.add(spell_slug)


@staticmethod
def plural_str() -> str:
"""Return a string specifying the plural name of this model."""
return "SpellLists"
26 changes: 23 additions & 3 deletions api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ class Meta:
'document__url'
)

class SpellSerializer(DynamicFieldsModelSerializer, serializers.HyperlinkedModelSerializer):
class SpellSerializer(DynamicFieldsModelSerializer):

ritual = serializers.CharField(source='v1_ritual')
level_int = serializers.IntegerField(source='spell_level')
Expand Down Expand Up @@ -152,6 +152,7 @@ class Meta:
'spell_level',
'school',
'dnd_class',
'spell_lists',
'archetype',
'circles',
'document__slug',
Expand All @@ -160,6 +161,21 @@ class Meta:
'document__url'
)

class SpellListSerializer(DynamicFieldsModelSerializer):
#spells = SpellSerializer(many=True, read_only=True, context={'request': ''}) #Passing a blank request.
class Meta:
model = models.SpellList
fields = (
'slug',
'name',
'desc',
'spells',
'document__slug',
'document__title',
'document__license_url',
'document__url'
)

class BackgroundSerializer(DynamicFieldsModelSerializer, serializers.HyperlinkedModelSerializer):
class Meta:
model = models.Background
Expand Down Expand Up @@ -239,7 +255,6 @@ class Meta:
'document__url'
)


class RaceSerializer(DynamicFieldsModelSerializer, serializers.HyperlinkedModelSerializer):
subraces = SubraceSerializer(many=True,read_only=True)
class Meta:
Expand Down Expand Up @@ -348,13 +363,18 @@ class Meta:
'document__title',
'document__license_url',
'document__url',
'base_ac',
'plus_dex_mod',
'plus_con_mod',
'plus_wis_mod',
'plus_flat_mod',
'plus_max',
'ac_string',
'strength_requirement',
'cost',
'weight',
'stealth_disadvantage')


class AggregateSerializer(HighlighterMixin, HaystackSerializer):

class Meta:
Expand Down
107 changes: 107 additions & 0 deletions api/tests/test_imports.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
from api.management.commands.importer import ImportSpec
from api.management.commands.importer import ImportOptions

from django.template.defaultfilters import slugify

from api.models import Subrace

# Create your tests here.
Expand Down Expand Up @@ -39,6 +41,7 @@ def test_get_root_list(self):

self.assertContains(response, 'manifest', count=2)
self.assertContains(response, 'spells', count=2)
self.assertContains(response, 'spelllist', count=2)
self.assertContains(response, 'monsters', count=2)
self.assertContains(response, 'documents', count=2)
self.assertContains(response, 'backgrounds', count=2)
Expand Down Expand Up @@ -226,6 +229,110 @@ def test_get_spell_data(self):
f'Mismatched value of unequal field: {field_names}')


class SpellListTestCase(APITestCase):
"""Testing for the spell list API endpoint."""

def setUp(self):
"""Create the spell endpoint test data."""

self.test_document_json = """
{
"title": "Test Reference Document",
"slug": "test-doc",
"desc": "This is a test document",
"license": "Open Gaming License",
"author": "John Doe",
"organization": "Open5e Test Org",
"version": "9.9",
"copyright": "",
"url": "http://example.com"
}
"""
self.test_spell_json = """
{
"name": "Magic Missile",
"desc": "You create three glowing darts of magical force. Each dart hits a creature of your choice that you can see within range. A dart deals 1d4 + 1 force damage to its target. The darts all strike simultaneously, and you can direct them to hit one creature or several.",
"higher_level": "When you cast this spell using a spell slot of 2nd level or higher, the spell creates one more dart for each slot level above 1st.",
"page": "phb 257",
"range": "120 feet",
"components": "V, S",
"ritual": "no",
"duration": "Instantaneous",
"concentration": "no",
"casting_time": "1 action",
"level": "1st-level",
"level_int": 1,
"school": "Evocation",
"class": "Sorcerer, Wizard"
}
"""

self.test_spell_list_json = """
{
"name":"wizard",
"spell_list":[
"magic-missile"
]
}
"""

i = Importer(ImportOptions(update=True, append=False, testrun=False))
i.import_document(
json.loads(
self.test_document_json),
ImportSpec(
"test_filename",
"test_model_class",
"import_spell"))
i.import_spell(
json.loads(
self.test_spell_json),
ImportSpec(
"test_filename",
"test_model_class",
"import_spell"))
i.import_spell_list(
json.loads(
self.test_spell_list_json),
ImportSpec(
"test_filename",
"test_model_class",
"import_spell_list")
)

def test_get_spell_lists(self):
"""Confirm that the list result has the proper elements."""
response = self.client.get(f'/spelllist/?format=json')
self.assertContains(response, 'count', count=1)
self.assertContains(response, 'next', count=1)
self.assertContains(response, 'previous', count=1)
self.assertContains(response, 'results', count=1)

def test_get_spell_list_data(self):
"""Confirm that the result itself has the proper formatting and values."""
import json
response = self.client.get(f'/spelllist/?format=json')
in_spell_list = json.loads(self.test_spell_list_json)
out_spell_list = response.json()['results'][0]
equal_fields = [
'name']
for field_name in equal_fields:
self.assertEqual(
in_spell_list[field_name],
out_spell_list[field_name],
f'Mismatched value of: {field_name}')

def test_get_spell_list_contents(self):
"""Make sure that the response data is the same as the original spell data."""
import json
response = self.client.get(f'/spelllist/?format=json')
in_spell_list = json.loads(self.test_spell_list_json)
in_spell = json.loads(self.test_spell_json)
out_spell_list = response.json()['results'][0]

self.assertEqual(slugify(in_spell['name']), out_spell_list['spells'][0])


class MonstersTestCase(APITestCase):
"""Test case for the monster API endpoint."""

Expand Down
Loading