From 981851420ef3d99267232c9fad8085c05c70b2aa Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Mon, 29 Dec 2014 14:57:01 +0100 Subject: [PATCH] Split tables.py into several files Also, remove the test alphabetical order --- pokedex/db/tables/__init__.py | 61 +++ pokedex/db/tables/base.py | 80 +++ pokedex/db/tables/conquest.py | 550 +++++++++++++++++++ pokedex/db/{tables.py => tables/core.py} | 644 +---------------------- pokedex/tests/test_schema.py | 14 +- 5 files changed, 697 insertions(+), 652 deletions(-) create mode 100644 pokedex/db/tables/__init__.py create mode 100644 pokedex/db/tables/base.py create mode 100644 pokedex/db/tables/conquest.py rename pokedex/db/{tables.py => tables/core.py} (77%) diff --git a/pokedex/db/tables/__init__.py b/pokedex/db/tables/__init__.py new file mode 100644 index 000000000..29532d9f4 --- /dev/null +++ b/pokedex/db/tables/__init__.py @@ -0,0 +1,61 @@ +# encoding: utf8 + +u"""The Pokédex schema. + +Columns have a info dictionary with these keys: +- official: True if the values appear in games or official material; False if + they are fan-created or fan-written. This flag is currently only set for + official text columns. +- format: The format of a text column. Can be one of: + - plaintext: Normal Unicode text (widely used in names) + - markdown: Veekun's Markdown flavor (generally used in effect descriptions) + - gametext: Transcription of in-game text that strives to be both + human-readable and represent the original text exactly. + - identifier: A fan-made identifier in the [-_a-z0-9]* format. Not intended + for translation. + - latex: A formula in LaTeX syntax. +- ripped: True for text that has been ripped from the games, and can be ripped + again for new versions or languages + +- string_getter: for translation columns, a function taking (text, session, + language) that is used for properties on the main table. Used for Markdown + text. + +See `pokedex.db.multilang` for how localizable text columns work. The session +classes in that module can be used to change the default language. +""" + +from pokedex.db.tables.base import ( + metadata, TableBase, mapped_classes, Language, create_translation_table) + +from pokedex.db.tables.core import ( + Ability, AbilityChangelog, AbilityFlavorText, Berry, BerryFirmness, + BerryFlavor, Characteristic, ContestCombo, ContestEffect, ContestType, + EggGroup, Encounter, EncounterCondition, EncounterConditionValue, + EncounterConditionValueMap, EncounterMethod, EncounterSlot, EvolutionChain, + EvolutionTrigger, Experience, Gender, Generation, GrowthRate, Item, + ItemCategory, ItemFlag, ItemFlagMap, ItemFlavorText, ItemFlingEffect, + ItemGameIndex, ItemPocket, Location, LocationArea, + LocationAreaEncounterRate, LocationGameIndex, Machine, Move, + MoveBattleStyle, MoveChangelog, MoveDamageClass, MoveEffect, + MoveEffectChangelog, MoveFlag, MoveFlagMap, MoveFlavorText, MoveMeta, + MoveMetaAilment, MoveMetaCategory, MoveMetaStatChange, MoveTarget, Nature, + NatureBattleStylePreference, NaturePokeathlonStat, PalPark, PalParkArea, + PokeathlonStat, Pokedex, PokedexVersionGroup, Pokemon, PokemonAbility, + PokemonColor, PokemonDexNumber, PokemonEggGroup, PokemonEvolution, + PokemonForm, PokemonFormGeneration, PokemonFormPokeathlonStat, + PokemonGameIndex, PokemonHabitat, PokemonItem, PokemonMove, + PokemonMoveMethod, PokemonShape, PokemonSpecies, PokemonSpeciesFlavorText, + PokemonStat, PokemonType, Region, Stat, SuperContestCombo, + SuperContestEffect, Type, TypeEfficacy, TypeGameIndex, Version, + VersionGroup, VersionGroupPokemonMoveMethod, VersionGroupRegion) + +from pokedex.db.tables.conquest import ( + ConquestEpisode, ConquestEpisodeWarrior, ConquestKingdom, ConquestMaxLink, + ConquestMoveData, ConquestMoveDisplacement, ConquestMoveEffect, + ConquestMoveRange, ConquestPokemonAbility, ConquestPokemonEvolution, + ConquestPokemonMove, ConquestPokemonStat, ConquestStat, + ConquestTransformationPokemon, ConquestTransformationWarrior, + ConquestWarrior, ConquestWarriorArchetype, ConquestWarriorRank, + ConquestWarriorRankStatMap, ConquestWarriorSkill, ConquestWarriorSpecialty, + ConquestWarriorStat, ConquestWarriorTransformation) diff --git a/pokedex/db/tables/base.py b/pokedex/db/tables/base.py new file mode 100644 index 000000000..05620318b --- /dev/null +++ b/pokedex/db/tables/base.py @@ -0,0 +1,80 @@ +# encoding: utf8 + +from functools import partial + +from sqlalchemy.ext.declarative import declarative_base, DeclarativeMeta +from sqlalchemy import Column, MetaData +from sqlalchemy.types import Boolean, Integer, Unicode + +from pokedex.db import multilang + + +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 u"<%s object (%s): %s>" % (typename, pk, self.identifier) + except AttributeError: + return u"<%s object (%s)>" % (typename, pk) + + def __str__(self): + return unicode(self).encode('utf8') + + def __repr__(self): + return unicode(self).encode('utf8') + + +mapped_classes = [] +class TableMetaclass(DeclarativeMeta): + def __init__(cls, name, bases, attrs): + super(TableMetaclass, cls).__init__(name, bases, attrs) + if hasattr(cls, '__tablename__'): + mapped_classes.append(cls) + cls.translation_classes = [] + +metadata = MetaData() +TableBase = declarative_base(metadata=metadata, cls=TableSuperclass, metaclass=TableMetaclass) + + +### Need Language first, to create the partial() below + +class Language(TableBase): + u"""A language the Pokémon games have been translated into.""" + __tablename__ = 'languages' + __singlename__ = 'language' + id = Column(Integer, primary_key=True, nullable=False, + doc=u"A numeric ID") + iso639 = Column(Unicode(79), nullable=False, + doc=u"The two-letter code of the country where this language is spoken. Note that it is not unique.", + info=dict(format='identifier')) + iso3166 = Column(Unicode(79), nullable=False, + doc=u"The two-letter code of the language. Note that it is not unique.", + info=dict(format='identifier')) + identifier = Column(Unicode(79), nullable=False, + doc=u"An identifier", + info=dict(format='identifier')) + official = Column(Boolean, nullable=False, index=True, + doc=u"True iff games are produced in the language.") + order = Column(Integer, nullable=True, + doc=u"Order for sorting in foreign name lists.") + +create_translation_table = partial(multilang.create_translation_table, language_class=Language) + +create_translation_table('language_names', Language, 'names', + name = Column(Unicode(79), nullable=False, index=True, + doc=u"The name", + info=dict(format='plaintext', official=True)), +) diff --git a/pokedex/db/tables/conquest.py b/pokedex/db/tables/conquest.py new file mode 100644 index 000000000..a63df918e --- /dev/null +++ b/pokedex/db/tables/conquest.py @@ -0,0 +1,550 @@ +# encoding: utf8 + +from sqlalchemy import Column, ForeignKey, UniqueConstraint +from sqlalchemy.orm import backref, relationship +from sqlalchemy.ext.associationproxy import association_proxy +from sqlalchemy.types import Boolean, Integer, Unicode, UnicodeText + +from pokedex.db.tables.base import TableBase, create_translation_table +from pokedex.db.tables.core import ( + Move, Type, Ability, PokemonSpecies, Gender, Item) + +from pokedex.db import markdown + +class ConquestEpisode(TableBase): + u"""An episode from Pokémon Conquest: one of a bunch of mini-stories + featuring a particular warrior. + + The main story, "The Legend of Ransei", also counts, even though it's not + in the episode select menu and there's no way to replay it. + """ + __tablename__ = 'conquest_episodes' + __singlename__ = 'episode' + id = Column(Integer, primary_key=True, autoincrement=True, + doc=u'An ID for this episode.') + identifier = Column(Unicode(79), nullable=False, + doc=u'A readable identifier for this episode.', + info=dict(format='identifier')) + +create_translation_table('conquest_episode_names', ConquestEpisode, 'names', + relation_lazy='joined', + name=Column(Unicode(79), nullable=False, index=True, + doc=u'The name.', + info=dict(format='plaintext', official=True)) +) + +class ConquestEpisodeWarrior(TableBase): + u"""A warrior featured in an episode in Pokémon Conquest. + + This needs its own table because of the player having two episodes and + there being two players. + """ + __tablename__ = 'conquest_episode_warriors' + episode_id = Column(Integer, ForeignKey('conquest_episodes.id'), primary_key=True, + doc=u'The ID of the episode.') + warrior_id = Column(Integer, ForeignKey('conquest_warriors.id'), primary_key=True, + doc=u'The ID of the warrior.') + +class ConquestKingdom(TableBase): + u"""A kingdom in Pokémon Conquest.""" + __tablename__ = 'conquest_kingdoms' + __singlename__ = 'kingdom' + id = Column(Integer, primary_key=True, autoincrement=True, + doc=u"An ID for this kingdom.") + identifier = Column(Unicode(79), nullable=False, + doc=u"A readable identifier for this kingdom.", + info=dict(format='identifier')) + type_id = Column(Integer, ForeignKey('types.id'), nullable=False, + doc=u"The type associated with this kingdom in-game.") + +create_translation_table('conquest_kingdom_names', ConquestKingdom, 'names', + relation_lazy='joined', + name=Column(Unicode(79), nullable=False, index=True, + doc=u'The name.', + info=dict(format='plaintext', official=True)) +) + +class ConquestMaxLink(TableBase): + u"""The maximum link a warrior rank can reach with a Pokémon in Pokémon Conquest.""" + __tablename__ = 'conquest_max_links' + warrior_rank_id = Column(Integer, ForeignKey('conquest_warrior_ranks.id'), primary_key=True, + doc=u"The ID of the warrior rank.") + pokemon_species_id = Column(Integer, ForeignKey('pokemon_species.id'), primary_key=True, + doc=u'The ID of the Pokémon species.') + max_link = Column(Integer, nullable=False, + doc=u'The maximum link percentage this warrior rank and Pokémon can reach.') + +class ConquestMoveData(TableBase): + u"""Data about a move in Pokémon Conquest.""" + __tablename__ = 'conquest_move_data' + move_id = Column(Integer, ForeignKey('moves.id'), primary_key=True, autoincrement=False, + doc=u'The ID of the move.') + power = Column(Integer, nullable=True, + doc=u"The move's power, null if it does no damage.") + accuracy = Column(Integer, nullable=True, + doc=u"The move's base accuracy, null if it is self-targeted or never misses.") + effect_chance = Column(Integer, nullable=True, + doc=u"The chance as a percentage that the move's secondary effect will trigger.") + effect_id = Column(Integer, ForeignKey('conquest_move_effects.id'), nullable=False, + doc=u"The ID of the move's effect.") + range_id = Column(Integer, ForeignKey('conquest_move_ranges.id'), nullable=False, + doc=u"The ID of the move's range.") + displacement_id = Column(Integer, ForeignKey('conquest_move_displacements.id'), nullable=True, + doc=u"The ID of the move's displacement.") + + @property + def star_rating(self): + """Return the move's in-game power rating as a number of stars.""" + if not self.power: + return 0 + else: + stars = (self.power - 1) // 10 + stars = min(stars, 5) # i.e. maximum of 5 stars + stars = max(stars, 1) # And minimum of 1 + return stars + +class ConquestMoveDisplacement(TableBase): + u"""A way in which a move can cause the user or target to move to a + different tile. + + If a move displaces its user, the move's range is relative to the user's + original position. + """ + __tablename__ = 'conquest_move_displacements' + __singlename__ = 'move_displacement' + id = Column(Integer, primary_key=True, autoincrement=True, + doc=u'An ID for this displacement.') + identifier = Column(Unicode(79), nullable=False, + doc=u'A readable identifier for this displacement.', + info=dict(format='identifier')) + affects_target = Column(Boolean, nullable=False, + doc=u'True iff the move displaces its target(s) and not its user.') + +create_translation_table('conquest_move_displacement_prose', ConquestMoveDisplacement, 'prose', + name = Column(Unicode(79), nullable=True, + doc=u'A name for the displacement.', + info=dict(format='plaintext')), + short_effect = Column(UnicodeText, nullable=True, + doc=u"A short summary of how the displacement works, to be used in the move's short effect.", + info=dict(format='markdown')), + effect = Column(UnicodeText, nullable=True, + doc=u"A detailed description of how the displacement works, to be used alongside the move's long effect.", + info=dict(format='markdown')), +) + +class ConquestMoveEffect(TableBase): + u"""An effect moves can have in Pokémon Conquest.""" + __tablename__ = 'conquest_move_effects' + __singlename__ = 'conquest_move_effect' + id = Column(Integer, primary_key=True, autoincrement=True, + doc=u'An ID for this effect.') + +create_translation_table('conquest_move_effect_prose', ConquestMoveEffect, 'prose', + short_effect = Column(UnicodeText, nullable=True, + doc=u"A short summary of the effect", + info=dict(format='markdown')), + effect = Column(UnicodeText, nullable=True, + doc=u"A detailed description of the effect", + info=dict(format='markdown')), +) + +class ConquestMoveRange(TableBase): + u"""A set of tiles moves can target in Pokémon Conquest.""" + __tablename__ = 'conquest_move_ranges' + __singlename__ = 'conquest_move_range' + id = Column(Integer, primary_key=True, autoincrement=True, + doc=u'An ID for this range.') + identifier = Column(Unicode(79), nullable=False, + doc=u'A readable identifier for this range.', + info=dict(format='identifier')) + targets = Column(Integer, nullable=False, + doc=u'The number of tiles this range targets.') + +create_translation_table('conquest_move_range_prose', ConquestMoveRange, 'prose', + name = Column(Unicode(79), nullable=True, + doc=u"A short name briefly describing the range", + info=dict(format='plaintext')), + description = Column(UnicodeText, nullable=True, + doc=u"A detailed description of the range", + info=dict(format='plaintext')), +) + +class ConquestPokemonAbility(TableBase): + u"""An ability a Pokémon species has in Pokémon Conquest.""" + __tablename__ = 'conquest_pokemon_abilities' + pokemon_species_id = Column(Integer, ForeignKey('pokemon_species.id'), primary_key=True, nullable=False, autoincrement=False, + doc=u'The ID of the Pokémon species with this ability.') + slot = Column(Integer, primary_key=True, nullable=False, autoincrement=False, + doc=u"The order abilities are listed in. Upon evolution, if a Pokémon's abilities change, it will receive the one in the same slot.") + ability_id = Column(Integer, ForeignKey('abilities.id'), nullable=False, + doc=u'The ID of the ability.') + +class ConquestPokemonEvolution(TableBase): + u"""The conditions under which a Pokémon must successfully complete an + action to evolve in Pokémon Conquest. + + Any condition may be null if it does not apply for a particular Pokémon. + """ + __tablename__ = 'conquest_pokemon_evolution' + evolved_species_id = Column(Integer, ForeignKey('pokemon_species.id'), primary_key=True, nullable=False, + doc=u"The ID of the post-evolution species.") + required_stat_id = Column(Integer, ForeignKey('conquest_stats.id'), nullable=True, + doc=u"The ID of the stat which minimum_stat applies to.") + minimum_stat = Column(Integer, nullable=True, + doc=u"The minimum value the Pokémon must have in a particular stat.") + minimum_link = Column(Integer, nullable=True, + doc=u"The minimum link percentage the Pokémon must have with its warrior.") + kingdom_id = Column(Integer, ForeignKey('conquest_kingdoms.id'), nullable=True, + doc=u"The ID of the kingdom in which this Pokémon must complete an action after meeting all other requirements.") + warrior_gender_id = Column(Integer, ForeignKey('genders.id'), nullable=True, + doc=u"The ID of the gender the Pokémon's warrior must be.") + item_id = Column(Integer, ForeignKey('items.id'), nullable=True, + doc=u"The ID of the item the Pokémon's warrior must have equipped.") + recruiting_ko_required = Column(Boolean, nullable=False, + doc=u"If true, the Pokémon must KO a Pokémon under the right conditions to recruit that Pokémon's warrior.") + +class ConquestPokemonMove(TableBase): + u"""A Pokémon's move in Pokémon Conquest. + + Yes, "move"; each Pokémon has exactly one. + """ + __tablename__ = 'conquest_pokemon_moves' + pokemon_species_id = Column(Integer, ForeignKey('pokemon_species.id'), primary_key=True, autoincrement=False, + doc=u'The ID of the Pokémon species.') + move_id = Column(Integer, ForeignKey('moves.id'), nullable=False, + doc=u'The ID of the move.') + +class ConquestPokemonStat(TableBase): + u"""A Pokémon's base stat in Pokémon Conquest. + + The main four base stats in Conquest are derived from level 100 stats in + the main series (ignoring effort, genes, and natures). Attack matches + either Attack or Special Attack, and Defense matches the average of Defense + and Special Defense. HP and Speed are the same. + """ + __tablename__ = 'conquest_pokemon_stats' + pokemon_species_id = Column(Integer, ForeignKey('pokemon_species.id'), primary_key=True, autoincrement=False, + doc=u'The ID of the Pokémon species.') + conquest_stat_id = Column(Integer, ForeignKey('conquest_stats.id'), primary_key=True, autoincrement=False, + doc=u'The ID of the stat.') + base_stat = Column(Integer, nullable=False, + doc=u'The base stat.') + +class ConquestStat(TableBase): + u"""A stat Pokémon have in Pokémon Conquest.""" + __tablename__ = 'conquest_stats' + __singlename__ = 'conquest_stat' # To be safe + id = Column(Integer, primary_key=True, autoincrement=True, + doc=u'An ID for this stat.') + identifier = Column(Unicode(79), nullable=False, + doc=u'A readable identifier for this stat.', + info=dict(format='identifier')) + is_base = Column(Boolean, nullable=False, + doc=u'True iff this is one of the main stats, calculated for individual Pokémon.') + +create_translation_table('conquest_stat_names', ConquestStat, 'names', + relation_lazy='joined', + name=Column(Unicode(79), nullable=False, index=True, + doc=u'The name.', + info=dict(format='plaintext', official=True)) +) + +class ConquestTransformationPokemon(TableBase): + u"""A Pokémon that satisfies a warrior transformation's link condition. + + If a warrior has one or more Pokémon listed here, they only need to raise + one of them to the required link. + """ + __tablename__ = 'conquest_transformation_pokemon' + transformation_id = Column(Integer, ForeignKey('conquest_warrior_transformation.transformed_warrior_rank_id'), primary_key=True, + doc=u'The ID of the corresponding transformation, in turn a warrior rank ID.') + pokemon_species_id = Column(Integer, ForeignKey('pokemon_species.id'), primary_key=True, + doc=u'The ID of the Pokémon species.') + +class ConquestTransformationWarrior(TableBase): + u"""A warrior who must be present in the same nation as another warrior for + the latter to transform into their next rank. + + If a warrior has one or more other warriors listed here, they *all* need to + gather in the same nation for the transformation to take place. + """ + __tablename__ = 'conquest_transformation_warriors' + transformation_id = Column(Integer, ForeignKey('conquest_warrior_transformation.transformed_warrior_rank_id'), primary_key=True, + doc=u'The ID of the corresponding transformation, in turn a warrior rank ID.') + present_warrior_id = Column(Integer, ForeignKey('conquest_warriors.id'), primary_key=True, + doc=u'The ID of the other warrior who must be present.') + +class ConquestWarrior(TableBase): + u"""A warrior in Pokémon Conquest.""" + __tablename__ = 'conquest_warriors' + __singlename__ = 'warrior' + id = Column(Integer, primary_key=True, nullable=False, autoincrement=True, + doc=u'An ID for this warrior.') + identifier = Column(Unicode(79), nullable=False, + doc=u'A readable identifier for this warrior.', + info=dict(format='identifier')) + gender_id = Column(Integer, ForeignKey('genders.id'), nullable=False, + doc=u"The ID of the warrior's gender.") + archetype_id = Column(Integer, ForeignKey('conquest_warrior_archetypes.id'), nullable=True, + doc=u"The ID of this warrior's archetype. Null for unique warriors.") + +create_translation_table('conquest_warrior_names', ConquestWarrior, 'names', + relation_lazy='joined', + name=Column(Unicode(79), nullable=False, index=True, + doc=u'The name.', + info=dict(format='plaintext', official=True)) +) + +class ConquestWarriorArchetype(TableBase): + u"""An archetype that generic warriors in Pokémon Conquest can have. All + warriors of a particular archetype share sprites and dialogue. + + Some of these are unused as warriors because they exist only as NPCs. They + should still be kept because we have their sprites and may eventually get + their dialogue. + """ + __tablename__ = 'conquest_warrior_archetypes' + __singlename__ = 'archetype' + id = Column(Integer, primary_key=True, autoincrement=True, + doc=u'An ID for this archetype.') + identifier = Column(Unicode(79), nullable=False, + doc=u'A readable identifier describing this archetype.', + info=dict(format='identifier')) + +class ConquestWarriorRank(TableBase): + u"""A warrior at a particular rank in Pokémon Conquest. + + These are used for whatever changes between ranks, much like Pokémon forms. + Generic warriors who have only one rank are also represented here, with a + single row. + + To clarify, each warrior's ranks are individually called "warrior ranks" + here; for example, "Rank 2 Nobunaga" is an example of a warrior rank, not + just "Rank 2". + """ + __tablename__ = 'conquest_warrior_ranks' + __singlename__ = 'warrior_rank' + id = Column(Integer, primary_key=True, autoincrement=True, + doc=u'An ID for this warrior rank.') + warrior_id = Column(Integer, ForeignKey('conquest_warriors.id'), nullable=False, + doc=u'The ID of the warrior.') + rank = Column(Integer, nullable=False, + doc=u'The rank number.') + skill_id = Column(Integer, ForeignKey('conquest_warrior_skills.id'), nullable=False, + doc=u"The ID of this warrior rank's warrior skill.") + + __table_args__ = ( + UniqueConstraint(warrior_id, rank), + {}, + ) + +class ConquestWarriorRankStatMap(TableBase): + u"""Any of a warrior rank's warrior stats in Pokémon Conquest.""" + __tablename__ = 'conquest_warrior_rank_stat_map' + warrior_rank_id = Column(Integer, ForeignKey('conquest_warrior_ranks.id'), primary_key=True, autoincrement=False, + doc=u'The ID of the warrior rank.') + warrior_stat_id = Column(Integer, ForeignKey('conquest_warrior_stats.id'), primary_key=True, autoincrement=False, + doc=u'The ID of the warrior stat.') + base_stat = Column(Integer, nullable=False, + doc=u'The stat.') + +class ConquestWarriorSkill(TableBase): + u"""A warrior skill in Pokémon Conquest.""" + __tablename__ = 'conquest_warrior_skills' + __singlename__ = 'skill' + id = Column(Integer, primary_key=True, nullable=False, autoincrement=True, + doc=u'An ID for this skill.') + identifier = Column(Unicode(79), nullable=False, + doc=u'A readable identifier for this skill.', + info=dict(format='identifier')) + +create_translation_table('conquest_warrior_skill_names', ConquestWarriorSkill, 'names', + relation_lazy='joined', + name=Column(Unicode(79), nullable=False, index=True, + doc=u'The name.', + info=dict(format='plaintext', official=True)) +) + +class ConquestWarriorSpecialty(TableBase): + u"""A warrior's specialty types in Pokémon Conquest. + + These have no actual effect on gameplay; they just indicate which types of + Pokémon each warrior generally has strong maximum links with. + """ + __tablename__ = 'conquest_warrior_specialties' + warrior_id = Column(Integer, ForeignKey('conquest_warriors.id'), primary_key=True, nullable=False, autoincrement=False, + doc=u'The ID of the warrior.') + type_id = Column(Integer, ForeignKey('types.id'), primary_key=True, nullable=False, autoincrement=False, + doc=u'The ID of the type.') + slot = Column(Integer, primary_key=True, nullable=False, autoincrement=False, + doc=u"The order in which the warrior's types are listed.") + +class ConquestWarriorStat(TableBase): + u"""A stat that warriors have in Pokémon Conquest.""" + __tablename__ = 'conquest_warrior_stats' + __singlename__ = 'warrior_stat' + id = Column(Integer, primary_key=True, autoincrement=True, + doc=u'An ID for this stat.') + identifier = Column(Unicode(79), nullable=False, + doc=u'A readable identifier for this stat.', + info=dict(format='identifier')) + +create_translation_table('conquest_warrior_stat_names', ConquestWarriorStat, 'names', + relation_lazy='joined', + name=Column(Unicode(79), nullable=False, index=True, + doc=u'The name.', + info=dict(format='plaintext', official=True)) +) + +class ConquestWarriorTransformation(TableBase): + u"""The conditions under which a warrior must perform an action in order + to transform to the next rank. + + Or most of them, anyway. See also ConquestTransformationPokemon and + ConquestTransformationWarrior. + """ + __tablename__ = 'conquest_warrior_transformation' + transformed_warrior_rank_id = Column(Integer, ForeignKey('conquest_warrior_ranks.id'), primary_key=True, + doc=u'The ID of the post-transformation warrior rank.') + is_automatic = Column(Boolean, nullable=False, + doc=u'True iff the transformation happens automatically in the story with no further requirements.') + required_link = Column(Integer, nullable=True, + doc=u'The link percentage the warrior must reach with one of several specific Pokémon, if any.') + completed_episode_id = Column(Integer, ForeignKey('conquest_episodes.id'), nullable=True, + doc=u'The ID of the episode the player must have completed, if any.') + current_episode_id = Column(Integer, ForeignKey('conquest_episodes.id'), nullable=True, + doc=u'The ID of the episode the player must currently be playing, if any.') + distant_warrior_id = Column(Integer, ForeignKey('conquest_warriors.id'), nullable=True, + doc=u'The ID of another warrior who must be in the army, but not in the same kingdom or in any adjacent kingdom.') + female_warlord_count = Column(Integer, nullable=True, + doc=u'The number of female warlords who must be in the same nation.') + pokemon_count = Column(Integer, nullable=True, + doc=u'The number of Pokémon that must be registered in the gallery.') + collection_type_id = Column(Integer, ForeignKey('types.id'), nullable=True, + doc=u'The ID of a type all Pokémon of which must be registered in the gallery.') + warrior_count = Column(Integer, nullable=True, + doc=u'The number of warriors that must be registered in the gallery.') + + +### Relationships down here, to avoid dependency ordering problems + +ConquestEpisode.warriors = relationship(ConquestWarrior, + secondary=ConquestEpisodeWarrior.__table__, + innerjoin=True, + backref='episodes') + +ConquestKingdom.type = relationship(Type, + uselist=False, + innerjoin=True, lazy='joined', + backref=backref('conquest_kingdom', uselist=False)) + +ConquestMaxLink.pokemon = relationship(PokemonSpecies, + uselist=False, + innerjoin=True, lazy='joined', + backref=backref('conquest_max_links', lazy='dynamic', + order_by=ConquestMaxLink.warrior_rank_id)) +ConquestMaxLink.warrior_rank = relationship(ConquestWarriorRank, + uselist=False, + innerjoin=True, lazy='joined', + backref=backref('max_links', lazy='dynamic')) +ConquestMaxLink.warrior = association_proxy('warrior_rank', 'warrior') + +ConquestMoveData.move_displacement = relationship(ConquestMoveDisplacement, + uselist=False, + backref='move_data') +ConquestMoveData.move = relationship(Move, + uselist=False, + innerjoin=True, lazy='joined', + backref=backref('conquest_data', uselist=False)) +ConquestMoveData.move_effect = relationship(ConquestMoveEffect, + innerjoin=True, lazy='joined', + backref='move_data') +ConquestMoveData.range = relationship(ConquestMoveRange, + innerjoin=True, lazy='joined', + backref='move_data') + +ConquestMoveData.effect = markdown.MoveEffectProperty('effect') +ConquestMoveData.effect_map = markdown.MoveEffectPropertyMap('effect_map') +ConquestMoveData.short_effect = markdown.MoveEffectProperty('short_effect') +ConquestMoveData.short_effect_map = markdown.MoveEffectPropertyMap('short_effect_map') +ConquestMoveData.displacement = markdown.MoveEffectProperty('effect', relationship='move_displacement') + +ConquestPokemonEvolution.gender = relationship(Gender, + backref='conquest_evolutions') +ConquestPokemonEvolution.item = relationship(Item, + backref='conquest_evolutions') +ConquestPokemonEvolution.kingdom = relationship(ConquestKingdom, + backref='evolutions') +ConquestPokemonEvolution.stat = relationship(ConquestStat, + backref='evolutions') + +ConquestPokemonStat.pokemon = relationship(PokemonSpecies, + uselist=False, + innerjoin=True, lazy='joined', + backref='conquest_stats') +ConquestPokemonStat.stat = relationship(ConquestStat, + uselist=False, + innerjoin=True, lazy='joined', + backref='pokemon_stats') + +ConquestWarrior.archetype = relationship(ConquestWarriorArchetype, + uselist=False, + backref=backref('warriors')) +ConquestWarrior.ranks = relationship(ConquestWarriorRank, + order_by=ConquestWarriorRank.rank, + innerjoin=True, + backref=backref('warrior', uselist=False)) +ConquestWarrior.types = relationship(Type, + secondary=ConquestWarriorSpecialty.__table__, + order_by=ConquestWarriorSpecialty.slot, + innerjoin=True, + backref='conquest_warriors') + +ConquestWarriorRank.skill = relationship(ConquestWarriorSkill, + uselist=False, + innerjoin=True, lazy='joined', + backref=backref('warrior_ranks', order_by=ConquestWarriorRank.id)) +ConquestWarriorRank.stats = relationship(ConquestWarriorRankStatMap, + innerjoin=True, + order_by=ConquestWarriorRankStatMap.warrior_stat_id, + backref=backref('warrior_rank', uselist=False, innerjoin=True, lazy='joined')) + +ConquestWarriorRankStatMap.stat = relationship(ConquestWarriorStat, + innerjoin=True, lazy='joined', + uselist=False, + backref='stat_map') + +ConquestWarriorTransformation.completed_episode = relationship(ConquestEpisode, + primaryjoin=ConquestWarriorTransformation.completed_episode_id==ConquestEpisode.id, + uselist=False) +ConquestWarriorTransformation.current_episode = relationship(ConquestEpisode, + primaryjoin=ConquestWarriorTransformation.current_episode_id==ConquestEpisode.id, + uselist=False) +ConquestWarriorTransformation.distant_warrior = relationship(ConquestWarrior, + uselist=False) +ConquestWarriorTransformation.pokemon = relationship(PokemonSpecies, + secondary=ConquestTransformationPokemon.__table__, + order_by=PokemonSpecies.conquest_order) +ConquestWarriorTransformation.present_warriors = relationship(ConquestWarrior, + secondary=ConquestTransformationWarrior.__table__, + order_by=ConquestWarrior.id) +ConquestWarriorTransformation.type = relationship(Type, + uselist=False) +ConquestWarriorTransformation.warrior_rank = relationship(ConquestWarriorRank, + uselist=False, + innerjoin=True, lazy='joined', + backref=backref('transformation', uselist=False, innerjoin=True)) + + +PokemonSpecies.conquest_abilities = relationship(Ability, + secondary=ConquestPokemonAbility.__table__, + order_by=ConquestPokemonAbility.slot, + backref=backref('conquest_pokemon', order_by=PokemonSpecies.conquest_order, + innerjoin=True)) +PokemonSpecies.conquest_move = relationship(Move, + secondary=ConquestPokemonMove.__table__, + uselist=False, + backref=backref('conquest_pokemon', order_by=PokemonSpecies.conquest_order)) +PokemonSpecies.conquest_evolution = relationship(ConquestPokemonEvolution, + uselist=False, + backref=backref('evolved_species', innerjoin=True, lazy='joined', uselist=False)) diff --git a/pokedex/db/tables.py b/pokedex/db/tables/core.py similarity index 77% rename from pokedex/db/tables.py rename to pokedex/db/tables/core.py index 447b6a58c..ff1b8d82e 100644 --- a/pokedex/db/tables.py +++ b/pokedex/db/tables/core.py @@ -1,117 +1,15 @@ # encoding: utf8 -u"""The Pokédex schema. - -Columns have a info dictionary with these keys: -- official: True if the values appear in games or official material; False if - they are fan-created or fan-written. This flag is currently only set for - official text columns. -- format: The format of a text column. Can be one of: - - plaintext: Normal Unicode text (widely used in names) - - markdown: Veekun's Markdown flavor (generally used in effect descriptions) - - gametext: Transcription of in-game text that strives to be both - human-readable and represent the original text exactly. - - identifier: A fan-made identifier in the [-_a-z0-9]* format. Not intended - for translation. - - latex: A formula in LaTeX syntax. -- ripped: True for text that has been ripped from the games, and can be ripped - again for new versions or languages - -- string_getter: for translation columns, a function taking (text, session, - language) that is used for properties on the main table. Used for Markdown - text. - -See `pokedex.db.multilang` for how localizable text columns work. The session -classes in that module can be used to change the default language. -""" -# 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 declarative_base, DeclarativeMeta +from sqlalchemy import Column, ForeignKey, PrimaryKeyConstraint, UniqueConstraint from sqlalchemy.ext.associationproxy import association_proxy from sqlalchemy.ext.hybrid import hybrid_property from sqlalchemy.orm import backref, relationship -from sqlalchemy.orm.session import Session -from sqlalchemy.orm.interfaces import AttributeExtension -from sqlalchemy.sql import and_, or_ -from sqlalchemy.schema import ColumnDefault +from sqlalchemy.sql import and_ from sqlalchemy.types import Boolean, Enum, Integer, SmallInteger, Unicode, UnicodeText -from pokedex.db import markdown, multilang +from pokedex.db import markdown +from pokedex.db.tables.base import TableBase, Language, create_translation_table -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 u"<%s object (%s): %s>" % (typename, pk, self.identifier) - except AttributeError: - return u"<%s object (%s)>" % (typename, pk) - - def __str__(self): - return unicode(self).encode('utf8') - - def __repr__(self): - return unicode(self).encode('utf8') - -mapped_classes = [] -class TableMetaclass(DeclarativeMeta): - def __init__(cls, name, bases, attrs): - super(TableMetaclass, cls).__init__(name, bases, attrs) - if hasattr(cls, '__tablename__'): - mapped_classes.append(cls) - cls.translation_classes = [] - -metadata = MetaData() -TableBase = declarative_base(metadata=metadata, cls=TableSuperclass, metaclass=TableMetaclass) - - -### Need Language first, to create the partial() below - -class Language(TableBase): - u"""A language the Pokémon games have been translated into.""" - __tablename__ = 'languages' - __singlename__ = 'language' - id = Column(Integer, primary_key=True, nullable=False, - doc=u"A numeric ID") - iso639 = Column(Unicode(79), nullable=False, - doc=u"The two-letter code of the country where this language is spoken. Note that it is not unique.", - info=dict(format='identifier')) - iso3166 = Column(Unicode(79), nullable=False, - doc=u"The two-letter code of the language. Note that it is not unique.", - info=dict(format='identifier')) - identifier = Column(Unicode(79), nullable=False, - doc=u"An identifier", - info=dict(format='identifier')) - official = Column(Boolean, nullable=False, index=True, - doc=u"True iff games are produced in the language.") - order = Column(Integer, nullable=True, - doc=u"Order for sorting in foreign name lists.") - -create_translation_table = partial(multilang.create_translation_table, language_class=Language) - -create_translation_table('language_names', Language, 'names', - name = Column(Unicode(79), nullable=False, index=True, - doc=u"The name", - info=dict(format='plaintext', official=True)), -) - -### The actual tables class Ability(TableBase): u"""An ability a Pokémon can have, such as Static or Pressure.""" @@ -244,420 +142,6 @@ class Characteristic(TableBase): info=dict(official=True, format='plaintext')), ) -class ConquestEpisode(TableBase): - u"""An episode from Pokémon Conquest: one of a bunch of mini-stories - featuring a particular warrior. - - The main story, "The Legend of Ransei", also counts, even though it's not - in the episode select menu and there's no way to replay it. - """ - __tablename__ = 'conquest_episodes' - __singlename__ = 'episode' - id = Column(Integer, primary_key=True, autoincrement=True, - doc=u'An ID for this episode.') - identifier = Column(Unicode(79), nullable=False, - doc=u'A readable identifier for this episode.', - info=dict(format='identifier')) - -create_translation_table('conquest_episode_names', ConquestEpisode, 'names', - relation_lazy='joined', - name=Column(Unicode(79), nullable=False, index=True, - doc=u'The name.', - info=dict(format='plaintext', official=True)) -) - -class ConquestEpisodeWarrior(TableBase): - u"""A warrior featured in an episode in Pokémon Conquest. - - This needs its own table because of the player having two episodes and - there being two players. - """ - __tablename__ = 'conquest_episode_warriors' - episode_id = Column(Integer, ForeignKey('conquest_episodes.id'), primary_key=True, - doc=u'The ID of the episode.') - warrior_id = Column(Integer, ForeignKey('conquest_warriors.id'), primary_key=True, - doc=u'The ID of the warrior.') - -class ConquestKingdom(TableBase): - u"""A kingdom in Pokémon Conquest.""" - __tablename__ = 'conquest_kingdoms' - __singlename__ = 'kingdom' - id = Column(Integer, primary_key=True, autoincrement=True, - doc=u"An ID for this kingdom.") - identifier = Column(Unicode(79), nullable=False, - doc=u"A readable identifier for this kingdom.", - info=dict(format='identifier')) - type_id = Column(Integer, ForeignKey('types.id'), nullable=False, - doc=u"The type associated with this kingdom in-game.") - -create_translation_table('conquest_kingdom_names', ConquestKingdom, 'names', - relation_lazy='joined', - name=Column(Unicode(79), nullable=False, index=True, - doc=u'The name.', - info=dict(format='plaintext', official=True)) -) - -class ConquestMaxLink(TableBase): - u"""The maximum link a warrior rank can reach with a Pokémon in Pokémon Conquest.""" - __tablename__ = 'conquest_max_links' - warrior_rank_id = Column(Integer, ForeignKey('conquest_warrior_ranks.id'), primary_key=True, - doc=u"The ID of the warrior rank.") - pokemon_species_id = Column(Integer, ForeignKey('pokemon_species.id'), primary_key=True, - doc=u'The ID of the Pokémon species.') - max_link = Column(Integer, nullable=False, - doc=u'The maximum link percentage this warrior rank and Pokémon can reach.') - -class ConquestMoveData(TableBase): - u"""Data about a move in Pokémon Conquest.""" - __tablename__ = 'conquest_move_data' - move_id = Column(Integer, ForeignKey('moves.id'), primary_key=True, autoincrement=False, - doc=u'The ID of the move.') - power = Column(Integer, nullable=True, - doc=u"The move's power, null if it does no damage.") - accuracy = Column(Integer, nullable=True, - doc=u"The move's base accuracy, null if it is self-targeted or never misses.") - effect_chance = Column(Integer, nullable=True, - doc=u"The chance as a percentage that the move's secondary effect will trigger.") - effect_id = Column(Integer, ForeignKey('conquest_move_effects.id'), nullable=False, - doc=u"The ID of the move's effect.") - range_id = Column(Integer, ForeignKey('conquest_move_ranges.id'), nullable=False, - doc=u"The ID of the move's range.") - displacement_id = Column(Integer, ForeignKey('conquest_move_displacements.id'), nullable=True, - doc=u"The ID of the move's displacement.") - - @property - def star_rating(self): - """Return the move's in-game power rating as a number of stars.""" - if not self.power: - return 0 - else: - stars = (self.power - 1) // 10 - stars = min(stars, 5) # i.e. maximum of 5 stars - stars = max(stars, 1) # And minimum of 1 - return stars - -class ConquestMoveDisplacement(TableBase): - u"""A way in which a move can cause the user or target to move to a - different tile. - - If a move displaces its user, the move's range is relative to the user's - original position. - """ - __tablename__ = 'conquest_move_displacements' - __singlename__ = 'move_displacement' - id = Column(Integer, primary_key=True, autoincrement=True, - doc=u'An ID for this displacement.') - identifier = Column(Unicode(79), nullable=False, - doc=u'A readable identifier for this displacement.', - info=dict(format='identifier')) - affects_target = Column(Boolean, nullable=False, - doc=u'True iff the move displaces its target(s) and not its user.') - -create_translation_table('conquest_move_displacement_prose', ConquestMoveDisplacement, 'prose', - name = Column(Unicode(79), nullable=True, - doc=u'A name for the displacement.', - info=dict(format='plaintext')), - short_effect = Column(UnicodeText, nullable=True, - doc=u"A short summary of how the displacement works, to be used in the move's short effect.", - info=dict(format='markdown')), - effect = Column(UnicodeText, nullable=True, - doc=u"A detailed description of how the displacement works, to be used alongside the move's long effect.", - info=dict(format='markdown')), -) - -class ConquestMoveEffect(TableBase): - u"""An effect moves can have in Pokémon Conquest.""" - __tablename__ = 'conquest_move_effects' - __singlename__ = 'conquest_move_effect' - id = Column(Integer, primary_key=True, autoincrement=True, - doc=u'An ID for this effect.') - -create_translation_table('conquest_move_effect_prose', ConquestMoveEffect, 'prose', - short_effect = Column(UnicodeText, nullable=True, - doc=u"A short summary of the effect", - info=dict(format='markdown')), - effect = Column(UnicodeText, nullable=True, - doc=u"A detailed description of the effect", - info=dict(format='markdown')), -) - -class ConquestMoveRange(TableBase): - u"""A set of tiles moves can target in Pokémon Conquest.""" - __tablename__ = 'conquest_move_ranges' - __singlename__ = 'conquest_move_range' - id = Column(Integer, primary_key=True, autoincrement=True, - doc=u'An ID for this range.') - identifier = Column(Unicode(79), nullable=False, - doc=u'A readable identifier for this range.', - info=dict(format='identifier')) - targets = Column(Integer, nullable=False, - doc=u'The number of tiles this range targets.') - -create_translation_table('conquest_move_range_prose', ConquestMoveRange, 'prose', - name = Column(Unicode(79), nullable=True, - doc=u"A short name briefly describing the range", - info=dict(format='plaintext')), - description = Column(UnicodeText, nullable=True, - doc=u"A detailed description of the range", - info=dict(format='plaintext')), -) - -class ConquestPokemonAbility(TableBase): - u"""An ability a Pokémon species has in Pokémon Conquest.""" - __tablename__ = 'conquest_pokemon_abilities' - pokemon_species_id = Column(Integer, ForeignKey('pokemon_species.id'), primary_key=True, nullable=False, autoincrement=False, - doc=u'The ID of the Pokémon species with this ability.') - slot = Column(Integer, primary_key=True, nullable=False, autoincrement=False, - doc=u"The order abilities are listed in. Upon evolution, if a Pokémon's abilities change, it will receive the one in the same slot.") - ability_id = Column(Integer, ForeignKey('abilities.id'), nullable=False, - doc=u'The ID of the ability.') - -class ConquestPokemonEvolution(TableBase): - u"""The conditions under which a Pokémon must successfully complete an - action to evolve in Pokémon Conquest. - - Any condition may be null if it does not apply for a particular Pokémon. - """ - __tablename__ = 'conquest_pokemon_evolution' - evolved_species_id = Column(Integer, ForeignKey('pokemon_species.id'), primary_key=True, nullable=False, - doc=u"The ID of the post-evolution species.") - required_stat_id = Column(Integer, ForeignKey('conquest_stats.id'), nullable=True, - doc=u"The ID of the stat which minimum_stat applies to.") - minimum_stat = Column(Integer, nullable=True, - doc=u"The minimum value the Pokémon must have in a particular stat.") - minimum_link = Column(Integer, nullable=True, - doc=u"The minimum link percentage the Pokémon must have with its warrior.") - kingdom_id = Column(Integer, ForeignKey('conquest_kingdoms.id'), nullable=True, - doc=u"The ID of the kingdom in which this Pokémon must complete an action after meeting all other requirements.") - warrior_gender_id = Column(Integer, ForeignKey('genders.id'), nullable=True, - doc=u"The ID of the gender the Pokémon's warrior must be.") - item_id = Column(Integer, ForeignKey('items.id'), nullable=True, - doc=u"The ID of the item the Pokémon's warrior must have equipped.") - recruiting_ko_required = Column(Boolean, nullable=False, - doc=u"If true, the Pokémon must KO a Pokémon under the right conditions to recruit that Pokémon's warrior.") - -class ConquestPokemonMove(TableBase): - u"""A Pokémon's move in Pokémon Conquest. - - Yes, "move"; each Pokémon has exactly one. - """ - __tablename__ = 'conquest_pokemon_moves' - pokemon_species_id = Column(Integer, ForeignKey('pokemon_species.id'), primary_key=True, autoincrement=False, - doc=u'The ID of the Pokémon species.') - move_id = Column(Integer, ForeignKey('moves.id'), nullable=False, - doc=u'The ID of the move.') - -class ConquestPokemonStat(TableBase): - u"""A Pokémon's base stat in Pokémon Conquest. - - The main four base stats in Conquest are derived from level 100 stats in - the main series (ignoring effort, genes, and natures). Attack matches - either Attack or Special Attack, and Defense matches the average of Defense - and Special Defense. HP and Speed are the same. - """ - __tablename__ = 'conquest_pokemon_stats' - pokemon_species_id = Column(Integer, ForeignKey('pokemon_species.id'), primary_key=True, autoincrement=False, - doc=u'The ID of the Pokémon species.') - conquest_stat_id = Column(Integer, ForeignKey('conquest_stats.id'), primary_key=True, autoincrement=False, - doc=u'The ID of the stat.') - base_stat = Column(Integer, nullable=False, - doc=u'The base stat.') - -class ConquestStat(TableBase): - u"""A stat Pokémon have in Pokémon Conquest.""" - __tablename__ = 'conquest_stats' - __singlename__ = 'conquest_stat' # To be safe - id = Column(Integer, primary_key=True, autoincrement=True, - doc=u'An ID for this stat.') - identifier = Column(Unicode(79), nullable=False, - doc=u'A readable identifier for this stat.', - info=dict(format='identifier')) - is_base = Column(Boolean, nullable=False, - doc=u'True iff this is one of the main stats, calculated for individual Pokémon.') - -create_translation_table('conquest_stat_names', ConquestStat, 'names', - relation_lazy='joined', - name=Column(Unicode(79), nullable=False, index=True, - doc=u'The name.', - info=dict(format='plaintext', official=True)) -) - -class ConquestTransformationPokemon(TableBase): - u"""A Pokémon that satisfies a warrior transformation's link condition. - - If a warrior has one or more Pokémon listed here, they only need to raise - one of them to the required link. - """ - __tablename__ = 'conquest_transformation_pokemon' - transformation_id = Column(Integer, ForeignKey('conquest_warrior_transformation.transformed_warrior_rank_id'), primary_key=True, - doc=u'The ID of the corresponding transformation, in turn a warrior rank ID.') - pokemon_species_id = Column(Integer, ForeignKey('pokemon_species.id'), primary_key=True, - doc=u'The ID of the Pokémon species.') - -class ConquestTransformationWarrior(TableBase): - u"""A warrior who must be present in the same nation as another warrior for - the latter to transform into their next rank. - - If a warrior has one or more other warriors listed here, they *all* need to - gather in the same nation for the transformation to take place. - """ - __tablename__ = 'conquest_transformation_warriors' - transformation_id = Column(Integer, ForeignKey('conquest_warrior_transformation.transformed_warrior_rank_id'), primary_key=True, - doc=u'The ID of the corresponding transformation, in turn a warrior rank ID.') - present_warrior_id = Column(Integer, ForeignKey('conquest_warriors.id'), primary_key=True, - doc=u'The ID of the other warrior who must be present.') - -class ConquestWarrior(TableBase): - u"""A warrior in Pokémon Conquest.""" - __tablename__ = 'conquest_warriors' - __singlename__ = 'warrior' - id = Column(Integer, primary_key=True, nullable=False, autoincrement=True, - doc=u'An ID for this warrior.') - identifier = Column(Unicode(79), nullable=False, - doc=u'A readable identifier for this warrior.', - info=dict(format='identifier')) - gender_id = Column(Integer, ForeignKey('genders.id'), nullable=False, - doc=u"The ID of the warrior's gender.") - archetype_id = Column(Integer, ForeignKey('conquest_warrior_archetypes.id'), nullable=True, - doc=u"The ID of this warrior's archetype. Null for unique warriors.") - -create_translation_table('conquest_warrior_names', ConquestWarrior, 'names', - relation_lazy='joined', - name=Column(Unicode(79), nullable=False, index=True, - doc=u'The name.', - info=dict(format='plaintext', official=True)) -) - -class ConquestWarriorArchetype(TableBase): - u"""An archetype that generic warriors in Pokémon Conquest can have. All - warriors of a particular archetype share sprites and dialogue. - - Some of these are unused as warriors because they exist only as NPCs. They - should still be kept because we have their sprites and may eventually get - their dialogue. - """ - __tablename__ = 'conquest_warrior_archetypes' - __singlename__ = 'archetype' - id = Column(Integer, primary_key=True, autoincrement=True, - doc=u'An ID for this archetype.') - identifier = Column(Unicode(79), nullable=False, - doc=u'A readable identifier describing this archetype.', - info=dict(format='identifier')) - -class ConquestWarriorRank(TableBase): - u"""A warrior at a particular rank in Pokémon Conquest. - - These are used for whatever changes between ranks, much like Pokémon forms. - Generic warriors who have only one rank are also represented here, with a - single row. - - To clarify, each warrior's ranks are individually called "warrior ranks" - here; for example, "Rank 2 Nobunaga" is an example of a warrior rank, not - just "Rank 2". - """ - __tablename__ = 'conquest_warrior_ranks' - __singlename__ = 'warrior_rank' - id = Column(Integer, primary_key=True, autoincrement=True, - doc=u'An ID for this warrior rank.') - warrior_id = Column(Integer, ForeignKey('conquest_warriors.id'), nullable=False, - doc=u'The ID of the warrior.') - rank = Column(Integer, nullable=False, - doc=u'The rank number.') - skill_id = Column(Integer, ForeignKey('conquest_warrior_skills.id'), nullable=False, - doc=u"The ID of this warrior rank's warrior skill.") - - __table_args__ = ( - UniqueConstraint(warrior_id, rank), - {}, - ) - -class ConquestWarriorRankStatMap(TableBase): - u"""Any of a warrior rank's warrior stats in Pokémon Conquest.""" - __tablename__ = 'conquest_warrior_rank_stat_map' - warrior_rank_id = Column(Integer, ForeignKey('conquest_warrior_ranks.id'), primary_key=True, autoincrement=False, - doc=u'The ID of the warrior rank.') - warrior_stat_id = Column(Integer, ForeignKey('conquest_warrior_stats.id'), primary_key=True, autoincrement=False, - doc=u'The ID of the warrior stat.') - base_stat = Column(Integer, nullable=False, - doc=u'The stat.') - -class ConquestWarriorSkill(TableBase): - u"""A warrior skill in Pokémon Conquest.""" - __tablename__ = 'conquest_warrior_skills' - __singlename__ = 'skill' - id = Column(Integer, primary_key=True, nullable=False, autoincrement=True, - doc=u'An ID for this skill.') - identifier = Column(Unicode(79), nullable=False, - doc=u'A readable identifier for this skill.', - info=dict(format='identifier')) - -create_translation_table('conquest_warrior_skill_names', ConquestWarriorSkill, 'names', - relation_lazy='joined', - name=Column(Unicode(79), nullable=False, index=True, - doc=u'The name.', - info=dict(format='plaintext', official=True)) -) - -class ConquestWarriorSpecialty(TableBase): - u"""A warrior's specialty types in Pokémon Conquest. - - These have no actual effect on gameplay; they just indicate which types of - Pokémon each warrior generally has strong maximum links with. - """ - __tablename__ = 'conquest_warrior_specialties' - warrior_id = Column(Integer, ForeignKey('conquest_warriors.id'), primary_key=True, nullable=False, autoincrement=False, - doc=u'The ID of the warrior.') - type_id = Column(Integer, ForeignKey('types.id'), primary_key=True, nullable=False, autoincrement=False, - doc=u'The ID of the type.') - slot = Column(Integer, primary_key=True, nullable=False, autoincrement=False, - doc=u"The order in which the warrior's types are listed.") - -class ConquestWarriorStat(TableBase): - u"""A stat that warriors have in Pokémon Conquest.""" - __tablename__ = 'conquest_warrior_stats' - __singlename__ = 'warrior_stat' - id = Column(Integer, primary_key=True, autoincrement=True, - doc=u'An ID for this stat.') - identifier = Column(Unicode(79), nullable=False, - doc=u'A readable identifier for this stat.', - info=dict(format='identifier')) - -create_translation_table('conquest_warrior_stat_names', ConquestWarriorStat, 'names', - relation_lazy='joined', - name=Column(Unicode(79), nullable=False, index=True, - doc=u'The name.', - info=dict(format='plaintext', official=True)) -) - -class ConquestWarriorTransformation(TableBase): - u"""The conditions under which a warrior must perform an action in order - to transform to the next rank. - - Or most of them, anyway. See also ConquestTransformationPokemon and - ConquestTransformationWarrior. - """ - __tablename__ = 'conquest_warrior_transformation' - transformed_warrior_rank_id = Column(Integer, ForeignKey('conquest_warrior_ranks.id'), primary_key=True, - doc=u'The ID of the post-transformation warrior rank.') - is_automatic = Column(Boolean, nullable=False, - doc=u'True iff the transformation happens automatically in the story with no further requirements.') - required_link = Column(Integer, nullable=True, - doc=u'The link percentage the warrior must reach with one of several specific Pokémon, if any.') - completed_episode_id = Column(Integer, ForeignKey('conquest_episodes.id'), nullable=True, - doc=u'The ID of the episode the player must have completed, if any.') - current_episode_id = Column(Integer, ForeignKey('conquest_episodes.id'), nullable=True, - doc=u'The ID of the episode the player must currently be playing, if any.') - distant_warrior_id = Column(Integer, ForeignKey('conquest_warriors.id'), nullable=True, - doc=u'The ID of another warrior who must be in the army, but not in the same kingdom or in any adjacent kingdom.') - female_warlord_count = Column(Integer, nullable=True, - doc=u'The number of female warlords who must be in the same nation.') - pokemon_count = Column(Integer, nullable=True, - doc=u'The number of Pokémon that must be registered in the gallery.') - collection_type_id = Column(Integer, ForeignKey('types.id'), nullable=True, - doc=u'The ID of a type all Pokémon of which must be registered in the gallery.') - warrior_count = Column(Integer, nullable=True, - doc=u'The number of warriors that must be registered in the gallery.') - class ContestCombo(TableBase): u"""Combo of two moves in a Contest.""" __tablename__ = 'contest_combos' @@ -2257,113 +1741,6 @@ class VersionGroupRegion(TableBase): backref='characteristics') -ConquestEpisode.warriors = relationship(ConquestWarrior, - secondary=ConquestEpisodeWarrior.__table__, - innerjoin=True, - backref='episodes') - -ConquestKingdom.type = relationship(Type, - uselist=False, - innerjoin=True, lazy='joined', - backref=backref('conquest_kingdom', uselist=False)) - -ConquestMaxLink.pokemon = relationship(PokemonSpecies, - uselist=False, - innerjoin=True, lazy='joined', - backref=backref('conquest_max_links', lazy='dynamic', - order_by=ConquestMaxLink.warrior_rank_id)) -ConquestMaxLink.warrior_rank = relationship(ConquestWarriorRank, - uselist=False, - innerjoin=True, lazy='joined', - backref=backref('max_links', lazy='dynamic')) -ConquestMaxLink.warrior = association_proxy('warrior_rank', 'warrior') - -ConquestMoveData.move_displacement = relationship(ConquestMoveDisplacement, - uselist=False, - backref='move_data') -ConquestMoveData.move = relationship(Move, - uselist=False, - innerjoin=True, lazy='joined', - backref=backref('conquest_data', uselist=False)) -ConquestMoveData.move_effect = relationship(ConquestMoveEffect, - innerjoin=True, lazy='joined', - backref='move_data') -ConquestMoveData.range = relationship(ConquestMoveRange, - innerjoin=True, lazy='joined', - backref='move_data') - -ConquestMoveData.effect = markdown.MoveEffectProperty('effect') -ConquestMoveData.effect_map = markdown.MoveEffectPropertyMap('effect_map') -ConquestMoveData.short_effect = markdown.MoveEffectProperty('short_effect') -ConquestMoveData.short_effect_map = markdown.MoveEffectPropertyMap('short_effect_map') -ConquestMoveData.displacement = markdown.MoveEffectProperty('effect', relationship='move_displacement') - -ConquestPokemonEvolution.gender = relationship(Gender, - backref='conquest_evolutions') -ConquestPokemonEvolution.item = relationship(Item, - backref='conquest_evolutions') -ConquestPokemonEvolution.kingdom = relationship(ConquestKingdom, - backref='evolutions') -ConquestPokemonEvolution.stat = relationship(ConquestStat, - backref='evolutions') - -ConquestPokemonStat.pokemon = relationship(PokemonSpecies, - uselist=False, - innerjoin=True, lazy='joined', - backref='conquest_stats') -ConquestPokemonStat.stat = relationship(ConquestStat, - uselist=False, - innerjoin=True, lazy='joined', - backref='pokemon_stats') - -ConquestWarrior.archetype = relationship(ConquestWarriorArchetype, - uselist=False, - backref=backref('warriors')) -ConquestWarrior.ranks = relationship(ConquestWarriorRank, - order_by=ConquestWarriorRank.rank, - innerjoin=True, - backref=backref('warrior', uselist=False)) -ConquestWarrior.types = relationship(Type, - secondary=ConquestWarriorSpecialty.__table__, - order_by=ConquestWarriorSpecialty.slot, - innerjoin=True, - backref='conquest_warriors') - -ConquestWarriorRank.skill = relationship(ConquestWarriorSkill, - uselist=False, - innerjoin=True, lazy='joined', - backref=backref('warrior_ranks', order_by=ConquestWarriorRank.id)) -ConquestWarriorRank.stats = relationship(ConquestWarriorRankStatMap, - innerjoin=True, - order_by=ConquestWarriorRankStatMap.warrior_stat_id, - backref=backref('warrior_rank', uselist=False, innerjoin=True, lazy='joined')) - -ConquestWarriorRankStatMap.stat = relationship(ConquestWarriorStat, - innerjoin=True, lazy='joined', - uselist=False, - backref='stat_map') - -ConquestWarriorTransformation.completed_episode = relationship(ConquestEpisode, - primaryjoin=ConquestWarriorTransformation.completed_episode_id==ConquestEpisode.id, - uselist=False) -ConquestWarriorTransformation.current_episode = relationship(ConquestEpisode, - primaryjoin=ConquestWarriorTransformation.current_episode_id==ConquestEpisode.id, - uselist=False) -ConquestWarriorTransformation.distant_warrior = relationship(ConquestWarrior, - uselist=False) -ConquestWarriorTransformation.pokemon = relationship(PokemonSpecies, - secondary=ConquestTransformationPokemon.__table__, - order_by=PokemonSpecies.conquest_order) -ConquestWarriorTransformation.present_warriors = relationship(ConquestWarrior, - secondary=ConquestTransformationWarrior.__table__, - order_by=ConquestWarrior.id) -ConquestWarriorTransformation.type = relationship(Type, - uselist=False) -ConquestWarriorTransformation.warrior_rank = relationship(ConquestWarriorRank, - uselist=False, - innerjoin=True, lazy='joined', - backref=backref('transformation', uselist=False, innerjoin=True)) - ContestCombo.first = relationship(Move, primaryjoin=ContestCombo.first_move_id==Move.id, @@ -2805,19 +2182,6 @@ class VersionGroupRegion(TableBase): uselist=False, backref='species') -PokemonSpecies.conquest_abilities = relationship(Ability, - secondary=ConquestPokemonAbility.__table__, - order_by=ConquestPokemonAbility.slot, - backref=backref('conquest_pokemon', order_by=PokemonSpecies.conquest_order, - innerjoin=True)) -PokemonSpecies.conquest_move = relationship(Move, - secondary=ConquestPokemonMove.__table__, - uselist=False, - backref=backref('conquest_pokemon', order_by=PokemonSpecies.conquest_order)) -PokemonSpecies.conquest_evolution = relationship(ConquestPokemonEvolution, - uselist=False, - backref=backref('evolved_species', innerjoin=True, lazy='joined', uselist=False)) - PokemonSpeciesFlavorText.version = relationship(Version, innerjoin=True, lazy='joined') PokemonSpeciesFlavorText.language = relationship(Language, innerjoin=True, lazy='joined') diff --git a/pokedex/tests/test_schema.py b/pokedex/tests/test_schema.py index ebfd3e059..0edbae48a 100644 --- a/pokedex/tests/test_schema.py +++ b/pokedex/tests/test_schema.py @@ -2,7 +2,7 @@ import pytest -from sqlalchemy import Column, Integer, String, create_engine +from sqlalchemy import Column, Integer, String, Unicode, create_engine from sqlalchemy.orm import class_mapper, joinedload, sessionmaker from sqlalchemy.orm.session import Session from sqlalchemy.ext.declarative import declarative_base @@ -31,16 +31,6 @@ def test_variable_names_2(table): """We also want all of the tables exported""" assert getattr(tables, table.__name__) is table -def test_class_order(): - """The declarative classes should be defined in alphabetical order. - Except for Language which should be first. - """ - class_names = [table.__name__ for table in tables.mapped_classes] - def key(name): - return name != 'Language', name - print [(a,b) for (a,b) in zip(class_names, sorted(class_names, key=key)) if a!=b] - assert class_names == sorted(class_names, key=key) - 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 @@ -190,7 +180,7 @@ def test_texts(cls): pytest.fail('%s: official text with bad format' % column) text_columns.append(column) else: - if isinstance(column.type, tables.Unicode): + if isinstance(column.type, Unicode): pytest.fail('%s: text column without format' % column) if column.name == 'name' and format != 'plaintext': pytest.fail('%s: non-plaintext name' % column)