From f773ef02ec754ec4715d8efbc761687f71274705 Mon Sep 17 00:00:00 2001 From: Eevee Date: Sun, 13 Mar 2011 23:12:17 -0700 Subject: [PATCH 01/17] Remove OfficiallyNamed and UnofficiallyNamed. --- pokedex/db/tables.py | 170 +++++++++++++++++++++++++++++-------------- 1 file changed, 114 insertions(+), 56 deletions(-) diff --git a/pokedex/db/tables.py b/pokedex/db/tables.py index f8afa9b..6c18177 100644 --- a/pokedex/db/tables.py +++ b/pokedex/db/tables.py @@ -60,6 +60,7 @@ metadata = MetaData() TableBase = declarative_base(metadata=metadata, metaclass=TableMetaclass) ### Helper classes +# XXX this stuff isn't covered anywhere; maybe put it in TableBase?? class Named(object): """Mixin for objects that have names""" def __unicode__(self): @@ -74,12 +75,6 @@ class Named(object): def __repr__(self): return str(self) -class OfficiallyNamed(Named): - """Mixin for stuff with official names""" - -class UnofficiallyNamed(Named): - """Mixin for stuff with unofficial names""" - class LanguageSpecific(object): """Mixin for prose and text tables""" @declared_attr @@ -109,7 +104,7 @@ class TextColumn(LanguageSpecificColumn): ### The actual tables -class Ability(TableBase, OfficiallyNamed): +class Ability(TableBase): u"""An ability a Pokémon can have, such as Static or Pressure. """ __tablename__ = 'abilities' @@ -124,6 +119,8 @@ class Ability(TableBase, OfficiallyNamed): info=dict(description="A detailed description of this ability's effect", format='markdown')) short_effect = ProseColumn(markdown.MarkdownColumn(255), plural='short_effects', nullable=False, info=dict(description="A short summary of this ability's effect", format='markdown')) + name = TextColumn(Unicode(24), nullable=False, index=True, plural='names', + info=dict(description="The name", format='plaintext', official=True)) class AbilityChangelog(TableBase): """History of changes to abilities across main game versions.""" @@ -176,7 +173,7 @@ class Berry(TableBase): smoothness = Column(Integer, nullable=False, info=dict(description="The smoothness of this Berry, used in making Pokéblocks or Poffins")) -class BerryFirmness(TableBase, OfficiallyNamed): +class BerryFirmness(TableBase): u"""A Berry firmness, such as "hard" or "very soft". """ __tablename__ = 'berry_firmness' @@ -185,6 +182,8 @@ class BerryFirmness(TableBase, OfficiallyNamed): info=dict(description="A unique ID for this firmness")) identifier = Column(Unicode(10), nullable=False, info=dict(description="An identifier", format='identifier')) + name = TextColumn(Unicode(10), nullable=False, index=True, plural='names', + info=dict(description="The name", format='plaintext', official=True)) class BerryFlavor(TableBase): u"""A Berry flavor level. @@ -222,7 +221,7 @@ class ContestEffect(TableBase): effect = ProseColumn(Unicode(255), plural='effects', nullable=False, info=dict(description="A detailed description of the effect", format='plaintext')) -class ContestType(TableBase, OfficiallyNamed): +class ContestType(TableBase): u"""A Contest type, such as "cool" or "smart", and their associated Berry flavors and Pokéblock colors. """ __tablename__ = 'contest_types' @@ -235,8 +234,10 @@ class ContestType(TableBase, OfficiallyNamed): info=dict(description="The name of the corresponding Berry flavor", official=True, format='plaintext')) color = TextColumn(Unicode(6), nullable=False, plural='colors', info=dict(description=u"The name of the corresponding Pokéblock color", official=True, format='plaintext')) + name = TextColumn(Unicode(6), nullable=False, index=True, plural='names', + info=dict(description="The name", format='plaintext', official=True)) -class EggGroup(TableBase, UnofficiallyNamed): +class EggGroup(TableBase): u"""An Egg group. Usually, two Pokémon can breed if they share an Egg Group. (exceptions are the Ditto and No Eggs groups) @@ -247,6 +248,8 @@ class EggGroup(TableBase, UnofficiallyNamed): info=dict(description="A unique ID for this group")) identifier = Column(Unicode(16), nullable=False, info=dict(description=u"An identifier.", format='identifier')) + name = ProseColumn(Unicode(16), nullable=False, index=True, plural='names', + info=dict(description="The name", format='plaintext', official=False)) class Encounter(TableBase): u"""Encounters with wild Pokémon. @@ -288,7 +291,7 @@ class Encounter(TableBase): max_level = Column(Integer, nullable=False, autoincrement=False, info=dict(description=u"The maxmum level of the encountered Pokémon")) -class EncounterCondition(TableBase, UnofficiallyNamed): +class EncounterCondition(TableBase): u"""A conditions in the game world that affects Pokémon encounters, such as time of day. """ @@ -298,8 +301,10 @@ class EncounterCondition(TableBase, UnofficiallyNamed): info=dict(description="A unique ID for this condition")) identifier = Column(Unicode(64), nullable=False, info=dict(description="An identifier", format='identifier')) + name = ProseColumn(Unicode(64), nullable=False, index=True, plural='names', + info=dict(description="The name", format='plaintext', official=False)) -class EncounterConditionValue(TableBase, UnofficiallyNamed): +class EncounterConditionValue(TableBase): u"""A possible state for a condition; for example, the state of 'swarm' could be 'swarm' or 'no swarm'. """ @@ -313,6 +318,8 @@ class EncounterConditionValue(TableBase, UnofficiallyNamed): info=dict(description="An identifier", format='identifier')) is_default = Column(Boolean, nullable=False, info=dict(description='Set if this value is the default state for the condition')) + name = ProseColumn(Unicode(64), nullable=False, index=True, plural='names', + info=dict(description="The name", format='plaintext', official=False)) class EncounterConditionValueMap(TableBase): u"""Maps encounters to the specific conditions under which they occur. @@ -323,7 +330,7 @@ class EncounterConditionValueMap(TableBase): encounter_condition_value_id = Column(Integer, ForeignKey('encounter_condition_values.id'), primary_key=True, nullable=False, autoincrement=False, info=dict(description="The ID of the encounter condition value")) -class EncounterTerrain(TableBase, UnofficiallyNamed): +class EncounterTerrain(TableBase): u"""A way the player can enter a wild encounter, e.g., surfing, fishing, or walking through tall grass. """ @@ -333,6 +340,8 @@ class EncounterTerrain(TableBase, UnofficiallyNamed): info=dict(description="A unique ID for the terrain")) identifier = Column(Unicode(64), nullable=False, info=dict(description="An identifier", format='identifier')) + name = ProseColumn(Unicode(64), nullable=False, index=True, plural='names', + info=dict(description="The name", format='plaintext', official=False)) class EncounterSlot(TableBase): u"""An abstract "slot" within a terrain, associated with both some set of conditions and a rarity. @@ -364,7 +373,7 @@ class EvolutionChain(TableBase): baby_trigger_item_id = Column(Integer, ForeignKey('items.id'), nullable=True, info=dict(description="Item that a parent must hold while breeding to produce a baby")) -class EvolutionTrigger(TableBase, UnofficiallyNamed): +class EvolutionTrigger(TableBase): u"""An evolution type, such as "level" or "trade". """ __tablename__ = 'evolution_triggers' @@ -373,6 +382,8 @@ class EvolutionTrigger(TableBase, UnofficiallyNamed): info=dict(description="A numeric ID")) identifier = Column(Unicode(16), nullable=False, info=dict(description="An identifier", format='identifier')) + name = ProseColumn(Unicode(16), nullable=False, index=True, plural='names', + info=dict(description="The name", format='plaintext', official=False)) class Experience(TableBase): u"""EXP needed for a certain level with a certain growth rate @@ -385,7 +396,7 @@ class Experience(TableBase): experience = Column(Integer, nullable=False, info=dict(description="The number of EXP points needed to get to that level")) -class Generation(TableBase, OfficiallyNamed): +class Generation(TableBase): u"""A Generation of the Pokémon franchise """ __tablename__ = 'generations' @@ -398,8 +409,10 @@ class Generation(TableBase, OfficiallyNamed): info=dict(description=u"ID of the Pokédex this generation's main games use by default")) identifier = Column(Unicode(16), nullable=False, info=dict(description=u'An identifier', format='identifier')) + name = TextColumn(Unicode(16), nullable=False, index=True, plural='names', + info=dict(description="The name", format='plaintext', official=True)) -class GrowthRate(TableBase, UnofficiallyNamed): +class GrowthRate(TableBase): u"""Growth rate of a Pokémon, i.e. the EXP → level function. """ __tablename__ = 'growth_rates' @@ -410,8 +423,10 @@ class GrowthRate(TableBase, UnofficiallyNamed): info=dict(description="An identifier", format='identifier')) formula = Column(Unicode(500), nullable=False, info=dict(description="The formula", format='latex')) + name = ProseColumn(Unicode(20), nullable=False, index=True, plural='names', + info=dict(description="The name", format='plaintext', official=False)) -class Item(TableBase, OfficiallyNamed): +class Item(TableBase): u"""An Item from the games, like "Poké Ball" or "Bicycle". """ __tablename__ = 'items' @@ -432,6 +447,8 @@ class Item(TableBase, OfficiallyNamed): info=dict(description="A short summary of the effect", format='plaintext')) effect = ProseColumn(markdown.MarkdownColumn(5120), plural='effects', nullable=False, info=dict(description=u"Detailed description of the item's effect.", format='markdown')) + name = TextColumn(Unicode(20), nullable=False, index=True, plural='names', + info=dict(description="The name", format='plaintext', official=True)) @property def appears_underground(self): @@ -439,7 +456,7 @@ class Item(TableBase, OfficiallyNamed): """ return any(flag.identifier == u'underground' for flag in self.flags) -class ItemCategory(TableBase, UnofficiallyNamed): +class ItemCategory(TableBase): u"""An item category """ # XXX: This is fanon, right? @@ -451,8 +468,10 @@ class ItemCategory(TableBase, UnofficiallyNamed): info=dict(description="ID of the pocket these items go to")) identifier = Column(Unicode(16), nullable=False, info=dict(description="An identifier", format='identifier')) + name = ProseColumn(Unicode(16), nullable=False, index=True, plural='names', + info=dict(description="The name", format='plaintext', official=False)) -class ItemFlag(TableBase, UnofficiallyNamed): +class ItemFlag(TableBase): u"""An item attribute such as "consumable" or "holdable". """ __tablename__ = 'item_flags' @@ -463,6 +482,8 @@ class ItemFlag(TableBase, UnofficiallyNamed): info=dict(description="Identifier of the flag", format='identifier')) description = ProseColumn(Unicode(64), plural='descriptions', nullable=False, info=dict(description="Short description of the flag", format='plaintext')) + name = ProseColumn(Unicode(24), nullable=False, index=True, plural='names', + info=dict(description="The name", format='plaintext', official=False)) class ItemFlagMap(TableBase): u"""Maps an item flag to its item. @@ -506,7 +527,7 @@ class ItemInternalID(TableBase): internal_id = Column(Integer, nullable=False, info=dict(description="Internal ID of the item in the generation")) -class ItemPocket(TableBase, OfficiallyNamed): +class ItemPocket(TableBase): u"""A pocket that categorizes items """ __tablename__ = 'item_pockets' @@ -515,8 +536,10 @@ class ItemPocket(TableBase, OfficiallyNamed): info=dict(description="A numeric ID")) identifier = Column(Unicode(16), nullable=False, info=dict(description="An identifier of this pocket", format='identifier')) + name = TextColumn(Unicode(16), nullable=False, index=True, plural='names', + info=dict(description="The name", format='plaintext', official=True)) -class Language(TableBase, OfficiallyNamed): +class Language(TableBase): u"""A language the Pokémon games have been transleted into """ __tablename__ = 'languages' @@ -533,6 +556,8 @@ class Language(TableBase, OfficiallyNamed): info=dict(description=u"True iff games are produced in the language.")) order = Column(Integer, nullable=True, info=dict(description=u"Order for sorting in foreign name lists.")) + name = TextColumn(Unicode(16), nullable=False, index=True, plural='names', + info=dict(description="The name", format='plaintext', official=True)) # Languages compare equal to its identifier, so a dictionary of # translations, with a Language as the key, can be indexed by the identifier @@ -552,7 +577,7 @@ class Language(TableBase, OfficiallyNamed): def __hash__(self): return hash(self.identifier) -class Location(TableBase, OfficiallyNamed): +class Location(TableBase): u"""A place in the Pokémon world """ __tablename__ = 'locations' @@ -563,8 +588,10 @@ class Location(TableBase, OfficiallyNamed): info=dict(description="ID of the region this location is in")) identifier = Column(Unicode(64), nullable=False, info=dict(description="An identifier", format='identifier')) + name = TextColumn(Unicode(64), nullable=False, index=True, plural='names', + info=dict(description="The name", format='plaintext', official=True)) -class LocationArea(TableBase, UnofficiallyNamed): +class LocationArea(TableBase): u"""A sub-area of a location """ __tablename__ = 'location_areas' @@ -577,6 +604,8 @@ class LocationArea(TableBase, UnofficiallyNamed): info=dict(description="ID the games ude for this area")) identifier = Column(Unicode(64), nullable=True, info=dict(description="An identifier", format='identifier')) + name = ProseColumn(Unicode(64), nullable=False, index=True, plural='names', + info=dict(description="The name", format='plaintext', official=False)) class LocationAreaEncounterRate(TableBase): # XXX: What's this exactly? Someone add the docstring & revise the descriptions @@ -620,7 +649,7 @@ class Machine(TableBase): """ return self.machine_number >= 100 -class MoveBattleStyle(TableBase, UnofficiallyNamed): +class MoveBattleStyle(TableBase): u"""A battle style of a move""" # XXX: Explain better __tablename__ = 'move_battle_styles' __singlename__ = 'move_battle_style' @@ -628,8 +657,10 @@ class MoveBattleStyle(TableBase, UnofficiallyNamed): info=dict(description="A numeric ID")) identifier = Column(Unicode(8), nullable=False, info=dict(description="An identifier", format='identifier')) + name = ProseColumn(Unicode(8), nullable=False, index=True, plural='names', + info=dict(description="The name", format='plaintext', official=False)) -class MoveEffectCategory(TableBase, UnofficiallyNamed): +class MoveEffectCategory(TableBase): u"""Category of a move effect """ __tablename__ = 'move_effect_categories' @@ -640,6 +671,8 @@ class MoveEffectCategory(TableBase, UnofficiallyNamed): info=dict(description="An identifier", format='identifier')) can_affect_user = Column(Boolean, nullable=False, info=dict(description="Set if the user can be affected")) + name = ProseColumn(Unicode(64), nullable=False, index=True, plural='names', + info=dict(description="The name", format='plaintext', official=False)) class MoveEffectCategoryMap(TableBase): u"""Maps a move effect category to a move effect @@ -652,7 +685,7 @@ class MoveEffectCategoryMap(TableBase): affects_user = Column(Boolean, primary_key=True, nullable=False, info=dict(description="Set if the user is affected")) -class MoveDamageClass(TableBase, UnofficiallyNamed): +class MoveDamageClass(TableBase): u"""Any of the damage classes moves can have, i.e. physical, special, or non-damaging. """ __tablename__ = 'move_damage_classes' @@ -663,6 +696,8 @@ class MoveDamageClass(TableBase, UnofficiallyNamed): info=dict(description="An identifier", format='identifier')) description = ProseColumn(Unicode(64), plural='descriptions', nullable=False, info=dict(description="A description of the class", format='plaintext')) + name = ProseColumn(Unicode(16), nullable=False, index=True, plural='names', + info=dict(description="The name", format='plaintext', official=False)) class MoveEffect(TableBase): u"""An effect of a move @@ -705,7 +740,7 @@ class MoveFlag(TableBase): move_flag_type_id = Column(Integer, ForeignKey('move_flag_types.id'), primary_key=True, nullable=False, autoincrement=False, info=dict(description="ID of the flag")) -class MoveFlagType(TableBase, UnofficiallyNamed): +class MoveFlagType(TableBase): u"""A Move attribute such as "snatchable" or "contact". """ __tablename__ = 'move_flag_types' @@ -716,6 +751,8 @@ class MoveFlagType(TableBase, UnofficiallyNamed): info=dict(description="A short identifier for the flag", format='identifier')) description = ProseColumn(markdown.MarkdownColumn(128), plural='descriptions', nullable=False, info=dict(description="A short description of the flag", format='markdown')) + name = ProseColumn(Unicode(32), nullable=False, index=True, plural='names', + info=dict(description="The name", format='plaintext', official=False)) class MoveFlavorText(TableBase, LanguageSpecific): u"""In-game description of a move @@ -758,7 +795,7 @@ class MoveMeta(TableBase): stat_chance = Column(Integer, nullable=False, index=True, info=dict(description="Chance to cause a stat change, in percent")) -class MoveMetaAilment(TableBase, OfficiallyNamed): +class MoveMetaAilment(TableBase): u"""Common status ailments moves can inflict on a single Pokémon, including major ailments like paralysis and minor ailments like trapping. """ @@ -768,6 +805,8 @@ class MoveMetaAilment(TableBase, OfficiallyNamed): info=dict(description="A numeric ID")) identifier = Column(Unicode(24), nullable=False, info=dict(description="An identifier", format='identifier')) + name = TextColumn(Unicode(24), nullable=False, index=True, plural='names', + info=dict(description="The name", format='plaintext', official=True)) class MoveMetaCategory(TableBase): u"""Very general categories that loosely group move effects.""" @@ -788,7 +827,7 @@ class MoveMetaStatChange(TableBase): change = Column(Integer, nullable=False, index=True, info=dict(description="Amount of increase/decrease, in stages")) -class MoveTarget(TableBase, UnofficiallyNamed): +class MoveTarget(TableBase): u"""Targetting or "range" of a move, e.g. "Affects all opponents" or "Affects user". """ __tablename__ = 'move_targets' @@ -799,8 +838,10 @@ class MoveTarget(TableBase, UnofficiallyNamed): info=dict(description="An identifier", format='identifier')) description = ProseColumn(Unicode(128), plural='descriptions', nullable=False, info=dict(description="A description", format='plaintext')) + name = ProseColumn(Unicode(32), nullable=False, index=True, plural='names', + info=dict(description="The name", format='plaintext', official=False)) -class Move(TableBase, OfficiallyNamed): +class Move(TableBase): u"""A Move: technique or attack a Pokémon can learn to use """ __tablename__ = 'moves' @@ -835,6 +876,8 @@ class Move(TableBase, OfficiallyNamed): info=dict(description="ID of the move's Contest effect")) super_contest_effect_id = Column(Integer, ForeignKey('super_contest_effects.id'), nullable=True, info=dict(description="ID of the move's Super Contest effect")) + name = TextColumn(Unicode(24), nullable=False, index=True, plural='names', + info=dict(description="The name", format='plaintext', official=True)) class MoveChangelog(TableBase): """History of changes to moves across main game versions.""" @@ -857,7 +900,7 @@ class MoveChangelog(TableBase): effect_chance = Column(Integer, nullable=True, info=dict(description="Prior effect chance, or NULL if unchanged")) -class Nature(TableBase, OfficiallyNamed): +class Nature(TableBase): u"""A nature a Pokémon can have, such as Calm or Brave """ __tablename__ = 'natures' @@ -874,6 +917,8 @@ class Nature(TableBase, OfficiallyNamed): info=dict(description=u"ID of the Berry flavor the Pokémon hates (if likes_flavor_id is the same, the effects cancel out)")) likes_flavor_id = Column(Integer, ForeignKey('contest_types.id'), nullable=False, info=dict(description=u"ID of the Berry flavor the Pokémon likes (if hates_flavor_id is the same, the effects cancel out)")) + name = TextColumn(Unicode(8), nullable=False, index=True, plural='names', + info=dict(description="The name", format='plaintext', official=True)) @property def is_neutral(self): @@ -909,7 +954,7 @@ class NaturePokeathlonStat(TableBase): max_change = Column(Integer, nullable=False, info=dict(description="Maximum change")) -class PokeathlonStat(TableBase, OfficiallyNamed): +class PokeathlonStat(TableBase): u"""A Pokéathlon stat, such as "Stamina" or "Jump". """ __tablename__ = 'pokeathlon_stats' @@ -918,8 +963,10 @@ class PokeathlonStat(TableBase, OfficiallyNamed): info=dict(description="A numeric ID")) identifier = Column(Unicode(8), nullable=False, info=dict(description="An identifier", format='identifier')) + name = TextColumn(Unicode(8), nullable=False, index=True, plural='names', + info=dict(description="The name", format='plaintext', official=True)) -class Pokedex(TableBase, UnofficiallyNamed): +class Pokedex(TableBase): u"""A collection of Pokémon species ordered in a particular way """ __tablename__ = 'pokedexes' @@ -932,8 +979,10 @@ class Pokedex(TableBase, UnofficiallyNamed): info=dict(description=u"An identifier", format='identifier')) description = ProseColumn(Unicode(512), plural='descriptions', nullable=False, info=dict(description=u"A longer description of the Pokédex", format='plaintext')) + name = ProseColumn(Unicode(16), nullable=False, index=True, plural='names', + info=dict(description="The name", format='plaintext', official=False)) -class Pokemon(TableBase, OfficiallyNamed): +class Pokemon(TableBase): u"""A species of Pokémon. The core to this whole mess. """ __tablename__ = 'pokemon' @@ -975,6 +1024,8 @@ class Pokemon(TableBase, OfficiallyNamed): info=dict(description=u"Set iff the species exhibits enough sexual dimorphism to have separate sets of sprites in Gen IV and beyond.")) order = Column(Integer, nullable=False, index=True, info=dict(description=u"Order for sorting. Almost national order, except families and forms are grouped together.")) + name = TextColumn(Unicode(20), nullable=False, index=True, plural='names', + info=dict(description="The name", format='plaintext', official=True)) ### Stuff to handle alternate Pokémon forms @@ -1074,7 +1125,7 @@ class PokemonAbility(TableBase): slot = Column(Integer, primary_key=True, nullable=False, autoincrement=False, info=dict(description=u"The ability slot, i.e. 1 or 2 for gen. IV")) -class PokemonColor(TableBase, OfficiallyNamed): +class PokemonColor(TableBase): u"""The "Pokédex color" of a Pokémon species. Usually based on the Pokémon's color. """ __tablename__ = 'pokemon_colors' @@ -1083,6 +1134,8 @@ class PokemonColor(TableBase, OfficiallyNamed): info=dict(description=u"ID of the Pokémon")) identifier = Column(Unicode(6), nullable=False, info=dict(description=u"An identifier", format='identifier')) + name = TextColumn(Unicode(6), nullable=False, index=True, plural='names', + info=dict(description="The name", format='plaintext', official=True)) class PokemonDexNumber(TableBase): u"""The number of a Pokémon in a particular Pokédex (e.g. Jigglypuff is #138 in Hoenn's 'dex) @@ -1153,7 +1206,7 @@ class PokemonFlavorText(TableBase, LanguageSpecific): flavor_text = Column(Unicode(255), nullable=False, info=dict(description=u"ID of the version that has this flavor text", official=True, format='gametext')) -class PokemonForm(TableBase, OfficiallyNamed): +class PokemonForm(TableBase): u"""An individual form of a Pokémon. Pokémon that do not have separate forms are still given a single row to @@ -1175,6 +1228,8 @@ class PokemonForm(TableBase, OfficiallyNamed): info=dict(description=u'Set for exactly one form used as the default for each species.')) order = Column(Integer, nullable=False, autoincrement=False, info=dict(description=u'The order in which forms should be sorted. Multiple forms may have equal order, in which case they should fall back on sorting by name.')) + name = TextColumn(Unicode(16), nullable=False, index=True, plural='names', + info=dict(description="The name", format='plaintext', official=True)) @property def pokemon(self): @@ -1233,7 +1288,7 @@ class PokemonFormPokeathlonStat(TableBase): maximum_stat = Column(Integer, nullable=False, autoincrement=False, info=dict(description=u'The maximum value for this stat for this Pokémon form.')) -class PokemonHabitat(TableBase, OfficiallyNamed): +class PokemonHabitat(TableBase): u"""The habitat of a Pokémon, as given in the FireRed/LeafGreen version Pokédex """ __tablename__ = 'pokemon_habitats' @@ -1242,6 +1297,8 @@ class PokemonHabitat(TableBase, OfficiallyNamed): info=dict(description=u"A numeric ID")) identifier = Column(Unicode(16), nullable=False, info=dict(description=u"An identifier", format='identifier')) + name = TextColumn(Unicode(16), nullable=False, index=True, plural='names', + info=dict(description="The name", format='plaintext', official=True)) class PokemonInternalID(TableBase): u"""The number of a Pokémon a game uses internally @@ -1289,7 +1346,7 @@ class PokemonMove(TableBase): {}, ) -class PokemonMoveMethod(TableBase, UnofficiallyNamed): +class PokemonMoveMethod(TableBase): u"""A method a move can be learned by, such as "Level up" or "Tutor". """ __tablename__ = 'pokemon_move_methods' @@ -1300,8 +1357,10 @@ class PokemonMoveMethod(TableBase, UnofficiallyNamed): info=dict(description=u"An identifier", format='identifier')) description = ProseColumn(Unicode(255), plural='descriptions', nullable=False, info=dict(description=u"A detailed description of how the method works", format='plaintext')) + name = ProseColumn(Unicode(64), nullable=False, index=True, plural='names', + info=dict(description="The name", format='plaintext', official=False)) -class PokemonShape(TableBase, UnofficiallyNamed): +class PokemonShape(TableBase): u"""The shape of a Pokémon's body, as used in generation IV Pokédexes. """ __tablename__ = 'pokemon_shapes' @@ -1312,6 +1371,8 @@ class PokemonShape(TableBase, UnofficiallyNamed): info=dict(description=u"An identifier", format='identifier')) awesome_name = ProseColumn(Unicode(16), plural='awesome_names', nullable=False, info=dict(description=u"A splendiferous name of the body shape", format='plaintext')) + name = ProseColumn(Unicode(24), nullable=False, index=True, plural='names', + info=dict(description="The name", format='plaintext', official=False)) class PokemonStat(TableBase): u"""A stat value of a Pokémon @@ -1337,7 +1398,7 @@ class PokemonType(TableBase): slot = Column(Integer, primary_key=True, nullable=False, autoincrement=False, info=dict(description=u"The type's slot, 1 or 2, used to sort types if there are two of them")) -class Region(TableBase, OfficiallyNamed): +class Region(TableBase): u"""Major areas of the world: Kanto, Johto, etc. """ __tablename__ = 'regions' @@ -1346,8 +1407,10 @@ class Region(TableBase, OfficiallyNamed): info=dict(description=u"A numeric ID")) identifier = Column(Unicode(16), nullable=False, info=dict(description=u"An identifier", format='identifier')) + name = TextColumn(Unicode(16), nullable=False, index=True, plural='names', + info=dict(description="The name", format='plaintext', official=True)) -class Stat(TableBase, OfficiallyNamed): +class Stat(TableBase): u"""A Stat, such as Attack or Speed """ __tablename__ = 'stats' @@ -1358,6 +1421,8 @@ class Stat(TableBase, OfficiallyNamed): info=dict(description=u"For offensive and defensive stats, the damage this stat relates to; otherwise None (the NULL value)")) identifier = Column(Unicode(16), nullable=False, info=dict(description=u"An identifier", format='identifier')) + name = TextColumn(Unicode(16), nullable=False, index=True, plural='names', + info=dict(description="The name", format='plaintext', official=True)) class StatHint(TableBase): u"""Flavor text for genes that appears in a Pokémon's summary. Sometimes @@ -1407,7 +1472,7 @@ class TypeEfficacy(TableBase): damage_factor = Column(Integer, nullable=False, info=dict(description=u"The multiplier, as a percentage of damage inflicted.")) -class Type(TableBase, OfficiallyNamed): +class Type(TableBase): u"""Any of the elemental types Pokémon and moves can have.""" __tablename__ = 'types' __singlename__ = 'type' @@ -1419,6 +1484,8 @@ class Type(TableBase, OfficiallyNamed): info=dict(description=u"The ID of the generation this type first appeared in.")) damage_class_id = Column(Integer, ForeignKey('move_damage_classes.id'), nullable=True, info=dict(description=u"The ID of the damage class this type's moves had before Generation IV, null if not applicable (e.g. ???).")) + name = TextColumn(Unicode(12), nullable=False, index=True, plural='names', + info=dict(description="The name", format='plaintext', official=True)) class VersionGroup(TableBase): u"""A group of versions, containing either two paired versions (such as Red @@ -1440,7 +1507,7 @@ class VersionGroupRegion(TableBase): region_id = Column(Integer, ForeignKey('regions.id'), primary_key=True, nullable=False, info=dict(description=u"The ID of the region.")) -class Version(TableBase, OfficiallyNamed): +class Version(TableBase): u"""An individual main-series Pokémon game.""" __tablename__ = 'versions' __singlename__ = 'version' @@ -1450,6 +1517,8 @@ class Version(TableBase, OfficiallyNamed): info=dict(description=u"The ID of the version group this game belongs to.")) identifier = Column(Unicode(32), nullable=False, info=dict(description=u'And identifier', format='identifier')) + name = TextColumn(Unicode(32), nullable=False, index=True, plural='names', + info=dict(description="The name", format='plaintext', official=True)) ### Relations down here, to avoid ordering problems @@ -1780,19 +1849,6 @@ VersionGroup.regions = association_proxy('version_group_regions', 'region') VersionGroup.pokedex = relation(Pokedex, back_populates='version_groups') -### Add name tables -for table in list(table_classes): - if issubclass(table, OfficiallyNamed): - cls = TextColumn - info=dict(description="The name", format='plaintext', official=True) - elif issubclass(table, UnofficiallyNamed): - cls = ProseColumn - info=dict(description="The name", format='plaintext', official=False) - else: - continue - table.name = cls(Unicode(class_mapper(table).c.identifier.type.length), - plural='names', index=True, nullable=False, info=info) - ### Add text/prose tables default_lang = u'en' @@ -1845,6 +1901,7 @@ def makeTextTable(object_table, name_plural, name_singular, columns, lazy): collection_class=attribute_mapped_collection('language'), lazy=lazy, )) + str(getattr(object_table, name_plural)) # [MORE MAGIC]. aka do not remove, or entire app fails. XXX what the fuck man Strings.object = getattr(Strings, safe_name) # Link the tables themselves, so we can get them if needed @@ -1964,6 +2021,7 @@ for table in list(table_classes): column = getattr(table, colname) if isinstance(column, LanguageSpecificColumn): all_columns.append((colname, column)) + delattr(table, colname) all_columns.sort(key=lambda pair: pair[1].order) # Break them into text and prose columns From b61acaff69f59e9c259c8ffe9cf441108c15ecbb Mon Sep 17 00:00:00 2001 From: Eevee Date: Sun, 13 Mar 2011 23:43:08 -0700 Subject: [PATCH 02/17] Tidy up relation creation for name tables. --- pokedex/db/tables.py | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/pokedex/db/tables.py b/pokedex/db/tables.py index 6c18177..6e23ebf 100644 --- a/pokedex/db/tables.py +++ b/pokedex/db/tables.py @@ -1884,24 +1884,22 @@ def makeTextTable(object_table, name_plural, name_singular, columns, lazy): ) mapper(Strings, table, - properties={ - "object_id": synonym(safe_name + '_id'), - "language": relation( - Language, - primaryjoin=table.c.language_id == Language.id, - ), - }, - ) + properties={ + "object_id": synonym(safe_name + '_id'), + "language": relation(Language, + primaryjoin=table.c.language_id == Language.id, + ), + safe_name: relation(object_table, + primaryjoin=(object_table.id == table.c[safe_name + "_id"]), + backref=backref(name_plural, + collection_class=attribute_mapped_collection('language'), + lazy=lazy, + ), + ), + }, + ) # The relation to the object - setattr(object_table, name_plural, relation( - Strings, - primaryjoin=(object_table.id == Strings.object_id), - backref=safe_name, - collection_class=attribute_mapped_collection('language'), - lazy=lazy, - )) - str(getattr(object_table, name_plural)) # [MORE MAGIC]. aka do not remove, or entire app fails. XXX what the fuck man Strings.object = getattr(Strings, safe_name) # Link the tables themselves, so we can get them if needed From a0f5c53193256a36d4eed4d3a9ab4487f2d9f8a4 Mon Sep 17 00:00:00 2001 From: Eevee Date: Mon, 14 Mar 2011 20:51:31 -0700 Subject: [PATCH 03/17] Give every db table a __str__. --- pokedex/db/tables.py | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/pokedex/db/tables.py b/pokedex/db/tables.py index 6e23ebf..1929210 100644 --- a/pokedex/db/tables.py +++ b/pokedex/db/tables.py @@ -56,25 +56,34 @@ class TableMetaclass(DeclarativeMeta): if hasattr(cls, '__tablename__'): table_classes.append(cls) -metadata = MetaData() -TableBase = declarative_base(metadata=metadata, metaclass=TableMetaclass) - -### Helper classes -# XXX this stuff isn't covered anywhere; maybe put it in TableBase?? -class Named(object): - """Mixin for objects that have names""" +class TableSuperclass(object): + """Superclass for declarative tables, to give them some generic niceties + like stringification. + """ def __unicode__(self): + """Be as useful as possible. Show the primary key, and an identifier + if we've got one. + """ + typename = u'.'.join((__name__, type(self).__name__)) + + pk_constraint = self.__table__.primary_key + if not pk_constraint: + return u"<%s object at %x>" % (typename, id(self)) + + pk = u', '.join(unicode(getattr(self, column.name)) + for column in pk_constraint.columns) try: - return '<%s: %s>' % (type(self).__name__, self.identifier) + return u"<%s object (%s): %s>" % (typename, pk, self.identifier) except AttributeError: - return '<%s>' % type(self).__name__ + return u"<%s object (%s)>" % (typename, pk) def __str__(self): - return unicode(self).encode('utf-8') + return unicode(self).encode('utf8') - def __repr__(self): - return str(self) +metadata = MetaData() +TableBase = declarative_base(metadata=metadata, cls=TableSuperclass, metaclass=TableMetaclass) +### Helper classes class LanguageSpecific(object): """Mixin for prose and text tables""" @declared_attr From a417a0a9e1a9f958e1fb88394456e04b82d27d3e Mon Sep 17 00:00:00 2001 From: Eevee Date: Mon, 14 Mar 2011 22:24:29 -0700 Subject: [PATCH 04/17] Replace String* sqla stuff with association_proxy. --- pokedex/db/tables.py | 87 ++++---------------------------------------- 1 file changed, 8 insertions(+), 79 deletions(-) diff --git a/pokedex/db/tables.py b/pokedex/db/tables.py index 1929210..f92b663 100644 --- a/pokedex/db/tables.py +++ b/pokedex/db/tables.py @@ -1918,9 +1918,14 @@ def makeTextTable(object_table, name_plural, name_singular, columns, lazy): for colname, pluralname, column in columns: # Provide a property with all the names, and an English accessor # for backwards compatibility - setattr(object_table, pluralname, StringProperty( - object_table, Strings, colname, - )) + def text_string_creator(language_code, string): + row = Strings() + row._language_identifier = language_code + setattr(row, colname, string) + return row + + setattr(object_table, pluralname, + association_proxy(name_plural, colname, creator=text_string_creator)) setattr(object_table, colname, DefaultLangProperty(pluralname)) if colname == 'name': @@ -1928,82 +1933,6 @@ def makeTextTable(object_table, name_plural, name_singular, columns, lazy): return Strings -class StringProperty(object): - def __init__(self, cls, stringclass, colname): - self.cls = cls - self.colname = colname - self.stringclass = stringclass - - def __get__(self, instance, cls): - if instance: - return StringMapping(instance, self) - else: - return self - - def __getitem__(self, lang): - return StringExpression(self, lang) - - def __str__(self): - return '' % (self.cls, self.colname) - -class StringMapping(collections.MutableMapping): - def __init__(self, instance, prop): - self.stringclass = prop.stringclass - self.instance = instance - self.strings = getattr(instance, prop.stringclass._attrname) - self.colname = prop.colname - - def __len__(self): - return len(self.strings) - - def __iter__(self): - return iter(self.strings) - - def __contains__(self, lang): - return lang in self.strings - - def __getitem__(self, lang): - return getattr(self.strings[lang], self.colname) - - def __setitem__(self, lang, value): - try: - # Modifying an existing row - row = self.strings[lang] - except KeyError: - # We need do add a whole row for the language - row = self.stringclass() - row.object_id = self.instance.id - session = object_session(self.instance) - if isinstance(lang, basestring): - lang = session.query(Language).filter_by( - identifier=lang).one() - row.language = lang - self.strings[lang] = row - session.add(row) - return setattr(row, self.colname, value) - - def __delitem__(self, lang): - raise NotImplementedError('Cannot delete a single string. ' - 'Perhaps you wan to delete all of %s.%s?' % - (self.instance, self.stringclass._attrname) - ) - -class StringExpression(ColumnOperators): - def __init__(self, prop, lang): - self.prop = prop - self.column = getattr(prop.stringclass, prop.colname) - self.lang_column = prop.stringclass._language_identifier - if isinstance(lang, basestring): - self.lang = lang - else: - self.lang = lang.identifier - - def operate(self, op, *values, **kwargs): - return getattr(self.prop.cls, self.prop.stringclass._attrname).any(and_( - self.lang_column == self.lang, - op(self.column, *values, **kwargs), - )) - class DefaultLangProperty(object): def __init__(self, colname): self.colname = colname From 46453491332341e7acdf4cdd39628da12c573972 Mon Sep 17 00:00:00 2001 From: Eevee Date: Fri, 18 Mar 2011 17:15:34 -0700 Subject: [PATCH 05/17] Rename some meta-schema variables for ease of debugging. --- pokedex/db/tables.py | 74 ++++++++++++++++++++++---------------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/pokedex/db/tables.py b/pokedex/db/tables.py index f92b663..d52c921 100644 --- a/pokedex/db/tables.py +++ b/pokedex/db/tables.py @@ -34,7 +34,7 @@ from sqlalchemy.ext.declarative import ( ) from sqlalchemy.ext.associationproxy import association_proxy from sqlalchemy.orm import ( - backref, eagerload_all, relation, class_mapper, synonym, mapper, + backref, compile_mappers, eagerload_all, relation, class_mapper, synonym, mapper, ) from sqlalchemy.orm.session import Session, object_session from sqlalchemy.orm.collections import attribute_mapped_collection @@ -1862,45 +1862,43 @@ VersionGroup.pokedex = relation(Pokedex, back_populates='version_groups') default_lang = u'en' -def makeTextTable(object_table, name_plural, name_singular, columns, lazy): +def makeTextTable(foreign_table_class, table_suffix_plural, table_suffix_singular, columns, lazy): # With "Language", we'd have two language_id. So, rename one to 'lang' - safe_name = object_table.__singlename__ - if safe_name == 'language': - safe_name = 'lang' + foreign_key_name = foreign_table_class.__singlename__ + if foreign_key_name == 'language': + foreign_key_name = 'lang' - tablename = object_table.__singlename__ + '_' + name_plural - singlename = object_table.__singlename__ + '_' + name_singular + table_name = foreign_table_class.__singlename__ + '_' + table_suffix_plural - class Strings(object): - __tablename__ = tablename - __singlename__ = singlename - _attrname = name_plural + class TranslatedStringsTable(object): + __tablename__ = table_name + _attrname = table_suffix_plural _language_identifier = association_proxy('language', 'identifier') - for name, plural, column in columns: - column.name = name + for column_name, column_name_plural, column in columns: + column.name = column_name if not column.nullable: # A Python side default value, so that the strings can be set # one by one without the DB complaining about missing values column.default = ColumnDefault(u'') - table = Table(tablename, metadata, - Column(safe_name + '_id', Integer, ForeignKey(object_table.id), + table = Table(table_name, metadata, + Column(foreign_key_name + '_id', Integer, ForeignKey(foreign_table_class.id), primary_key=True, nullable=False), Column('language_id', Integer, ForeignKey(Language.id), primary_key=True, index=True, nullable=False), *(column for name, plural, column in columns) ) - mapper(Strings, table, + mapper(TranslatedStringsTable, table, properties={ - "object_id": synonym(safe_name + '_id'), + "object_id": synonym(foreign_key_name + '_id'), "language": relation(Language, primaryjoin=table.c.language_id == Language.id, ), - safe_name: relation(object_table, - primaryjoin=(object_table.id == table.c[safe_name + "_id"]), - backref=backref(name_plural, + foreign_key_name: relation(foreign_table_class, + primaryjoin=(foreign_table_class.id == table.c[foreign_key_name + "_id"]), + backref=backref(table_suffix_plural, collection_class=attribute_mapped_collection('language'), lazy=lazy, ), @@ -1909,39 +1907,41 @@ def makeTextTable(object_table, name_plural, name_singular, columns, lazy): ) # The relation to the object - Strings.object = getattr(Strings, safe_name) + TranslatedStringsTable.object = getattr(TranslatedStringsTable, foreign_key_name) # Link the tables themselves, so we can get them if needed - Strings.object_table = object_table - setattr(object_table, name_singular + '_table', Strings) + TranslatedStringsTable.foreign_table_class = foreign_table_class + setattr(foreign_table_class, table_suffix_singular + '_table', TranslatedStringsTable) - for colname, pluralname, column in columns: + for column_name, column_name_plural, column in columns: # Provide a property with all the names, and an English accessor # for backwards compatibility def text_string_creator(language_code, string): - row = Strings() + row = TranslatedStringsTable() row._language_identifier = language_code - setattr(row, colname, string) + setattr(row, column_name, string) return row - setattr(object_table, pluralname, - association_proxy(name_plural, colname, creator=text_string_creator)) - setattr(object_table, colname, DefaultLangProperty(pluralname)) + setattr(foreign_table_class, column_name_plural, + association_proxy(table_suffix_plural, column_name, creator=text_string_creator)) + setattr(foreign_table_class, column_name, DefaultLangProperty(column_name_plural)) - if colname == 'name': - object_table.name_table = Strings + if column_name == 'name': + foreign_table_class.name_table = TranslatedStringsTable - return Strings + compile_mappers() + return TranslatedStringsTable class DefaultLangProperty(object): - def __init__(self, colname): - self.colname = colname + def __init__(self, column_name): + self.column_name = column_name def __get__(self, instance, cls): if instance: - return getattr(instance, self.colname)[default_lang] + return getattr(instance, self.column_name)[default_lang] else: - return getattr(cls, self.colname)[default_lang] + # TODO I think this is kind of broken + return getattr(cls, self.column_name)[default_lang] def __set__(self, instance, value): getattr(instance, self.colname)[default_lang] = value @@ -1976,7 +1976,7 @@ for table in list(table_classes): if text_columns: string_table = makeTextTable(table, 'texts', 'text', text_columns, lazy=False) if prose_columns: - string_table = makeTextTable(table, 'prose', 'prose', prose_columns, lazy=True) + string_table = makeTextTable(table, 'prose', 'prose', prose_columns, lazy='select') ### Add language relations for table in list(table_classes): From 542aa670ae0a2d080955e0f3293ae4c3df9604ab Mon Sep 17 00:00:00 2001 From: Eevee Date: Fri, 18 Mar 2011 18:22:18 -0700 Subject: [PATCH 06/17] Added a test for the i18n dynamic table generation. It fails spectacularly, but hopefully documents what I'm ultimately going for. --- pokedex/db/tables.py | 6 +- pokedex/tests/test_schema.py | 110 ++++++++++++++++++++++++++++++++++- 2 files changed, 112 insertions(+), 4 deletions(-) diff --git a/pokedex/db/tables.py b/pokedex/db/tables.py index d52c921..828b902 100644 --- a/pokedex/db/tables.py +++ b/pokedex/db/tables.py @@ -1862,7 +1862,7 @@ VersionGroup.pokedex = relation(Pokedex, back_populates='version_groups') default_lang = u'en' -def makeTextTable(foreign_table_class, table_suffix_plural, table_suffix_singular, columns, lazy): +def makeTextTable(foreign_table_class, table_suffix_plural, table_suffix_singular, columns, lazy, Language=Language): # With "Language", we'd have two language_id. So, rename one to 'lang' foreign_key_name = foreign_table_class.__singlename__ if foreign_key_name == 'language': @@ -1882,7 +1882,7 @@ def makeTextTable(foreign_table_class, table_suffix_plural, table_suffix_singula # one by one without the DB complaining about missing values column.default = ColumnDefault(u'') - table = Table(table_name, metadata, + table = Table(table_name, foreign_table_class.__table__.metadata, Column(foreign_key_name + '_id', Integer, ForeignKey(foreign_table_class.id), primary_key=True, nullable=False), Column('language_id', Integer, ForeignKey(Language.id), @@ -1899,7 +1899,7 @@ def makeTextTable(foreign_table_class, table_suffix_plural, table_suffix_singula foreign_key_name: relation(foreign_table_class, primaryjoin=(foreign_table_class.id == table.c[foreign_key_name + "_id"]), backref=backref(table_suffix_plural, - collection_class=attribute_mapped_collection('language'), + collection_class=attribute_mapped_collection('_language_identifier'), lazy=lazy, ), ), diff --git a/pokedex/tests/test_schema.py b/pokedex/tests/test_schema.py index f1583a7..c050c87 100644 --- a/pokedex/tests/test_schema.py +++ b/pokedex/tests/test_schema.py @@ -1,7 +1,9 @@ # encoding: utf8 from nose.tools import * import unittest -from sqlalchemy.orm import class_mapper +from sqlalchemy import Column, Integer, String, create_engine +from sqlalchemy.orm import class_mapper, joinedload, sessionmaker +from sqlalchemy.ext.declarative import declarative_base from pokedex.db import tables, markdown @@ -22,6 +24,112 @@ def test_variable_names(): for table in tables.table_classes: assert getattr(tables, table.__name__) is table +def test_i18n_table_creation(): + """Creates and manipulates a magical i18n table, completely independent of + the existing schema and data. Makes sure that the expected behavior of the + various proxies and columns works. + """ + Base = declarative_base() + engine = create_engine("sqlite:///:memory:") + + Base.metadata.bind = engine + + # Need this for the foreign keys to work! + class Language(Base): + __tablename__ = 'languages' + id = Column(Integer, primary_key=True, nullable=False) + identifier = Column(String(2), nullable=False, unique=True) + + class Foo(Base): + __tablename__ = 'foos' + __singlename__ = 'foo' + id = Column(Integer, primary_key=True, nullable=False) + + FooText = tables.makeTextTable( + foreign_table_class=Foo, + table_suffix_plural='blorp', + table_suffix_singular='klink', + columns=[ + ('name', 'names', Column(String(100))), + ], + lazy='select', + Language=Language, + ) + + # OK, create all the tables and gimme a session + Base.metadata.create_all() + sess = sessionmaker(engine)() + + # Create some languages and foos to bind together + lang_en = Language(identifier='en') + sess.add(lang_en) + lang_jp = Language(identifier='jp') + sess.add(lang_jp) + + foo = Foo() + sess.add(foo) + + # Commit so the above get primary keys filled in + sess.commit() + + # Give our foo some names, as directly as possible + foo_text = FooText() + foo_text.object_id = foo.id + foo_text.language_id = lang_en.id + foo_text.name = 'english' + sess.add(foo_text) + + foo_text = FooText() + foo_text.object_id = foo.id + foo_text.language_id = lang_jp.id + foo_text.name = 'nihongo' + sess.add(foo_text) + + # Commit! This will expire all of the above. + sess.commit() + + ### Test 1: re-fetch foo and check its attributes + foo = sess.query(Foo).one() + + # Dictionary of language identifiers => names + assert foo.names['en'] == 'english' + assert foo.names['jp'] == 'nihongo' + + # Default language, currently English + assert foo.name == 'english' + + sess.expire_all() + + ### Test 2: joinedload on the default name should appear to work + foo = sess.query(Foo) \ + .options(joinedload(Foo.name)) \ + .first + + assert foo.name == 'english' + + sess.expire_all() + + ### Test 3: joinedload on all the names should appear to work + foo = sess.query(Foo) \ + .options(joinedload(Foo.names)) \ + .first + + assert foo.names['en'] == 'english' + assert foo.names['jp'] == 'nihongo' + + sess.expire_all() + + ### Test 4: Mutating the dict collection should work + foo = sess.query(Foo).first + + foo.names['en'] = 'different english' + del foo.names['jp'] + + sess.commit() + + assert foo.names['en'] == 'different english' + assert 'jp' not in foo.names + def test_texts(): """Check DB schema for integrity of text columns & translations. From 1da816af4b024f5f8d6b554be0a10d48c3b63833 Mon Sep 17 00:00:00 2001 From: Eevee Date: Sun, 20 Mar 2011 01:06:45 -0700 Subject: [PATCH 07/17] New i18n schema thing impl, and fixed the new tests to match. --- pokedex/db/tables.py | 177 ++++++++++++++++++++++++++++++++++- pokedex/tests/test_schema.py | 59 +++++++----- 2 files changed, 208 insertions(+), 28 deletions(-) diff --git a/pokedex/db/tables.py b/pokedex/db/tables.py index 828b902..f40583e 100644 --- a/pokedex/db/tables.py +++ b/pokedex/db/tables.py @@ -27,6 +27,7 @@ The singular-name property returns the name in the default language, English. # XXX: Check if "gametext" is set correctly everywhere import collections +from functools import partial from sqlalchemy import Column, ForeignKey, MetaData, PrimaryKeyConstraint, Table, UniqueConstraint from sqlalchemy.ext.declarative import ( @@ -37,10 +38,11 @@ from sqlalchemy.orm import ( backref, compile_mappers, eagerload_all, relation, class_mapper, synonym, mapper, ) from sqlalchemy.orm.session import Session, object_session -from sqlalchemy.orm.collections import attribute_mapped_collection -from sqlalchemy.ext.associationproxy import association_proxy +from sqlalchemy.orm.interfaces import AttributeExtension +from sqlalchemy.orm.collections import attribute_mapped_collection, MappedCollection, collection, collection_adapter +from sqlalchemy.ext.associationproxy import _AssociationDict, association_proxy from sqlalchemy.sql import and_ -from sqlalchemy.sql.expression import ColumnOperators +from sqlalchemy.sql.expression import ColumnOperators, bindparam from sqlalchemy.schema import ColumnDefault from sqlalchemy.types import * from inspect import isclass @@ -1862,6 +1864,175 @@ VersionGroup.pokedex = relation(Pokedex, back_populates='version_groups') default_lang = u'en' +def create_translation_table(_table_name, foreign_class, + _language_class=Language, **kwargs): + """Creates a table that represents some kind of data attached to the given + foreign class, but translated across several languages. Returns the new + table's mapped class. +TODO give it a __table__ or __init__? + + `foreign_class` must have a `__singlename__`, currently only used to create + the name of the foreign key column. +TODO remove this requirement + + Also supports the notion of a default language, which is attached to the + session. This is English by default, for historical and practical reasons. + + Usage looks like this: + + class Foo(Base): ... + + create_translation_table('foo_bars', Foo, + name = Column(...), + ) + + # Now you can do the following: + foo.name + foo.name_map['en'] + foo.foo_bars['en'] + + foo.name_map['en'] = "new name" + del foo.name_map['en'] + + q.options(joinedload(Foo.default_translation)) + q.options(joinedload(Foo.foo_bars)) + + In the above example, the following attributes are added to Foo: + + - `foo_bars`, a relation to the new table. It uses a dict-based collection + class, where the keys are language identifiers and the values are rows in + the created tables. + - `foo_bars_local`, a relation to the row in the new table that matches the + current default language. + + Note that these are distinct relations. Even though the former necessarily + includes the latter, SQLAlchemy doesn't treat them as linked; loading one + will not load the other. Modifying both within the same transaction has + undefined behavior. + + For each column provided, the following additional attributes are added to + Foo: + + - `(column)_map`, an association proxy onto `foo_bars`. + - `(column)`, an association proxy onto `foo_bars_local`. + + Pardon the naming disparity, but the grammar suffers otherwise. + + Modifying these directly is not likely to be a good idea. + """ + # n.b.: _language_class only exists for the sake of tests, which sometimes + # want to create tables entirely separate from the pokedex metadata + + foreign_key_name = foreign_class.__singlename__ + '_id' + # A foreign key "language_id" will clash with the language_id we naturally + # put in every table. Rename it something else + if foreign_key_name == 'language_id': + # TODO change language_id below instead and rename this + foreign_key_name = 'lang_id' + + Translations = type(_table_name, (object,), { + '_language_identifier': association_proxy('language', 'identifier'), + }) + + # Create the table object + table = Table(_table_name, foreign_class.__table__.metadata, + Column(foreign_key_name, Integer, ForeignKey(foreign_class.id), + primary_key=True, nullable=False), + Column('language_id', Integer, ForeignKey(_language_class.id), + primary_key=True, nullable=False), + ) + + # Add ye columns + # Column objects have a _creation_order attribute in ascending order; use + # this to get the (unordered) kwargs sorted correctly + kwitems = kwargs.items() + kwitems.sort(key=lambda kv: kv[1]._creation_order) + for name, column in kwitems: + column.name = name + table.append_column(column) + + # Construct ye mapper + mapper(Translations, table, properties={ + # TODO change to foreign_id + 'object_id': synonym(foreign_key_name), + # TODO change this as appropriate + 'language': relation(_language_class, + primaryjoin=table.c.language_id == _language_class.id, + lazy='joined', + innerjoin=True), + # TODO does this need to join to the original table? + }) + + # Add full-table relations to the original class + # Class.foo_bars + class LanguageMapping(MappedCollection): + """Baby class that converts a language identifier key into an actual + language object, allowing for `foo.bars['en'] = Translations(...)`. + + Needed for per-column association proxies to function as setters. + """ + @collection.internally_instrumented + def __setitem__(self, key, value, _sa_initiator=None): + if key in self: + raise NotImplementedError("Can't replace the whole row, sorry!") + + # Only do this nonsense if the value is a dangling object; if it's + # in the db it already has its language_id + if not object_session(value): + # This took quite some source-diving to find, but it oughta be + # the object that actually owns this collection. + obj = collection_adapter(self).owner_state.obj() + session = object_session(obj) + value.language = session.query(_language_class) \ + .filter_by(identifier=key).one() + + super(LanguageMapping, self).__setitem__(key, value, _sa_initiator) + + setattr(foreign_class, _table_name, relation(Translations, + primaryjoin=foreign_class.id == Translations.object_id, + #collection_class=attribute_mapped_collection('_language_identifier'), + collection_class=partial(LanguageMapping, + lambda obj: obj._language_identifier), + # TODO + lazy='select', + )) + # Class.foo_bars_local + # This is a bit clever; it uses bindparam() to make the join clause + # modifiable on the fly. db sessions know the current language identifier + # populates the bindparam. + local_relation_name = _table_name + '_local' + setattr(foreign_class, local_relation_name, relation(Translations, + primaryjoin=and_( + foreign_class.id == Translations.object_id, + Translations._language_identifier == + bindparam('_default_language', required=True), + ), + uselist=False, + # TODO MORESO HERE + lazy='select', + )) + + # Add per-column proxies to the original class + for name, column in kwitems: + # TODO should these proxies be mutable? + + # Class.(column) -- accessor for the default language's value + setattr(foreign_class, name, + association_proxy(local_relation_name, name)) + + # Class.(column)_map -- accessor for the language dict + # Need a custom creator since Translations doesn't have an init, and + # these are passed as *args anyway + def creator(language_code, value): + row = Translations() + setattr(row, name, value) + return row + setattr(foreign_class, name + '_map', + association_proxy(_table_name, name, creator=creator)) + + # Done + return Translations + def makeTextTable(foreign_table_class, table_suffix_plural, table_suffix_singular, columns, lazy, Language=Language): # With "Language", we'd have two language_id. So, rename one to 'lang' foreign_key_name = foreign_table_class.__singlename__ diff --git a/pokedex/tests/test_schema.py b/pokedex/tests/test_schema.py index c050c87..63f48ce 100644 --- a/pokedex/tests/test_schema.py +++ b/pokedex/tests/test_schema.py @@ -3,6 +3,7 @@ from nose.tools import * import unittest from sqlalchemy import Column, Integer, String, create_engine from sqlalchemy.orm import class_mapper, joinedload, sessionmaker +from sqlalchemy.orm.session import Session from sqlalchemy.ext.declarative import declarative_base from pokedex.db import tables, markdown @@ -30,7 +31,7 @@ def test_i18n_table_creation(): various proxies and columns works. """ Base = declarative_base() - engine = create_engine("sqlite:///:memory:") + engine = create_engine("sqlite:///:memory:", echo=True) Base.metadata.bind = engine @@ -45,26 +46,30 @@ def test_i18n_table_creation(): __singlename__ = 'foo' id = Column(Integer, primary_key=True, nullable=False) - FooText = tables.makeTextTable( - foreign_table_class=Foo, - table_suffix_plural='blorp', - table_suffix_singular='klink', - columns=[ - ('name', 'names', Column(String(100))), - ], - lazy='select', - Language=Language, + FooText = tables.create_translation_table('foo_text', Foo, + _language_class=Language, + name = Column(String(100)), ) + # TODO move this to the real code + class DurpSession(Session): + def execute(self, clause, params=None, *args, **kwargs): + if not params: + params = {} + params.setdefault('_default_language', 'en') + return super(DurpSession, self).execute(clause, params, *args, **kwargs) + # OK, create all the tables and gimme a session Base.metadata.create_all() - sess = sessionmaker(engine)() + sess = sessionmaker(engine, class_=DurpSession)() # Create some languages and foos to bind together lang_en = Language(identifier='en') sess.add(lang_en) lang_jp = Language(identifier='jp') sess.add(lang_jp) + lang_ru = Language(identifier='ru') + sess.add(lang_ru) foo = Foo() sess.add(foo) @@ -89,11 +94,11 @@ def test_i18n_table_creation(): sess.commit() ### Test 1: re-fetch foo and check its attributes - foo = sess.query(Foo).one() + foo = sess.query(Foo).params(_default_language='en').one() # Dictionary of language identifiers => names - assert foo.names['en'] == 'english' - assert foo.names['jp'] == 'nihongo' + assert foo.name_map['en'] == 'english' + assert foo.name_map['jp'] == 'nihongo' # Default language, currently English assert foo.name == 'english' @@ -101,34 +106,38 @@ def test_i18n_table_creation(): sess.expire_all() ### Test 2: joinedload on the default name should appear to work + # THIS SHOULD WORK SOMEDAY + # .options(joinedload(Foo.name)) \ foo = sess.query(Foo) \ - .options(joinedload(Foo.name)) \ - .first + .options(joinedload(Foo.foo_text_local)) \ + .one() assert foo.name == 'english' sess.expire_all() ### Test 3: joinedload on all the names should appear to work + # THIS SHOULD ALSO WORK SOMEDAY + # .options(joinedload(Foo.name_map)) \ foo = sess.query(Foo) \ - .options(joinedload(Foo.names)) \ - .first + .options(joinedload(Foo.foo_text)) \ + .one() - assert foo.names['en'] == 'english' - assert foo.names['jp'] == 'nihongo' + assert foo.name_map['en'] == 'english' + assert foo.name_map['jp'] == 'nihongo' sess.expire_all() ### Test 4: Mutating the dict collection should work - foo = sess.query(Foo).first + foo = sess.query(Foo).one() - foo.names['en'] = 'different english' - del foo.names['jp'] + foo.name_map['en'] = 'different english' + foo.name_map['ru'] = 'new russian' sess.commit() - assert foo.names['en'] == 'different english' - assert 'jp' not in foo.names + assert foo.name_map['en'] == 'different english' + assert foo.name_map['ru'] == 'new russian' def test_texts(): """Check DB schema for integrity of text columns & translations. From 6a9172151a78f27228187e8e40979e35a87a812d Mon Sep 17 00:00:00 2001 From: Eevee Date: Mon, 21 Mar 2011 17:54:28 -0700 Subject: [PATCH 08/17] Sigh! Remove support for strings as keys; use Language objects. --- pokedex/db/tables.py | 50 +++--------------------------------- pokedex/tests/test_schema.py | 33 +++++++++++++++--------- 2 files changed, 24 insertions(+), 59 deletions(-) diff --git a/pokedex/db/tables.py b/pokedex/db/tables.py index f40583e..20fcbea 100644 --- a/pokedex/db/tables.py +++ b/pokedex/db/tables.py @@ -570,24 +570,6 @@ class Language(TableBase): name = TextColumn(Unicode(16), nullable=False, index=True, plural='names', info=dict(description="The name", format='plaintext', official=True)) - # Languages compare equal to its identifier, so a dictionary of - # translations, with a Language as the key, can be indexed by the identifier - def __eq__(self, other): - try: - return ( - self is other or - self.identifier == other or - self.identifier == other.identifier - ) - except AttributeError: - return NotImplemented - - def __ne__(self, other): - return not (self == other) - - def __hash__(self): - return hash(self.identifier) - class Location(TableBase): u"""A place in the Pokémon world """ @@ -1965,34 +1947,9 @@ TODO remove this requirement # Add full-table relations to the original class # Class.foo_bars - class LanguageMapping(MappedCollection): - """Baby class that converts a language identifier key into an actual - language object, allowing for `foo.bars['en'] = Translations(...)`. - - Needed for per-column association proxies to function as setters. - """ - @collection.internally_instrumented - def __setitem__(self, key, value, _sa_initiator=None): - if key in self: - raise NotImplementedError("Can't replace the whole row, sorry!") - - # Only do this nonsense if the value is a dangling object; if it's - # in the db it already has its language_id - if not object_session(value): - # This took quite some source-diving to find, but it oughta be - # the object that actually owns this collection. - obj = collection_adapter(self).owner_state.obj() - session = object_session(obj) - value.language = session.query(_language_class) \ - .filter_by(identifier=key).one() - - super(LanguageMapping, self).__setitem__(key, value, _sa_initiator) - setattr(foreign_class, _table_name, relation(Translations, primaryjoin=foreign_class.id == Translations.object_id, - #collection_class=attribute_mapped_collection('_language_identifier'), - collection_class=partial(LanguageMapping, - lambda obj: obj._language_identifier), + collection_class=attribute_mapped_collection('language'), # TODO lazy='select', )) @@ -2014,8 +1971,6 @@ TODO remove this requirement # Add per-column proxies to the original class for name, column in kwitems: - # TODO should these proxies be mutable? - # Class.(column) -- accessor for the default language's value setattr(foreign_class, name, association_proxy(local_relation_name, name)) @@ -2023,8 +1978,9 @@ TODO remove this requirement # Class.(column)_map -- accessor for the language dict # Need a custom creator since Translations doesn't have an init, and # these are passed as *args anyway - def creator(language_code, value): + def creator(language, value): row = Translations() + row.language = language setattr(row, name, value) return row setattr(foreign_class, name + '_map', diff --git a/pokedex/tests/test_schema.py b/pokedex/tests/test_schema.py index 63f48ce..9aa3943 100644 --- a/pokedex/tests/test_schema.py +++ b/pokedex/tests/test_schema.py @@ -7,6 +7,8 @@ from sqlalchemy.orm.session import Session from sqlalchemy.ext.declarative import declarative_base from pokedex.db import tables, markdown +from pokedex.db.multilang import create_translation_table +from pokedex.db.tables import create_translation_table def test_variable_names(): """We want pokedex.db.tables to export tables using the class name""" @@ -46,7 +48,7 @@ def test_i18n_table_creation(): __singlename__ = 'foo' id = Column(Integer, primary_key=True, nullable=False) - FooText = tables.create_translation_table('foo_text', Foo, + FooText = create_translation_table('foo_text', Foo, _language_class=Language, name = Column(String(100)), ) @@ -97,15 +99,22 @@ def test_i18n_table_creation(): foo = sess.query(Foo).params(_default_language='en').one() # Dictionary of language identifiers => names - assert foo.name_map['en'] == 'english' - assert foo.name_map['jp'] == 'nihongo' + assert foo.name_map[lang_en] == 'english' + assert foo.name_map[lang_jp] == 'nihongo' # Default language, currently English assert foo.name == 'english' sess.expire_all() - ### Test 2: joinedload on the default name should appear to work + ### Test 2: querying by default language name should work + foo = sess.query(Foo).filter_by(name='english').one() + + assert foo.name == 'english' + + sess.expire_all() + + ### Test 3: joinedload on the default name should appear to work # THIS SHOULD WORK SOMEDAY # .options(joinedload(Foo.name)) \ foo = sess.query(Foo) \ @@ -116,28 +125,28 @@ def test_i18n_table_creation(): sess.expire_all() - ### Test 3: joinedload on all the names should appear to work + ### Test 4: joinedload on all the names should appear to work # THIS SHOULD ALSO WORK SOMEDAY # .options(joinedload(Foo.name_map)) \ foo = sess.query(Foo) \ .options(joinedload(Foo.foo_text)) \ .one() - assert foo.name_map['en'] == 'english' - assert foo.name_map['jp'] == 'nihongo' + assert foo.name_map[lang_en] == 'english' + assert foo.name_map[lang_jp] == 'nihongo' sess.expire_all() - ### Test 4: Mutating the dict collection should work + ### Test 5: Mutating the dict collection should work foo = sess.query(Foo).one() - foo.name_map['en'] = 'different english' - foo.name_map['ru'] = 'new russian' + foo.name_map[lang_en] = 'different english' + foo.name_map[lang_ru] = 'new russian' sess.commit() - assert foo.name_map['en'] == 'different english' - assert foo.name_map['ru'] == 'new russian' + assert foo.name_map[lang_en] == 'different english' + assert foo.name_map[lang_ru] == 'new russian' def test_texts(): """Check DB schema for integrity of text columns & translations. From 68e14e663e93c258dabbb373a9228ba54fa9de9a Mon Sep 17 00:00:00 2001 From: Eevee Date: Mon, 21 Mar 2011 22:32:52 -0700 Subject: [PATCH 09/17] Started switching to create_translation_table. - Moved the function to its own file. - Implemented the session-based default language switching. - Migrated a couple tables. --- pokedex/db/__init__.py | 3 +- pokedex/db/multilang.py | 168 ++++++++++++++++++++++++ pokedex/db/tables.py | 243 +++++++++-------------------------- pokedex/tests/test_schema.py | 10 +- 4 files changed, 232 insertions(+), 192 deletions(-) create mode 100644 pokedex/db/multilang.py diff --git a/pokedex/db/__init__.py b/pokedex/db/__init__.py index 13e9a19..a6c8f6e 100644 --- a/pokedex/db/__init__.py +++ b/pokedex/db/__init__.py @@ -2,6 +2,7 @@ from sqlalchemy import MetaData, Table, engine_from_config, orm from ..defaults import get_default_db_uri from .tables import metadata +from .multilang import MultilangSession def connect(uri=None, session_args={}, engine_args={}, engine_prefix=''): @@ -40,7 +41,7 @@ def connect(uri=None, session_args={}, engine_args={}, engine_prefix=''): all_session_args = dict(autoflush=True, autocommit=False, bind=engine) all_session_args.update(session_args) - sm = orm.sessionmaker(**all_session_args) + sm = orm.sessionmaker(class_=MultilangSession, **all_session_args) session = orm.scoped_session(sm) return session diff --git a/pokedex/db/multilang.py b/pokedex/db/multilang.py new file mode 100644 index 0000000..e2d9009 --- /dev/null +++ b/pokedex/db/multilang.py @@ -0,0 +1,168 @@ +from functools import partial + +from sqlalchemy.ext.associationproxy import association_proxy +from sqlalchemy.orm import compile_mappers, mapper, relationship, synonym +from sqlalchemy.orm.collections import attribute_mapped_collection +from sqlalchemy.orm.session import Session, object_session +from sqlalchemy.schema import Column, ForeignKey, Table +from sqlalchemy.sql.expression import and_, bindparam +from sqlalchemy.types import Integer + +def create_translation_table(_table_name, foreign_class, relation_name, + language_class, **kwargs): + """Creates a table that represents some kind of data attached to the given + foreign class, but translated across several languages. Returns the new + table's mapped class. It won't be declarative, but it will have a + `__table__` attribute so you can retrieve the Table object. + + `foreign_class` must have a `__singlename__`, currently only used to create + the name of the foreign key column. +TODO remove this requirement + + Also supports the notion of a default language, which is attached to the + session. This is English by default, for historical and practical reasons. + + Usage looks like this: + + class Foo(Base): ... + + create_translation_table('foo_bars', Foo, 'bars', + name = Column(...), + ) + + # Now you can do the following: + foo.name + foo.name_map['en'] + foo.foo_bars['en'] + + foo.name_map['en'] = "new name" + del foo.name_map['en'] + + q.options(joinedload(Foo.bars_local)) + q.options(joinedload(Foo.bars)) + + The following properties are added to the passed class: + + - `(relation_name)`, a relation to the new table. It uses a dict-based + collection class, where the keys are language identifiers and the values + are rows in the created tables. + - `(relation_name)_local`, a relation to the row in the new table that + matches the current default language. + + Note that these are distinct relations. Even though the former necessarily + includes the latter, SQLAlchemy doesn't treat them as linked; loading one + will not load the other. Modifying both within the same transaction has + undefined behavior. + + For each column provided, the following additional attributes are added to + Foo: + + - `(column)_map`, an association proxy onto `foo_bars`. + - `(column)`, an association proxy onto `foo_bars_local`. + + Pardon the naming disparity, but the grammar suffers otherwise. + + Modifying these directly is not likely to be a good idea. + """ + # n.b.: language_class only exists for the sake of tests, which sometimes + # want to create tables entirely separate from the pokedex metadata + + foreign_key_name = foreign_class.__singlename__ + '_id' + # A foreign key "language_id" will clash with the language_id we naturally + # put in every table. Rename it something else + if foreign_key_name == 'language_id': + # TODO change language_id below instead and rename this + foreign_key_name = 'lang_id' + + Translations = type(_table_name, (object,), { + '_language_identifier': association_proxy('language', 'identifier'), + }) + + # Create the table object + table = Table(_table_name, foreign_class.__table__.metadata, + Column(foreign_key_name, Integer, ForeignKey(foreign_class.id), + primary_key=True, nullable=False), + Column('language_id', Integer, ForeignKey(language_class.id), + primary_key=True, nullable=False), + ) + Translations.__table__ = table + + # Add ye columns + # Column objects have a _creation_order attribute in ascending order; use + # this to get the (unordered) kwargs sorted correctly + kwitems = kwargs.items() + kwitems.sort(key=lambda kv: kv[1]._creation_order) + for name, column in kwitems: + column.name = name + table.append_column(column) + + # Construct ye mapper + mapper(Translations, table, properties={ + # TODO change to foreign_id + 'object_id': synonym(foreign_key_name), + # TODO change this as appropriate + 'language': relationship(language_class, + primaryjoin=table.c.language_id == language_class.id, + lazy='joined', + innerjoin=True), + # TODO does this need to join to the original table? + }) + + # Add full-table relations to the original class + # Foo.bars + setattr(foreign_class, relation_name, relationship(Translations, + primaryjoin=foreign_class.id == Translations.object_id, + collection_class=attribute_mapped_collection('language'), + # TODO + lazy='select', + )) + # Foo.bars_local + # This is a bit clever; it uses bindparam() to make the join clause + # modifiable on the fly. db sessions know the current language identifier + # populates the bindparam. + local_relation_name = relation_name + '_local' + setattr(foreign_class, local_relation_name, relationship(Translations, + primaryjoin=and_( + foreign_class.id == Translations.object_id, + Translations._language_identifier == + bindparam('_default_language', required=True), + ), + uselist=False, + # TODO MORESO HERE + lazy='select', + )) + + # Add per-column proxies to the original class + for name, column in kwitems: + # Class.(column) -- accessor for the default language's value + setattr(foreign_class, name, + association_proxy(local_relation_name, name)) + + # Class.(column)_map -- accessor for the language dict + # Need a custom creator since Translations doesn't have an init, and + # these are passed as *args anyway + def creator(language, value): + row = Translations() + row.language = language + setattr(row, name, value) + return row + setattr(foreign_class, name + '_map', + association_proxy(relation_name, name, creator=creator)) + + # Done + return Translations + +class MultilangSession(Session): + """A tiny Session subclass that adds support for a default language. + + Change the default_language attribute to whatever language's IDENTIFIER you + would like to be the default. + """ + default_language = 'en' + + def execute(self, clause, params=None, *args, **kwargs): + if not params: + params = {} + params.setdefault('_default_language', self.default_language) + return super(MultilangSession, self).execute( + clause, params, *args, **kwargs) diff --git a/pokedex/db/tables.py b/pokedex/db/tables.py index 20fcbea..0c2f5d3 100644 --- a/pokedex/db/tables.py +++ b/pokedex/db/tables.py @@ -47,7 +47,7 @@ from sqlalchemy.schema import ColumnDefault from sqlalchemy.types import * from inspect import isclass -from pokedex.db import markdown +from pokedex.db import markdown, multilang # A list of all table classes will live in table_classes table_classes = [] @@ -113,6 +113,30 @@ class TextColumn(LanguageSpecificColumn): """A column that will appear in the corresponding _text table""" +### Need Language first, to create the partial() below + +class Language(TableBase): + u"""A language the Pokémon games have been transleted into + """ + __tablename__ = 'languages' + __singlename__ = 'language' + id = Column(Integer, primary_key=True, nullable=False, + info=dict(description="A numeric ID")) + iso639 = Column(Unicode(2), nullable=False, + info=dict(description="The two-letter code of the country where this language is spoken. Note that it is not unique.", format='identifier')) + iso3166 = Column(Unicode(2), nullable=False, + info=dict(description="The two-letter code of the language. Note that it is not unique.", format='identifier')) + identifier = Column(Unicode(16), nullable=False, + info=dict(description="An identifier", format='identifier')) + official = Column(Boolean, nullable=False, index=True, + info=dict(description=u"True iff games are produced in the language.")) + order = Column(Integer, nullable=True, + info=dict(description=u"Order for sorting in foreign name lists.")) + name = TextColumn(Unicode(16), nullable=False, index=True, plural='names', + info=dict(description="The name", format='plaintext', official=True)) + +create_translation_table = partial(multilang.create_translation_table, language_class=Language) + ### The actual tables class Ability(TableBase): @@ -126,12 +150,17 @@ class Ability(TableBase): info=dict(description="An identifier", format='identifier')) generation_id = Column(Integer, ForeignKey('generations.id'), nullable=False, info=dict(description="The ID of the generation this ability was introduced in", detail=True)) - effect = ProseColumn(markdown.MarkdownColumn(5120), plural='effects', nullable=False, - info=dict(description="A detailed description of this ability's effect", format='markdown')) - short_effect = ProseColumn(markdown.MarkdownColumn(255), plural='short_effects', nullable=False, - info=dict(description="A short summary of this ability's effect", format='markdown')) - name = TextColumn(Unicode(24), nullable=False, index=True, plural='names', - info=dict(description="The name", format='plaintext', official=True)) + +create_translation_table('ability_texts', Ability, 'names', + name = Column(Unicode(24), nullable=False, index=True, + info=dict(description="The name", format='plaintext', official=True)), +) +create_translation_table('ability_prose', Ability, 'prose', + effect = Column(markdown.MarkdownColumn(5120), nullable=False, + info=dict(description="A detailed description of this ability's effect", format='markdown')), + short_effect = Column(markdown.MarkdownColumn(255), nullable=False, + info=dict(description="A short summary of this ability's effect", format='markdown')), +) class AbilityChangelog(TableBase): """History of changes to abilities across main game versions.""" @@ -550,26 +579,6 @@ class ItemPocket(TableBase): name = TextColumn(Unicode(16), nullable=False, index=True, plural='names', info=dict(description="The name", format='plaintext', official=True)) -class Language(TableBase): - u"""A language the Pokémon games have been transleted into - """ - __tablename__ = 'languages' - __singlename__ = 'language' - id = Column(Integer, primary_key=True, nullable=False, - info=dict(description="A numeric ID")) - iso639 = Column(Unicode(2), nullable=False, - info=dict(description="The two-letter code of the country where this language is spoken. Note that it is not unique.", format='identifier')) - iso3166 = Column(Unicode(2), nullable=False, - info=dict(description="The two-letter code of the language. Note that it is not unique.", format='identifier')) - identifier = Column(Unicode(16), nullable=False, - info=dict(description="An identifier", format='identifier')) - official = Column(Boolean, nullable=False, index=True, - info=dict(description=u"True iff games are produced in the language.")) - order = Column(Integer, nullable=True, - info=dict(description=u"Order for sorting in foreign name lists.")) - name = TextColumn(Unicode(16), nullable=False, index=True, plural='names', - info=dict(description="The name", format='plaintext', official=True)) - class Location(TableBase): u"""A place in the Pokémon world """ @@ -869,8 +878,12 @@ class Move(TableBase): info=dict(description="ID of the move's Contest effect")) super_contest_effect_id = Column(Integer, ForeignKey('super_contest_effects.id'), nullable=True, info=dict(description="ID of the move's Super Contest effect")) - name = TextColumn(Unicode(24), nullable=False, index=True, plural='names', + +create_translation_table('move_texts', Move, 'names', + name = Column(Unicode(24), nullable=False, index=True, info=dict(description="The name", format='plaintext', official=True)) +) + class MoveChangelog(TableBase): """History of changes to moves across main game versions.""" @@ -992,9 +1005,6 @@ class Pokemon(TableBase): info=dict(description=u"The height of the Pokémon, in decimeters (tenths of a meter)")) weight = Column(Integer, nullable=False, info=dict(description=u"The weight of the Pokémon, in tenths of a kilogram (decigrams)")) - species = TextColumn(Unicode(16), nullable=False, plural='species_names', - info=dict(description=u'The short flavor text, such as "Seed" or "Lizard"; usually affixed with the word "Pokémon"', - official=True, format='plaintext')) color_id = Column(Integer, ForeignKey('pokemon_colors.id'), nullable=False, info=dict(description=u"ID of this Pokémon's Pokédex color, as used for a gimmick search function in the games.")) pokemon_shape_id = Column(Integer, ForeignKey('pokemon_shapes.id'), nullable=True, @@ -1017,8 +1027,6 @@ class Pokemon(TableBase): info=dict(description=u"Set iff the species exhibits enough sexual dimorphism to have separate sets of sprites in Gen IV and beyond.")) order = Column(Integer, nullable=False, index=True, info=dict(description=u"Order for sorting. Almost national order, except families and forms are grouped together.")) - name = TextColumn(Unicode(20), nullable=False, index=True, plural='names', - info=dict(description="The name", format='plaintext', official=True)) ### Stuff to handle alternate Pokémon forms @@ -1102,6 +1110,14 @@ class Pokemon(TableBase): else: return None +create_translation_table('pokemon_texts', Pokemon, 'names', + name = Column(Unicode(20), nullable=False, index=True, + info=dict(description="The name", format='plaintext', official=True)), + species = Column(Unicode(16), nullable=False, + info=dict(description=u'The short flavor text, such as "Seed" or "Lizard"; usually affixed with the word "Pokémon"', + official=True, format='plaintext')), +) + class PokemonAbility(TableBase): u"""Maps an ability to a Pokémon that can have it """ @@ -1524,7 +1540,7 @@ Ability.generation = relation(Generation, backref='abilities') Ability.all_pokemon = relation(Pokemon, secondary=PokemonAbility.__table__, order_by=Pokemon.order, - back_populates='all_abilities', + #back_populates='all_abilities', ) Ability.pokemon = relation(Pokemon, secondary=PokemonAbility.__table__, @@ -1533,7 +1549,7 @@ Ability.pokemon = relation(Pokemon, PokemonAbility.is_dream == False ), order_by=Pokemon.order, - back_populates='abilities', + #back_populates='abilities', ) Ability.dream_pokemon = relation(Pokemon, secondary=PokemonAbility.__table__, @@ -1542,7 +1558,7 @@ Ability.dream_pokemon = relation(Pokemon, PokemonAbility.is_dream == True ), order_by=Pokemon.order, - back_populates='dream_ability', + #back_populates='dream_ability', ) AbilityChangelog.changed_in = relation(VersionGroup, backref='ability_changelog') @@ -1578,7 +1594,7 @@ EncounterSlot.version_group = relation(VersionGroup) EvolutionChain.growth_rate = relation(GrowthRate, backref='evolution_chains') EvolutionChain.baby_trigger_item = relation(Item, backref='evolution_chains') -EvolutionChain.pokemon = relation(Pokemon, order_by=Pokemon.order, back_populates='evolution_chain') +EvolutionChain.pokemon = relation(Pokemon, order_by=Pokemon.order)#, back_populates='evolution_chain') Experience.growth_rate = relation(GrowthRate, backref='experience_table') @@ -1638,7 +1654,7 @@ Move.super_contest_effect = relation(SuperContestEffect, backref='moves') Move.super_contest_combo_next = association_proxy('super_contest_combo_first', 'second') Move.super_contest_combo_prev = association_proxy('super_contest_combo_second', 'first') Move.target = relation(MoveTarget, backref='moves') -Move.type = relation(Type, back_populates='moves') +Move.type = relation(Type)#, back_populates='moves') MoveChangelog.changed_in = relation(VersionGroup, backref='move_changelog') MoveChangelog.move_effect = relation(MoveEffect, backref='move_changelog') @@ -1681,7 +1697,7 @@ NatureBattleStylePreference.battle_style = relation(MoveBattleStyle, backref='na NaturePokeathlonStat.pokeathlon_stat = relation(PokeathlonStat, backref='nature_effects') Pokedex.region = relation(Region, backref='pokedexes') -Pokedex.version_groups = relation(VersionGroup, order_by=VersionGroup.id, back_populates='pokedex') +Pokedex.version_groups = relation(VersionGroup, order_by=VersionGroup.id)#, back_populates='pokedex') Pokemon.all_abilities = relation(Ability, secondary=PokemonAbility.__table__, @@ -1709,7 +1725,7 @@ Pokemon.dex_numbers = relation(PokemonDexNumber, order_by=PokemonDexNumber.poked Pokemon.egg_groups = relation(EggGroup, secondary=PokemonEggGroup.__table__, order_by=PokemonEggGroup.egg_group_id, backref=backref('pokemon', order_by=Pokemon.order)) -Pokemon.evolution_chain = relation(EvolutionChain, back_populates='pokemon') +Pokemon.evolution_chain = relation(EvolutionChain)#, back_populates='pokemon') Pokemon.child_pokemon = relation(Pokemon, primaryjoin=Pokemon.id==PokemonEvolution.from_pokemon_id, secondary=PokemonEvolution.__table__, @@ -1731,7 +1747,7 @@ Pokemon.shape = relation(PokemonShape, backref='pokemon') Pokemon.stats = relation(PokemonStat, backref='pokemon', order_by=PokemonStat.stat_id.asc()) Pokemon.types = relation(Type, secondary=PokemonType.__table__, order_by=PokemonType.slot.asc(), - back_populates='pokemon') + )#back_populates='pokemon') PokemonDexNumber.pokedex = relation(Pokedex) @@ -1829,7 +1845,7 @@ Type.generation = relation(Generation, backref='types') Type.damage_class = relation(MoveDamageClass, backref='types') Type.pokemon = relation(Pokemon, secondary=PokemonType.__table__, order_by=Pokemon.order, - back_populates='types') + )#back_populates='types') Type.moves = relation(Move, back_populates='type', order_by=Move.id) Version.version_group = relation(VersionGroup, back_populates='versions') @@ -1846,149 +1862,6 @@ VersionGroup.pokedex = relation(Pokedex, back_populates='version_groups') default_lang = u'en' -def create_translation_table(_table_name, foreign_class, - _language_class=Language, **kwargs): - """Creates a table that represents some kind of data attached to the given - foreign class, but translated across several languages. Returns the new - table's mapped class. -TODO give it a __table__ or __init__? - - `foreign_class` must have a `__singlename__`, currently only used to create - the name of the foreign key column. -TODO remove this requirement - - Also supports the notion of a default language, which is attached to the - session. This is English by default, for historical and practical reasons. - - Usage looks like this: - - class Foo(Base): ... - - create_translation_table('foo_bars', Foo, - name = Column(...), - ) - - # Now you can do the following: - foo.name - foo.name_map['en'] - foo.foo_bars['en'] - - foo.name_map['en'] = "new name" - del foo.name_map['en'] - - q.options(joinedload(Foo.default_translation)) - q.options(joinedload(Foo.foo_bars)) - - In the above example, the following attributes are added to Foo: - - - `foo_bars`, a relation to the new table. It uses a dict-based collection - class, where the keys are language identifiers and the values are rows in - the created tables. - - `foo_bars_local`, a relation to the row in the new table that matches the - current default language. - - Note that these are distinct relations. Even though the former necessarily - includes the latter, SQLAlchemy doesn't treat them as linked; loading one - will not load the other. Modifying both within the same transaction has - undefined behavior. - - For each column provided, the following additional attributes are added to - Foo: - - - `(column)_map`, an association proxy onto `foo_bars`. - - `(column)`, an association proxy onto `foo_bars_local`. - - Pardon the naming disparity, but the grammar suffers otherwise. - - Modifying these directly is not likely to be a good idea. - """ - # n.b.: _language_class only exists for the sake of tests, which sometimes - # want to create tables entirely separate from the pokedex metadata - - foreign_key_name = foreign_class.__singlename__ + '_id' - # A foreign key "language_id" will clash with the language_id we naturally - # put in every table. Rename it something else - if foreign_key_name == 'language_id': - # TODO change language_id below instead and rename this - foreign_key_name = 'lang_id' - - Translations = type(_table_name, (object,), { - '_language_identifier': association_proxy('language', 'identifier'), - }) - - # Create the table object - table = Table(_table_name, foreign_class.__table__.metadata, - Column(foreign_key_name, Integer, ForeignKey(foreign_class.id), - primary_key=True, nullable=False), - Column('language_id', Integer, ForeignKey(_language_class.id), - primary_key=True, nullable=False), - ) - - # Add ye columns - # Column objects have a _creation_order attribute in ascending order; use - # this to get the (unordered) kwargs sorted correctly - kwitems = kwargs.items() - kwitems.sort(key=lambda kv: kv[1]._creation_order) - for name, column in kwitems: - column.name = name - table.append_column(column) - - # Construct ye mapper - mapper(Translations, table, properties={ - # TODO change to foreign_id - 'object_id': synonym(foreign_key_name), - # TODO change this as appropriate - 'language': relation(_language_class, - primaryjoin=table.c.language_id == _language_class.id, - lazy='joined', - innerjoin=True), - # TODO does this need to join to the original table? - }) - - # Add full-table relations to the original class - # Class.foo_bars - setattr(foreign_class, _table_name, relation(Translations, - primaryjoin=foreign_class.id == Translations.object_id, - collection_class=attribute_mapped_collection('language'), - # TODO - lazy='select', - )) - # Class.foo_bars_local - # This is a bit clever; it uses bindparam() to make the join clause - # modifiable on the fly. db sessions know the current language identifier - # populates the bindparam. - local_relation_name = _table_name + '_local' - setattr(foreign_class, local_relation_name, relation(Translations, - primaryjoin=and_( - foreign_class.id == Translations.object_id, - Translations._language_identifier == - bindparam('_default_language', required=True), - ), - uselist=False, - # TODO MORESO HERE - lazy='select', - )) - - # Add per-column proxies to the original class - for name, column in kwitems: - # Class.(column) -- accessor for the default language's value - setattr(foreign_class, name, - association_proxy(local_relation_name, name)) - - # Class.(column)_map -- accessor for the language dict - # Need a custom creator since Translations doesn't have an init, and - # these are passed as *args anyway - def creator(language, value): - row = Translations() - row.language = language - setattr(row, name, value) - return row - setattr(foreign_class, name + '_map', - association_proxy(_table_name, name, creator=creator)) - - # Done - return Translations - def makeTextTable(foreign_table_class, table_suffix_plural, table_suffix_singular, columns, lazy, Language=Language): # With "Language", we'd have two language_id. So, rename one to 'lang' foreign_key_name = foreign_table_class.__singlename__ diff --git a/pokedex/tests/test_schema.py b/pokedex/tests/test_schema.py index 9aa3943..88ee1e5 100644 --- a/pokedex/tests/test_schema.py +++ b/pokedex/tests/test_schema.py @@ -8,7 +8,6 @@ from sqlalchemy.ext.declarative import declarative_base from pokedex.db import tables, markdown from pokedex.db.multilang import create_translation_table -from pokedex.db.tables import create_translation_table def test_variable_names(): """We want pokedex.db.tables to export tables using the class name""" @@ -49,21 +48,20 @@ def test_i18n_table_creation(): id = Column(Integer, primary_key=True, nullable=False) FooText = create_translation_table('foo_text', Foo, - _language_class=Language, + language_class=Language, name = Column(String(100)), ) - # TODO move this to the real code - class DurpSession(Session): + class FauxSession(Session): def execute(self, clause, params=None, *args, **kwargs): if not params: params = {} params.setdefault('_default_language', 'en') - return super(DurpSession, self).execute(clause, params, *args, **kwargs) + return super(FauxSession, self).execute(clause, params, *args, **kwargs) # OK, create all the tables and gimme a session Base.metadata.create_all() - sess = sessionmaker(engine, class_=DurpSession)() + sess = sessionmaker(engine, class_=FauxSession)() # Create some languages and foos to bind together lang_en = Language(identifier='en') From 8ad84e40320f0848d5aba1eff61196c230f267b2 Mon Sep 17 00:00:00 2001 From: Eevee Date: Wed, 23 Mar 2011 22:17:02 -0700 Subject: [PATCH 10/17] Removed ProseColumn and TextColumn. Huzzah. --- pokedex/db/markdown.py | 50 ++-- pokedex/db/multilang.py | 27 ++- pokedex/db/tables.py | 517 ++++++++++++++++++++-------------------- pokedex/lookup.py | 2 +- 4 files changed, 298 insertions(+), 298 deletions(-) diff --git a/pokedex/db/markdown.py b/pokedex/db/markdown.py index 0bc6971..dd86746 100644 --- a/pokedex/db/markdown.py +++ b/pokedex/db/markdown.py @@ -56,34 +56,25 @@ class MarkdownString(object): """ return self.source_text +def _markdownify_effect_text(move, effect_text): + effect_text = effect_text.replace( + u'$effect_chance', + unicode(move.effect_chance), + ) -class _MoveEffects(object): - def __init__(self, effect_column, move): - self.effect_column = effect_column - self.move = move + return MarkdownString(effect_text) - def __contains__(self, lang): - return lang in self.move.move_effect.prose - - def __getitem__(self, lang): - try: - effect_text = getattr(self.move.move_effect.prose[lang], self.effect_column) - except AttributeError: - return None - effect_text = effect_text.replace( - u'$effect_chance', - unicode(self.move.effect_chance), - ) - - return MarkdownString(effect_text) - -class MoveEffectsProperty(object): +class MoveEffectProperty(object): """Property that wraps move effects. Used like this: - MoveClass.effects = MoveEffectProperty('effect') + MoveClass.effect = MoveEffectProperty('effect') - some_move.effects[lang] # returns a MarkdownString - some_move.effects[lang].as_html # returns a chunk of HTML + some_move.effect # returns a MarkdownString + some_move.effect.as_html # returns a chunk of HTML + + This class attempts to detect if the wrapped property is a dict-based + association proxy, and will act like such a dict if so. Don't rely on it + for querying, of course. This class also performs simple substitution on the effect, replacing `$effect_chance` with the move's actual effect chance. @@ -92,8 +83,17 @@ class MoveEffectsProperty(object): def __init__(self, effect_column): self.effect_column = effect_column - def __get__(self, move, move_class): - return _MoveEffects(self.effect_column, move) + def __get__(self, obj, cls): + prop = getattr(obj.move_effect, self.effect_column) + if isinstance(prop, dict): + # Looks like a dict proxy; markdownify everyone + newdict = dict(prop) + for key in newdict: + newdict[key] = _markdownify_effect_text(obj, newdict[key]) + return newdict + + # Otherwise, scalar prop. Boring + return _markdownify_effect_text(obj, prop) class MarkdownColumn(sqlalchemy.types.TypeDecorator): """Generic SQLAlchemy column type for Markdown text. diff --git a/pokedex/db/multilang.py b/pokedex/db/multilang.py index e2d9009..96d70d6 100644 --- a/pokedex/db/multilang.py +++ b/pokedex/db/multilang.py @@ -1,11 +1,11 @@ from functools import partial from sqlalchemy.ext.associationproxy import association_proxy -from sqlalchemy.orm import compile_mappers, mapper, relationship, synonym +from sqlalchemy.orm import aliased, compile_mappers, mapper, relationship, synonym from sqlalchemy.orm.collections import attribute_mapped_collection from sqlalchemy.orm.session import Session, object_session from sqlalchemy.schema import Column, ForeignKey, Table -from sqlalchemy.sql.expression import and_, bindparam +from sqlalchemy.sql.expression import and_, bindparam, select from sqlalchemy.types import Integer def create_translation_table(_table_name, foreign_class, relation_name, @@ -48,6 +48,7 @@ TODO remove this requirement are rows in the created tables. - `(relation_name)_local`, a relation to the row in the new table that matches the current default language. + - `(relation_name)_class`, the class created by this function. Note that these are distinct relations. Even though the former necessarily includes the latter, SQLAlchemy doesn't treat them as linked; loading one @@ -77,7 +78,7 @@ TODO remove this requirement Translations = type(_table_name, (object,), { '_language_identifier': association_proxy('language', 'identifier'), }) - + # Create the table object table = Table(_table_name, foreign_class.__table__.metadata, Column(foreign_key_name, Integer, ForeignKey(foreign_class.id), @@ -109,6 +110,8 @@ TODO remove this requirement }) # Add full-table relations to the original class + # Foo.bars_table + setattr(foreign_class, relation_name + '_table', Translations) # Foo.bars setattr(foreign_class, relation_name, relationship(Translations, primaryjoin=foreign_class.id == Translations.object_id, @@ -119,13 +122,19 @@ TODO remove this requirement # Foo.bars_local # This is a bit clever; it uses bindparam() to make the join clause # modifiable on the fly. db sessions know the current language identifier - # populates the bindparam. + # populates the bindparam. The manual alias and join are (a) to make the + # condition nice (sqla prefers an EXISTS) and to make the columns play nice + # when foreign_class == language_class. local_relation_name = relation_name + '_local' + language_class_a = aliased(language_class) setattr(foreign_class, local_relation_name, relationship(Translations, primaryjoin=and_( foreign_class.id == Translations.object_id, - Translations._language_identifier == - bindparam('_default_language', required=True), + Translations.language_id == select( + [language_class_a.id], + language_class_a.identifier == + bindparam('_default_language', required=True), + ), ), uselist=False, # TODO MORESO HERE @@ -153,11 +162,7 @@ TODO remove this requirement return Translations class MultilangSession(Session): - """A tiny Session subclass that adds support for a default language. - - Change the default_language attribute to whatever language's IDENTIFIER you - would like to be the default. - """ + """A tiny Session subclass that adds support for a default language.""" default_language = 'en' def execute(self, clause, params=None, *args, **kwargs): diff --git a/pokedex/db/tables.py b/pokedex/db/tables.py index 0c2f5d3..7d1cd5c 100644 --- a/pokedex/db/tables.py +++ b/pokedex/db/tables.py @@ -93,25 +93,6 @@ class LanguageSpecific(object): return Column(Integer, ForeignKey('languages.id'), primary_key=True, nullable=False, info=dict(description="The language")) -class LanguageSpecificColumn(object): - """A column that will not appear in the table it's defined in, but in a related one""" - _ordering = [1] - def __init__(self, *args, **kwargs): - self.args = args - self.plural = kwargs.pop('plural') - self.kwargs = kwargs - self.order = self._ordering[0] - self._ordering[0] += 1 - - def makeSAColumn(self): - return Column(*self.args, **self.kwargs) - -class ProseColumn(LanguageSpecificColumn): - """A column that will appear in the corresponding _prose table""" - -class TextColumn(LanguageSpecificColumn): - """A column that will appear in the corresponding _text table""" - ### Need Language first, to create the partial() below @@ -132,11 +113,14 @@ class Language(TableBase): info=dict(description=u"True iff games are produced in the language.")) order = Column(Integer, nullable=True, info=dict(description=u"Order for sorting in foreign name lists.")) - name = TextColumn(Unicode(16), nullable=False, index=True, plural='names', - info=dict(description="The name", format='plaintext', official=True)) create_translation_table = partial(multilang.create_translation_table, language_class=Language) +create_translation_table('language_texts', Language, 'names', + name = Column(Unicode(16), nullable=False, index=True, + info=dict(description="The name", format='plaintext', official=True)), +) + ### The actual tables class Ability(TableBase): @@ -172,8 +156,11 @@ class AbilityChangelog(TableBase): info=dict(description="The ID of the ability that changed")) changed_in_version_group_id = Column(Integer, ForeignKey('version_groups.id'), nullable=False, info=dict(description="The ID of the version group in which the ability changed")) - effect = ProseColumn(markdown.MarkdownColumn(255), plural='effects', nullable=False, + +create_translation_table('ability_changelog_prose', AbilityChangelog, 'prose', + effect = Column(markdown.MarkdownColumn(255), nullable=False, info=dict(description="A description of the old behavior", format='markdown')) +) class AbilityFlavorText(TableBase, LanguageSpecific): u"""In-game flavor text of an ability @@ -222,8 +209,11 @@ class BerryFirmness(TableBase): info=dict(description="A unique ID for this firmness")) identifier = Column(Unicode(10), nullable=False, info=dict(description="An identifier", format='identifier')) - name = TextColumn(Unicode(10), nullable=False, index=True, plural='names', - info=dict(description="The name", format='plaintext', official=True)) + +create_translation_table('berry_firmness_texts', BerryFirmness, 'names', + name = Column(Unicode(10), nullable=False, index=True, + info=dict(description="The name", format='plaintext', official=True)), +) class BerryFlavor(TableBase): u"""A Berry flavor level. @@ -256,10 +246,13 @@ class ContestEffect(TableBase): info=dict(description="The base number of hearts the user of this move gets")) jam = Column(SmallInteger, nullable=False, info=dict(description="The base number of hearts the user's opponent loses")) - flavor_text = ProseColumn(Unicode(64), plural='flavor_texts', nullable=False, - info=dict(description="The in-game description of this effect", official=True, format='gametext')) - effect = ProseColumn(Unicode(255), plural='effects', nullable=False, - info=dict(description="A detailed description of the effect", format='plaintext')) + +create_translation_table('contest_effect_prose', ContestEffect, 'prose', + effect = Column(Unicode(255), nullable=False, + info=dict(description="A detailed description of the effect", format='plaintext')), + flavor_text = Column(Unicode(64), nullable=False, + info=dict(description="The in-game description of this effect", official=True, format='gametext')), +) class ContestType(TableBase): u"""A Contest type, such as "cool" or "smart", and their associated Berry flavors and Pokéblock colors. @@ -270,12 +263,15 @@ class ContestType(TableBase): info=dict(description="A unique ID for this Contest type")) identifier = Column(Unicode(6), nullable=False, info=dict(description="An identifier", format='identifier')) - flavor = TextColumn(Unicode(6), nullable=False, plural='flavors', - info=dict(description="The name of the corresponding Berry flavor", official=True, format='plaintext')) - color = TextColumn(Unicode(6), nullable=False, plural='colors', - info=dict(description=u"The name of the corresponding Pokéblock color", official=True, format='plaintext')) - name = TextColumn(Unicode(6), nullable=False, index=True, plural='names', - info=dict(description="The name", format='plaintext', official=True)) + +create_translation_table('contest_type_texts', ContestType, 'names', + flavor = Column(Unicode(6), nullable=False, + info=dict(description="The name of the corresponding Berry flavor", official=True, format='plaintext')), + color = Column(Unicode(6), nullable=False, + info=dict(description=u"The name of the corresponding Pokéblock color", official=True, format='plaintext')), + name = Column(Unicode(6), nullable=False, index=True, + info=dict(description="The name", format='plaintext', official=True)), +) class EggGroup(TableBase): u"""An Egg group. Usually, two Pokémon can breed if they share an Egg Group. @@ -288,8 +284,11 @@ class EggGroup(TableBase): info=dict(description="A unique ID for this group")) identifier = Column(Unicode(16), nullable=False, info=dict(description=u"An identifier.", format='identifier')) - name = ProseColumn(Unicode(16), nullable=False, index=True, plural='names', - info=dict(description="The name", format='plaintext', official=False)) + +create_translation_table('egg_group_prose', EggGroup, 'names', + name = Column(Unicode(16), nullable=False, index=True, + info=dict(description="The name", format='plaintext', official=False)), +) class Encounter(TableBase): u"""Encounters with wild Pokémon. @@ -341,8 +340,11 @@ class EncounterCondition(TableBase): info=dict(description="A unique ID for this condition")) identifier = Column(Unicode(64), nullable=False, info=dict(description="An identifier", format='identifier')) - name = ProseColumn(Unicode(64), nullable=False, index=True, plural='names', - info=dict(description="The name", format='plaintext', official=False)) + +create_translation_table('encounter_condition_prose', EncounterCondition, 'prose', + name = Column(Unicode(64), nullable=False, index=True, + info=dict(description="The name", format='plaintext', official=False)), +) class EncounterConditionValue(TableBase): u"""A possible state for a condition; for example, the state of 'swarm' could be 'swarm' or 'no swarm'. @@ -358,8 +360,11 @@ class EncounterConditionValue(TableBase): info=dict(description="An identifier", format='identifier')) is_default = Column(Boolean, nullable=False, info=dict(description='Set if this value is the default state for the condition')) - name = ProseColumn(Unicode(64), nullable=False, index=True, plural='names', - info=dict(description="The name", format='plaintext', official=False)) + +create_translation_table('encounter_condition_value_prose', EncounterConditionValue, 'prose', + name = Column(Unicode(64), nullable=False, index=True, + info=dict(description="The name", format='plaintext', official=False)), +) class EncounterConditionValueMap(TableBase): u"""Maps encounters to the specific conditions under which they occur. @@ -380,8 +385,11 @@ class EncounterTerrain(TableBase): info=dict(description="A unique ID for the terrain")) identifier = Column(Unicode(64), nullable=False, info=dict(description="An identifier", format='identifier')) - name = ProseColumn(Unicode(64), nullable=False, index=True, plural='names', - info=dict(description="The name", format='plaintext', official=False)) + +create_translation_table('encounter_terrain_prose', EncounterTerrain, 'prose', + name = Column(Unicode(64), nullable=False, index=True, + info=dict(description="The name", format='plaintext', official=False)), +) class EncounterSlot(TableBase): u"""An abstract "slot" within a terrain, associated with both some set of conditions and a rarity. @@ -422,8 +430,11 @@ class EvolutionTrigger(TableBase): info=dict(description="A numeric ID")) identifier = Column(Unicode(16), nullable=False, info=dict(description="An identifier", format='identifier')) - name = ProseColumn(Unicode(16), nullable=False, index=True, plural='names', - info=dict(description="The name", format='plaintext', official=False)) + +create_translation_table('evolution_trigger_prose', EvolutionTrigger, 'prose', + name = Column(Unicode(16), nullable=False, index=True, + info=dict(description="The name", format='plaintext', official=False)), +) class Experience(TableBase): u"""EXP needed for a certain level with a certain growth rate @@ -449,8 +460,11 @@ class Generation(TableBase): info=dict(description=u"ID of the Pokédex this generation's main games use by default")) identifier = Column(Unicode(16), nullable=False, info=dict(description=u'An identifier', format='identifier')) - name = TextColumn(Unicode(16), nullable=False, index=True, plural='names', - info=dict(description="The name", format='plaintext', official=True)) + +create_translation_table('generation_texts', Generation, 'names', + name = Column(Unicode(16), nullable=False, index=True, + info=dict(description="The name", format='plaintext', official=True)), +) class GrowthRate(TableBase): u"""Growth rate of a Pokémon, i.e. the EXP → level function. @@ -463,8 +477,11 @@ class GrowthRate(TableBase): info=dict(description="An identifier", format='identifier')) formula = Column(Unicode(500), nullable=False, info=dict(description="The formula", format='latex')) - name = ProseColumn(Unicode(20), nullable=False, index=True, plural='names', - info=dict(description="The name", format='plaintext', official=False)) + +create_translation_table('growth_rate_prose', GrowthRate, 'prose', + name = Column(Unicode(20), nullable=False, index=True, + info=dict(description="The name", format='plaintext', official=False)), +) class Item(TableBase): u"""An Item from the games, like "Poké Ball" or "Bicycle". @@ -483,12 +500,6 @@ class Item(TableBase): info=dict(description=u"Power of the move Fling when used with this item.")) fling_effect_id = Column(Integer, ForeignKey('item_fling_effects.id'), nullable=True, info=dict(description=u"ID of the fling-effect of the move Fling when used with this item. Note that these are different from move effects.")) - short_effect = ProseColumn(Unicode(256), plural='short_effects', nullable=False, - info=dict(description="A short summary of the effect", format='plaintext')) - effect = ProseColumn(markdown.MarkdownColumn(5120), plural='effects', nullable=False, - info=dict(description=u"Detailed description of the item's effect.", format='markdown')) - name = TextColumn(Unicode(20), nullable=False, index=True, plural='names', - info=dict(description="The name", format='plaintext', official=True)) @property def appears_underground(self): @@ -496,6 +507,17 @@ class Item(TableBase): """ return any(flag.identifier == u'underground' for flag in self.flags) +create_translation_table('item_texts', Item, 'names', + name = Column(Unicode(20), nullable=False, index=True, + info=dict(description="The name", format='plaintext', official=True)), +) +create_translation_table('item_prose', Item, 'prose', + short_effect = Column(Unicode(256), nullable=False, + info=dict(description="A short summary of the effect", format='plaintext')), + effect = Column(markdown.MarkdownColumn(5120), nullable=False, + info=dict(description=u"Detailed description of the item's effect.", format='markdown')), +) + class ItemCategory(TableBase): u"""An item category """ @@ -508,8 +530,11 @@ class ItemCategory(TableBase): info=dict(description="ID of the pocket these items go to")) identifier = Column(Unicode(16), nullable=False, info=dict(description="An identifier", format='identifier')) - name = ProseColumn(Unicode(16), nullable=False, index=True, plural='names', - info=dict(description="The name", format='plaintext', official=False)) + +create_translation_table('item_category_prose', ItemCategory, 'prose', + name = Column(Unicode(16), nullable=False, index=True, + info=dict(description="The name", format='plaintext', official=False)), +) class ItemFlag(TableBase): u"""An item attribute such as "consumable" or "holdable". @@ -520,10 +545,13 @@ class ItemFlag(TableBase): info=dict(description="A numeric ID")) identifier = Column(Unicode(24), nullable=False, info=dict(description="Identifier of the flag", format='identifier')) - description = ProseColumn(Unicode(64), plural='descriptions', nullable=False, - info=dict(description="Short description of the flag", format='plaintext')) - name = ProseColumn(Unicode(24), nullable=False, index=True, plural='names', - info=dict(description="The name", format='plaintext', official=False)) + +create_translation_table('item_flag_prose', ItemFlag, 'prose', + name = Column(Unicode(24), nullable=False, index=True, + info=dict(description="The name", format='plaintext', official=False)), + description = Column(Unicode(64), nullable=False, + info=dict(description="Short description of the flag", format='plaintext')), +) class ItemFlagMap(TableBase): u"""Maps an item flag to its item. @@ -553,8 +581,11 @@ class ItemFlingEffect(TableBase): __singlename__ = 'item_fling_effect' id = Column(Integer, primary_key=True, nullable=False, info=dict(description="A numeric ID")) - effect = ProseColumn(Unicode(255), plural='effects', nullable=False, - info=dict(description="Description of the effect", format='plaintext')) + +create_translation_table('item_fling_effect_prose', ItemFlingEffect, 'prose', + effect = Column(Unicode(255), nullable=False, + info=dict(description="Description of the effect", format='plaintext')), +) class ItemInternalID(TableBase): u"""The internal ID number a game uses for an item @@ -576,8 +607,11 @@ class ItemPocket(TableBase): info=dict(description="A numeric ID")) identifier = Column(Unicode(16), nullable=False, info=dict(description="An identifier of this pocket", format='identifier')) - name = TextColumn(Unicode(16), nullable=False, index=True, plural='names', - info=dict(description="The name", format='plaintext', official=True)) + +create_translation_table('item_pocket_texts', ItemPocket, 'names', + name = Column(Unicode(16), nullable=False, index=True, + info=dict(description="The name", format='plaintext', official=True)), +) class Location(TableBase): u"""A place in the Pokémon world @@ -590,8 +624,11 @@ class Location(TableBase): info=dict(description="ID of the region this location is in")) identifier = Column(Unicode(64), nullable=False, info=dict(description="An identifier", format='identifier')) - name = TextColumn(Unicode(64), nullable=False, index=True, plural='names', - info=dict(description="The name", format='plaintext', official=True)) + +create_translation_table('location_texts', Location, 'names', + name = Column(Unicode(64), nullable=False, index=True, + info=dict(description="The name", format='plaintext', official=True)), +) class LocationArea(TableBase): u"""A sub-area of a location @@ -606,8 +643,11 @@ class LocationArea(TableBase): info=dict(description="ID the games ude for this area")) identifier = Column(Unicode(64), nullable=True, info=dict(description="An identifier", format='identifier')) - name = ProseColumn(Unicode(64), nullable=False, index=True, plural='names', - info=dict(description="The name", format='plaintext', official=False)) + +create_translation_table('location_area_prose', LocationArea, 'prose', + name = Column(Unicode(64), nullable=False, index=True, + info=dict(description="The name", format='plaintext', official=False)), +) class LocationAreaEncounterRate(TableBase): # XXX: What's this exactly? Someone add the docstring & revise the descriptions @@ -659,8 +699,11 @@ class MoveBattleStyle(TableBase): info=dict(description="A numeric ID")) identifier = Column(Unicode(8), nullable=False, info=dict(description="An identifier", format='identifier')) - name = ProseColumn(Unicode(8), nullable=False, index=True, plural='names', - info=dict(description="The name", format='plaintext', official=False)) + +create_translation_table('move_battle_style_prose', MoveBattleStyle, 'prose', + name = Column(Unicode(8), nullable=False, index=True, + info=dict(description="The name", format='plaintext', official=False)), +) class MoveEffectCategory(TableBase): u"""Category of a move effect @@ -673,8 +716,11 @@ class MoveEffectCategory(TableBase): info=dict(description="An identifier", format='identifier')) can_affect_user = Column(Boolean, nullable=False, info=dict(description="Set if the user can be affected")) - name = ProseColumn(Unicode(64), nullable=False, index=True, plural='names', - info=dict(description="The name", format='plaintext', official=False)) + +create_translation_table('move_effect_category_prose', MoveEffectCategory, 'prose', + name = Column(Unicode(64), nullable=False, index=True, + info=dict(description="The name", format='plaintext', official=False)), +) class MoveEffectCategoryMap(TableBase): u"""Maps a move effect category to a move effect @@ -696,10 +742,13 @@ class MoveDamageClass(TableBase): info=dict(description="A numeric ID")) identifier = Column(Unicode(16), nullable=False, info=dict(description="An identifier", format='identifier')) - description = ProseColumn(Unicode(64), plural='descriptions', nullable=False, - info=dict(description="A description of the class", format='plaintext')) - name = ProseColumn(Unicode(16), nullable=False, index=True, plural='names', - info=dict(description="The name", format='plaintext', official=False)) + +create_translation_table('move_damage_class_prose', MoveDamageClass, 'prose', + name = Column(Unicode(16), nullable=False, index=True, + info=dict(description="The name", format='plaintext', official=False)), + description = Column(Unicode(64), nullable=False, + info=dict(description="A description of the class", format='plaintext')), +) class MoveEffect(TableBase): u"""An effect of a move @@ -708,10 +757,13 @@ class MoveEffect(TableBase): __singlename__ = 'move_effect' id = Column(Integer, primary_key=True, nullable=False, info=dict(description="A numeric ID")) - short_effect = ProseColumn(Unicode(256), plural='short_effects', nullable=False, - info=dict(description="A short summary of the effect", format='plaintext')) - effect = ProseColumn(Unicode(5120), plural='effects', nullable=False, - info=dict(description="A detailed description of the effect", format='plaintext')) + +create_translation_table('move_effect_prose', MoveEffect, 'prose', + short_effect = Column(Unicode(256), nullable=False, + info=dict(description="A short summary of the effect", format='plaintext')), + effect = Column(Unicode(5120), nullable=False, + info=dict(description="A detailed description of the effect", format='plaintext')), +) class MoveEffectChangelog(TableBase): """History of changes to move effects across main game versions.""" @@ -723,14 +775,17 @@ class MoveEffectChangelog(TableBase): info=dict(description="The ID of the effect that changed")) changed_in_version_group_id = Column(Integer, ForeignKey('version_groups.id'), nullable=False, info=dict(description="The ID of the version group in which the effect changed")) - effect = ProseColumn(markdown.MarkdownColumn(512), plural='effects', nullable=False, - info=dict(description="A description of the old behavior", format='markdown')) __table_args__ = ( UniqueConstraint(effect_id, changed_in_version_group_id), {}, ) +create_translation_table('move_effect_changelog_prose', MoveEffectChangelog, 'prose', + effect = Column(markdown.MarkdownColumn(512), nullable=False, + info=dict(description="A description of the old behavior", format='markdown')), +) + class MoveFlag(TableBase): u"""Maps a move flag to a move """ @@ -751,10 +806,13 @@ class MoveFlagType(TableBase): info=dict(description="A numeric ID")) identifier = Column(Unicode(32), nullable=False, info=dict(description="A short identifier for the flag", format='identifier')) - description = ProseColumn(markdown.MarkdownColumn(128), plural='descriptions', nullable=False, - info=dict(description="A short description of the flag", format='markdown')) - name = ProseColumn(Unicode(32), nullable=False, index=True, plural='names', - info=dict(description="The name", format='plaintext', official=False)) + +create_translation_table('move_flag_type_prose', MoveFlagType, 'prose', + name = Column(Unicode(32), nullable=False, index=True, + info=dict(description="The name", format='plaintext', official=False)), + description = Column(markdown.MarkdownColumn(128), nullable=False, + info=dict(description="A short description of the flag", format='markdown')), +) class MoveFlavorText(TableBase, LanguageSpecific): u"""In-game description of a move @@ -807,8 +865,11 @@ class MoveMetaAilment(TableBase): info=dict(description="A numeric ID")) identifier = Column(Unicode(24), nullable=False, info=dict(description="An identifier", format='identifier')) - name = TextColumn(Unicode(24), nullable=False, index=True, plural='names', - info=dict(description="The name", format='plaintext', official=True)) + +create_translation_table('move_meta_ailment_texts', MoveMetaAilment, 'names', + name = Column(Unicode(24), nullable=False, index=True, + info=dict(description="The name", format='plaintext', official=True)), +) class MoveMetaCategory(TableBase): u"""Very general categories that loosely group move effects.""" @@ -816,8 +877,11 @@ class MoveMetaCategory(TableBase): __singlename__ = 'move_meta_category' 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")) + +create_translation_table('move_meta_category_prose', MoveMetaCategory, 'prose', + description = Column(Unicode(64), nullable=False, + info=dict(description="A description of the category")), +) class MoveMetaStatChange(TableBase): u"""Stat changes moves (may) make.""" @@ -838,10 +902,13 @@ class MoveTarget(TableBase): info=dict(description="A numeric ID")) identifier = Column(Unicode(32), nullable=False, info=dict(description="An identifier", format='identifier')) - description = ProseColumn(Unicode(128), plural='descriptions', nullable=False, - info=dict(description="A description", format='plaintext')) - name = ProseColumn(Unicode(32), nullable=False, index=True, plural='names', - info=dict(description="The name", format='plaintext', official=False)) + +create_translation_table('move_target_prose', MoveTarget, 'prose', + name = Column(Unicode(32), nullable=False, index=True, + info=dict(description="The name", format='plaintext', official=False)), + description = Column(Unicode(128), nullable=False, + info=dict(description="A description", format='plaintext')), +) class Move(TableBase): u"""A Move: technique or attack a Pokémon can learn to use @@ -923,8 +990,6 @@ class Nature(TableBase): info=dict(description=u"ID of the Berry flavor the Pokémon hates (if likes_flavor_id is the same, the effects cancel out)")) likes_flavor_id = Column(Integer, ForeignKey('contest_types.id'), nullable=False, info=dict(description=u"ID of the Berry flavor the Pokémon likes (if hates_flavor_id is the same, the effects cancel out)")) - name = TextColumn(Unicode(8), nullable=False, index=True, plural='names', - info=dict(description="The name", format='plaintext', official=True)) @property def is_neutral(self): @@ -933,6 +998,11 @@ class Nature(TableBase): """ return self.increased_stat_id == self.decreased_stat_id +create_translation_table('nature_texts', Nature, 'names', + name = Column(Unicode(8), nullable=False, index=True, + info=dict(description="The name", format='plaintext', official=True)), +) + class NatureBattleStylePreference(TableBase): u"""Battle Palace move preference @@ -969,8 +1039,11 @@ class PokeathlonStat(TableBase): info=dict(description="A numeric ID")) identifier = Column(Unicode(8), nullable=False, info=dict(description="An identifier", format='identifier')) - name = TextColumn(Unicode(8), nullable=False, index=True, plural='names', - info=dict(description="The name", format='plaintext', official=True)) + +create_translation_table('pokeathlon_stat_texts', PokeathlonStat, 'names', + name = Column(Unicode(8), nullable=False, index=True, + info=dict(description="The name", format='plaintext', official=True)), +) class Pokedex(TableBase): u"""A collection of Pokémon species ordered in a particular way @@ -983,10 +1056,13 @@ class Pokedex(TableBase): info=dict(description=u"ID of the region this Pokédex is used in, or None if it's global")) identifier = Column(Unicode(16), nullable=False, info=dict(description=u"An identifier", format='identifier')) - description = ProseColumn(Unicode(512), plural='descriptions', nullable=False, - info=dict(description=u"A longer description of the Pokédex", format='plaintext')) - name = ProseColumn(Unicode(16), nullable=False, index=True, plural='names', - info=dict(description="The name", format='plaintext', official=False)) + +create_translation_table('pokedex_prose', Pokedex, 'prose', + name = Column(Unicode(16), nullable=False, index=True, + info=dict(description="The name", format='plaintext', official=False)), + description = Column(Unicode(512), nullable=False, + info=dict(description=u"A longer description of the Pokédex", format='plaintext')), +) class Pokemon(TableBase): u"""A species of Pokémon. The core to this whole mess. @@ -1143,8 +1219,11 @@ class PokemonColor(TableBase): info=dict(description=u"ID of the Pokémon")) identifier = Column(Unicode(6), nullable=False, info=dict(description=u"An identifier", format='identifier')) - name = TextColumn(Unicode(6), nullable=False, index=True, plural='names', - info=dict(description="The name", format='plaintext', official=True)) + +create_translation_table('pokemon_color_texts', PokemonColor, 'names', + name = Column(Unicode(6), nullable=False, index=True, + info=dict(description="The name", format='plaintext', official=True)), +) class PokemonDexNumber(TableBase): u"""The number of a Pokémon in a particular Pokédex (e.g. Jigglypuff is #138 in Hoenn's 'dex) @@ -1237,8 +1316,6 @@ class PokemonForm(TableBase): info=dict(description=u'Set for exactly one form used as the default for each species.')) order = Column(Integer, nullable=False, autoincrement=False, info=dict(description=u'The order in which forms should be sorted. Multiple forms may have equal order, in which case they should fall back on sorting by name.')) - name = TextColumn(Unicode(16), nullable=False, index=True, plural='names', - info=dict(description="The name", format='plaintext', official=True)) @property def pokemon(self): @@ -1269,20 +1346,29 @@ class PokemonForm(TableBase): else: return self.form_base_pokemon.name +create_translation_table('pokemon_form_texts', PokemonForm, 'names', + name = Column(Unicode(16), nullable=False, index=True, + info=dict(description="The name", format='plaintext', official=True)), +) + class PokemonFormGroup(TableBase): u"""Information about a Pokémon's forms as a group.""" __tablename__ = 'pokemon_form_groups' __singlename__ = 'pokemon_form_group' pokemon_id = Column(Integer, ForeignKey('pokemon.id'), primary_key=True, nullable=False, autoincrement=False, info=dict(description=u"ID of the base form Pokémon")) - term = ProseColumn(Unicode(16), plural='terms', nullable=True, - info=dict(description=u"The term for this Pokémon's forms, e.g. \"Cloak\" for Burmy or \"Forme\" for Deoxys.", official=True, format='plaintext')) is_battle_only = Column(Boolean, nullable=False, info=dict(description=u"Set iff the forms only change in battle")) - description = ProseColumn(markdown.MarkdownColumn(1024), plural='descriptions', nullable=False, - info=dict(description=u"Description of how the forms work", format='markdown')) +# FIXME remooove PokemonFormGroup.id = PokemonFormGroup.pokemon_id +create_translation_table('pokemon_form_group_prose', PokemonFormGroup, 'prose', + term = Column(Unicode(16), nullable=True, + info=dict(description=u"The term for this Pokémon's forms, e.g. \"Cloak\" for Burmy or \"Forme\" for Deoxys.", official=True, format='plaintext')), + description = Column(markdown.MarkdownColumn(1024), nullable=False, + info=dict(description=u"Description of how the forms work", format='markdown')), +) + class PokemonFormPokeathlonStat(TableBase): u"""A Pokémon form's performance in one Pokéathlon stat.""" __tablename__ = 'pokemon_form_pokeathlon_stats' @@ -1306,8 +1392,11 @@ class PokemonHabitat(TableBase): info=dict(description=u"A numeric ID")) identifier = Column(Unicode(16), nullable=False, info=dict(description=u"An identifier", format='identifier')) - name = TextColumn(Unicode(16), nullable=False, index=True, plural='names', - info=dict(description="The name", format='plaintext', official=True)) + +create_translation_table('pokemon_habitat_texts', PokemonHabitat, 'names', + name = Column(Unicode(16), nullable=False, index=True, + info=dict(description="The name", format='plaintext', official=True)), +) class PokemonInternalID(TableBase): u"""The number of a Pokémon a game uses internally @@ -1364,10 +1453,14 @@ class PokemonMoveMethod(TableBase): info=dict(description=u"A numeric ID")) identifier = Column(Unicode(64), nullable=False, info=dict(description=u"An identifier", format='identifier')) - description = ProseColumn(Unicode(255), plural='descriptions', nullable=False, - info=dict(description=u"A detailed description of how the method works", format='plaintext')) - name = ProseColumn(Unicode(64), nullable=False, index=True, plural='names', - info=dict(description="The name", format='plaintext', official=False)) + +create_translation_table('pokemon_move_method_prose', PokemonMoveMethod, 'prose', + name = Column(Unicode(64), nullable=False, index=True, + info=dict(description="The name", format='plaintext', official=False)), + description = Column(Unicode(255), nullable=False, + info=dict(description=u"A detailed description of how the method works", format='plaintext')), +) + class PokemonShape(TableBase): u"""The shape of a Pokémon's body, as used in generation IV Pokédexes. @@ -1378,10 +1471,13 @@ class PokemonShape(TableBase): info=dict(description=u"A numeric ID")) identifier = Column(Unicode(24), nullable=False, info=dict(description=u"An identifier", format='identifier')) - awesome_name = ProseColumn(Unicode(16), plural='awesome_names', nullable=False, - info=dict(description=u"A splendiferous name of the body shape", format='plaintext')) - name = ProseColumn(Unicode(24), nullable=False, index=True, plural='names', - info=dict(description="The name", format='plaintext', official=False)) + +create_translation_table('pokemon_shape_prose', PokemonShape, 'prose', + awesome_name = Column(Unicode(16), nullable=False, + info=dict(description=u"A splendiferous name of the body shape", format='plaintext')), + name = Column(Unicode(24), nullable=False, index=True, + info=dict(description="The name", format='plaintext', official=False)), +) class PokemonStat(TableBase): u"""A stat value of a Pokémon @@ -1416,8 +1512,11 @@ class Region(TableBase): info=dict(description=u"A numeric ID")) identifier = Column(Unicode(16), nullable=False, info=dict(description=u"An identifier", format='identifier')) - name = TextColumn(Unicode(16), nullable=False, index=True, plural='names', - info=dict(description="The name", format='plaintext', official=True)) + +create_translation_table('region_texts', Region, 'names', + name = Column(Unicode(16), nullable=False, index=True, + info=dict(description="The name", format='plaintext', official=True)), +) class Stat(TableBase): u"""A Stat, such as Attack or Speed @@ -1430,8 +1529,11 @@ class Stat(TableBase): info=dict(description=u"For offensive and defensive stats, the damage this stat relates to; otherwise None (the NULL value)")) identifier = Column(Unicode(16), nullable=False, info=dict(description=u"An identifier", format='identifier')) - name = TextColumn(Unicode(16), nullable=False, index=True, plural='names', - info=dict(description="The name", format='plaintext', official=True)) + +create_translation_table('stat_texts', Stat, 'names', + name = Column(Unicode(16), nullable=False, index=True, + info=dict(description="The name", format='plaintext', official=True)), +) class StatHint(TableBase): u"""Flavor text for genes that appears in a Pokémon's summary. Sometimes @@ -1445,8 +1547,11 @@ class StatHint(TableBase): 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")) - message = TextColumn(Unicode(24), plural='messages', nullable=False, index=True, unique=True, - info=dict(description=u"The text displayed", official=True, format='plaintext')) + +create_translation_table('stat_hint_texts', StatHint, 'names', + message = Column(Unicode(24), nullable=False, index=True, + info=dict(description=u"The text displayed", official=True, format='plaintext')), +) class SuperContestCombo(TableBase): u"""Combo of two moves in a Super Contest. @@ -1466,8 +1571,12 @@ class SuperContestEffect(TableBase): info=dict(description=u"This effect's unique ID.")) appeal = Column(SmallInteger, nullable=False, info=dict(description=u"The number of hearts the user gains.")) - flavor_text = ProseColumn(Unicode(64), plural='flavor_texts', nullable=False, - info=dict(description=u"A description of the effect.", format='plaintext')) + +create_translation_table('super_contest_effect_prose', SuperContestEffect, 'prose', + flavor_text = Column(Unicode(64), nullable=False, + info=dict(description=u"A description of the effect.", format='plaintext')), +) + class TypeEfficacy(TableBase): u"""The damage multiplier used when a move of a particular type damages a @@ -1493,8 +1602,11 @@ class Type(TableBase): info=dict(description=u"The ID of the generation this type first appeared in.")) damage_class_id = Column(Integer, ForeignKey('move_damage_classes.id'), nullable=True, info=dict(description=u"The ID of the damage class this type's moves had before Generation IV, null if not applicable (e.g. ???).")) - name = TextColumn(Unicode(12), nullable=False, index=True, plural='names', - info=dict(description="The name", format='plaintext', official=True)) + +create_translation_table('type_texts', Type, 'names', + name = Column(Unicode(12), nullable=False, index=True, + info=dict(description="The name", format='plaintext', official=True)), +) class VersionGroup(TableBase): u"""A group of versions, containing either two paired versions (such as Red @@ -1526,8 +1638,11 @@ class Version(TableBase): info=dict(description=u"The ID of the version group this game belongs to.")) identifier = Column(Unicode(32), nullable=False, info=dict(description=u'And identifier', format='identifier')) - name = TextColumn(Unicode(32), nullable=False, index=True, plural='names', - info=dict(description="The name", format='plaintext', official=True)) + +create_translation_table('version_texts', Version, 'names', + name = Column(Unicode(32), nullable=False, index=True, + info=dict(description="The name", format='plaintext', official=True)), +) ### Relations down here, to avoid ordering problems @@ -1848,7 +1963,7 @@ Type.pokemon = relation(Pokemon, secondary=PokemonType.__table__, )#back_populates='types') Type.moves = relation(Move, back_populates='type', order_by=Move.id) -Version.version_group = relation(VersionGroup, back_populates='versions') +Version.version_group = relation(VersionGroup)#, back_populates='versions') Version.generation = association_proxy('version_group', 'generation') VersionGroup.versions = relation(Version, order_by=Version.id, back_populates='version_group') @@ -1858,137 +1973,17 @@ VersionGroup.regions = association_proxy('version_group_regions', 'region') VersionGroup.pokedex = relation(Pokedex, back_populates='version_groups') -### Add text/prose tables - -default_lang = u'en' - -def makeTextTable(foreign_table_class, table_suffix_plural, table_suffix_singular, columns, lazy, Language=Language): - # With "Language", we'd have two language_id. So, rename one to 'lang' - foreign_key_name = foreign_table_class.__singlename__ - if foreign_key_name == 'language': - foreign_key_name = 'lang' - - table_name = foreign_table_class.__singlename__ + '_' + table_suffix_plural - - class TranslatedStringsTable(object): - __tablename__ = table_name - _attrname = table_suffix_plural - _language_identifier = association_proxy('language', 'identifier') - - for column_name, column_name_plural, column in columns: - column.name = column_name - if not column.nullable: - # A Python side default value, so that the strings can be set - # one by one without the DB complaining about missing values - column.default = ColumnDefault(u'') - - table = Table(table_name, foreign_table_class.__table__.metadata, - Column(foreign_key_name + '_id', Integer, ForeignKey(foreign_table_class.id), - primary_key=True, nullable=False), - Column('language_id', Integer, ForeignKey(Language.id), - primary_key=True, index=True, nullable=False), - *(column for name, plural, column in columns) - ) - - mapper(TranslatedStringsTable, table, - properties={ - "object_id": synonym(foreign_key_name + '_id'), - "language": relation(Language, - primaryjoin=table.c.language_id == Language.id, - ), - foreign_key_name: relation(foreign_table_class, - primaryjoin=(foreign_table_class.id == table.c[foreign_key_name + "_id"]), - backref=backref(table_suffix_plural, - collection_class=attribute_mapped_collection('_language_identifier'), - lazy=lazy, - ), - ), - }, - ) - - # The relation to the object - TranslatedStringsTable.object = getattr(TranslatedStringsTable, foreign_key_name) - - # Link the tables themselves, so we can get them if needed - TranslatedStringsTable.foreign_table_class = foreign_table_class - setattr(foreign_table_class, table_suffix_singular + '_table', TranslatedStringsTable) - - for column_name, column_name_plural, column in columns: - # Provide a property with all the names, and an English accessor - # for backwards compatibility - def text_string_creator(language_code, string): - row = TranslatedStringsTable() - row._language_identifier = language_code - setattr(row, column_name, string) - return row - - setattr(foreign_table_class, column_name_plural, - association_proxy(table_suffix_plural, column_name, creator=text_string_creator)) - setattr(foreign_table_class, column_name, DefaultLangProperty(column_name_plural)) - - if column_name == 'name': - foreign_table_class.name_table = TranslatedStringsTable - - compile_mappers() - return TranslatedStringsTable - -class DefaultLangProperty(object): - def __init__(self, column_name): - self.column_name = column_name - - def __get__(self, instance, cls): - if instance: - return getattr(instance, self.column_name)[default_lang] - else: - # TODO I think this is kind of broken - return getattr(cls, self.column_name)[default_lang] - - def __set__(self, instance, value): - getattr(instance, self.colname)[default_lang] = value - - def __delete__(self, instance): - del getattr(instance, self.colname)[default_lang] - -for table in list(table_classes): - # Find all the language-specific columns, keeping them in the order they - # were defined - all_columns = [] - for colname in dir(table): - column = getattr(table, colname) - if isinstance(column, LanguageSpecificColumn): - all_columns.append((colname, column)) - delattr(table, colname) - all_columns.sort(key=lambda pair: pair[1].order) - - # Break them into text and prose columns - text_columns = [] - prose_columns = [] - for colname, column in all_columns: - spec = colname, column.plural, column.makeSAColumn() - if isinstance(column, TextColumn): - text_columns.append(spec) - elif isinstance(column, ProseColumn): - prose_columns.append(spec) - - if (text_columns or prose_columns) and issubclass(table, LanguageSpecific): - raise AssertionError("Language-specific table %s shouldn't have explicit language-specific columns" % table) - - if text_columns: - string_table = makeTextTable(table, 'texts', 'text', text_columns, lazy=False) - if prose_columns: - string_table = makeTextTable(table, 'prose', 'prose', prose_columns, lazy='select') - ### Add language relations for table in list(table_classes): if issubclass(table, LanguageSpecific): table.language = relation(Language, primaryjoin=table.language_id == Language.id) -Move.effect = DefaultLangProperty('effects') -Move.effects = markdown.MoveEffectsProperty('effect') -Move.short_effect = DefaultLangProperty('short_effects') -Move.short_effects = markdown.MoveEffectsProperty('short_effect') +Move.effect = markdown.MoveEffectProperty('effect') +Move.effect_map = markdown.MoveEffectProperty('effect_map') +Move.short_effect = markdown.MoveEffectProperty('short_effect') +Move.short_effect_map = markdown.MoveEffectProperty('short_effect_map') -MoveChangelog.effect = DefaultLangProperty('effects') -MoveChangelog.effects = markdown.MoveEffectsProperty('effect') -MoveChangelog.short_effect = DefaultLangProperty('short_effects') -MoveChangelog.short_effects = markdown.MoveEffectsProperty('short_effect') +MoveChangelog.effect = markdown.MoveEffectProperty('effect') +MoveChangelog.effect_map = markdown.MoveEffectProperty('effect_map') +MoveChangelog.short_effect = markdown.MoveEffectProperty('short_effect') +MoveChangelog.short_effect_map = markdown.MoveEffectProperty('short_effect_map') diff --git a/pokedex/lookup.py b/pokedex/lookup.py index 1b99065..a0946cf 100644 --- a/pokedex/lookup.py +++ b/pokedex/lookup.py @@ -225,7 +225,7 @@ class PokedexLookup(object): # Some things also have other languages' names # XXX other language form names..? seen = set() - for language, name in getattr(row, 'names', []).items(): + for language, name in getattr(row, 'name_map', {}).items(): if name in seen: # Don't add the name again as a different # language; no point and it makes spell results From ef1db6029d614a7ef83ab510c80895af1a626561 Mon Sep 17 00:00:00 2001 From: Eevee Date: Wed, 23 Mar 2011 22:39:21 -0700 Subject: [PATCH 11/17] Remove LanguageSpecific. --- pokedex/db/tables.py | 45 +++++++++++++++++--------------------------- 1 file changed, 17 insertions(+), 28 deletions(-) diff --git a/pokedex/db/tables.py b/pokedex/db/tables.py index 7d1cd5c..ae3cb99 100644 --- a/pokedex/db/tables.py +++ b/pokedex/db/tables.py @@ -49,15 +49,6 @@ from inspect import isclass from pokedex.db import markdown, multilang -# A list of all table classes will live in table_classes -table_classes = [] - -class TableMetaclass(DeclarativeMeta): - def __init__(cls, name, bases, attrs): - super(TableMetaclass, cls).__init__(name, bases, attrs) - if hasattr(cls, '__tablename__'): - table_classes.append(cls) - class TableSuperclass(object): """Superclass for declarative tables, to give them some generic niceties like stringification. @@ -83,15 +74,7 @@ class TableSuperclass(object): return unicode(self).encode('utf8') metadata = MetaData() -TableBase = declarative_base(metadata=metadata, cls=TableSuperclass, metaclass=TableMetaclass) - -### Helper classes -class LanguageSpecific(object): - """Mixin for prose and text tables""" - @declared_attr - def language_id(cls): - return Column(Integer, ForeignKey('languages.id'), primary_key=True, nullable=False, - info=dict(description="The language")) +TableBase = declarative_base(metadata=metadata, cls=TableSuperclass) ### Need Language first, to create the partial() below @@ -162,7 +145,7 @@ create_translation_table('ability_changelog_prose', AbilityChangelog, 'prose', info=dict(description="A description of the old behavior", format='markdown')) ) -class AbilityFlavorText(TableBase, LanguageSpecific): +class AbilityFlavorText(TableBase): u"""In-game flavor text of an ability """ __tablename__ = 'ability_flavor_text' @@ -170,6 +153,8 @@ class AbilityFlavorText(TableBase, LanguageSpecific): info=dict(description="The ID of the ability")) version_group_id = Column(Integer, ForeignKey('version_groups.id'), primary_key=True, nullable=False, autoincrement=False, info=dict(description="The ID of the version group this flavor text is taken from")) + language_id = Column(Integer, ForeignKey('languages.id'), primary_key=True, nullable=False, + info=dict(description="The language")) flavor_text = Column(Unicode(64), nullable=False, info=dict(description="The actual flavor text", official=True, format='gametext')) @@ -562,7 +547,7 @@ class ItemFlagMap(TableBase): item_flag_id = Column(Integer, ForeignKey('item_flags.id'), primary_key=True, autoincrement=False, nullable=False, info=dict(description="The ID of the item flag")) -class ItemFlavorText(TableBase, LanguageSpecific): +class ItemFlavorText(TableBase): u"""An in-game description of an item """ __tablename__ = 'item_flavor_text' @@ -571,6 +556,8 @@ class ItemFlavorText(TableBase, LanguageSpecific): info=dict(description="The ID of the item")) version_group_id = Column(Integer, ForeignKey('version_groups.id'), primary_key=True, autoincrement=False, nullable=False, info=dict(description="ID of the version group that sports this text")) + language_id = Column(Integer, ForeignKey('languages.id'), primary_key=True, nullable=False, + info=dict(description="The language")) flavor_text = Column(Unicode(255), nullable=False, info=dict(description="The flavor text itself", official=True, format='gametext')) @@ -814,7 +801,7 @@ create_translation_table('move_flag_type_prose', MoveFlagType, 'prose', info=dict(description="A short description of the flag", format='markdown')), ) -class MoveFlavorText(TableBase, LanguageSpecific): +class MoveFlavorText(TableBase): u"""In-game description of a move """ __tablename__ = 'move_flavor_text' @@ -822,6 +809,8 @@ class MoveFlavorText(TableBase, LanguageSpecific): info=dict(description="ID of the move")) version_group_id = Column(Integer, ForeignKey('version_groups.id'), primary_key=True, nullable=False, autoincrement=False, info=dict(description="ID of the version group this text appears in")) + language_id = Column(Integer, ForeignKey('languages.id'), primary_key=True, nullable=False, + info=dict(description="The language")) flavor_text = Column(Unicode(255), nullable=False, info=dict(description="The flavor text", official=True, format='gametext')) @@ -1283,7 +1272,7 @@ class PokemonEvolution(TableBase): trade_pokemon_id = Column(Integer, ForeignKey('pokemon.id'), nullable=True, info=dict(description=u"The ID of the Pokémon for which this Pokémon must be traded.")) -class PokemonFlavorText(TableBase, LanguageSpecific): +class PokemonFlavorText(TableBase): u"""In-game Pokédex descrption of a Pokémon. """ __tablename__ = 'pokemon_flavor_text' @@ -1291,6 +1280,8 @@ class PokemonFlavorText(TableBase, LanguageSpecific): info=dict(description=u"ID of the Pokémon")) version_id = Column(Integer, ForeignKey('versions.id'), primary_key=True, nullable=False, autoincrement=False, info=dict(description=u"ID of the version that has this flavor text")) + language_id = Column(Integer, ForeignKey('languages.id'), primary_key=True, nullable=False, + info=dict(description="The language")) flavor_text = Column(Unicode(255), nullable=False, info=dict(description=u"ID of the version that has this flavor text", official=True, format='gametext')) @@ -1679,6 +1670,7 @@ Ability.dream_pokemon = relation(Pokemon, AbilityChangelog.changed_in = relation(VersionGroup, backref='ability_changelog') AbilityFlavorText.version_group = relation(VersionGroup) +AbilityFlavorText.language = relation(Language) Berry.berry_firmness = relation(BerryFirmness, backref='berries') Berry.firmness = association_proxy('berry_firmness', 'name') @@ -1732,6 +1724,7 @@ ItemCategory.items = relation(Item, order_by=Item.identifier) ItemCategory.pocket = relation(ItemPocket) ItemFlavorText.version_group = relation(VersionGroup) +ItemFlavorText.language = relation(Language) ItemInternalID.item = relation(Item, backref='internal_ids') ItemInternalID.generation = relation(Generation) @@ -1788,6 +1781,7 @@ MoveEffectChangelog.changed_in = relation(VersionGroup, backref='move_effect_cha MoveFlag.flag = relation(MoveFlagType) MoveFlavorText.version_group = relation(VersionGroup) +MoveFlavorText.language = relation(Language) MoveMeta.category = relation(MoveMetaCategory, backref='move_meta') MoveMeta.ailment = relation(MoveMetaAilment, backref='move_meta') @@ -1902,6 +1896,7 @@ PokemonEvolution.trade_pokemon = relation(Pokemon, ) PokemonFlavorText.version = relation(Version) +PokemonFlavorText.language = relation(Language) PokemonForm.form_base_pokemon = relation(Pokemon, primaryjoin=PokemonForm.form_base_pokemon_id==Pokemon.id) PokemonForm.unique_pokemon = relation(Pokemon, backref=backref('unique_form', uselist=False), @@ -1972,12 +1967,6 @@ VersionGroup.version_group_regions = relation(VersionGroupRegion, backref='versi VersionGroup.regions = association_proxy('version_group_regions', 'region') VersionGroup.pokedex = relation(Pokedex, back_populates='version_groups') - -### Add language relations -for table in list(table_classes): - if issubclass(table, LanguageSpecific): - table.language = relation(Language, primaryjoin=table.language_id == Language.id) - Move.effect = markdown.MoveEffectProperty('effect') Move.effect_map = markdown.MoveEffectProperty('effect_map') Move.short_effect = markdown.MoveEffectProperty('short_effect') From a3e0e4912b848cd9efe20d8e18f19dd7a8f2b267 Mon Sep 17 00:00:00 2001 From: Eevee Date: Thu, 24 Mar 2011 17:17:24 -0700 Subject: [PATCH 12/17] Whoops; preserve column order. --- pokedex/db/tables.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pokedex/db/tables.py b/pokedex/db/tables.py index ae3cb99..aadd245 100644 --- a/pokedex/db/tables.py +++ b/pokedex/db/tables.py @@ -233,10 +233,10 @@ class ContestEffect(TableBase): info=dict(description="The base number of hearts the user's opponent loses")) create_translation_table('contest_effect_prose', ContestEffect, 'prose', - effect = Column(Unicode(255), nullable=False, - info=dict(description="A detailed description of the effect", format='plaintext')), flavor_text = Column(Unicode(64), nullable=False, info=dict(description="The in-game description of this effect", official=True, format='gametext')), + effect = Column(Unicode(255), nullable=False, + info=dict(description="A detailed description of the effect", format='plaintext')), ) class ContestType(TableBase): @@ -250,12 +250,12 @@ class ContestType(TableBase): info=dict(description="An identifier", format='identifier')) create_translation_table('contest_type_texts', ContestType, 'names', + name = Column(Unicode(6), nullable=False, index=True, + info=dict(description="The name", format='plaintext', official=True)), flavor = Column(Unicode(6), nullable=False, info=dict(description="The name of the corresponding Berry flavor", official=True, format='plaintext')), color = Column(Unicode(6), nullable=False, info=dict(description=u"The name of the corresponding Pokéblock color", official=True, format='plaintext')), - name = Column(Unicode(6), nullable=False, index=True, - info=dict(description="The name", format='plaintext', official=True)), ) class EggGroup(TableBase): @@ -1464,10 +1464,10 @@ class PokemonShape(TableBase): info=dict(description=u"An identifier", format='identifier')) create_translation_table('pokemon_shape_prose', PokemonShape, 'prose', - awesome_name = Column(Unicode(16), nullable=False, - info=dict(description=u"A splendiferous name of the body shape", format='plaintext')), name = Column(Unicode(24), nullable=False, index=True, info=dict(description="The name", format='plaintext', official=False)), + awesome_name = Column(Unicode(16), nullable=False, + info=dict(description=u"A splendiferous name of the body shape", format='plaintext')), ) class PokemonStat(TableBase): From 3a6fe6d539eb7fbedeeec7b716f4a686fd690bf2 Mon Sep 17 00:00:00 2001 From: Eevee Date: Thu, 24 Mar 2011 18:46:46 -0700 Subject: [PATCH 13/17] Rename *_texts tables to *_names. --- .../{ability_texts.csv => ability_names.csv} | 0 ...ess_texts.csv => berry_firmness_names.csv} | 0 ..._type_texts.csv => contest_type_names.csv} | 0 ...eration_texts.csv => generation_names.csv} | 0 .../csv/{item_texts.csv => item_names.csv} | 0 ...pocket_texts.csv => item_pocket_names.csv} | 0 ...{language_texts.csv => language_names.csv} | 0 ...{location_texts.csv => location_names.csv} | 0 ..._texts.csv => move_meta_ailment_names.csv} | 0 .../csv/{move_texts.csv => move_names.csv} | 0 .../{nature_texts.csv => nature_names.csv} | 0 ...at_texts.csv => pokeathlon_stat_names.csv} | 0 ...olor_texts.csv => pokemon_color_names.csv} | 0 ..._form_texts.csv => pokemon_form_names.csv} | 0 ...at_texts.csv => pokemon_habitat_names.csv} | 0 .../{pokemon_texts.csv => pokemon_names.csv} | 0 .../{region_texts.csv => region_names.csv} | 0 ...tat_hint_texts.csv => stat_hint_names.csv} | 0 .../csv/{stat_texts.csv => stat_names.csv} | 0 .../csv/{type_texts.csv => type_names.csv} | 0 .../{version_texts.csv => version_names.csv} | 0 pokedex/db/tables.py | 42 +++++++++---------- 22 files changed, 21 insertions(+), 21 deletions(-) rename pokedex/data/csv/{ability_texts.csv => ability_names.csv} (100%) rename pokedex/data/csv/{berry_firmness_texts.csv => berry_firmness_names.csv} (100%) rename pokedex/data/csv/{contest_type_texts.csv => contest_type_names.csv} (100%) rename pokedex/data/csv/{generation_texts.csv => generation_names.csv} (100%) rename pokedex/data/csv/{item_texts.csv => item_names.csv} (100%) rename pokedex/data/csv/{item_pocket_texts.csv => item_pocket_names.csv} (100%) rename pokedex/data/csv/{language_texts.csv => language_names.csv} (100%) rename pokedex/data/csv/{location_texts.csv => location_names.csv} (100%) rename pokedex/data/csv/{move_meta_ailment_texts.csv => move_meta_ailment_names.csv} (100%) rename pokedex/data/csv/{move_texts.csv => move_names.csv} (100%) rename pokedex/data/csv/{nature_texts.csv => nature_names.csv} (100%) rename pokedex/data/csv/{pokeathlon_stat_texts.csv => pokeathlon_stat_names.csv} (100%) rename pokedex/data/csv/{pokemon_color_texts.csv => pokemon_color_names.csv} (100%) rename pokedex/data/csv/{pokemon_form_texts.csv => pokemon_form_names.csv} (100%) rename pokedex/data/csv/{pokemon_habitat_texts.csv => pokemon_habitat_names.csv} (100%) rename pokedex/data/csv/{pokemon_texts.csv => pokemon_names.csv} (100%) rename pokedex/data/csv/{region_texts.csv => region_names.csv} (100%) rename pokedex/data/csv/{stat_hint_texts.csv => stat_hint_names.csv} (100%) rename pokedex/data/csv/{stat_texts.csv => stat_names.csv} (100%) rename pokedex/data/csv/{type_texts.csv => type_names.csv} (100%) rename pokedex/data/csv/{version_texts.csv => version_names.csv} (100%) diff --git a/pokedex/data/csv/ability_texts.csv b/pokedex/data/csv/ability_names.csv similarity index 100% rename from pokedex/data/csv/ability_texts.csv rename to pokedex/data/csv/ability_names.csv diff --git a/pokedex/data/csv/berry_firmness_texts.csv b/pokedex/data/csv/berry_firmness_names.csv similarity index 100% rename from pokedex/data/csv/berry_firmness_texts.csv rename to pokedex/data/csv/berry_firmness_names.csv diff --git a/pokedex/data/csv/contest_type_texts.csv b/pokedex/data/csv/contest_type_names.csv similarity index 100% rename from pokedex/data/csv/contest_type_texts.csv rename to pokedex/data/csv/contest_type_names.csv diff --git a/pokedex/data/csv/generation_texts.csv b/pokedex/data/csv/generation_names.csv similarity index 100% rename from pokedex/data/csv/generation_texts.csv rename to pokedex/data/csv/generation_names.csv diff --git a/pokedex/data/csv/item_texts.csv b/pokedex/data/csv/item_names.csv similarity index 100% rename from pokedex/data/csv/item_texts.csv rename to pokedex/data/csv/item_names.csv diff --git a/pokedex/data/csv/item_pocket_texts.csv b/pokedex/data/csv/item_pocket_names.csv similarity index 100% rename from pokedex/data/csv/item_pocket_texts.csv rename to pokedex/data/csv/item_pocket_names.csv diff --git a/pokedex/data/csv/language_texts.csv b/pokedex/data/csv/language_names.csv similarity index 100% rename from pokedex/data/csv/language_texts.csv rename to pokedex/data/csv/language_names.csv diff --git a/pokedex/data/csv/location_texts.csv b/pokedex/data/csv/location_names.csv similarity index 100% rename from pokedex/data/csv/location_texts.csv rename to pokedex/data/csv/location_names.csv diff --git a/pokedex/data/csv/move_meta_ailment_texts.csv b/pokedex/data/csv/move_meta_ailment_names.csv similarity index 100% rename from pokedex/data/csv/move_meta_ailment_texts.csv rename to pokedex/data/csv/move_meta_ailment_names.csv diff --git a/pokedex/data/csv/move_texts.csv b/pokedex/data/csv/move_names.csv similarity index 100% rename from pokedex/data/csv/move_texts.csv rename to pokedex/data/csv/move_names.csv diff --git a/pokedex/data/csv/nature_texts.csv b/pokedex/data/csv/nature_names.csv similarity index 100% rename from pokedex/data/csv/nature_texts.csv rename to pokedex/data/csv/nature_names.csv diff --git a/pokedex/data/csv/pokeathlon_stat_texts.csv b/pokedex/data/csv/pokeathlon_stat_names.csv similarity index 100% rename from pokedex/data/csv/pokeathlon_stat_texts.csv rename to pokedex/data/csv/pokeathlon_stat_names.csv diff --git a/pokedex/data/csv/pokemon_color_texts.csv b/pokedex/data/csv/pokemon_color_names.csv similarity index 100% rename from pokedex/data/csv/pokemon_color_texts.csv rename to pokedex/data/csv/pokemon_color_names.csv diff --git a/pokedex/data/csv/pokemon_form_texts.csv b/pokedex/data/csv/pokemon_form_names.csv similarity index 100% rename from pokedex/data/csv/pokemon_form_texts.csv rename to pokedex/data/csv/pokemon_form_names.csv diff --git a/pokedex/data/csv/pokemon_habitat_texts.csv b/pokedex/data/csv/pokemon_habitat_names.csv similarity index 100% rename from pokedex/data/csv/pokemon_habitat_texts.csv rename to pokedex/data/csv/pokemon_habitat_names.csv diff --git a/pokedex/data/csv/pokemon_texts.csv b/pokedex/data/csv/pokemon_names.csv similarity index 100% rename from pokedex/data/csv/pokemon_texts.csv rename to pokedex/data/csv/pokemon_names.csv diff --git a/pokedex/data/csv/region_texts.csv b/pokedex/data/csv/region_names.csv similarity index 100% rename from pokedex/data/csv/region_texts.csv rename to pokedex/data/csv/region_names.csv diff --git a/pokedex/data/csv/stat_hint_texts.csv b/pokedex/data/csv/stat_hint_names.csv similarity index 100% rename from pokedex/data/csv/stat_hint_texts.csv rename to pokedex/data/csv/stat_hint_names.csv diff --git a/pokedex/data/csv/stat_texts.csv b/pokedex/data/csv/stat_names.csv similarity index 100% rename from pokedex/data/csv/stat_texts.csv rename to pokedex/data/csv/stat_names.csv diff --git a/pokedex/data/csv/type_texts.csv b/pokedex/data/csv/type_names.csv similarity index 100% rename from pokedex/data/csv/type_texts.csv rename to pokedex/data/csv/type_names.csv diff --git a/pokedex/data/csv/version_texts.csv b/pokedex/data/csv/version_names.csv similarity index 100% rename from pokedex/data/csv/version_texts.csv rename to pokedex/data/csv/version_names.csv diff --git a/pokedex/db/tables.py b/pokedex/db/tables.py index aadd245..48fed81 100644 --- a/pokedex/db/tables.py +++ b/pokedex/db/tables.py @@ -99,7 +99,7 @@ class Language(TableBase): create_translation_table = partial(multilang.create_translation_table, language_class=Language) -create_translation_table('language_texts', Language, 'names', +create_translation_table('language_names', Language, 'names', name = Column(Unicode(16), nullable=False, index=True, info=dict(description="The name", format='plaintext', official=True)), ) @@ -118,7 +118,7 @@ class Ability(TableBase): generation_id = Column(Integer, ForeignKey('generations.id'), nullable=False, info=dict(description="The ID of the generation this ability was introduced in", detail=True)) -create_translation_table('ability_texts', Ability, 'names', +create_translation_table('ability_names', Ability, 'names', name = Column(Unicode(24), nullable=False, index=True, info=dict(description="The name", format='plaintext', official=True)), ) @@ -195,7 +195,7 @@ class BerryFirmness(TableBase): identifier = Column(Unicode(10), nullable=False, info=dict(description="An identifier", format='identifier')) -create_translation_table('berry_firmness_texts', BerryFirmness, 'names', +create_translation_table('berry_firmness_names', BerryFirmness, 'names', name = Column(Unicode(10), nullable=False, index=True, info=dict(description="The name", format='plaintext', official=True)), ) @@ -249,7 +249,7 @@ class ContestType(TableBase): identifier = Column(Unicode(6), nullable=False, info=dict(description="An identifier", format='identifier')) -create_translation_table('contest_type_texts', ContestType, 'names', +create_translation_table('contest_type_names', ContestType, 'names', name = Column(Unicode(6), nullable=False, index=True, info=dict(description="The name", format='plaintext', official=True)), flavor = Column(Unicode(6), nullable=False, @@ -446,7 +446,7 @@ class Generation(TableBase): identifier = Column(Unicode(16), nullable=False, info=dict(description=u'An identifier', format='identifier')) -create_translation_table('generation_texts', Generation, 'names', +create_translation_table('generation_names', Generation, 'names', name = Column(Unicode(16), nullable=False, index=True, info=dict(description="The name", format='plaintext', official=True)), ) @@ -492,7 +492,7 @@ class Item(TableBase): """ return any(flag.identifier == u'underground' for flag in self.flags) -create_translation_table('item_texts', Item, 'names', +create_translation_table('item_names', Item, 'names', name = Column(Unicode(20), nullable=False, index=True, info=dict(description="The name", format='plaintext', official=True)), ) @@ -595,7 +595,7 @@ class ItemPocket(TableBase): identifier = Column(Unicode(16), nullable=False, info=dict(description="An identifier of this pocket", format='identifier')) -create_translation_table('item_pocket_texts', ItemPocket, 'names', +create_translation_table('item_pocket_names', ItemPocket, 'names', name = Column(Unicode(16), nullable=False, index=True, info=dict(description="The name", format='plaintext', official=True)), ) @@ -612,7 +612,7 @@ class Location(TableBase): identifier = Column(Unicode(64), nullable=False, info=dict(description="An identifier", format='identifier')) -create_translation_table('location_texts', Location, 'names', +create_translation_table('location_names', Location, 'names', name = Column(Unicode(64), nullable=False, index=True, info=dict(description="The name", format='plaintext', official=True)), ) @@ -855,7 +855,7 @@ class MoveMetaAilment(TableBase): identifier = Column(Unicode(24), nullable=False, info=dict(description="An identifier", format='identifier')) -create_translation_table('move_meta_ailment_texts', MoveMetaAilment, 'names', +create_translation_table('move_meta_ailment_names', MoveMetaAilment, 'names', name = Column(Unicode(24), nullable=False, index=True, info=dict(description="The name", format='plaintext', official=True)), ) @@ -935,7 +935,7 @@ class Move(TableBase): super_contest_effect_id = Column(Integer, ForeignKey('super_contest_effects.id'), nullable=True, info=dict(description="ID of the move's Super Contest effect")) -create_translation_table('move_texts', Move, 'names', +create_translation_table('move_names', Move, 'names', name = Column(Unicode(24), nullable=False, index=True, info=dict(description="The name", format='plaintext', official=True)) ) @@ -987,7 +987,7 @@ class Nature(TableBase): """ return self.increased_stat_id == self.decreased_stat_id -create_translation_table('nature_texts', Nature, 'names', +create_translation_table('nature_names', Nature, 'names', name = Column(Unicode(8), nullable=False, index=True, info=dict(description="The name", format='plaintext', official=True)), ) @@ -1029,7 +1029,7 @@ class PokeathlonStat(TableBase): identifier = Column(Unicode(8), nullable=False, info=dict(description="An identifier", format='identifier')) -create_translation_table('pokeathlon_stat_texts', PokeathlonStat, 'names', +create_translation_table('pokeathlon_stat_names', PokeathlonStat, 'names', name = Column(Unicode(8), nullable=False, index=True, info=dict(description="The name", format='plaintext', official=True)), ) @@ -1175,7 +1175,7 @@ class Pokemon(TableBase): else: return None -create_translation_table('pokemon_texts', Pokemon, 'names', +create_translation_table('pokemon_names', Pokemon, 'names', name = Column(Unicode(20), nullable=False, index=True, info=dict(description="The name", format='plaintext', official=True)), species = Column(Unicode(16), nullable=False, @@ -1209,7 +1209,7 @@ class PokemonColor(TableBase): identifier = Column(Unicode(6), nullable=False, info=dict(description=u"An identifier", format='identifier')) -create_translation_table('pokemon_color_texts', PokemonColor, 'names', +create_translation_table('pokemon_color_names', PokemonColor, 'names', name = Column(Unicode(6), nullable=False, index=True, info=dict(description="The name", format='plaintext', official=True)), ) @@ -1337,7 +1337,7 @@ class PokemonForm(TableBase): else: return self.form_base_pokemon.name -create_translation_table('pokemon_form_texts', PokemonForm, 'names', +create_translation_table('pokemon_form_names', PokemonForm, 'names', name = Column(Unicode(16), nullable=False, index=True, info=dict(description="The name", format='plaintext', official=True)), ) @@ -1384,7 +1384,7 @@ class PokemonHabitat(TableBase): identifier = Column(Unicode(16), nullable=False, info=dict(description=u"An identifier", format='identifier')) -create_translation_table('pokemon_habitat_texts', PokemonHabitat, 'names', +create_translation_table('pokemon_habitat_names', PokemonHabitat, 'names', name = Column(Unicode(16), nullable=False, index=True, info=dict(description="The name", format='plaintext', official=True)), ) @@ -1504,7 +1504,7 @@ class Region(TableBase): identifier = Column(Unicode(16), nullable=False, info=dict(description=u"An identifier", format='identifier')) -create_translation_table('region_texts', Region, 'names', +create_translation_table('region_names', Region, 'names', name = Column(Unicode(16), nullable=False, index=True, info=dict(description="The name", format='plaintext', official=True)), ) @@ -1521,7 +1521,7 @@ class Stat(TableBase): identifier = Column(Unicode(16), nullable=False, info=dict(description=u"An identifier", format='identifier')) -create_translation_table('stat_texts', Stat, 'names', +create_translation_table('stat_names', Stat, 'names', name = Column(Unicode(16), nullable=False, index=True, info=dict(description="The name", format='plaintext', official=True)), ) @@ -1539,7 +1539,7 @@ class StatHint(TableBase): gene_mod_5 = Column(Integer, nullable=False, index=True, info=dict(description=u"Value of the highest stat modulo 5")) -create_translation_table('stat_hint_texts', StatHint, 'names', +create_translation_table('stat_hint_names', StatHint, 'names', message = Column(Unicode(24), nullable=False, index=True, info=dict(description=u"The text displayed", official=True, format='plaintext')), ) @@ -1594,7 +1594,7 @@ class Type(TableBase): damage_class_id = Column(Integer, ForeignKey('move_damage_classes.id'), nullable=True, info=dict(description=u"The ID of the damage class this type's moves had before Generation IV, null if not applicable (e.g. ???).")) -create_translation_table('type_texts', Type, 'names', +create_translation_table('type_names', Type, 'names', name = Column(Unicode(12), nullable=False, index=True, info=dict(description="The name", format='plaintext', official=True)), ) @@ -1630,7 +1630,7 @@ class Version(TableBase): identifier = Column(Unicode(32), nullable=False, info=dict(description=u'And identifier', format='identifier')) -create_translation_table('version_texts', Version, 'names', +create_translation_table('version_names', Version, 'names', name = Column(Unicode(32), nullable=False, index=True, info=dict(description="The name", format='plaintext', official=True)), ) From d986355bf308a187aacaf1eea3935c8e08f907d9 Mon Sep 17 00:00:00 2001 From: Eevee Date: Thu, 24 Mar 2011 19:37:12 -0700 Subject: [PATCH 14/17] Rename internal_id to game_index. --- ...internal_ids.csv => item_game_indices.csv} | 2 +- pokedex/data/csv/location_areas.csv | 2 +- ...rnal_ids.csv => location_game_indices.csv} | 2 +- ...ernal_ids.csv => pokemon_game_indices.csv} | 2 +- pokedex/db/tables.py | 28 +++++++++---------- pokedex/struct/__init__.py | 16 +++++------ 6 files changed, 26 insertions(+), 26 deletions(-) rename pokedex/data/csv/{item_internal_ids.csv => item_game_indices.csv} (99%) rename pokedex/data/csv/{location_internal_ids.csv => location_game_indices.csv} (98%) rename pokedex/data/csv/{pokemon_internal_ids.csv => pokemon_game_indices.csv} (99%) diff --git a/pokedex/data/csv/item_internal_ids.csv b/pokedex/data/csv/item_game_indices.csv similarity index 99% rename from pokedex/data/csv/item_internal_ids.csv rename to pokedex/data/csv/item_game_indices.csv index 69349c5..153763f 100644 --- a/pokedex/data/csv/item_internal_ids.csv +++ b/pokedex/data/csv/item_game_indices.csv @@ -1,4 +1,4 @@ -item_id,generation_id,internal_id +item_id,generation_id,game_index 1,3,1 1,4,1 1,5,1 diff --git a/pokedex/data/csv/location_areas.csv b/pokedex/data/csv/location_areas.csv index c4abce7..648446b 100644 --- a/pokedex/data/csv/location_areas.csv +++ b/pokedex/data/csv/location_areas.csv @@ -1,4 +1,4 @@ -id,location_id,internal_id,identifier +id,location_id,game_index,identifier 1,1,1, 2,2,2, 3,3,3, diff --git a/pokedex/data/csv/location_internal_ids.csv b/pokedex/data/csv/location_game_indices.csv similarity index 98% rename from pokedex/data/csv/location_internal_ids.csv rename to pokedex/data/csv/location_game_indices.csv index 7e11ecf..798db39 100644 --- a/pokedex/data/csv/location_internal_ids.csv +++ b/pokedex/data/csv/location_game_indices.csv @@ -1,4 +1,4 @@ -location_id,generation_id,internal_id +location_id,generation_id,game_index 1,4,7 2,4,9 3,4,11 diff --git a/pokedex/data/csv/pokemon_internal_ids.csv b/pokedex/data/csv/pokemon_game_indices.csv similarity index 99% rename from pokedex/data/csv/pokemon_internal_ids.csv rename to pokedex/data/csv/pokemon_game_indices.csv index a5664e6..3c4032a 100644 --- a/pokedex/data/csv/pokemon_internal_ids.csv +++ b/pokedex/data/csv/pokemon_game_indices.csv @@ -1,4 +1,4 @@ -pokemon_id,generation_id,internal_id +pokemon_id,generation_id,game_index 1,1,153 1,2,1 1,3,1 diff --git a/pokedex/db/tables.py b/pokedex/db/tables.py index 48fed81..aaa2fcb 100644 --- a/pokedex/db/tables.py +++ b/pokedex/db/tables.py @@ -574,15 +574,15 @@ create_translation_table('item_fling_effect_prose', ItemFlingEffect, 'prose', info=dict(description="Description of the effect", format='plaintext')), ) -class ItemInternalID(TableBase): +class ItemGameIndex(TableBase): u"""The internal ID number a game uses for an item """ - __tablename__ = 'item_internal_ids' + __tablename__ = 'item_game_indices' item_id = Column(Integer, ForeignKey('items.id'), primary_key=True, autoincrement=False, nullable=False, info=dict(description="The database ID of the item")) generation_id = Column(Integer, ForeignKey('generations.id'), primary_key=True, autoincrement=False, nullable=False, info=dict(description="ID of the generation of games")) - internal_id = Column(Integer, nullable=False, + game_index = Column(Integer, nullable=False, info=dict(description="Internal ID of the item in the generation")) class ItemPocket(TableBase): @@ -626,7 +626,7 @@ class LocationArea(TableBase): info=dict(description="A numeric ID")) location_id = Column(Integer, ForeignKey('locations.id'), nullable=False, info=dict(description="ID of the location this area is part of")) - internal_id = Column(Integer, nullable=False, + game_index = Column(Integer, nullable=False, info=dict(description="ID the games ude for this area")) identifier = Column(Unicode(64), nullable=True, info=dict(description="An identifier", format='identifier')) @@ -648,15 +648,15 @@ class LocationAreaEncounterRate(TableBase): rate = Column(Integer, nullable=True, info=dict(description="The encounter rate")) # units? -class LocationInternalID(TableBase): +class LocationGameIndex(TableBase): u"""IDs the games use internally for locations """ - __tablename__ = 'location_internal_ids' + __tablename__ = 'location_game_indices' location_id = Column(Integer, ForeignKey('locations.id'), nullable=False, primary_key=True, info=dict(description="Database ID of the locaion")) generation_id = Column(Integer, ForeignKey('generations.id'), nullable=False, primary_key=True, info=dict(description="ID of the generation this entry to")) - internal_id = Column(Integer, nullable=False, + game_index = Column(Integer, nullable=False, info=dict(description="Internal game ID of the location")) class Machine(TableBase): @@ -1389,15 +1389,15 @@ create_translation_table('pokemon_habitat_names', PokemonHabitat, 'names', info=dict(description="The name", format='plaintext', official=True)), ) -class PokemonInternalID(TableBase): +class PokemonGameIndex(TableBase): u"""The number of a Pokémon a game uses internally """ - __tablename__ = 'pokemon_internal_ids' + __tablename__ = 'pokemon_game_indices' pokemon_id = Column(Integer, ForeignKey('pokemon.id'), primary_key=True, autoincrement=False, nullable=False, info=dict(description=u"Database ID of the Pokémon")) generation_id = Column(Integer, ForeignKey('generations.id'), primary_key=True, autoincrement=False, nullable=False, info=dict(description=u"Database ID of the generation")) - internal_id = Column(Integer, nullable=False, + game_index = Column(Integer, nullable=False, info=dict(description=u"Internal ID the generation's games use for the Pokémon")) class PokemonItem(TableBase): @@ -1726,8 +1726,8 @@ ItemCategory.pocket = relation(ItemPocket) ItemFlavorText.version_group = relation(VersionGroup) ItemFlavorText.language = relation(Language) -ItemInternalID.item = relation(Item, backref='internal_ids') -ItemInternalID.generation = relation(Generation) +ItemGameIndex.item = relation(Item, backref='game_indices') +ItemGameIndex.generation = relation(Generation) ItemPocket.categories = relation(ItemCategory, order_by=ItemCategory.identifier) @@ -1735,8 +1735,8 @@ Location.region = relation(Region, backref='locations') LocationArea.location = relation(Location, backref='areas') -LocationInternalID.location = relation(Location, backref='internal_ids') -LocationInternalID.generation = relation(Generation) +LocationGameIndex.location = relation(Location, backref='game_indices') +LocationGameIndex.generation = relation(Generation) Machine.item = relation(Item) Machine.version_group = relation(VersionGroup) diff --git a/pokedex/struct/__init__.py b/pokedex/struct/__init__.py index 962377f..5b52539 100644 --- a/pokedex/struct/__init__.py +++ b/pokedex/struct/__init__.py @@ -128,8 +128,8 @@ class SaveFilePokemon(object): self._held_item = None if st.held_item_id: - self._held_item = session.query(tables.ItemInternalID) \ - .filter_by(generation_id = 4, internal_id = st.held_item_id).one().item + self._held_item = session.query(tables.ItemGameIndex) \ + .filter_by(generation_id = 4, game_index = st.held_item_id).one().item self._stats = [] for pokemon_stat in self._pokemon.stats: @@ -173,19 +173,19 @@ class SaveFilePokemon(object): pokeball_id = st.hgss_pokeball - 17 + 492 else: pokeball_id = st.dppt_pokeball - self._pokeball = session.query(tables.ItemInternalID) \ - .filter_by(generation_id = 4, internal_id = pokeball_id).one().item + self._pokeball = session.query(tables.ItemGameIndex) \ + .filter_by(generation_id = 4, game_index = pokeball_id).one().item egg_loc_id = st.pt_egg_location_id or st.dp_egg_location_id met_loc_id = st.pt_met_location_id or st.dp_met_location_id self._egg_location = None if egg_loc_id: - self._egg_location = session.query(tables.LocationInternalID) \ - .filter_by(generation_id = 4, internal_id = egg_loc_id).one().location + self._egg_location = session.query(tables.LocationGameIndex) \ + .filter_by(generation_id = 4, game_index = egg_loc_id).one().location - self._met_location = session.query(tables.LocationInternalID) \ - .filter_by(generation_id = 4, internal_id = met_loc_id).one().location + self._met_location = session.query(tables.LocationGameIndex) \ + .filter_by(generation_id = 4, game_index = met_loc_id).one().location @property def species(self): From 027fd6236f2d6a183475d2bca48278399c0175b8 Mon Sep 17 00:00:00 2001 From: Eevee Date: Thu, 24 Mar 2011 20:02:00 -0700 Subject: [PATCH 15/17] Remove a bunch of imports from tables.py. --- pokedex/db/tables.py | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/pokedex/db/tables.py b/pokedex/db/tables.py index aaa2fcb..f0e60bf 100644 --- a/pokedex/db/tables.py +++ b/pokedex/db/tables.py @@ -30,22 +30,14 @@ import collections from functools import partial from sqlalchemy import Column, ForeignKey, MetaData, PrimaryKeyConstraint, Table, UniqueConstraint -from sqlalchemy.ext.declarative import ( - declarative_base, declared_attr, DeclarativeMeta, - ) +from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.ext.associationproxy import association_proxy -from sqlalchemy.orm import ( - backref, compile_mappers, eagerload_all, relation, class_mapper, synonym, mapper, - ) -from sqlalchemy.orm.session import Session, object_session +from sqlalchemy.orm import backref, relation +from sqlalchemy.orm.session import Session from sqlalchemy.orm.interfaces import AttributeExtension -from sqlalchemy.orm.collections import attribute_mapped_collection, MappedCollection, collection, collection_adapter -from sqlalchemy.ext.associationproxy import _AssociationDict, association_proxy from sqlalchemy.sql import and_ -from sqlalchemy.sql.expression import ColumnOperators, bindparam from sqlalchemy.schema import ColumnDefault from sqlalchemy.types import * -from inspect import isclass from pokedex.db import markdown, multilang From 93a85f020fadf2064891240a5c3cbfde313d54de Mon Sep 17 00:00:00 2001 From: Eevee Date: Thu, 24 Mar 2011 20:44:08 -0700 Subject: [PATCH 16/17] Remove back_populates, which doesn't seem to work. --- pokedex/db/tables.py | 75 +++++++++++++++++--------------------------- 1 file changed, 28 insertions(+), 47 deletions(-) diff --git a/pokedex/db/tables.py b/pokedex/db/tables.py index f0e60bf..d56bf65 100644 --- a/pokedex/db/tables.py +++ b/pokedex/db/tables.py @@ -1635,29 +1635,6 @@ Ability.changelog = relation(AbilityChangelog, ) Ability.flavor_text = relation(AbilityFlavorText, order_by=AbilityFlavorText.version_group_id, backref='ability') Ability.generation = relation(Generation, backref='abilities') -Ability.all_pokemon = relation(Pokemon, - secondary=PokemonAbility.__table__, - order_by=Pokemon.order, - #back_populates='all_abilities', -) -Ability.pokemon = relation(Pokemon, - secondary=PokemonAbility.__table__, - primaryjoin=and_( - PokemonAbility.ability_id == Ability.id, - PokemonAbility.is_dream == False - ), - order_by=Pokemon.order, - #back_populates='abilities', -) -Ability.dream_pokemon = relation(Pokemon, - secondary=PokemonAbility.__table__, - primaryjoin=and_( - PokemonAbility.ability_id == Ability.id, - PokemonAbility.is_dream == True - ), - order_by=Pokemon.order, - #back_populates='dream_ability', -) AbilityChangelog.changed_in = relation(VersionGroup, backref='ability_changelog') @@ -1693,7 +1670,6 @@ EncounterSlot.version_group = relation(VersionGroup) EvolutionChain.growth_rate = relation(GrowthRate, backref='evolution_chains') EvolutionChain.baby_trigger_item = relation(Item, backref='evolution_chains') -EvolutionChain.pokemon = relation(Pokemon, order_by=Pokemon.order)#, back_populates='evolution_chain') Experience.growth_rate = relation(GrowthRate, backref='experience_table') @@ -1754,12 +1730,22 @@ Move.super_contest_effect = relation(SuperContestEffect, backref='moves') Move.super_contest_combo_next = association_proxy('super_contest_combo_first', 'second') Move.super_contest_combo_prev = association_proxy('super_contest_combo_second', 'first') Move.target = relation(MoveTarget, backref='moves') -Move.type = relation(Type)#, back_populates='moves') +Move.type = relation(Type, backref='moves') + +Move.effect = markdown.MoveEffectProperty('effect') +Move.effect_map = markdown.MoveEffectProperty('effect_map') +Move.short_effect = markdown.MoveEffectProperty('short_effect') +Move.short_effect_map = markdown.MoveEffectProperty('short_effect_map') MoveChangelog.changed_in = relation(VersionGroup, backref='move_changelog') MoveChangelog.move_effect = relation(MoveEffect, backref='move_changelog') MoveChangelog.type = relation(Type, backref='move_changelog') +MoveChangelog.effect = markdown.MoveEffectProperty('effect') +MoveChangelog.effect_map = markdown.MoveEffectProperty('effect_map') +MoveChangelog.short_effect = markdown.MoveEffectProperty('short_effect') +MoveChangelog.short_effect_map = markdown.MoveEffectProperty('short_effect_map') + MoveEffect.category_map = relation(MoveEffectCategoryMap) MoveEffect.categories = association_proxy('category_map', 'category') MoveEffect.changelog = relation(MoveEffectChangelog, @@ -1798,11 +1784,14 @@ NatureBattleStylePreference.battle_style = relation(MoveBattleStyle, backref='na NaturePokeathlonStat.pokeathlon_stat = relation(PokeathlonStat, backref='nature_effects') Pokedex.region = relation(Region, backref='pokedexes') -Pokedex.version_groups = relation(VersionGroup, order_by=VersionGroup.id)#, back_populates='pokedex') +Pokedex.version_groups = relation(VersionGroup, order_by=VersionGroup.id, backref='pokedex') Pokemon.all_abilities = relation(Ability, secondary=PokemonAbility.__table__, order_by=PokemonAbility.slot, + backref=backref('all_pokemon', + order_by=Pokemon.order, + ), ) Pokemon.abilities = relation(Ability, secondary=PokemonAbility.__table__, @@ -1811,6 +1800,9 @@ Pokemon.abilities = relation(Ability, PokemonAbility.is_dream == False, ), order_by=PokemonAbility.slot, + backref=backref('pokemon', + order_by=Pokemon.order, + ), ) Pokemon.dream_ability = relation(Ability, secondary=PokemonAbility.__table__, @@ -1819,6 +1811,9 @@ Pokemon.dream_ability = relation(Ability, PokemonAbility.is_dream == True, ), uselist=False, + backref=backref('dream_pokemon', + order_by=Pokemon.order, + ), ) Pokemon.pokemon_color = relation(PokemonColor, backref='pokemon') Pokemon.color = association_proxy('pokemon_color', 'name') @@ -1826,7 +1821,7 @@ Pokemon.dex_numbers = relation(PokemonDexNumber, order_by=PokemonDexNumber.poked Pokemon.egg_groups = relation(EggGroup, secondary=PokemonEggGroup.__table__, order_by=PokemonEggGroup.egg_group_id, backref=backref('pokemon', order_by=Pokemon.order)) -Pokemon.evolution_chain = relation(EvolutionChain)#, back_populates='pokemon') +Pokemon.evolution_chain = relation(EvolutionChain, backref=backref('pokemon', order_by=Pokemon.order)) Pokemon.child_pokemon = relation(Pokemon, primaryjoin=Pokemon.id==PokemonEvolution.from_pokemon_id, secondary=PokemonEvolution.__table__, @@ -1846,9 +1841,11 @@ Pokemon.items = relation(PokemonItem, backref='pokemon') Pokemon.generation = relation(Generation, backref='pokemon') Pokemon.shape = relation(PokemonShape, backref='pokemon') Pokemon.stats = relation(PokemonStat, backref='pokemon', order_by=PokemonStat.stat_id.asc()) -Pokemon.types = relation(Type, secondary=PokemonType.__table__, - order_by=PokemonType.slot.asc(), - )#back_populates='pokemon') +Pokemon.types = relation(Type, + secondary=PokemonType.__table__, + order_by=PokemonType.slot.asc(), + backref=backref('pokemon', order_by=Pokemon.order), +) PokemonDexNumber.pokedex = relation(Pokedex) @@ -1945,26 +1942,10 @@ Type.target_efficacies = relation(TypeEfficacy, Type.generation = relation(Generation, backref='types') Type.damage_class = relation(MoveDamageClass, backref='types') -Type.pokemon = relation(Pokemon, secondary=PokemonType.__table__, - order_by=Pokemon.order, - )#back_populates='types') -Type.moves = relation(Move, back_populates='type', order_by=Move.id) -Version.version_group = relation(VersionGroup)#, back_populates='versions') Version.generation = association_proxy('version_group', 'generation') -VersionGroup.versions = relation(Version, order_by=Version.id, back_populates='version_group') +VersionGroup.versions = relation(Version, order_by=Version.id, backref='version_group') VersionGroup.generation = relation(Generation, backref='version_groups') VersionGroup.version_group_regions = relation(VersionGroupRegion, backref='version_group') VersionGroup.regions = association_proxy('version_group_regions', 'region') -VersionGroup.pokedex = relation(Pokedex, back_populates='version_groups') - -Move.effect = markdown.MoveEffectProperty('effect') -Move.effect_map = markdown.MoveEffectProperty('effect_map') -Move.short_effect = markdown.MoveEffectProperty('short_effect') -Move.short_effect_map = markdown.MoveEffectProperty('short_effect_map') - -MoveChangelog.effect = markdown.MoveEffectProperty('effect') -MoveChangelog.effect_map = markdown.MoveEffectProperty('effect_map') -MoveChangelog.short_effect = markdown.MoveEffectProperty('short_effect') -MoveChangelog.short_effect_map = markdown.MoveEffectProperty('short_effect_map') From f24702b7a916001bf1fe6a78205c5763ef147dc3 Mon Sep 17 00:00:00 2001 From: Eevee Date: Mon, 28 Mar 2011 19:12:30 -0700 Subject: [PATCH 17/17] language_id -> local_language_id --- pokedex/data/csv/ability_changelog_prose.csv | 2 +- pokedex/data/csv/ability_names.csv | 2 +- pokedex/data/csv/ability_prose.csv | 2 +- pokedex/data/csv/berry_firmness_names.csv | 2 +- pokedex/data/csv/contest_effect_prose.csv | 2 +- pokedex/data/csv/contest_type_names.csv | 2 +- pokedex/data/csv/egg_group_prose.csv | 2 +- .../data/csv/encounter_condition_prose.csv | 2 +- .../csv/encounter_condition_value_prose.csv | 2 +- pokedex/data/csv/encounter_terrain_prose.csv | 2 +- pokedex/data/csv/evolution_trigger_prose.csv | 2 +- pokedex/data/csv/generation_names.csv | 2 +- pokedex/data/csv/growth_rate_prose.csv | 2 +- pokedex/data/csv/item_category_prose.csv | 2 +- pokedex/data/csv/item_flag_prose.csv | 2 +- pokedex/data/csv/item_fling_effect_prose.csv | 2 +- pokedex/data/csv/item_names.csv | 2 +- pokedex/data/csv/item_pocket_names.csv | 2 +- pokedex/data/csv/item_prose.csv | 2 +- pokedex/data/csv/language_names.csv | 2 +- pokedex/data/csv/location_area_prose.csv | 2 +- pokedex/data/csv/location_names.csv | 2 +- pokedex/data/csv/move_battle_style_prose.csv | 2 +- pokedex/data/csv/move_damage_class_prose.csv | 2 +- .../data/csv/move_effect_category_prose.csv | 2 +- .../data/csv/move_effect_changelog_prose.csv | 2 +- pokedex/data/csv/move_effect_prose.csv | 2 +- pokedex/data/csv/move_flag_type_prose.csv | 2 +- pokedex/data/csv/move_meta_ailment_names.csv | 2 +- pokedex/data/csv/move_meta_category_prose.csv | 2 +- pokedex/data/csv/move_names.csv | 2 +- pokedex/data/csv/move_target_prose.csv | 2 +- pokedex/data/csv/nature_names.csv | 2 +- pokedex/data/csv/pokeathlon_stat_names.csv | 2 +- pokedex/data/csv/pokedex_prose.csv | 2 +- pokedex/data/csv/pokemon_color_names.csv | 2 +- pokedex/data/csv/pokemon_form_group_prose.csv | 2 +- pokedex/data/csv/pokemon_form_names.csv | 2 +- pokedex/data/csv/pokemon_habitat_names.csv | 2 +- .../data/csv/pokemon_move_method_prose.csv | 2 +- pokedex/data/csv/pokemon_names.csv | 2 +- pokedex/data/csv/pokemon_shape_prose.csv | 2 +- pokedex/data/csv/region_names.csv | 2 +- pokedex/data/csv/stat_hint_names.csv | 2 +- pokedex/data/csv/stat_names.csv | 2 +- .../data/csv/super_contest_effect_prose.csv | 2 +- pokedex/data/csv/type_names.csv | 2 +- pokedex/data/csv/version_names.csv | 2 +- pokedex/db/multilang.py | 29 +++++++------------ pokedex/db/tables.py | 4 +-- 50 files changed, 60 insertions(+), 69 deletions(-) diff --git a/pokedex/data/csv/ability_changelog_prose.csv b/pokedex/data/csv/ability_changelog_prose.csv index 45e8544..ee498c5 100644 --- a/pokedex/data/csv/ability_changelog_prose.csv +++ b/pokedex/data/csv/ability_changelog_prose.csv @@ -1,4 +1,4 @@ -ability_changelog_id,language_id,effect +ability_changelog_id,local_language_id,effect 1,9,Has no effect in battle. 2,9,Does not prevent regular KOs from full [HP]{mechanic}. 3,9,Has no overworld effect. diff --git a/pokedex/data/csv/ability_names.csv b/pokedex/data/csv/ability_names.csv index 7fa67a7..cc6b089 100644 --- a/pokedex/data/csv/ability_names.csv +++ b/pokedex/data/csv/ability_names.csv @@ -1,4 +1,4 @@ -ability_id,language_id,name +ability_id,local_language_id,name 1,1,あくしゅう 1,5,Puanteur 1,6,Duftnote diff --git a/pokedex/data/csv/ability_prose.csv b/pokedex/data/csv/ability_prose.csv index ed87876..6850969 100644 --- a/pokedex/data/csv/ability_prose.csv +++ b/pokedex/data/csv/ability_prose.csv @@ -1,4 +1,4 @@ -ability_id,language_id,effect,short_effect +ability_id,local_language_id,effect,short_effect 1,9,"This Pokémon's moves have approximately a 10% chance to make the target [flinch]{mechanic}. This ability does not stack with a held [King's Rock]{item}. diff --git a/pokedex/data/csv/berry_firmness_names.csv b/pokedex/data/csv/berry_firmness_names.csv index ff2ae24..c66ff7c 100644 --- a/pokedex/data/csv/berry_firmness_names.csv +++ b/pokedex/data/csv/berry_firmness_names.csv @@ -1,4 +1,4 @@ -berry_firmness_id,language_id,name +berry_firmness_id,local_language_id,name 1,9,Very Soft 2,9,Soft 3,9,Hard diff --git a/pokedex/data/csv/contest_effect_prose.csv b/pokedex/data/csv/contest_effect_prose.csv index b8061b8..2dd6114 100644 --- a/pokedex/data/csv/contest_effect_prose.csv +++ b/pokedex/data/csv/contest_effect_prose.csv @@ -1,4 +1,4 @@ -contest_effect_id,language_id,flavor_text,effect +contest_effect_id,local_language_id,flavor_text,effect 1,9,A highly appealing move.,Gives a high number of appeal points wth no other effects. 2,9,Affected by how well the appeal in front goes.,"If the Pokémon that appealed before the user earned less than three appeal points, user earns six; if three, user earns three; if more than three, user earns none." 3,9,"After this move, the user is more easily startled.","If the user is jammed this turn after using this move, it will receive twice as many jam points." diff --git a/pokedex/data/csv/contest_type_names.csv b/pokedex/data/csv/contest_type_names.csv index 8b71fd1..9bebf39 100644 --- a/pokedex/data/csv/contest_type_names.csv +++ b/pokedex/data/csv/contest_type_names.csv @@ -1,4 +1,4 @@ -contest_type_id,language_id,name,flavor,color +contest_type_id,local_language_id,name,flavor,color 1,9,Cool,Spicy,Red 2,9,Beauty,Dry,Blue 3,9,Cute,Sweet,Pink diff --git a/pokedex/data/csv/egg_group_prose.csv b/pokedex/data/csv/egg_group_prose.csv index 43342f6..ffe8375 100644 --- a/pokedex/data/csv/egg_group_prose.csv +++ b/pokedex/data/csv/egg_group_prose.csv @@ -1,4 +1,4 @@ -egg_group_id,language_id,name +egg_group_id,local_language_id,name 1,9,Monster 2,9,Water 1 3,9,Bug diff --git a/pokedex/data/csv/encounter_condition_prose.csv b/pokedex/data/csv/encounter_condition_prose.csv index 87e524a..c6ee690 100644 --- a/pokedex/data/csv/encounter_condition_prose.csv +++ b/pokedex/data/csv/encounter_condition_prose.csv @@ -1,4 +1,4 @@ -encounter_condition_id,language_id,name +encounter_condition_id,local_language_id,name 1,9,Swarm 2,9,Time of day 3,9,PokeRadar diff --git a/pokedex/data/csv/encounter_condition_value_prose.csv b/pokedex/data/csv/encounter_condition_value_prose.csv index 8096c03..146b2c5 100644 --- a/pokedex/data/csv/encounter_condition_value_prose.csv +++ b/pokedex/data/csv/encounter_condition_value_prose.csv @@ -1,4 +1,4 @@ -encounter_condition_value_id,language_id,name +encounter_condition_value_id,local_language_id,name 1,9,During a swarm 2,9,Not during a swarm 3,9,In the morning diff --git a/pokedex/data/csv/encounter_terrain_prose.csv b/pokedex/data/csv/encounter_terrain_prose.csv index e30ed27..789ae19 100644 --- a/pokedex/data/csv/encounter_terrain_prose.csv +++ b/pokedex/data/csv/encounter_terrain_prose.csv @@ -1,4 +1,4 @@ -encounter_terrain_id,language_id,name +encounter_terrain_id,local_language_id,name 1,9,Walking in tall grass or a cave 2,9,Fishing with an Old Rod 3,9,Fishing with a Good Rod diff --git a/pokedex/data/csv/evolution_trigger_prose.csv b/pokedex/data/csv/evolution_trigger_prose.csv index 33d3983..625ae13 100644 --- a/pokedex/data/csv/evolution_trigger_prose.csv +++ b/pokedex/data/csv/evolution_trigger_prose.csv @@ -1,4 +1,4 @@ -evolution_trigger_id,language_id,name +evolution_trigger_id,local_language_id,name 1,9,Level_up 2,9,Trade 3,9,Use_item diff --git a/pokedex/data/csv/generation_names.csv b/pokedex/data/csv/generation_names.csv index 81bfd2c..50d3b55 100644 --- a/pokedex/data/csv/generation_names.csv +++ b/pokedex/data/csv/generation_names.csv @@ -1,4 +1,4 @@ -generation_id,language_id,name +generation_id,local_language_id,name 1,9,Generation I 2,9,Generation II 3,9,Generation III diff --git a/pokedex/data/csv/growth_rate_prose.csv b/pokedex/data/csv/growth_rate_prose.csv index af6af90..f2f79b3 100644 --- a/pokedex/data/csv/growth_rate_prose.csv +++ b/pokedex/data/csv/growth_rate_prose.csv @@ -1,4 +1,4 @@ -growth_rate_id,language_id,name +growth_rate_id,local_language_id,name 1,9,slow 2,9,medium 3,9,fast diff --git a/pokedex/data/csv/item_category_prose.csv b/pokedex/data/csv/item_category_prose.csv index 0e8f1d2..7d09263 100644 --- a/pokedex/data/csv/item_category_prose.csv +++ b/pokedex/data/csv/item_category_prose.csv @@ -1,4 +1,4 @@ -item_category_id,language_id,name +item_category_id,local_language_id,name 1,9,Stat boosts 2,9,Effort drop 3,9,Medicine diff --git a/pokedex/data/csv/item_flag_prose.csv b/pokedex/data/csv/item_flag_prose.csv index 4d48bd2..037b563 100644 --- a/pokedex/data/csv/item_flag_prose.csv +++ b/pokedex/data/csv/item_flag_prose.csv @@ -1,4 +1,4 @@ -item_flag_id,language_id,name,description +item_flag_id,local_language_id,name,description 1,9,Countable,Has a count in the bag 2,9,Consumable,Consumed when used 3,9,Usable_overworld,Usable outside battle diff --git a/pokedex/data/csv/item_fling_effect_prose.csv b/pokedex/data/csv/item_fling_effect_prose.csv index e965e03..038d393 100644 --- a/pokedex/data/csv/item_fling_effect_prose.csv +++ b/pokedex/data/csv/item_fling_effect_prose.csv @@ -1,4 +1,4 @@ -item_fling_effect_id,language_id,effect +item_fling_effect_id,local_language_id,effect 1,9,Badly poisons the target. 2,9,Burns the target. 3,9,Immediately activates the berry's effect on the target. diff --git a/pokedex/data/csv/item_names.csv b/pokedex/data/csv/item_names.csv index 7eaa8a8..223a522 100644 --- a/pokedex/data/csv/item_names.csv +++ b/pokedex/data/csv/item_names.csv @@ -1,4 +1,4 @@ -item_id,language_id,name +item_id,local_language_id,name 1,1,マスターボール 1,5,Master Ball 1,6,Meisterball diff --git a/pokedex/data/csv/item_pocket_names.csv b/pokedex/data/csv/item_pocket_names.csv index 0be9437..ca85fd6 100644 --- a/pokedex/data/csv/item_pocket_names.csv +++ b/pokedex/data/csv/item_pocket_names.csv @@ -1,4 +1,4 @@ -item_pocket_id,language_id,name +item_pocket_id,local_language_id,name 1,9,Items 2,9,Medicine 3,9,Poké Balls diff --git a/pokedex/data/csv/item_prose.csv b/pokedex/data/csv/item_prose.csv index 7d3b85b..38ce9e7 100644 --- a/pokedex/data/csv/item_prose.csv +++ b/pokedex/data/csv/item_prose.csv @@ -1,4 +1,4 @@ -item_id,language_id,short_effect,effect +item_id,local_language_id,short_effect,effect 1,9,,In battle: Captures one Pokémon without fail. Has a capture rate of 255. 2,9,,In battle: Attempts to capture one Pokémon. Has a capture rate of 2. 3,9,,In battle: Attempts to capture one Pokémon. Has a capture rate of 1.5. diff --git a/pokedex/data/csv/language_names.csv b/pokedex/data/csv/language_names.csv index 8e1cdfa..42663ae 100644 --- a/pokedex/data/csv/language_names.csv +++ b/pokedex/data/csv/language_names.csv @@ -1,4 +1,4 @@ -lang_id,language_id,name +language_id,local_language_id,name 1,9,Japanese 2,9,Official Roomaji 3,9,Korean diff --git a/pokedex/data/csv/location_area_prose.csv b/pokedex/data/csv/location_area_prose.csv index 43ac3b3..5aec6b5 100644 --- a/pokedex/data/csv/location_area_prose.csv +++ b/pokedex/data/csv/location_area_prose.csv @@ -1,4 +1,4 @@ -location_area_id,language_id,name +location_area_id,local_language_id,name 1,9, 2,9, 3,9, diff --git a/pokedex/data/csv/location_names.csv b/pokedex/data/csv/location_names.csv index a36cfe2..ae74b6a 100644 --- a/pokedex/data/csv/location_names.csv +++ b/pokedex/data/csv/location_names.csv @@ -1,4 +1,4 @@ -location_id,language_id,name +location_id,local_language_id,name 1,9,Canalave City 2,9,Eterna City 3,9,Pastoria City diff --git a/pokedex/data/csv/move_battle_style_prose.csv b/pokedex/data/csv/move_battle_style_prose.csv index 408f76c..441b73b 100644 --- a/pokedex/data/csv/move_battle_style_prose.csv +++ b/pokedex/data/csv/move_battle_style_prose.csv @@ -1,4 +1,4 @@ -move_battle_style_id,language_id,name +move_battle_style_id,local_language_id,name 1,9,Attack 2,9,Defense 3,9,Support diff --git a/pokedex/data/csv/move_damage_class_prose.csv b/pokedex/data/csv/move_damage_class_prose.csv index a7cd3d7..2083337 100644 --- a/pokedex/data/csv/move_damage_class_prose.csv +++ b/pokedex/data/csv/move_damage_class_prose.csv @@ -1,4 +1,4 @@ -move_damage_class_id,language_id,name,description +move_damage_class_id,local_language_id,name,description 1,9,non-damaging,No damage 2,9,physical,"Physical damage, controlled by Attack and Defense" 3,9,special,"Special damage, controlled by Special Attack and Special Defense" diff --git a/pokedex/data/csv/move_effect_category_prose.csv b/pokedex/data/csv/move_effect_category_prose.csv index 9d8e1a0..edf7734 100644 --- a/pokedex/data/csv/move_effect_category_prose.csv +++ b/pokedex/data/csv/move_effect_category_prose.csv @@ -1,4 +1,4 @@ -move_effect_category_id,language_id,name +move_effect_category_id,local_language_id,name 1,9,Regular damage 2,9,Power varies 3,9,Special damage diff --git a/pokedex/data/csv/move_effect_changelog_prose.csv b/pokedex/data/csv/move_effect_changelog_prose.csv index 0305775..11df252 100644 --- a/pokedex/data/csv/move_effect_changelog_prose.csv +++ b/pokedex/data/csv/move_effect_changelog_prose.csv @@ -1,4 +1,4 @@ -move_effect_changelog_id,language_id,effect +move_effect_changelog_id,local_language_id,effect 1,9,"Halves the target's [Defense]{mechanic} for damage calculation, which is similar to doubling the attack's [power]{mechanic}." 2,9,Hits Pokémon under the effects of [Dig]{move} and [Fly]{move}. 3,9,Does nothing in trainer battles. diff --git a/pokedex/data/csv/move_effect_prose.csv b/pokedex/data/csv/move_effect_prose.csv index 41f1e5a..a88a070 100644 --- a/pokedex/data/csv/move_effect_prose.csv +++ b/pokedex/data/csv/move_effect_prose.csv @@ -1,4 +1,4 @@ -move_effect_id,language_id,short_effect,effect +move_effect_id,local_language_id,short_effect,effect 1,9,Inflicts regular damage with no additional effect.,Inflicts [regular damage]{mechanic}. 2,9,Puts the target to sleep.,Puts the target to [sleep]{mechanic}. 3,9,Has a $effect_chance% chance to poison the target.,Inflicts [regular damage]{mechanic}. Has a $effect_chance% chance to [poison]{mechanic} the target. diff --git a/pokedex/data/csv/move_flag_type_prose.csv b/pokedex/data/csv/move_flag_type_prose.csv index 3083f83..33e7fe9 100644 --- a/pokedex/data/csv/move_flag_type_prose.csv +++ b/pokedex/data/csv/move_flag_type_prose.csv @@ -1,4 +1,4 @@ -move_flag_type_id,language_id,name,description +move_flag_type_id,local_language_id,name,description 1,9,Makes contact,"User touches the target. This triggers some abilities (e.g., [Static]{ability}) and items (e.g., [Sticky Barb]{item})." 2,9,Has a charging turn,This move has a charging turn that can be skipped with a [Power Herb]{item}. 3,9,Must recharge,"The turn after this move is used, the Pokémon's action is skipped so it can recharge." diff --git a/pokedex/data/csv/move_meta_ailment_names.csv b/pokedex/data/csv/move_meta_ailment_names.csv index 0512c3e..a202a71 100644 --- a/pokedex/data/csv/move_meta_ailment_names.csv +++ b/pokedex/data/csv/move_meta_ailment_names.csv @@ -1,4 +1,4 @@ -move_meta_ailment_id,language_id,name +move_meta_ailment_id,local_language_id,name -1,9,???? 0,9,none 1,9,Paralysis diff --git a/pokedex/data/csv/move_meta_category_prose.csv b/pokedex/data/csv/move_meta_category_prose.csv index 287d5fa..8ea22f1 100644 --- a/pokedex/data/csv/move_meta_category_prose.csv +++ b/pokedex/data/csv/move_meta_category_prose.csv @@ -1,4 +1,4 @@ -move_meta_category_id,language_id,description +move_meta_category_id,local_language_id,description 0,9,Inflicts damage 1,9,No damage; inflicts status ailment 2,9,No damage; lowers target's stats or raises user's stats diff --git a/pokedex/data/csv/move_names.csv b/pokedex/data/csv/move_names.csv index 321a0ee..cacf76f 100644 --- a/pokedex/data/csv/move_names.csv +++ b/pokedex/data/csv/move_names.csv @@ -1,4 +1,4 @@ -move_id,language_id,name +move_id,local_language_id,name 1,1,はたく 1,5,Écras'Face 1,6,Pfund diff --git a/pokedex/data/csv/move_target_prose.csv b/pokedex/data/csv/move_target_prose.csv index d907c5c..b1c2aee 100644 --- a/pokedex/data/csv/move_target_prose.csv +++ b/pokedex/data/csv/move_target_prose.csv @@ -1,4 +1,4 @@ -move_target_id,language_id,name,description +move_target_id,local_language_id,name,description 1,9,Specific move,One specific move. How this move is chosen depends upon on the move being used. 2,9,Selected Pokémon,"One other Pokémon on the field, selected by the trainer. Stolen moves reuse the same target." 3,9,Ally,The user's ally (if any). diff --git a/pokedex/data/csv/nature_names.csv b/pokedex/data/csv/nature_names.csv index e5b4e03..eaab7a5 100644 --- a/pokedex/data/csv/nature_names.csv +++ b/pokedex/data/csv/nature_names.csv @@ -1,4 +1,4 @@ -nature_id,language_id,name +nature_id,local_language_id,name 1,1,がんばりや 1,5,Hardi 1,6,Robust diff --git a/pokedex/data/csv/pokeathlon_stat_names.csv b/pokedex/data/csv/pokeathlon_stat_names.csv index 1112bba..4f47d7e 100644 --- a/pokedex/data/csv/pokeathlon_stat_names.csv +++ b/pokedex/data/csv/pokeathlon_stat_names.csv @@ -1,4 +1,4 @@ -pokeathlon_stat_id,language_id,name +pokeathlon_stat_id,local_language_id,name 1,9,Speed 2,9,Power 3,9,Skill diff --git a/pokedex/data/csv/pokedex_prose.csv b/pokedex/data/csv/pokedex_prose.csv index 32e93b0..b739880 100644 --- a/pokedex/data/csv/pokedex_prose.csv +++ b/pokedex/data/csv/pokedex_prose.csv @@ -1,4 +1,4 @@ -pokedex_id,language_id,name,description +pokedex_id,local_language_id,name,description 1,9,National,Entire National dex 2,9,Kanto,Red/Blue/Yellow Kanto dex 3,9,Original Johto,"Gold/Silver/Crystal Johto dex—called the ""New"" Pokédex in-game" diff --git a/pokedex/data/csv/pokemon_color_names.csv b/pokedex/data/csv/pokemon_color_names.csv index d25c717..6e8eb80 100644 --- a/pokedex/data/csv/pokemon_color_names.csv +++ b/pokedex/data/csv/pokemon_color_names.csv @@ -1,4 +1,4 @@ -pokemon_color_id,language_id,name +pokemon_color_id,local_language_id,name 1,9,Black 2,9,Blue 3,9,Brown diff --git a/pokedex/data/csv/pokemon_form_group_prose.csv b/pokedex/data/csv/pokemon_form_group_prose.csv index 03dca2e..ec15308 100644 --- a/pokedex/data/csv/pokemon_form_group_prose.csv +++ b/pokedex/data/csv/pokemon_form_group_prose.csv @@ -1,4 +1,4 @@ -pokemon_form_group_id,language_id,term,description +pokemon_form_group_id,local_language_id,term,description 172,9,,"Spiky-eared Pichu can only be received by taking the shiny Pichu from an official promotion to [Celebi]{pokemon}'s shrine in [Ilex Forest]{location}. Spiky-eared Pichu is always female, cannot evolve, and cannot be taken into the Wi-Fi Club or the Union Room, but is otherwise a normal Pichu." 201,9,,Forms only affect appearance. A form is determined at random before a wild encounter and cannot be changed. 351,9,Form,"Form changes along with type to match the [weather]{mechanic} in battle, due to [Forecast]{ability}. Castform is always in its normal form outside of battle, regardless of weather." diff --git a/pokedex/data/csv/pokemon_form_names.csv b/pokedex/data/csv/pokemon_form_names.csv index 70d4dfe..e51e486 100644 --- a/pokedex/data/csv/pokemon_form_names.csv +++ b/pokedex/data/csv/pokemon_form_names.csv @@ -1,4 +1,4 @@ -pokemon_form_id,language_id,name +pokemon_form_id,local_language_id,name 1,9, 2,9, 3,9, diff --git a/pokedex/data/csv/pokemon_habitat_names.csv b/pokedex/data/csv/pokemon_habitat_names.csv index d78b059..d974bc6 100644 --- a/pokedex/data/csv/pokemon_habitat_names.csv +++ b/pokedex/data/csv/pokemon_habitat_names.csv @@ -1,4 +1,4 @@ -pokemon_habitat_id,language_id,name +pokemon_habitat_id,local_language_id,name 1,9,cave 2,9,forest 3,9,grassland diff --git a/pokedex/data/csv/pokemon_move_method_prose.csv b/pokedex/data/csv/pokemon_move_method_prose.csv index b39a211..8d3802c 100644 --- a/pokedex/data/csv/pokemon_move_method_prose.csv +++ b/pokedex/data/csv/pokemon_move_method_prose.csv @@ -1,4 +1,4 @@ -pokemon_move_method_id,language_id,name,description +pokemon_move_method_id,local_language_id,name,description 1,9,Level up,Learned when a Pokémon reaches a certain level. 2,9,Egg,"Appears on a newly-hatched Pokémon, if the father had the same move." 3,9,Tutor,Can be taught at any time by an NPC. diff --git a/pokedex/data/csv/pokemon_names.csv b/pokedex/data/csv/pokemon_names.csv index 87f77b1..09db3d5 100644 --- a/pokedex/data/csv/pokemon_names.csv +++ b/pokedex/data/csv/pokemon_names.csv @@ -1,4 +1,4 @@ -pokemon_id,language_id,name,species +pokemon_id,local_language_id,name,species 1,1,フシギダネ, 1,2,Fushigidane, 1,3,이상해씨, diff --git a/pokedex/data/csv/pokemon_shape_prose.csv b/pokedex/data/csv/pokemon_shape_prose.csv index 5a3cf37..38fca57 100644 --- a/pokedex/data/csv/pokemon_shape_prose.csv +++ b/pokedex/data/csv/pokemon_shape_prose.csv @@ -1,4 +1,4 @@ -pokemon_shape_id,language_id,name,awesome_name +pokemon_shape_id,local_language_id,name,awesome_name 1,9,Ball,Pomaceous 2,9,Squiggle,Caudal 3,9,Fish,Ichthyic diff --git a/pokedex/data/csv/region_names.csv b/pokedex/data/csv/region_names.csv index 5f05e2b..43a76bc 100644 --- a/pokedex/data/csv/region_names.csv +++ b/pokedex/data/csv/region_names.csv @@ -1,4 +1,4 @@ -region_id,language_id,name +region_id,local_language_id,name 1,9,Kanto 2,9,Johto 3,9,Hoenn diff --git a/pokedex/data/csv/stat_hint_names.csv b/pokedex/data/csv/stat_hint_names.csv index 36acada..44b3450 100644 --- a/pokedex/data/csv/stat_hint_names.csv +++ b/pokedex/data/csv/stat_hint_names.csv @@ -1,4 +1,4 @@ -stat_hint_id,language_id,message +stat_hint_id,local_language_id,message 1,9,Loves to eat 2,9,Proud of its power 3,9,Sturdy body diff --git a/pokedex/data/csv/stat_names.csv b/pokedex/data/csv/stat_names.csv index ceb73a2..aca8e1a 100644 --- a/pokedex/data/csv/stat_names.csv +++ b/pokedex/data/csv/stat_names.csv @@ -1,4 +1,4 @@ -stat_id,language_id,name +stat_id,local_language_id,name 1,9,HP 2,9,Attack 3,9,Defense diff --git a/pokedex/data/csv/super_contest_effect_prose.csv b/pokedex/data/csv/super_contest_effect_prose.csv index 8585c26..3715323 100644 --- a/pokedex/data/csv/super_contest_effect_prose.csv +++ b/pokedex/data/csv/super_contest_effect_prose.csv @@ -1,4 +1,4 @@ -super_contest_effect_id,language_id,flavor_text +super_contest_effect_id,local_language_id,flavor_text 1,9,Enables the user to perform first in the next turn. 2,9,Enables the user to perform last in the next turn. 4,9,Earn +2 if the Judge's Voltage goes up. diff --git a/pokedex/data/csv/type_names.csv b/pokedex/data/csv/type_names.csv index 4ad1b6e..49c3165 100644 --- a/pokedex/data/csv/type_names.csv +++ b/pokedex/data/csv/type_names.csv @@ -1,4 +1,4 @@ -type_id,language_id,name +type_id,local_language_id,name 1,1,ノーマル 1,5,Normal 1,6,Normal diff --git a/pokedex/data/csv/version_names.csv b/pokedex/data/csv/version_names.csv index 518a11c..1902bfe 100644 --- a/pokedex/data/csv/version_names.csv +++ b/pokedex/data/csv/version_names.csv @@ -1,4 +1,4 @@ -version_id,language_id,name +version_id,local_language_id,name 1,9,Red 2,9,Blue 3,9,Yellow diff --git a/pokedex/db/multilang.py b/pokedex/db/multilang.py index 96d70d6..8ac1bfc 100644 --- a/pokedex/db/multilang.py +++ b/pokedex/db/multilang.py @@ -17,7 +17,6 @@ def create_translation_table(_table_name, foreign_class, relation_name, `foreign_class` must have a `__singlename__`, currently only used to create the name of the foreign key column. -TODO remove this requirement Also supports the notion of a default language, which is attached to the session. This is English by default, for historical and practical reasons. @@ -69,21 +68,16 @@ TODO remove this requirement # want to create tables entirely separate from the pokedex metadata foreign_key_name = foreign_class.__singlename__ + '_id' - # A foreign key "language_id" will clash with the language_id we naturally - # put in every table. Rename it something else - if foreign_key_name == 'language_id': - # TODO change language_id below instead and rename this - foreign_key_name = 'lang_id' Translations = type(_table_name, (object,), { - '_language_identifier': association_proxy('language', 'identifier'), + '_language_identifier': association_proxy('local_language', 'identifier'), }) # Create the table object table = Table(_table_name, foreign_class.__table__.metadata, Column(foreign_key_name, Integer, ForeignKey(foreign_class.id), primary_key=True, nullable=False), - Column('language_id', Integer, ForeignKey(language_class.id), + Column('local_language_id', Integer, ForeignKey(language_class.id), primary_key=True, nullable=False), ) Translations.__table__ = table @@ -99,14 +93,11 @@ TODO remove this requirement # Construct ye mapper mapper(Translations, table, properties={ - # TODO change to foreign_id - 'object_id': synonym(foreign_key_name), - # TODO change this as appropriate - 'language': relationship(language_class, - primaryjoin=table.c.language_id == language_class.id, + 'foreign_id': synonym(foreign_key_name), + 'local_language': relationship(language_class, + primaryjoin=table.c.local_language_id == language_class.id, lazy='joined', innerjoin=True), - # TODO does this need to join to the original table? }) # Add full-table relations to the original class @@ -114,8 +105,8 @@ TODO remove this requirement setattr(foreign_class, relation_name + '_table', Translations) # Foo.bars setattr(foreign_class, relation_name, relationship(Translations, - primaryjoin=foreign_class.id == Translations.object_id, - collection_class=attribute_mapped_collection('language'), + primaryjoin=foreign_class.id == Translations.foreign_id, + collection_class=attribute_mapped_collection('local_language'), # TODO lazy='select', )) @@ -129,8 +120,8 @@ TODO remove this requirement language_class_a = aliased(language_class) setattr(foreign_class, local_relation_name, relationship(Translations, primaryjoin=and_( - foreign_class.id == Translations.object_id, - Translations.language_id == select( + foreign_class.id == Translations.foreign_id, + Translations.local_language_id == select( [language_class_a.id], language_class_a.identifier == bindparam('_default_language', required=True), @@ -152,7 +143,7 @@ TODO remove this requirement # these are passed as *args anyway def creator(language, value): row = Translations() - row.language = language + row.local_language = language setattr(row, name, value) return row setattr(foreign_class, name + '_map', diff --git a/pokedex/db/tables.py b/pokedex/db/tables.py index d56bf65..1c1db60 100644 --- a/pokedex/db/tables.py +++ b/pokedex/db/tables.py @@ -264,7 +264,7 @@ class EggGroup(TableBase): create_translation_table('egg_group_prose', EggGroup, 'names', name = Column(Unicode(16), nullable=False, index=True, - info=dict(description="The name", format='plaintext', official=False)), + info=dict(description="The name", format='plaintext', official=True)), ) class Encounter(TableBase): @@ -1557,7 +1557,7 @@ class SuperContestEffect(TableBase): create_translation_table('super_contest_effect_prose', SuperContestEffect, 'prose', flavor_text = Column(Unicode(64), nullable=False, - info=dict(description=u"A description of the effect.", format='plaintext')), + info=dict(description=u"A description of the effect.", format='plaintext', official=True)), )