X-Git-Url: http://git.veekun.com/zzz-pokedex.git/blobdiff_plain/203ce7da363fac395f6f40d6478541fe8a2ef937..1d07bbc3950b1d59094d22dc75973ef9a24592a8:/pokedex/db/tables.py diff --git a/pokedex/db/tables.py b/pokedex/db/tables.py index 88b206a..89a0efa 100644 --- a/pokedex/db/tables.py +++ b/pokedex/db/tables.py @@ -25,7 +25,9 @@ from sqlalchemy.ext.declarative import ( declarative_base, declared_attr, DeclarativeMeta, ) from sqlalchemy.ext.associationproxy import association_proxy -from sqlalchemy.orm import backref, eagerload_all, relation, class_mapper +from sqlalchemy.orm import ( + backref, eagerload_all, relation, class_mapper, synonym, mapper, + ) from sqlalchemy.orm.session import Session from sqlalchemy.orm.collections import attribute_mapped_collection from sqlalchemy.sql import and_ @@ -713,19 +715,32 @@ class MoveFlavorText(TableBase, LanguageSpecific): class MoveMeta(TableBase): u"""Metadata for move effects, sorta-kinda ripped straight from the game""" __tablename__ = 'move_meta' - move_id = Column(Integer, ForeignKey('moves.id'), primary_key=True, nullable=False, autoincrement=False) - meta_category_id = Column(Integer, ForeignKey('move_meta_categories.id'), nullable=False) - meta_ailment_id = Column(Integer, ForeignKey('move_meta_ailments.id'), nullable=False) - min_hits = Column(Integer, nullable=True, index=True) - max_hits = Column(Integer, nullable=True, index=True) - min_turns = Column(Integer, nullable=True, index=True) - max_turns = Column(Integer, nullable=True, index=True) - recoil = Column(Integer, nullable=False, index=True) - healing = Column(Integer, nullable=False, index=True) - crit_rate = Column(Integer, nullable=False, index=True) - ailment_chance = Column(Integer, nullable=False, index=True) - flinch_chance = Column(Integer, nullable=False, index=True) - stat_chance = Column(Integer, nullable=False, index=True) + move_id = Column(Integer, ForeignKey('moves.id'), primary_key=True, nullable=False, autoincrement=False, + info=dict(description="A numeric ID")) + meta_category_id = Column(Integer, ForeignKey('move_meta_categories.id'), nullable=False, + info=dict(description="ID of the move category")) + meta_ailment_id = Column(Integer, ForeignKey('move_meta_ailments.id'), nullable=False, + info=dict(description="ID of the caused ailment")) + min_hits = Column(Integer, nullable=True, index=True, + info=dict(description="Minimum number of hits per use")) + max_hits = Column(Integer, nullable=True, index=True, + info=dict(description="Maximum number of hits per use")) + min_turns = Column(Integer, nullable=True, index=True, + info=dict(description="Minimum number of turns the user is forced to use the move")) + max_turns = Column(Integer, nullable=True, index=True, + info=dict(description="Maximum number of turns the user is forced to use the move")) + recoil = Column(Integer, nullable=False, index=True, + info=dict(description="Recoil damage, in percent of damage done")) + healing = Column(Integer, nullable=False, index=True, + info=dict(description="Healing, in percent of user's max HP")) + crit_rate = Column(Integer, nullable=False, index=True, + info=dict(description="Critical hit rate bonus")) + ailment_chance = Column(Integer, nullable=False, index=True, + info=dict(description="Chance to cause an ailment, in percent")) + flinch_chance = Column(Integer, nullable=False, index=True, + info=dict(description="Chance to cause flinching, in percent")) + stat_chance = Column(Integer, nullable=False, index=True, + info=dict(description="Chance to cause a stat change, in percent")) class MoveMetaAilment(TableBase, OfficiallyNamed): u"""Common status ailments moves can inflict on a single Pokémon, including @@ -733,22 +748,29 @@ class MoveMetaAilment(TableBase, OfficiallyNamed): """ __tablename__ = 'move_meta_ailments' __singlename__ = 'move_meta_ailment' - id = Column(Integer, primary_key=True, nullable=False) - identifier = Column(Unicode(24), nullable=False) + id = Column(Integer, primary_key=True, nullable=False, + info=dict(description="A numeric ID")) + identifier = Column(Unicode(24), nullable=False, + info=dict(description="An identifier", format='identifier')) class MoveMetaCategory(TableBase): u"""Very general categories that loosely group move effects.""" __tablename__ = 'move_meta_categories' __singlename__ = 'move_meta_category' - id = Column(Integer, primary_key=True, nullable=False) - description = ProseColumn(Unicode(64), plural='descriptions', nullable=False) + id = Column(Integer, primary_key=True, nullable=False, + info=dict(description="A numeric ID")) + description = ProseColumn(Unicode(64), plural='descriptions', nullable=False, + info=dict(description="A description of the category")) class MoveMetaStatChange(TableBase): u"""Stat changes moves (may) make.""" __tablename__ = 'move_meta_stat_changes' - move_id = Column(Integer, ForeignKey('moves.id'), primary_key=True, nullable=False, autoincrement=False) - stat_id = Column(Integer, ForeignKey('stats.id'), primary_key=True, nullable=False, autoincrement=False) - change = Column(Integer, nullable=False, index=True) + move_id = Column(Integer, ForeignKey('moves.id'), primary_key=True, nullable=False, autoincrement=False, + info=dict(description="ID of the move")) + stat_id = Column(Integer, ForeignKey('stats.id'), primary_key=True, nullable=False, autoincrement=False, + info=dict(description="ID of the stat")) + change = Column(Integer, nullable=False, index=True, + info=dict(description="Amount of increase/decrease, in stages")) class MoveTarget(TableBase, UnofficiallyNamed): u"""Targetting or "range" of a move, e.g. "Affects all opponents" or "Affects user". @@ -1327,11 +1349,14 @@ class StatHint(TableBase): """ __tablename__ = 'stat_hints' __singlename__ = 'stat_hint' - id = Column(Integer, primary_key=True, nullable=False) - stat_id = Column(Integer, ForeignKey('stats.id'), nullable=False) - gene_mod_5 = Column(Integer, nullable=False, index=True) + id = Column(Integer, primary_key=True, nullable=False, + info=dict(description=u"A numeric ID")) + stat_id = Column(Integer, ForeignKey('stats.id'), nullable=False, + info=dict(description=u"ID of the highest stat")) + gene_mod_5 = Column(Integer, nullable=False, index=True, + info=dict(description=u"Value of the highest stat modulo 5")) text = TextColumn(Unicode(24), plural='texts', nullable=False, index=True, unique=True, - info=dict(description=u"The English text displayed", official=True, format='plaintext')) + info=dict(description=u"The text displayed", official=True, format='plaintext')) class SuperContestCombo(TableBase): u"""Combo of two moves in a Super Contest. @@ -1770,52 +1795,53 @@ def makeTextTable(object_table, name_plural, name_singular, columns, lazy): if safe_name == 'language': safe_name = 'lang' - fields = { - safe_name + '_id': Column(Integer, - ForeignKey(object_table.id), primary_key=True, nullable=False, - info=dict(description="ID of the object this table represents") - ), - '__tablename__': table.__singlename__ + '_' + name_plural, - '__singlename__': table.__singlename__ + '_' + name_singular, - 'is_%s_table' % name_singular: True, - } + tablename = object_table.__singlename__ + '_' + name_plural + singlename = object_table.__singlename__ + '_' + name_singular - fields.update((name, col) for name, plural, col in columns) + class Strings(object): + __tablename__ = tablename + __singlename__ = singlename - name = table.__name__ + name_singular.capitalize() + for name, plural, column in columns: + column.name = name - # There are some dynamic things that can only be set at class - # creation time because of declarative metaclass magic. - # So create class dynamically. - Strings = type(name, (TableBase, LanguageSpecific), fields) + table = Table(tablename, metadata, + Column(safe_name + '_id', Integer, ForeignKey(object_table.id), + primary_key=True, nullable=False), + Column('language_id', Integer, ForeignKey(Language.id), + primary_key=True, nullable=False), + *(column for name, plural, column in columns) + ) - # Alias the described thing to 'object', to make meta stuff easier - Strings.object_id = getattr(Strings, safe_name + '_id') + mapper(Strings, table, + properties={ + "object_id": synonym(safe_name + '_id'), + "language": relation( + Language, + primaryjoin=table.c.language_id == Language.id, + ), + }, + ) # The relation to the object - setattr(table, name_plural, relation( + setattr(object_table, name_plural, relation( Strings, - primaryjoin=(table.id == Strings.object_id), + primaryjoin=(object_table.id == Strings.object_id), backref=safe_name, collection_class=attribute_mapped_collection('language'), lazy=lazy, )) - Strings.object = getattr(Strings, safe_name) - Strings.object_table = table - setattr(table, name_singular + '_table', Strings) - - Strings.language = relation( - Language, - primaryjoin=Strings.language_id == Language.id, - ) + # Link the tables themselves, so we can get to them + Strings.object_table = object_table + setattr(object_table, name_singular + '_table', Strings) for colname, pluralname, column in columns: # Provide a relation with all the names, and an English accessor # for backwards compatibility def scope(colname, pluralname, column): - def get_string(self): + def get_strings(self): return dict( (l, getattr(t, colname)) for l, t in getattr(self, name_plural).items() @@ -1823,16 +1849,16 @@ def makeTextTable(object_table, name_plural, name_singular, columns, lazy): def get_english_string(self): try: - return get_string(self)['en'] + return get_strings(self)['en'] except KeyError: raise AttributeError(colname) - setattr(table, pluralname, property(get_string)) - setattr(table, colname, property(get_english_string)) + setattr(object_table, pluralname, property(get_strings)) + setattr(object_table, colname, property(get_english_string)) scope(colname, pluralname, column) if colname == 'name': - table.name_table = Strings + object_table.name_table = Strings return Strings