a0a4e9251908387ff46a45bbd774a6394477b25e
[zzz-pokedex.git] / pokedex / db / tables.py
1 # encoding: utf8
2
3 from sqlalchemy import Column, ForeignKey, MetaData, Table
4 from sqlalchemy.ext.declarative import declarative_base
5 from sqlalchemy.ext.associationproxy import association_proxy
6 from sqlalchemy.orm import backref, relation
7 from sqlalchemy.types import *
8 from sqlalchemy.databases.mysql import *
9
10 from pokedex.db import rst
11
12 metadata = MetaData()
13 TableBase = declarative_base(metadata=metadata)
14
15 class Ability(TableBase):
16 __tablename__ = 'abilities'
17 __singlename__ = 'ability'
18 id = Column(Integer, primary_key=True, nullable=False)
19 name = Column(Unicode(24), nullable=False)
20 flavor_text = Column(Unicode(64), nullable=False)
21 effect = Column(Unicode(255), nullable=False)
22
23 class ContestEffect(TableBase):
24 __tablename__ = 'contest_effects'
25 id = Column(Integer, primary_key=True, nullable=False)
26 appeal = Column(SmallInteger, nullable=False)
27 jam = Column(SmallInteger, nullable=False)
28 flavor = Column(Unicode(255), nullable=False)
29 effect = Column(Unicode(255), nullable=False)
30
31 class EggGroup(TableBase):
32 __tablename__ = 'egg_groups'
33 id = Column(Integer, primary_key=True, nullable=False)
34 name = Column(Unicode(16), nullable=False)
35
36 class Encounter(TableBase):
37 """Rows in this table represent encounters with wild Pokémon.
38
39 Within a given area in a given game, encounters are differentiated by the
40 slot they are in and a world condition.
41
42 Groups of slots belong to encounter types; these are what the player is
43 doing to get an encounter, such as surfing or walking through tall grass.
44
45 Within an encounter type, slots are defined primarily by rarity. Each slot
46 can also be affected by a world condition; for example, the 20% slot for
47 walking in tall grass is affected by whether a swarm is in effect in the
48 areas. "There is a swarm" and "there is not a swarm" are conditions, and
49 together they make a condition group. However, since "not a swarm" is a
50 base state rather than any sort of new state, it is omitted and instead
51 referred to by a NULL.
52
53 A slot (20% walking in grass) and single world condition (NULL, i.e. no
54 swarm) are thus enough to define a specific encounter.
55
56 Well, okay, almost: each slot actually appears twice.
57 """
58
59 __tablename__ = 'encounters'
60 id = Column(Integer, primary_key=True, nullable=False)
61 version_id = Column(Integer, ForeignKey('versions.id'), nullable=False, autoincrement=False)
62 location_area_id = Column(Integer, ForeignKey('location_areas.id'), nullable=False, autoincrement=False)
63 encounter_type_slot_id = Column(Integer, ForeignKey('encounter_type_slots.id'), nullable=False, autoincrement=False)
64 encounter_condition_id = Column(Integer, ForeignKey('encounter_conditions.id'), nullable=True, autoincrement=False)
65 pokemon_id = Column(Integer, ForeignKey('pokemon.id'), nullable=False, autoincrement=False)
66 min_level = Column(Integer, nullable=False, autoincrement=False)
67 max_level = Column(Integer, nullable=False, autoincrement=False)
68
69 class EncounterCondition(TableBase):
70 """Rows in this table represent something different about the world that
71 can affect what Pokémon are encountered.
72 """
73
74 __tablename__ = 'encounter_conditions'
75 id = Column(Integer, primary_key=True, nullable=False)
76 encounter_condition_group_id = Column(Integer, ForeignKey('encounter_condition_groups.id'), primary_key=False, nullable=False, autoincrement=False)
77 name = Column(Unicode(64), nullable=False)
78
79 class EncounterConditionGroup(TableBase):
80 """Rows in this table represent a group of mutually exclusive conditions,
81 such as morning/day/night. "Conditions" that are part of the default state
82 of the world, such as "not during a swarm" or "not using the PokéRadar",
83 are not included in this table and are referred to by NULLs in other
84 tables.
85 """
86
87 __tablename__ = 'encounter_condition_groups'
88 id = Column(Integer, primary_key=True, nullable=False)
89 name = Column(Unicode(64), nullable=False)
90
91 class EncounterType(TableBase):
92 """Rows in this table represent ways the player can enter a wild encounter;
93 i.e. surfing, fishing, or walking through tall grass.
94 """
95
96 __tablename__ = 'encounter_types'
97 id = Column(Integer, primary_key=True, nullable=False)
98 name = Column(Unicode(64), nullable=False)
99
100 class EncounterTypeSlot(TableBase):
101 """Rows in this table represent an abstract "slot" within an encounter
102 type, associated with both a condition group and a rarity.
103
104 Note that there are two encounters per slot, so the rarities will only add
105 up to 50.
106 """
107
108 __tablename__ = 'encounter_type_slots'
109 id = Column(Integer, primary_key=True, nullable=False)
110 encounter_type_id = Column(Integer, ForeignKey('encounter_types.id'), primary_key=False, nullable=False, autoincrement=False)
111 encounter_condition_group_id = Column(Integer, ForeignKey('encounter_condition_groups.id'), primary_key=False, nullable=True, autoincrement=False)
112 rarity = Column(Integer, nullable=False, autoincrement=False)
113
114 class EvolutionChain(TableBase):
115 __tablename__ = 'evolution_chains'
116 id = Column(Integer, primary_key=True, nullable=False)
117 growth_rate_id = Column(Integer, ForeignKey('growth_rates.id'), nullable=False)
118 steps_to_hatch = Column(Integer, nullable=False)
119 baby_trigger_item = Column(Unicode(12))
120
121 class EvolutionMethod(TableBase):
122 __tablename__ = 'evolution_methods'
123 id = Column(Integer, primary_key=True, nullable=False)
124 name = Column(Unicode(64), nullable=False)
125 description = Column(Unicode(255), nullable=False)
126
127 class Generation(TableBase):
128 __tablename__ = 'generations'
129 id = Column(Integer, primary_key=True, nullable=False)
130 name = Column(Unicode(16), nullable=False)
131 main_region = Column(Unicode(16), nullable=False)
132
133 class GrowthRate(TableBase):
134 """`formula` is written in LaTeX math notation."""
135 __tablename__ = 'growth_rates'
136 id = Column(Integer, primary_key=True, nullable=False)
137 name = Column(Unicode(20), nullable=False)
138 formula = Column(Unicode(500), nullable=False)
139
140 class Item(TableBase):
141 __tablename__ = 'items'
142 __singlename__ = 'item'
143 id = Column(Integer, primary_key=True, nullable=False)
144 name = Column(Unicode(16), nullable=False)
145
146 class Language(TableBase):
147 __tablename__ = 'languages'
148 id = Column(Integer, primary_key=True, nullable=False)
149 name = Column(Unicode(16), nullable=False)
150
151 class Location(TableBase):
152 __tablename__ = 'locations'
153 __singlename__ = 'location'
154 id = Column(Integer, primary_key=True, nullable=False)
155 generation_id = Column(Integer, ForeignKey('generations.id'), nullable=False)
156 name = Column(Unicode(64), nullable=False)
157
158 class LocationArea(TableBase):
159 __tablename__ = 'location_areas'
160 id = Column(Integer, primary_key=True, nullable=False)
161 location_id = Column(Integer, ForeignKey('locations.id'), nullable=False)
162 internal_id = Column(Integer, nullable=False)
163 name = Column(Unicode(64), nullable=True)
164
165 class Machine(TableBase):
166 __tablename__ = 'machines'
167 machine_number = Column(Integer, primary_key=True, nullable=False, autoincrement=False)
168 generation_id = Column(Integer, ForeignKey('generations.id'), primary_key=True, nullable=False, autoincrement=False)
169 move_id = Column(Integer, ForeignKey('moves.id'), nullable=False)
170
171 class MoveDamageClass(TableBase):
172 __tablename__ = 'move_damage_classes'
173 id = Column(Integer, primary_key=True, nullable=False)
174 name = Column(Unicode(8), nullable=False)
175 description = Column(Unicode(64), nullable=False)
176
177 class MoveEffect(TableBase):
178 __tablename__ = 'move_effects'
179 id = Column(Integer, primary_key=True, nullable=False)
180 priority = Column(SmallInteger, nullable=False)
181 short_effect = Column(Unicode(256), nullable=False)
182 effect = Column(Unicode(5120), nullable=False)
183
184 class MoveFlag(TableBase):
185 __tablename__ = 'move_flags'
186 move_id = Column(Integer, ForeignKey('moves.id'), primary_key=True, nullable=False, autoincrement=False)
187 move_flag_type_id = Column(Integer, ForeignKey('move_flag_types.id'), primary_key=True, nullable=False, autoincrement=False)
188
189 class MoveFlagType(TableBase):
190 __tablename__ = 'move_flag_types'
191 id = Column(Integer, primary_key=True, nullable=False)
192 name = Column(Unicode(32), nullable=False)
193 description = Column(rst.RstTextColumn(128), nullable=False)
194
195 class MoveName(TableBase):
196 __tablename__ = 'move_names'
197 move_id = Column(Integer, ForeignKey('moves.id'), primary_key=True, nullable=False, autoincrement=False)
198 language_id = Column(Integer, ForeignKey('languages.id'), primary_key=True, nullable=False, autoincrement=False)
199 name = Column(Unicode(16), nullable=False)
200
201 class MoveTarget(TableBase):
202 __tablename__ = 'move_targets'
203 id = Column(Integer, primary_key=True, nullable=False)
204 name = Column(Unicode(32), nullable=False)
205 description = Column(Unicode(128), nullable=False)
206
207 class Move(TableBase):
208 __tablename__ = 'moves'
209 __singlename__ = 'move'
210 id = Column(Integer, primary_key=True, nullable=False)
211 name = Column(Unicode(12), nullable=False)
212 generation_id = Column(Integer, ForeignKey('generations.id'), nullable=False)
213 type_id = Column(Integer, ForeignKey('types.id'), nullable=False)
214 power = Column(SmallInteger)
215 pp = Column(SmallInteger, nullable=False)
216 accuracy = Column(SmallInteger)
217 target_id = Column(Integer, ForeignKey('move_targets.id'), nullable=False)
218 damage_class_id = Column(Integer, ForeignKey('move_damage_classes.id'), nullable=False)
219 effect_id = Column(Integer, ForeignKey('move_effects.id'), nullable=False)
220 effect_chance = Column(Integer)
221 contest_type = Column(Unicode(8), nullable=False)
222 contest_effect_id = Column(Integer, ForeignKey('contest_effects.id'), nullable=True)
223 super_contest_effect_id = Column(Integer, nullable=False)
224
225 class Pokemon(TableBase):
226 """The core to this whole mess.
227
228 Note that I use both 'forme' and 'form' in both code and the database. I
229 only use 'forme' when specifically referring to Pokémon that have multiple
230 distinct species as forms—i.e., different stats or movesets. 'Form' is a
231 more general term referring to any variation within a species, including
232 purely cosmetic forms like Unown.
233 """
234 __tablename__ = 'pokemon'
235 __singlename__ = 'pokemon'
236 id = Column(Integer, primary_key=True, nullable=False)
237 name = Column(Unicode(20), nullable=False)
238 forme_name = Column(Unicode(16))
239 forme_base_pokemon_id = Column(Integer, ForeignKey('pokemon.id'))
240 generation_id = Column(Integer, ForeignKey('generations.id'))
241 evolution_chain_id = Column(Integer, ForeignKey('evolution_chains.id'))
242 evolution_parent_pokemon_id = Column(Integer, ForeignKey('pokemon.id'))
243 evolution_method_id = Column(Integer, ForeignKey('evolution_methods.id'))
244 evolution_parameter = Column(Unicode(32))
245 height = Column(Integer, nullable=False)
246 weight = Column(Integer, nullable=False)
247 species = Column(Unicode(16), nullable=False)
248 color = Column(Unicode(6), nullable=False)
249 pokemon_shape_id = Column(Integer, ForeignKey('pokemon_shapes.id'), nullable=False)
250 habitat = Column(Unicode(16), nullable=False)
251 gender_rate = Column(Integer, nullable=False)
252 capture_rate = Column(Integer, nullable=False)
253 base_experience = Column(Integer, nullable=False)
254 base_happiness = Column(Integer, nullable=False)
255 gen1_internal_id = Column(Integer)
256 is_baby = Column(Boolean, nullable=False)
257 has_gen4_fem_sprite = Column(Boolean, nullable=False)
258 has_gen4_fem_back_sprite = Column(Boolean, nullable=False)
259
260 ### Stuff to handle alternate Pokémon forms
261
262 @property
263 def national_id(self):
264 """Returns the National Pokédex number for this Pokémon. Use this
265 instead of the id directly; alternate formes may make the id incorrect.
266 """
267
268 if self.forme_base_pokemon_id:
269 return self.forme_base_pokemon_id
270 return self.id
271
272 @property
273 def full_name(self):
274 """Returns the name of this Pokémon, including its Forme, if any."""
275
276 if self.forme_name:
277 return "%s %s" % (self.forme_name.capitalize(), self.name)
278 return self.name
279
280 @property
281 def normal_form(self):
282 """Returns the normal form for this Pokémon; i.e., this will return
283 regular Deoxys when called on any Deoxys form.
284 """
285
286 if self.forme_base_pokemon:
287 return self.forme_base_pokemon
288
289 return self
290
291 class PokemonAbility(TableBase):
292 __tablename__ = 'pokemon_abilities'
293 pokemon_id = Column(Integer, ForeignKey('pokemon.id'), primary_key=True, nullable=False, autoincrement=False)
294 ability_id = Column(Integer, ForeignKey('abilities.id'), nullable=False)
295 slot = Column(Integer, primary_key=True, nullable=False, autoincrement=False)
296
297 class PokemonDexNumber(TableBase):
298 __tablename__ = 'pokemon_dex_numbers'
299 pokemon_id = Column(Integer, ForeignKey('pokemon.id'), primary_key=True, nullable=False, autoincrement=False)
300 generation_id = Column(Integer, ForeignKey('generations.id'), primary_key=True, nullable=False, autoincrement=False)
301 pokedex_number = Column(Integer, nullable=False)
302
303 class PokemonEggGroup(TableBase):
304 __tablename__ = 'pokemon_egg_groups'
305 pokemon_id = Column(Integer, ForeignKey('pokemon.id'), primary_key=True, nullable=False, autoincrement=False)
306 egg_group_id = Column(Integer, ForeignKey('egg_groups.id'), primary_key=True, nullable=False, autoincrement=False)
307
308 class PokemonFlavorText(TableBase):
309 __tablename__ = 'pokemon_flavor_text'
310 pokemon_id = Column(Integer, ForeignKey('pokemon.id'), primary_key=True, nullable=False, autoincrement=False)
311 version_id = Column(Integer, ForeignKey('versions.id'), primary_key=True, nullable=False, autoincrement=False)
312 flavor_text = Column(Unicode(255), nullable=False)
313
314 class PokemonFormGroup(TableBase):
315 __tablename__ = 'pokemon_form_groups'
316 pokemon_id = Column(Integer, ForeignKey('pokemon.id'), primary_key=True, nullable=False, autoincrement=False)
317 description = Column(Unicode(512), nullable=False)
318
319 class PokemonFormSprite(TableBase):
320 __tablename__ = 'pokemon_form_sprites'
321 id = Column(Integer, primary_key=True, nullable=False)
322 pokemon_id = Column(Integer, ForeignKey('pokemon.id'), primary_key=True, nullable=False, autoincrement=False)
323 name = Column(Unicode(16), nullable=True)
324
325 class PokemonItem(TableBase):
326 __tablename__ = 'pokemon_items'
327 pokemon_id = Column(Integer, ForeignKey('pokemon.id'), primary_key=True, nullable=False, autoincrement=False)
328 version_id = Column(Integer, ForeignKey('versions.id'), primary_key=True, nullable=False, autoincrement=False)
329 item_id = Column(Integer, ForeignKey('items.id'), primary_key=True, nullable=False, autoincrement=False)
330 rarity = Column(Integer, nullable=False)
331
332 class PokemonMove(TableBase):
333 __tablename__ = 'pokemon_moves'
334 pokemon_id = Column(Integer, ForeignKey('pokemon.id'), primary_key=True, nullable=False, autoincrement=False)
335 version_group_id = Column(Integer, ForeignKey('version_groups.id'), primary_key=True, nullable=False, autoincrement=False)
336 move_id = Column(Integer, ForeignKey('moves.id'), primary_key=True, nullable=False, autoincrement=False, index=True)
337 pokemon_move_method_id = Column(Integer, ForeignKey('pokemon_move_methods.id'), primary_key=True, nullable=False, autoincrement=False)
338 level = Column(Integer, primary_key=True, nullable=True, autoincrement=False)
339 order = Column(Integer, nullable=True)
340
341 class PokemonMoveMethod(TableBase):
342 __tablename__ = 'pokemon_move_methods'
343 id = Column(Integer, primary_key=True, nullable=False, autoincrement=False)
344 name = Column(Unicode(64), nullable=False)
345 description = Column(Unicode(255), nullable=False)
346
347 class PokemonName(TableBase):
348 __tablename__ = 'pokemon_names'
349 pokemon_id = Column(Integer, ForeignKey('pokemon.id'), primary_key=True, nullable=False, autoincrement=False)
350 language_id = Column(Integer, ForeignKey('languages.id'), primary_key=True, nullable=False, autoincrement=False)
351 name = Column(Unicode(16), nullable=False)
352
353 class PokemonShape(TableBase):
354 __tablename__ = 'pokemon_shapes'
355 id = Column(Integer, primary_key=True, nullable=False)
356 name = Column(Unicode(24), nullable=False)
357 awesome_name = Column(Unicode(16), nullable=False)
358
359 class PokemonStat(TableBase):
360 __tablename__ = 'pokemon_stats'
361 pokemon_id = Column(Integer, ForeignKey('pokemon.id'), primary_key=True, nullable=False, autoincrement=False)
362 stat_id = Column(Integer, ForeignKey('stats.id'), primary_key=True, nullable=False, autoincrement=False)
363 base_stat = Column(Integer, nullable=False)
364 effort = Column(Integer, nullable=False)
365
366 class PokemonType(TableBase):
367 __tablename__ = 'pokemon_types'
368 pokemon_id = Column(Integer, ForeignKey('pokemon.id'), primary_key=True, nullable=False, autoincrement=False)
369 type_id = Column(Integer, ForeignKey('types.id'), nullable=False)
370 slot = Column(Integer, primary_key=True, nullable=False, autoincrement=False)
371
372 class Stat(TableBase):
373 __tablename__ = 'stats'
374 id = Column(Integer, primary_key=True, nullable=False)
375 name = Column(Unicode(16), nullable=False)
376
377 class TypeEfficacy(TableBase):
378 __tablename__ = 'type_efficacy'
379 damage_type_id = Column(Integer, ForeignKey('types.id'), primary_key=True, nullable=False, autoincrement=False)
380 target_type_id = Column(Integer, ForeignKey('types.id'), primary_key=True, nullable=False, autoincrement=False)
381 damage_factor = Column(Integer, nullable=False)
382
383 class Type(TableBase):
384 __tablename__ = 'types'
385 __singlename__ = 'type'
386 id = Column(Integer, primary_key=True, nullable=False)
387 name = Column(Unicode(8), nullable=False)
388 abbreviation = Column(Unicode(3), nullable=False)
389
390 class VersionGroup(TableBase):
391 __tablename__ = 'version_groups'
392 id = Column(Integer, primary_key=True, nullable=False)
393 generation_id = Column(Integer, ForeignKey('generations.id'), nullable=False)
394
395 class Version(TableBase):
396 __tablename__ = 'versions'
397 id = Column(Integer, primary_key=True, nullable=False)
398 version_group_id = Column(Integer, ForeignKey('version_groups.id'), nullable=False)
399 name = Column(Unicode(32), nullable=False)
400
401
402 ### Relations down here, to avoid ordering problems
403 Encounter.pokemon = relation(Pokemon, backref='encounters')
404 Encounter.version = relation(Version, backref='encounters')
405 Encounter.location_area = relation(LocationArea, backref='encounters')
406 Encounter.slot = relation(EncounterTypeSlot, backref='encounters')
407 Encounter.condition = relation(EncounterCondition, backref='encounters')
408
409 EncounterCondition.group = relation(EncounterConditionGroup,
410 backref='conditions')
411
412 EncounterTypeSlot.type = relation(EncounterType, backref='slots')
413
414 EvolutionChain.growth_rate = relation(GrowthRate, backref='evolution_chains')
415
416 Generation.versions = relation(Version, secondary=VersionGroup.__table__)
417
418 LocationArea.location = relation(Location, backref='areas')
419
420 Machine.generation = relation(Generation)
421
422 Move.damage_class = relation(MoveDamageClass, backref='moves')
423 Move.flags = association_proxy('move_flags', 'flag')
424 Move.foreign_names = relation(MoveName, backref='pokemon')
425 Move.generation = relation(Generation, backref='moves')
426 Move.machines = relation(Machine, backref='move')
427 Move.move_effect = relation(MoveEffect, backref='moves')
428 Move.move_flags = relation(MoveFlag, backref='move')
429 Move.target = relation(MoveTarget, backref='moves')
430 Move.type = relation(Type, backref='moves')
431
432 Move.effect = rst.MoveEffectProperty('effect')
433 Move.priority = association_proxy('move_effect', 'priority')
434 Move.short_effect = rst.MoveEffectProperty('short_effect')
435
436 MoveFlag.flag = relation(MoveFlagType)
437
438 MoveName.language = relation(Language)
439
440 Pokemon.abilities = relation(Ability, secondary=PokemonAbility.__table__,
441 order_by=PokemonAbility.slot,
442 backref='pokemon')
443 Pokemon.formes = relation(Pokemon, primaryjoin=Pokemon.id==Pokemon.forme_base_pokemon_id,
444 backref=backref('forme_base_pokemon',
445 remote_side=[Pokemon.id]))
446 Pokemon.dex_numbers = relation(PokemonDexNumber, backref='pokemon')
447 Pokemon.egg_groups = relation(EggGroup, secondary=PokemonEggGroup.__table__,
448 order_by=PokemonEggGroup.egg_group_id,
449 backref='pokemon')
450 Pokemon.evolution_chain = relation(EvolutionChain, backref='pokemon')
451 Pokemon.evolution_method = relation(EvolutionMethod)
452 Pokemon.evolution_children = relation(Pokemon, primaryjoin=Pokemon.id==Pokemon.evolution_parent_pokemon_id,
453 backref=backref('evolution_parent',
454 remote_side=[Pokemon.id]))
455 Pokemon.flavor_text = relation(PokemonFlavorText, backref='pokemon')
456 Pokemon.foreign_names = relation(PokemonName, backref='pokemon')
457 Pokemon.items = relation(PokemonItem)
458 Pokemon.generation = relation(Generation, backref='pokemon')
459 Pokemon.shape = relation(PokemonShape, backref='pokemon')
460 Pokemon.stats = relation(PokemonStat, backref='pokemon')
461 Pokemon.types = relation(Type, secondary=PokemonType.__table__)
462
463 PokemonDexNumber.generation = relation(Generation)
464
465 PokemonFlavorText.version = relation(Version)
466
467 PokemonItem.item = relation(Item, backref='pokemon')
468 PokemonItem.version = relation(Version)
469
470 PokemonFormGroup.pokemon = relation(Pokemon, backref=backref('form_group',
471 uselist=False))
472 PokemonFormSprite.pokemon = relation(Pokemon, backref='form_sprites')
473
474 PokemonMove.pokemon = relation(Pokemon, backref='pokemon_moves')
475 PokemonMove.version_group = relation(VersionGroup)
476 PokemonMove.move = relation(Move, backref='pokemon_moves')
477 PokemonMove.method = relation(PokemonMoveMethod)
478
479 PokemonName.language = relation(Language)
480
481 PokemonStat.stat = relation(Stat)
482
483 Type.damage_efficacies = relation(TypeEfficacy,
484 primaryjoin=Type.id
485 ==TypeEfficacy.damage_type_id,
486 backref='damage_type')
487 Type.target_efficacies = relation(TypeEfficacy,
488 primaryjoin=Type.id
489 ==TypeEfficacy.target_type_id,
490 backref='target_type')
491
492 Version.version_group = relation(VersionGroup, backref='versions')
493 Version.generation = association_proxy('version_group', 'generation')
494
495 VersionGroup.generation = relation(Generation, backref='version_groups')