""" 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)