MosswartOverlord/inventory-service/database.py

263 lines
No EOL
9.6 KiB
Python

"""
Database models and setup for Inventory Service.
Normalized schema designed around actual item JSON structure with proper enum translation.
"""
import sqlalchemy as sa
from sqlalchemy import (
Column, Integer, String, Float, Boolean, DateTime, BigInteger, Text, JSON,
ForeignKey, Index, UniqueConstraint, CheckConstraint, create_engine, MetaData
)
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.dialects.postgresql import JSONB
from sqlalchemy.orm import relationship
from datetime import datetime
Base = declarative_base()
class Item(Base):
"""Core item identification and basic properties."""
__tablename__ = 'items'
id = Column(Integer, primary_key=True)
character_name = Column(String(50), nullable=False, index=True)
item_id = Column(BigInteger, nullable=False) # Game item instance ID
timestamp = Column(DateTime, nullable=False, default=datetime.utcnow)
# Basic properties (always present)
name = Column(String(200), nullable=False, index=True)
icon = Column(Integer, nullable=False)
object_class = Column(Integer, nullable=False, index=True)
# Core stats
value = Column(Integer, default=0)
burden = Column(Integer, default=0)
# Equipment status
current_wielded_location = Column(Integer, default=0, index=True) # 0 = not equipped
# Item state
bonded = Column(Integer, default=0) # 0=Normal, 1=Bonded, 2=Sticky, 4=Destroy on drop
attuned = Column(Integer, default=0) # 0=Normal, 1=Attuned
unique = Column(Boolean, default=False)
# Stack/Container properties
stack_size = Column(Integer, default=1)
max_stack_size = Column(Integer, default=1)
items_capacity = Column(Integer) # For containers
containers_capacity = Column(Integer) # For containers
# Durability
structure = Column(Integer) # Current durability
max_structure = Column(Integer) # Maximum durability
# Special item flags
rare_id = Column(Integer) # For rare items
lifespan = Column(Integer) # Item decay timer
remaining_lifespan = Column(Integer) # Remaining decay time
# Metadata flags
has_id_data = Column(Boolean, default=False)
last_id_time = Column(BigInteger, default=0)
# Unique constraint per character
__table_args__ = (
UniqueConstraint('character_name', 'item_id', name='uq_char_item'),
Index('ix_items_name_char', 'character_name', 'name'),
Index('ix_items_class_char', 'character_name', 'object_class'),
)
class ItemCombatStats(Base):
"""Combat-related properties for weapons and armor."""
__tablename__ = 'item_combat_stats'
item_id = Column(Integer, ForeignKey('items.id'), primary_key=True)
# Damage properties
max_damage = Column(Integer)
damage = Column(Integer)
damage_type = Column(Integer)
damage_bonus = Column(Float)
elemental_damage_bonus = Column(Integer)
elemental_damage_vs_monsters = Column(Float)
variance = Column(Float)
# Advanced damage properties
cleaving = Column(Integer) # Cleaving damage
crit_damage_rating = Column(Integer) # Critical damage multiplier
damage_over_time = Column(Integer) # DoT damage
# Attack properties
attack_bonus = Column(Float)
weapon_time = Column(Integer)
weapon_skill = Column(Integer)
# Defense properties
armor_level = Column(Integer, index=True)
shield_value = Column(Integer)
melee_defense_bonus = Column(Float)
missile_defense_bonus = Column(Float)
magic_defense_bonus = Column(Float)
# Resistances
resist_magic = Column(Integer)
crit_resist_rating = Column(Integer)
crit_damage_resist_rating = Column(Integer)
dot_resist_rating = Column(Integer)
life_resist_rating = Column(Integer)
nether_resist_rating = Column(Integer)
# Healing/Recovery
heal_over_time = Column(Integer)
healing_resist_rating = Column(Integer)
# Mana properties
mana_conversion_bonus = Column(Float)
# PvP properties
pk_damage_rating = Column(Integer)
pk_damage_resist_rating = Column(Integer)
gear_pk_damage_rating = Column(Integer)
gear_pk_damage_resist_rating = Column(Integer)
class ItemRequirements(Base):
"""Wield requirements and skill prerequisites."""
__tablename__ = 'item_requirements'
item_id = Column(Integer, ForeignKey('items.id'), primary_key=True)
wield_level = Column(Integer, index=True)
wield_requirement = Column(Integer)
skill_level = Column(Integer)
lore_requirement = Column(Integer)
# String-based requirements (parsed from StringValues)
equip_skill = Column(String(50))
mastery = Column(String(50))
class ItemEnhancements(Base):
"""Enhancements, materials, and modifications."""
__tablename__ = 'item_enhancements'
item_id = Column(Integer, ForeignKey('items.id'), primary_key=True)
material = Column(String(50), index=True)
imbue = Column(String(50))
tinks = Column(Integer)
workmanship = Column(Float)
salvage_workmanship = Column(Float)
# Advanced tinkering
num_times_tinkered = Column(Integer, default=0)
free_tinkers_bitfield = Column(Integer) # Which tinkers are free
num_items_in_material = Column(Integer) # Salvage yield
# Additional imbue effects
imbue_attempts = Column(Integer, default=0)
imbue_successes = Column(Integer, default=0)
imbued_effect2 = Column(Integer)
imbued_effect3 = Column(Integer)
imbued_effect4 = Column(Integer)
imbued_effect5 = Column(Integer)
imbue_stacking_bits = Column(Integer) # Which imbues stack
# Set information
item_set = Column(String(100), index=True)
equipment_set_extra = Column(Integer) # Additional set bonuses
# Special properties
aetheria_bitfield = Column(Integer) # Aetheria slot properties
heritage_specific_armor = Column(Integer) # Heritage armor type
# Cooldowns
shared_cooldown = Column(Integer) # Cooldown group ID
class ItemRatings(Base):
"""Modern rating system properties."""
__tablename__ = 'item_ratings'
item_id = Column(Integer, ForeignKey('items.id'), primary_key=True)
# Damage ratings
damage_rating = Column(Integer)
damage_resist_rating = Column(Integer)
crit_rating = Column(Integer)
crit_resist_rating = Column(Integer)
crit_damage_rating = Column(Integer)
crit_damage_resist_rating = Column(Integer)
# Utility ratings
heal_boost_rating = Column(Integer)
vitality_rating = Column(Integer)
mana_conversion_rating = Column(Integer)
weakness_rating = Column(Integer) # Weakness debuff strength
nether_over_time = Column(Integer) # Nether DoT
# Gear totals
gear_damage = Column(Integer) # Total gear damage
gear_damage_resist = Column(Integer) # Total gear damage resist
gear_crit = Column(Integer) # Total gear crit
gear_crit_resist = Column(Integer) # Total gear crit resist
gear_crit_damage = Column(Integer) # Total gear crit damage
gear_crit_damage_resist = Column(Integer) # Total gear crit damage resist
gear_healing_boost = Column(Integer) # Total gear healing boost
gear_max_health = Column(Integer) # Total gear max health
gear_nether_resist = Column(Integer) # Total gear nether resist
gear_life_resist = Column(Integer) # Total gear life resist
gear_overpower = Column(Integer) # Total gear overpower
gear_overpower_resist = Column(Integer) # Total gear overpower resist
# Calculated totals
total_rating = Column(Integer)
class ItemSpells(Base):
"""Spell information for items."""
__tablename__ = 'item_spells'
item_id = Column(Integer, ForeignKey('items.id'), primary_key=True)
spell_id = Column(Integer, primary_key=True)
is_active = Column(Boolean, default=False)
class ItemRawData(Base):
"""Preserve original raw enum data for completeness."""
__tablename__ = 'item_raw_data'
item_id = Column(Integer, ForeignKey('items.id'), primary_key=True)
# Raw enum collections
int_values = Column(JSONB)
double_values = Column(JSONB)
string_values = Column(JSONB)
bool_values = Column(JSONB)
# Original full JSON for reference
original_json = Column(JSONB)
# Database URL configuration
import os
DATABASE_URL = os.getenv("DATABASE_URL", "postgresql://inventory_user:inventory_pass@inventory-db:5432/inventory_db")
# Create all indexes for performance
def create_indexes(engine):
"""Create additional performance indexes."""
# Item search indexes
sa.Index('ix_items_search', Item.character_name, Item.name, Item.object_class).create(engine, checkfirst=True)
# Equipment status index for filtering equipped items
sa.Index('ix_items_equipped', Item.character_name, Item.current_wielded_location).create(engine, checkfirst=True)
# Combat stats indexes for filtering
sa.Index('ix_combat_damage', ItemCombatStats.max_damage).create(engine, checkfirst=True)
sa.Index('ix_combat_armor', ItemCombatStats.armor_level).create(engine, checkfirst=True)
# Requirements indexes for filtering
sa.Index('ix_req_level', ItemRequirements.wield_level).create(engine, checkfirst=True)
# Enhancement indexes
sa.Index('ix_enh_material_set', ItemEnhancements.material, ItemEnhancements.item_set).create(engine, checkfirst=True)
# JSONB indexes for raw data querying
sa.Index('ix_raw_int_gin', ItemRawData.int_values, postgresql_using='gin').create(engine, checkfirst=True)
sa.Index('ix_raw_original_gin', ItemRawData.original_json, postgresql_using='gin').create(engine, checkfirst=True)