""" 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) # 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) # 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) # Mana properties mana_conversion_bonus = Column(Float) 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) # Set information item_set = Column(String(100), index=True) 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) # 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) # 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)