674 lines
24 KiB
Python
674 lines
24 KiB
Python
from django.db import models
|
|
|
|
import uuid
|
|
from django.contrib.auth.models import AbstractUser
|
|
|
|
class CustomUser(AbstractUser):
|
|
"""
|
|
Custom user model that extends AbstractUser, adding a UUID field
|
|
for unique identification and linking to other tables.
|
|
"""
|
|
uuid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
|
|
|
|
def __str__(self):
|
|
return self.username
|
|
|
|
|
|
|
|
def check_requirements(character, requirements):
|
|
for requirement in requirements:
|
|
char_prop = getattr(character, requirement['property'])
|
|
#print(char_prop, requirement['value'], requirement['condition'])
|
|
if requirement['condition'] == "==":
|
|
#print("==")
|
|
if requirement['value'] != char_prop:
|
|
return False
|
|
if requirement['condition'] == "!=":
|
|
#print("!=")
|
|
if requirement['value'] == char_prop:
|
|
return False
|
|
if requirement['condition'] == "<=":
|
|
#print("<=")
|
|
if requirement['value'] < char_prop:
|
|
return False
|
|
if requirement['condition'] == ">=":
|
|
#print(">=")
|
|
if requirement['value'] > char_prop:
|
|
return False
|
|
return True
|
|
|
|
def get_operations_for_hp_max(character, attr):
|
|
ops = []
|
|
for feature in character.hp_features:
|
|
if check_requirements(character, feature['feature_requirements']):
|
|
feature_ops = feature['feature_data'].get('operations', [])
|
|
for op in feature_ops:
|
|
if op['attr'] == attr:
|
|
ops.append(op)
|
|
return ops
|
|
|
|
def get_operations_for_attr(character, attr):
|
|
ops = []
|
|
for feature in character.features.all():
|
|
feature_ops = feature.feature_data.get('operations', [])
|
|
if check_requirements(character, feature.feature_requirements):
|
|
for op in feature_ops:
|
|
if op['attr'] == attr:
|
|
ops.append(op)
|
|
|
|
return ops
|
|
|
|
def apply_operations(base, operations, character):
|
|
"""Apply a list of operations (add, multiply, set, etc) in order."""
|
|
value = base
|
|
for op in operations:
|
|
if op['operation'] == 'add':
|
|
if isinstance(op['value'], int):
|
|
value += op['value']
|
|
if isinstance(op['value'], str):
|
|
value += getattr(character, op['value'])
|
|
elif op['operation'] == 'subtract':
|
|
if isinstance(op['value'], int):
|
|
value -= op['value']
|
|
if isinstance(op['value'], str):
|
|
value -= getattr(character, op['value'])
|
|
elif op['operation'] == 'multiply':
|
|
if isinstance(op['value'], int):
|
|
value *= op['value']
|
|
if isinstance(op['value'], str):
|
|
value *= getattr(character, op['value'])
|
|
elif op['operation'] == 'divide':
|
|
if isinstance(op['value'], int):
|
|
value /= op['value']
|
|
if isinstance(op['value'], str):
|
|
value /= getattr(character, op['value'])
|
|
elif op['operation'] == 'set':
|
|
if isinstance(op['value'], int):
|
|
value = op['value']
|
|
if isinstance(op['value'], str):
|
|
value = getattr(character, op['value'])
|
|
return value
|
|
|
|
|
|
# Character model based on character_template.json
|
|
class Character(models.Model):
|
|
objects = models.Manager()
|
|
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
|
|
owner = models.ForeignKey('CustomUser', on_delete=models.CASCADE, related_name='characters')
|
|
name = models.CharField(max_length=100)
|
|
level = models.IntegerField(default=0)
|
|
race = models.ForeignKey('Package', related_name='race_characters', null=True, blank=True, on_delete=models.SET_NULL)
|
|
subrace = models.ForeignKey('Package', related_name='subrace_characters', null=True, blank=True, on_delete=models.SET_NULL)
|
|
char_class = models.ForeignKey('Package', related_name='class_characters', null=True, blank=True, on_delete=models.SET_NULL)
|
|
subclass = models.ForeignKey('Package', related_name='subclass_characters', null=True, blank=True, on_delete=models.SET_NULL)
|
|
background = models.ForeignKey('Package', related_name='background_characters', null=True, blank=True, on_delete=models.SET_NULL)
|
|
alignment = models.CharField(max_length=50, blank=True)
|
|
size = models.CharField(max_length=20, blank=True)
|
|
age = models.IntegerField(default=0)
|
|
gender = models.CharField(max_length=30, blank=True)
|
|
height = models.CharField(max_length=20, blank=True)
|
|
weight = models.IntegerField(default=0)
|
|
deity = models.CharField(max_length=100, blank=True)
|
|
|
|
# Relationships to packages and features
|
|
packages = models.ManyToManyField('Package', blank=True, related_name='characters')
|
|
features = models.ManyToManyField('Feature', blank=True, related_name='characters')
|
|
|
|
# Base and current stats
|
|
strength_base = models.IntegerField(default=0)
|
|
dexterity_base = models.IntegerField(default=0)
|
|
constitution_base = models.IntegerField(default=0)
|
|
intelligence_base = models.IntegerField(default=0)
|
|
wisdom_base = models.IntegerField(default=0)
|
|
charisma_base = models.IntegerField(default=0)
|
|
armor_base = models.IntegerField(default=0)
|
|
|
|
inspiration = models.BooleanField(default=False)
|
|
experience = models.IntegerField(default=0)
|
|
|
|
# Proficiencies and languages
|
|
proficiencies_armor = models.JSONField(default=list, blank=True)
|
|
proficiencies_weapons = models.JSONField(default=list, blank=True)
|
|
proficiencies_tools = models.JSONField(default=list, blank=True)
|
|
languages = models.JSONField(default=list, blank=True)
|
|
|
|
# Saving throws
|
|
strength_save_prof = models.BooleanField(default=False)
|
|
dexterity_save_prof = models.BooleanField(default=False)
|
|
constitution_save_prof = models.BooleanField(default=False)
|
|
intelligence_save_prof = models.BooleanField(default=False)
|
|
wisdom_save_prof = models.BooleanField(default=False)
|
|
charisma_save_prof = models.BooleanField(default=False)
|
|
|
|
# HP and combat
|
|
hp = models.IntegerField(default=0)
|
|
#hp_max = models.IntegerField(default=0)
|
|
hp_temp = models.IntegerField(default=0)
|
|
hit_die = models.CharField(max_length=20, blank=True)
|
|
#initiative = models.IntegerField(default=0)
|
|
|
|
# Death saves
|
|
deathsaves_successes = models.IntegerField(default=0)
|
|
deathsaves_failures = models.IntegerField(default=0)
|
|
|
|
# Resistances
|
|
fire_resistance = models.BooleanField(default=False)
|
|
poison_resistance = models.BooleanField(default=False)
|
|
psychic_resistance = models.BooleanField(default=False)
|
|
cold_resistance = models.BooleanField(default=False)
|
|
thunder_resistance = models.BooleanField(default=False)
|
|
acid_resistance = models.BooleanField(default=False)
|
|
force_resistance = models.BooleanField(default=False)
|
|
radiant_resistance = models.BooleanField(default=False)
|
|
necrotic_resistance = models.BooleanField(default=False)
|
|
bludgeoning_resistance = models.BooleanField(default=False)
|
|
piercing_resistance = models.BooleanField(default=False)
|
|
slashing_resistance = models.BooleanField(default=False)
|
|
immunities = models.JSONField(default=list, blank=True)
|
|
vulnerabilities = models.JSONField(default=list, blank=True)
|
|
|
|
# Movement & vision
|
|
speed_base = models.IntegerField(default=0)
|
|
speed_type = models.JSONField(default=list, blank=True)
|
|
darkvision = models.IntegerField(default=0)
|
|
blindsight = models.IntegerField(default=0)
|
|
tremorsense = models.IntegerField(default=0)
|
|
truesight = models.IntegerField(default=0)
|
|
|
|
# Spellcasting
|
|
spellcasting_ability = models.CharField(max_length=50, blank=True)
|
|
spell_save_dc = models.IntegerField(default=0)
|
|
spell_attack_bonus = models.IntegerField(default=0)
|
|
known_spells = models.JSONField(default=list, blank=True)
|
|
prepared_spells = models.JSONField(default=list, blank=True)
|
|
spell_slots = models.JSONField(default=dict, blank=True)
|
|
cantrips = models.JSONField(default=list, blank=True)
|
|
spellcasting_class = models.CharField(max_length=100, blank=True)
|
|
|
|
# Exhaustion, conditions, notes, and attributes
|
|
exhaustion_level = models.IntegerField(default=0)
|
|
conditions_active = models.JSONField(default=list, blank=True)
|
|
cp = models.IntegerField(default=0)
|
|
sp = models.IntegerField(default=0)
|
|
ep = models.IntegerField(default=0)
|
|
gp = models.IntegerField(default=0)
|
|
pp = models.IntegerField(default=0)
|
|
equipment = models.JSONField(default=list, blank=True)
|
|
traits = models.JSONField(default=list, blank=True)
|
|
personality_traits = models.JSONField(default=list, blank=True)
|
|
ideals = models.JSONField(default=list, blank=True)
|
|
bonds = models.JSONField(default=list, blank=True)
|
|
flaws = models.JSONField(default=list, blank=True)
|
|
notes = models.TextField(blank=True)
|
|
hp_features = models.JSONField(default=list, blank=True)
|
|
custom_attributes = models.JSONField(default=dict, blank=True)
|
|
|
|
created_at = models.DateTimeField(auto_now_add=True)
|
|
|
|
def __str__(self):
|
|
return f"{self.name} (Level {self.level})"
|
|
|
|
@classmethod
|
|
def from_db(cls, db, field_names, values):
|
|
instance = super().from_db(db, field_names, values)
|
|
# Here, do your consolidation logic:
|
|
instance.feature_operations = instance.build_feature_operations_dict()
|
|
return instance
|
|
|
|
def build_feature_operations_dict(self):
|
|
feature_dict = {}
|
|
#Build HP
|
|
for feature in self.hp_features:
|
|
feature_requirements = feature['feature_requirements']
|
|
if check_requirements(self, feature_requirements):
|
|
for operation in feature['feature_data'].get('operations', []):
|
|
if 'requirements' in operation:
|
|
if check_requirements(self, operation['requirements']):
|
|
feature_dict.setdefault(operation['attr'], []).append(operation)
|
|
else:
|
|
feature_dict.setdefault(operation['attr'], []).append(operation)
|
|
|
|
#Build features
|
|
for feature in self.features.all():
|
|
feature_requirements = feature.feature_requirements
|
|
if check_requirements(self, feature_requirements):
|
|
for operation in feature.feature_data.get('operations', []):
|
|
if 'requirements' in operation:
|
|
if check_requirements(self, operation['requirements']):
|
|
feature_dict.setdefault(operation['attr'], []).append(operation)
|
|
else:
|
|
feature_dict.setdefault(operation['attr'], []).append(operation)
|
|
|
|
print(self.packages.all())
|
|
|
|
# Build race
|
|
if self.race != None:
|
|
for feature in self.race.features.all():
|
|
feature_requirements = feature.feature_requirements
|
|
if check_requirements(self, feature_requirements):
|
|
for operation in feature.feature_data.get('operations', []):
|
|
if 'requirements' in operation:
|
|
if check_requirements(self, operation['requirements']):
|
|
feature_dict.setdefault(operation['attr'], []).append(operation)
|
|
else:
|
|
feature_dict.setdefault(operation['attr'], []).append(operation)
|
|
|
|
for feature in feature_dict:
|
|
print(feature)
|
|
return feature_dict
|
|
|
|
def stat_total(self, attr):
|
|
base = getattr(self, f"{attr}_base")
|
|
ops = self.feature_operations.get(attr, [])
|
|
return apply_operations(base, ops, self)
|
|
|
|
@property
|
|
def proficiency(self):
|
|
lvl = getattr(self, 'level')
|
|
return 2 + (lvl-1) // 4
|
|
|
|
@property
|
|
def strength(self):
|
|
return self.stat_total('strength')
|
|
|
|
@property
|
|
def strength_modifier(self):
|
|
return (getattr(self, 'strength')-10) // 2
|
|
|
|
@property
|
|
def strength_save(self):
|
|
sav = getattr(self, 'strength_modifier')
|
|
if getattr(self, 'strength_save_prof'):
|
|
return getattr(self, 'proficiency') + sav
|
|
return sav
|
|
|
|
@property
|
|
def dexterity(self):
|
|
return self.stat_total('dexterity')
|
|
|
|
@property
|
|
def dexterity_modifier(self):
|
|
return (getattr(self, 'dexterity')-10) //2
|
|
|
|
@property
|
|
def dexterity_save(self):
|
|
sav = getattr(self, 'dexterity_modifier')
|
|
if getattr(self, 'dexterity_save_prof'):
|
|
return getattr(self, 'proficiency') + sav
|
|
return sav
|
|
|
|
@property
|
|
def constitution(self):
|
|
return self.stat_total('constitution')
|
|
|
|
@property
|
|
def constitution_modifier(self):
|
|
return (getattr(self, 'constitution')-10) //2
|
|
|
|
@property
|
|
def constitution_save(self):
|
|
sav = getattr(self, 'constitution_modifier')
|
|
if getattr(self, 'constitution_save_prof'):
|
|
return getattr(self, 'proficiency') + sav
|
|
return sav
|
|
|
|
@property
|
|
def intelligence(self):
|
|
return self.stat_total('intelligence')
|
|
|
|
@property
|
|
def intelligence_modifier(self):
|
|
return (getattr(self, 'intelligence')-10) //2
|
|
|
|
@property
|
|
def intelligence_save(self):
|
|
sav = getattr(self, 'intelligence_modifier')
|
|
if getattr(self, 'intelligence_save_prof'):
|
|
return getattr(self, 'proficiency') + sav
|
|
return sav
|
|
|
|
@property
|
|
def wisdom(self):
|
|
return self.stat_total('wisdom')
|
|
|
|
@property
|
|
def wisdom_modifier(self):
|
|
return (getattr(self, 'wisdom')-10) //2
|
|
|
|
@property
|
|
def wisdom_save(self):
|
|
sav = getattr(self, 'wisdom_modifier')
|
|
if getattr(self, 'wisdom_save_prof'):
|
|
return getattr(self, 'proficiency') + sav
|
|
return sav
|
|
|
|
@property
|
|
def charisma(self):
|
|
return self.stat_total('charisma')
|
|
|
|
@property
|
|
def charisma_modifier(self):
|
|
return (getattr(self, 'charisma')-10) //2
|
|
|
|
@property
|
|
def charisma_save(self):
|
|
sav = getattr(self, 'charisma_modifier')
|
|
if getattr(self, 'charisma_save_prof'):
|
|
return getattr(self, 'proficiency') + sav
|
|
return sav
|
|
|
|
@property
|
|
def armor_class(self):
|
|
return self.stat_total('armor_class')
|
|
|
|
@property
|
|
def hp_max(self):
|
|
con = getattr(self, 'constitution_modifier')
|
|
level = getattr(self, 'level')
|
|
base = con * level
|
|
ops = self.feature_operations.get('hp_max', [])
|
|
print(ops, base)
|
|
bonus = apply_operations(base, ops, self)
|
|
return bonus
|
|
|
|
@property
|
|
def initiative(self):
|
|
return 0
|
|
|
|
@property
|
|
def athletics(self):
|
|
ops = get_operations_for_attr(self, 'athletics')
|
|
base = getattr(self, 'strength_modifier')
|
|
bonus = apply_operations(0, ops, self)
|
|
return base + bonus + getattr(self, 'proficiency')
|
|
|
|
@property
|
|
def athletics_passive(self):
|
|
return getattr(self, 'athletics') + 10
|
|
|
|
@property
|
|
def acrobatics(self):
|
|
ops = get_operations_for_attr(self, 'acrobatics')
|
|
base = getattr(self, 'dexterity_modifier')
|
|
bonus = apply_operations(0, ops, self)
|
|
return base + bonus
|
|
|
|
@property
|
|
def acrobatics_passive(self):
|
|
return getattr(self, 'acrobatics') + 10
|
|
|
|
@property
|
|
def sleight_of_hand(self):
|
|
ops = get_operations_for_attr(self, 'sleight_of_hand')
|
|
base = getattr(self, 'dexterity_modifier')
|
|
bonus = apply_operations(0, ops, self)
|
|
return base + bonus
|
|
|
|
@property
|
|
def sleight_of_hand_passive(self):
|
|
return getattr(self, 'sleight_of_hand') + 10
|
|
|
|
@property
|
|
def stealth(self):
|
|
ops = get_operations_for_attr(self, 'stealth')
|
|
base = getattr(self, 'dexterity_modifier')
|
|
bonus = apply_operations(0, ops, self)
|
|
return base + bonus
|
|
|
|
@property
|
|
def stealth_passive(self):
|
|
return getattr(self, 'stealth') + 10
|
|
|
|
@property
|
|
def arcana(self):
|
|
ops = get_operations_for_attr(self, 'arcana')
|
|
base = getattr(self, 'intelligence_modifier')
|
|
bonus = apply_operations(0, ops, self)
|
|
return base + bonus
|
|
|
|
@property
|
|
def arcana_passive(self):
|
|
return getattr(self, 'arcana') + 10
|
|
|
|
@property
|
|
def history(self):
|
|
ops = get_operations_for_attr(self, 'history')
|
|
base = getattr(self, 'intelligence_modifier')
|
|
bonus = apply_operations(0, ops, self)
|
|
return base + bonus
|
|
|
|
@property
|
|
def history_passive(self):
|
|
return getattr(self, 'history') + 10
|
|
|
|
@property
|
|
def investigation(self):
|
|
ops = get_operations_for_attr(self, 'investigation')
|
|
base = getattr(self, 'intelligence_modifier')
|
|
bonus = apply_operations(0, ops, self)
|
|
return base + bonus
|
|
|
|
@property
|
|
def investigation_passive(self):
|
|
return getattr(self, 'investigation') + 10
|
|
|
|
@property
|
|
def nature(self):
|
|
ops = get_operations_for_attr(self, 'nature')
|
|
base = getattr(self, 'intelligence_modifier')
|
|
bonus = apply_operations(0, ops, self)
|
|
return base + bonus
|
|
|
|
@property
|
|
def nature_passive(self):
|
|
return getattr(self, 'nature') + 10
|
|
|
|
@property
|
|
def religion(self):
|
|
ops = get_operations_for_attr(self, 'religion')
|
|
base = getattr(self, 'intelligence_modifier')
|
|
bonus = apply_operations(0, ops, self)
|
|
return base + bonus
|
|
|
|
@property
|
|
def religion_passive(self):
|
|
return getattr(self, 'religion') + 10
|
|
|
|
@property
|
|
def animal_handling(self):
|
|
ops = get_operations_for_attr(self, 'animal_handling')
|
|
base = getattr(self, 'wisdom_modifier')
|
|
bonus = apply_operations(0, ops, self)
|
|
return base + bonus
|
|
|
|
@property
|
|
def animal_handling_passive(self):
|
|
return getattr(self, 'animal_handling') + 10
|
|
|
|
@property
|
|
def insight(self):
|
|
ops = get_operations_for_attr(self, 'insight')
|
|
base = getattr(self, 'wisdom_modifier')
|
|
bonus = apply_operations(0, ops, self)
|
|
return base + bonus
|
|
|
|
@property
|
|
def insight_passive(self):
|
|
return getattr(self, 'insight') + 10
|
|
|
|
@property
|
|
def medicine(self):
|
|
ops = get_operations_for_attr(self, 'medicine')
|
|
base = getattr(self, 'wisdom_modifier')
|
|
bonus = apply_operations(0, ops, self)
|
|
return base + bonus
|
|
|
|
@property
|
|
def medicine_passive(self):
|
|
return getattr(self, 'medicine') + 10
|
|
|
|
@property
|
|
def perception(self):
|
|
ops = get_operations_for_attr(self, 'perception')
|
|
base = getattr(self, 'wisdom_modifier')
|
|
bonus = apply_operations(0, ops, self)
|
|
return base + bonus
|
|
|
|
@property
|
|
def perception_passive(self):
|
|
return getattr(self, 'perception') + 10
|
|
|
|
@property
|
|
def survival(self):
|
|
ops = get_operations_for_attr(self, 'survival')
|
|
base = getattr(self, 'wisdom_modifier')
|
|
bonus = apply_operations(0, ops, self)
|
|
return base + bonus
|
|
|
|
@property
|
|
def survival_passive(self):
|
|
return getattr(self, 'survival') + 10
|
|
|
|
@property
|
|
def deception(self):
|
|
ops = get_operations_for_attr(self, 'deception')
|
|
base = getattr(self, 'charisma_modifier')
|
|
bonus = apply_operations(0, ops, self)
|
|
return base + bonus
|
|
|
|
@property
|
|
def deception_passive(self):
|
|
return getattr(self, 'deception') + 10
|
|
|
|
@property
|
|
def intimidation(self):
|
|
ops = get_operations_for_attr(self, 'intimidation')
|
|
base = getattr(self, 'charisma_modifier')
|
|
bonus = apply_operations(0, ops, self)
|
|
return base + bonus
|
|
|
|
@property
|
|
def intimidation_passive(self):
|
|
return getattr(self, 'intimidation') + 10
|
|
|
|
@property
|
|
def performance(self):
|
|
ops = get_operations_for_attr(self, 'performance')
|
|
base = getattr(self, 'charisma_modifier')
|
|
bonus = apply_operations(0, ops, self)
|
|
return base + bonus
|
|
|
|
@property
|
|
def performance_passive(self):
|
|
return getattr(self, 'performance') + 10
|
|
|
|
@property
|
|
def persuasion(self):
|
|
ops = get_operations_for_attr(self, 'persuasion')
|
|
base = getattr(self, 'charisma_modifier')
|
|
bonus = apply_operations(0, ops, self)
|
|
return base + bonus
|
|
|
|
@property
|
|
def persuasion_passive(self):
|
|
return getattr(self, 'persuasion') + 10
|
|
|
|
|
|
# Feature model based on feature_template.json
|
|
class Feature(models.Model):
|
|
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
|
|
feature_name = models.CharField(max_length=200)
|
|
feature_system = models.CharField(max_length=36, blank=True)
|
|
feature_description = models.TextField(blank=True)
|
|
feature_requirements = models.JSONField(default=list, blank=True)
|
|
#feature requirements requires 3 keys, {"property", "value", "condition"}
|
|
feature_data = models.JSONField(default=dict, blank=True)
|
|
#feature_data holds a value of {"operations", "sources"}
|
|
# ----> operations is a list of dictionaries that have to have 6 values
|
|
# ----> {"attr", "value", "operation", "limits", "operation_requirements", "priority"}
|
|
feature_traits = models.ManyToManyField('ObjectTrait', blank=True, related_name='features')
|
|
|
|
def __str__(self):
|
|
return self.feature_name
|
|
|
|
def to_json(self):
|
|
return {
|
|
'id': str(self.id),
|
|
'feature_name': self.feature_name,
|
|
'feature_description': self.feature_description
|
|
}
|
|
|
|
# Package <-> Feature through model for priorities
|
|
class PackageFeature(models.Model):
|
|
package = models.ForeignKey('Package', on_delete=models.CASCADE)
|
|
feature = models.ForeignKey('Feature', on_delete=models.CASCADE)
|
|
priority = models.IntegerField(default=0)
|
|
requirements_override = models.JSONField(blank=True, null=True)
|
|
|
|
class Meta:
|
|
unique_together = ('package', 'feature')
|
|
ordering = ['priority']
|
|
|
|
def __str__(self):
|
|
return f"{self.package} - {self.feature} (priority {self.priority})"
|
|
|
|
# Package model based on package_template.json
|
|
class Package(models.Model):
|
|
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
|
|
package_name = models.CharField(max_length=200)
|
|
package_system = models.CharField(max_length=36, blank=True)
|
|
package_description = models.TextField(blank=True)
|
|
package_type = models.CharField(max_length=100, blank=True)
|
|
package_doc_md = models.TextField(blank=True)
|
|
package_requirements = models.JSONField(default=list, blank=True)
|
|
#feature requirements requires 3 keys, {"property", "value", "condition"}
|
|
package_operations = models.JSONField(default=list, blank=True)
|
|
#feature_data holds a value of {"operations", "sources"}
|
|
# ----> operations is a list of dictionaries that have to have 6 values
|
|
# ----> {"attr", "value", "operation", "limits", "operation_requirements", "priority"}
|
|
features = models.ManyToManyField('Feature', through='PackageFeature', blank=True, related_name='packages')
|
|
package_traits = models.ManyToManyField('ObjectTrait', blank=True, related_name='packages')
|
|
|
|
def __str__(self):
|
|
return self.package_name
|
|
|
|
|
|
class Asset(models.Model):
|
|
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
|
|
asset_name = models.CharField(max_length=256)
|
|
asset_description = models.TextField(blank=True)
|
|
asset_doc_md = models.TextField(blank=True)
|
|
asset_system = models.CharField(max_length=32, blank=True)
|
|
asset_requirements = models.JSONField(default=list, blank=True)
|
|
|
|
class ObjectTrait(models.Model):
|
|
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
|
|
trait_name = models.CharField(max_length=64)
|
|
trait_description = models.TextField(blank=True)
|
|
trait_system = models.CharField(max_length=32, blank=True)
|
|
|
|
def __str__(self):
|
|
return self.trait_name
|
|
|
|
def to_json(self):
|
|
return {
|
|
'id': str(self.id),
|
|
'trait_name': self.trait_name
|
|
}
|
|
|
|
class Pin(models.Model):
|
|
label = models.CharField(max_length=100)
|
|
url = models.URLField(max_length=300)
|
|
x = models.FloatField(help_text="X position as percentage (0-100)")
|
|
y = models.FloatField(help_text="Y position as percentage (0-100)")
|
|
pin_type = models.CharField(max_length=100, default="general")
|
|
def as_dict(self):
|
|
return {
|
|
"label": self.label,
|
|
"url": self.url,
|
|
"x": self.x,
|
|
"y": self.y,
|
|
"pin_type": self.pin_type,
|
|
}
|
|
def __str__(self):
|
|
return f"{self.label} ({self.x:.2f}%, {self.y:.2f}%)" |