From 164a3c3bf5d5922103aefd5e1dea3366ef6a8783 Mon Sep 17 00:00:00 2001 From: August Johnson Date: Sun, 1 Dec 2024 12:12:26 -0600 Subject: [PATCH] Lots of changes to support new table method. --- .../0019_classfeatureitem_column.py | 19 +++++ ...remove_classfeatureitem_column_and_more.py | 22 ++++++ api_v2/models/characterclass.py | 71 ++++++++++--------- api_v2/serializers/characterclass.py | 11 ++- 4 files changed, 87 insertions(+), 36 deletions(-) create mode 100644 api_v2/migrations/0019_classfeatureitem_column.py create mode 100644 api_v2/migrations/0020_remove_classfeatureitem_column_and_more.py diff --git a/api_v2/migrations/0019_classfeatureitem_column.py b/api_v2/migrations/0019_classfeatureitem_column.py new file mode 100644 index 00000000..680ff01d --- /dev/null +++ b/api_v2/migrations/0019_classfeatureitem_column.py @@ -0,0 +1,19 @@ +# Generated by Django 5.1.2 on 2024-12-01 15:06 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api_v2', '0018_characterclass_caster_type'), + ] + + operations = [ + migrations.AddField( + model_name='classfeatureitem', + name='column', + field=models.BooleanField(default=False, help_text='Whether or not the field should be displayed as a column.'), + preserve_default=False, + ), + ] diff --git a/api_v2/migrations/0020_remove_classfeatureitem_column_and_more.py b/api_v2/migrations/0020_remove_classfeatureitem_column_and_more.py new file mode 100644 index 00000000..0fa58a7f --- /dev/null +++ b/api_v2/migrations/0020_remove_classfeatureitem_column_and_more.py @@ -0,0 +1,22 @@ +# Generated by Django 5.1.2 on 2024-12-01 15:11 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api_v2', '0019_classfeatureitem_column'), + ] + + operations = [ + migrations.RemoveField( + model_name='classfeatureitem', + name='column', + ), + migrations.AddField( + model_name='classfeatureitem', + name='column_value', + field=models.CharField(blank=True, help_text='The value that should be displayed in the table column (where applicable).', max_length=20, null=True), + ), + ] diff --git a/api_v2/models/characterclass.py b/api_v2/models/characterclass.py index 5180a952..f285acd6 100644 --- a/api_v2/models/characterclass.py +++ b/api_v2/models/characterclass.py @@ -16,7 +16,7 @@ class ClassFeatureItem(models.Model): """This is the class for an individual class feature item, a subset of a class - feature. The name field is unused.""" + feature.""" key = key_field() @@ -26,6 +26,20 @@ class ClassFeatureItem(models.Model): parent = models.ForeignKey('ClassFeature', on_delete=models.CASCADE) level = models.IntegerField(validators=[MinValueValidator(0),MaxValueValidator(20)]) + column_value = models.CharField( + # The value displayed in a column, or null if no value. + null=True, + blank=True, + max_length=20, + help_text='The value that should be displayed in the table column (where applicable).' + ) + + @property + def column(self): + # Represents whether or not this should be displaye as it's own column. + return self.column_value is not None + + def __str__(self): return "{} {} ({})".format( self.parent.parent.name, @@ -39,6 +53,12 @@ class ClassFeature(HasName, HasDescription, FromDocument): parent = models.ForeignKey('CharacterClass', on_delete=models.CASCADE) + + def featureitem_data(self): + return self.classfeatureitem_set + + def column(self): + return len(self.classfeatureitem_set.exclude(column_value=None))>0 def __str__(self): return "{} ({})".format(self.name,self.parent.name) @@ -46,12 +66,13 @@ def __str__(self): class CharacterClass(HasName, FromDocument): """The model for a character class or subclass.""" + subclass_of = models.ForeignKey('self', default=None, blank=True, null=True, on_delete=models.CASCADE) - + hit_dice = models.CharField( max_length=100, default=None, @@ -60,11 +81,18 @@ class CharacterClass(HasName, FromDocument): choices=DIE_TYPES, help_text='Dice notation hit dice option.') - saving_throws = models.ManyToManyField(Ability, related_name="characterclass_saving_throws", help_text='Saving throw proficiencies for this class.') + caster_type = models.CharField( + max_length=100, + default=None, + blank=True, + null=True, + choices=CASTER_TYPES, + help_text='Type of caster. Options are full, half, none.') + @property @extend_schema_field(inline_serializer( name="hit_points", @@ -95,17 +123,6 @@ def is_subclass(self): """Returns whether the object is a subclass.""" return self.subclass_of is not None - caster_type = models.CharField( - max_length=100, - default=None, - blank=True, - null=True, - choices=CASTER_TYPES, - help_text='Type of caster. Options are full, half, none.' - ) - - - @property def features(self): """Returns the set of features that are related to this class.""" @@ -123,23 +140,9 @@ def features(self): } ) )) - def levels(self): - """Returns an array of level information for the given class.""" - by_level = {} - - for classfeature in self.classfeature_set.all(): - for fl in classfeature.classfeatureitem_set.all(): - if (str(fl.level)) not in by_level.keys(): - by_level[str(fl.level)] = {} - by_level[str(fl.level)]['features'] = [] - - by_level[str(fl.level)]['features'].append(fl.parent.key) - by_level[str(fl.level)]['proficiency-bonus'] = self.proficiency_bonus(player_level=fl.level) - by_level[str(fl.level)]['level'] = fl.level - - return by_level - - def get_slots_by_player_level(self,level=1,full=True): + + + def get_slots_by_player_level(self,level,caster_type): # full is for a full caster, not including cantrips. # full=False is for a half caster. if level<0: # Invalid player level. @@ -193,10 +196,12 @@ def get_slots_by_player_level(self,level=1,full=True): [0,4,3,3,3,2] ] - if full: + if caster_type=='FULL': return full[level] - else: + if caster_type=='HALF': return half[level] + else: + return [] def proficiency_bonus(self, player_level): # Consider as part of enums diff --git a/api_v2/serializers/characterclass.py b/api_v2/serializers/characterclass.py index 40ff7897..83b60588 100644 --- a/api_v2/serializers/characterclass.py +++ b/api_v2/serializers/characterclass.py @@ -7,23 +7,28 @@ from .abstracts import GameContentSerializer from .document import DocumentSerializer + class ClassFeatureItemSerializer(GameContentSerializer): class Meta: model = models.ClassFeatureItem - fields = ['name','desc','type'] + fields = ['level','column_value'] + class ClassFeatureSerializer(GameContentSerializer): key = serializers.ReadOnlyField() + column = serializers.ReadOnlyField() + featureitem_data = ClassFeatureItemSerializer( + many=True, context={'request': {}} + ) class Meta: model = models.ClassFeature - fields = ['key', 'name', 'desc'] + fields = ['key', 'name', 'desc','column','featureitem_data'] class CharacterClassSerializer(GameContentSerializer): key = serializers.ReadOnlyField() features = ClassFeatureSerializer( many=True, context={'request': {}}) - levels = serializers.ReadOnlyField() hit_points = serializers.ReadOnlyField() document = DocumentSerializer()