179 lines
No EOL
6 KiB
Python
179 lines
No EOL
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)
|
|
|
|
# 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) |