c295ddd48333291273d689bb00f200925a3a3798
[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 MoveName(TableBase):
185 __tablename__ = 'move_names'
186 move_id = Column(Integer, ForeignKey('moves.id'), primary_key=True, nullable=False, autoincrement=False)
187 language_id = Column(Integer, ForeignKey('languages.id'), primary_key=True, nullable=False, autoincrement=False)
188 name = Column(Unicode(16), nullable=False)
189
190 class MoveTarget(TableBase):
191 __tablename__ = 'move_targets'
192 id = Column(Integer, primary_key=True, nullable=False)
193 name = Column(Unicode(32), nullable=False)
194 description = Column(Unicode(128), nullable=False)
195
196 class Move(TableBase):
197 __tablename__ = 'moves'
198 __singlename__ = 'move'
199 id = Column(Integer, primary_key=True, nullable=False)
200 name = Column(Unicode(12), nullable=False)
201 generation_id = Column(Integer, ForeignKey('generations.id'), nullable=False)
202 type_id = Column(Integer, ForeignKey('types.id'), nullable=False)
203 power = Column(SmallInteger)
204 pp = Column(SmallInteger, nullable=False)
205 accuracy = Column(SmallInteger)
206 target_id = Column(Integer, ForeignKey('move_targets.id'), nullable=False)
207 damage_class_id = Column(Integer, ForeignKey('move_damage_classes.id'), nullable=False)
208 effect_id = Column(Integer, ForeignKey('move_effects.id'), nullable=False)
209 effect_chance = Column(Integer)
210 contest_type = Column(Unicode(8), nullable=False)
211 contest_effect_id = Column(Integer, ForeignKey('contest_effects.id'), nullable=True)
212 super_contest_effect_id = Column(Integer, nullable=False)
213
214 class Pokemon(TableBase):
215 """The core to this whole mess.
216
217 Note that I use both 'forme' and 'form' in both code and the database. I
218 only use 'forme' when specifically referring to Pokémon that have multiple
219 distinct species as forms—i.e., different stats or movesets. 'Form' is a
220 more general term referring to any variation within a species, including
221 purely cosmetic forms like Unown.
222 """
223 __tablename__ = 'pokemon'
224 __singlename__ = 'pokemon'
225 id = Column(Integer, primary_key=True, nullable=False)
226 name = Column(Unicode(20), nullable=False)
227 forme_name = Column(Unicode(16))
228 forme_base_pokemon_id = Column(Integer, ForeignKey('pokemon.id'))
229 generation_id = Column(Integer, ForeignKey('generations.id'))
230 evolution_chain_id = Column(Integer, ForeignKey('evolution_chains.id'))
231 evolution_parent_pokemon_id = Column(Integer, ForeignKey('pokemon.id'))
232 evolution_method_id = Column(Integer, ForeignKey('evolution_methods.id'))
233 evolution_parameter = Column(Unicode(32))
234 height = Column(Integer, nullable=False)
235 weight = Column(Integer, nullable=False)
236 species = Column(Unicode(16), nullable=False)
237 color = Column(Unicode(6), nullable=False)
238 pokemon_shape_id = Column(Integer, ForeignKey('pokemon_shapes.id'), nullable=False)
239 habitat = Column(Unicode(16), nullable=False)
240 gender_rate = Column(Integer, nullable=False)
241 capture_rate = Column(Integer, nullable=False)
242 base_experience = Column(Integer, nullable=False)
243 base_happiness = Column(Integer, nullable=False)
244 gen1_internal_id = Column(Integer)
245 is_baby = Column(Boolean, nullable=False)
246 has_gen4_fem_sprite = Column(Boolean, nullable=False)
247 has_gen4_fem_back_sprite = Column(Boolean, nullable=False)
248
249 ### Stuff to handle alternate Pokémon forms
250
251 @property
252 def national_id(self):
253 """Returns the National Pokédex number for this Pokémon. Use this
254 instead of the id directly; alternate formes may make the id incorrect.
255 """
256
257 if self.forme_base_pokemon_id:
258 return self.forme_base_pokemon_id
259 return self.id
260
261 @property
262 def full_name(self):
263 """Returns the name of this Pokémon, including its Forme, if any."""
264
265 if self.forme_name:
266 return "%s %s" % (self.forme_name.capitalize(), self.name)
267 return self.name
268
269 @property
270 def normal_form(self):
271 """Returns the normal form for this Pokémon; i.e., this will return
272 regular Deoxys when called on any Deoxys form.
273 """
274
275 if self.forme_base_pokemon:
276 return self.forme_base_pokemon
277
278 return self
279
280 class PokemonAbility(TableBase):
281 __tablename__ = 'pokemon_abilities'
282 pokemon_id = Column(Integer, ForeignKey('pokemon.id'), primary_key=True, nullable=False, autoincrement=False)
283 ability_id = Column(Integer, ForeignKey('abilities.id'), nullable=False)
284 slot = Column(Integer, primary_key=True, nullable=False, autoincrement=False)
285
286 class PokemonDexNumber(TableBase):
287 __tablename__ = 'pokemon_dex_numbers'
288 pokemon_id = Column(Integer, ForeignKey('pokemon.id'), primary_key=True, nullable=False, autoincrement=False)
289 generation_id = Column(Integer, ForeignKey('generations.id'), primary_key=True, nullable=False, autoincrement=False)
290 pokedex_number = Column(Integer, nullable=False)
291
292 class PokemonEggGroup(TableBase):
293 __tablename__ = 'pokemon_egg_groups'
294 pokemon_id = Column(Integer, ForeignKey('pokemon.id'), primary_key=True, nullable=False, autoincrement=False)
295 egg_group_id = Column(Integer, ForeignKey('egg_groups.id'), primary_key=True, nullable=False, autoincrement=False)
296
297 class PokemonFlavorText(TableBase):
298 __tablename__ = 'pokemon_flavor_text'
299 pokemon_id = Column(Integer, ForeignKey('pokemon.id'), primary_key=True, nullable=False, autoincrement=False)
300 version_id = Column(Integer, ForeignKey('versions.id'), primary_key=True, nullable=False, autoincrement=False)
301 flavor_text = Column(Unicode(255), nullable=False)
302
303 class PokemonFormGroup(TableBase):
304 __tablename__ = 'pokemon_form_groups'
305 pokemon_id = Column(Integer, ForeignKey('pokemon.id'), primary_key=True, nullable=False, autoincrement=False)
306 description = Column(Unicode(512), nullable=False)
307
308 class PokemonFormSprite(TableBase):
309 __tablename__ = 'pokemon_form_sprites'
310 id = Column(Integer, primary_key=True, nullable=False)
311 pokemon_id = Column(Integer, ForeignKey('pokemon.id'), primary_key=True, nullable=False, autoincrement=False)
312 name = Column(Unicode(16), nullable=True)
313
314 class PokemonItem(TableBase):
315 __tablename__ = 'pokemon_items'
316 pokemon_id = Column(Integer, ForeignKey('pokemon.id'), primary_key=True, nullable=False, autoincrement=False)
317 version_id = Column(Integer, ForeignKey('versions.id'), primary_key=True, nullable=False, autoincrement=False)
318 item_id = Column(Integer, ForeignKey('items.id'), primary_key=True, nullable=False, autoincrement=False)
319 rarity = Column(Integer, nullable=False)
320
321 class PokemonMove(TableBase):
322 __tablename__ = 'pokemon_moves'
323 pokemon_id = Column(Integer, ForeignKey('pokemon.id'), primary_key=True, nullable=False, autoincrement=False)
324 version_group_id = Column(Integer, ForeignKey('version_groups.id'), primary_key=True, nullable=False, autoincrement=False)
325 move_id = Column(Integer, ForeignKey('moves.id'), primary_key=True, nullable=False, autoincrement=False, index=True)
326 pokemon_move_method_id = Column(Integer, ForeignKey('pokemon_move_methods.id'), primary_key=True, nullable=False, autoincrement=False)
327 level = Column(Integer, primary_key=True, nullable=True, autoincrement=False)
328 order = Column(Integer, nullable=True)
329
330 class PokemonMoveMethod(TableBase):
331 __tablename__ = 'pokemon_move_methods'
332 id = Column(Integer, primary_key=True, nullable=False, autoincrement=False)
333 name = Column(Unicode(64), nullable=False)
334 description = Column(Unicode(255), nullable=False)
335
336 class PokemonName(TableBase):
337 __tablename__ = 'pokemon_names'
338 pokemon_id = Column(Integer, ForeignKey('pokemon.id'), primary_key=True, nullable=False, autoincrement=False)
339 language_id = Column(Integer, ForeignKey('languages.id'), primary_key=True, nullable=False, autoincrement=False)
340 name = Column(Unicode(16), nullable=False)
341
342 class PokemonShape(TableBase):
343 __tablename__ = 'pokemon_shapes'
344 id = Column(Integer, primary_key=True, nullable=False)
345 name = Column(Unicode(24), nullable=False)
346 awesome_name = Column(Unicode(16), nullable=False)
347
348 class PokemonStat(TableBase):
349 __tablename__ = 'pokemon_stats'
350 pokemon_id = Column(Integer, ForeignKey('pokemon.id'), primary_key=True, nullable=False, autoincrement=False)
351 stat_id = Column(Integer, ForeignKey('stats.id'), primary_key=True, nullable=False, autoincrement=False)
352 base_stat = Column(Integer, nullable=False)
353 effort = Column(Integer, nullable=False)
354
355 class PokemonType(TableBase):
356 __tablename__ = 'pokemon_types'
357 pokemon_id = Column(Integer, ForeignKey('pokemon.id'), primary_key=True, nullable=False, autoincrement=False)
358 type_id = Column(Integer, ForeignKey('types.id'), nullable=False)
359 slot = Column(Integer, primary_key=True, nullable=False, autoincrement=False)
360
361 class Stat(TableBase):
362 __tablename__ = 'stats'
363 id = Column(Integer, primary_key=True, nullable=False)
364 name = Column(Unicode(16), nullable=False)
365
366 class TypeEfficacy(TableBase):
367 __tablename__ = 'type_efficacy'
368 damage_type_id = Column(Integer, ForeignKey('types.id'), primary_key=True, nullable=False, autoincrement=False)
369 target_type_id = Column(Integer, ForeignKey('types.id'), primary_key=True, nullable=False, autoincrement=False)
370 damage_factor = Column(Integer, nullable=False)
371
372 class Type(TableBase):
373 __tablename__ = 'types'
374 __singlename__ = 'type'
375 id = Column(Integer, primary_key=True, nullable=False)
376 name = Column(Unicode(8), nullable=False)
377 abbreviation = Column(Unicode(3), nullable=False)
378
379 class VersionGroup(TableBase):
380 __tablename__ = 'version_groups'
381 id = Column(Integer, primary_key=True, nullable=False)
382 generation_id = Column(Integer, ForeignKey('generations.id'), nullable=False)
383
384 class Version(TableBase):
385 __tablename__ = 'versions'
386 id = Column(Integer, primary_key=True, nullable=False)
387 version_group_id = Column(Integer, ForeignKey('version_groups.id'), nullable=False)
388 name = Column(Unicode(32), nullable=False)
389
390
391 ### Relations down here, to avoid ordering problems
392 Encounter.pokemon = relation(Pokemon, backref='encounters')
393 Encounter.version = relation(Version, backref='encounters')
394 Encounter.location_area = relation(LocationArea, backref='encounters')
395 Encounter.slot = relation(EncounterTypeSlot, backref='encounters')
396 Encounter.condition = relation(EncounterCondition, backref='encounters')
397
398 EncounterCondition.group = relation(EncounterConditionGroup,
399 backref='conditions')
400
401 EncounterTypeSlot.type = relation(EncounterType, backref='slots')
402
403 EvolutionChain.growth_rate = relation(GrowthRate, backref='evolution_chains')
404
405 Generation.versions = relation(Version, secondary=VersionGroup.__table__)
406
407 LocationArea.location = relation(Location, backref='areas')
408
409 Machine.generation = relation(Generation)
410
411 Move.damage_class = relation(MoveDamageClass, backref='moves')
412 Move.foreign_names = relation(MoveName, backref='pokemon')
413 Move.generation = relation(Generation, backref='moves')
414 Move.machines = relation(Machine, backref='move')
415 Move.move_effect = relation(MoveEffect, backref='moves')
416 Move.target = relation(MoveTarget, backref='moves')
417 Move.type = relation(Type, backref='moves')
418
419 Move.effect = rst.MoveEffectProperty('effect')
420 Move.priority = association_proxy('move_effect', 'priority')
421 Move.short_effect = rst.MoveEffectProperty('short_effect')
422
423 MoveName.language = relation(Language)
424
425 Pokemon.abilities = relation(Ability, secondary=PokemonAbility.__table__,
426 order_by=PokemonAbility.slot,
427 backref='pokemon')
428 Pokemon.formes = relation(Pokemon, primaryjoin=Pokemon.id==Pokemon.forme_base_pokemon_id,
429 backref=backref('forme_base_pokemon',
430 remote_side=[Pokemon.id]))
431 Pokemon.dex_numbers = relation(PokemonDexNumber, backref='pokemon')
432 Pokemon.egg_groups = relation(EggGroup, secondary=PokemonEggGroup.__table__,
433 order_by=PokemonEggGroup.egg_group_id,
434 backref='pokemon')
435 Pokemon.evolution_chain = relation(EvolutionChain, backref='pokemon')
436 Pokemon.evolution_method = relation(EvolutionMethod)
437 Pokemon.evolution_children = relation(Pokemon, primaryjoin=Pokemon.id==Pokemon.evolution_parent_pokemon_id,
438 backref=backref('evolution_parent',
439 remote_side=[Pokemon.id]))
440 Pokemon.flavor_text = relation(PokemonFlavorText, backref='pokemon')
441 Pokemon.foreign_names = relation(PokemonName, backref='pokemon')
442 Pokemon.items = relation(PokemonItem)
443 Pokemon.generation = relation(Generation, backref='pokemon')
444 Pokemon.shape = relation(PokemonShape, backref='pokemon')
445 Pokemon.stats = relation(PokemonStat, backref='pokemon')
446 Pokemon.types = relation(Type, secondary=PokemonType.__table__)
447
448 PokemonDexNumber.generation = relation(Generation)
449
450 PokemonFlavorText.version = relation(Version)
451
452 PokemonItem.item = relation(Item, backref='pokemon')
453 PokemonItem.version = relation(Version)
454
455 PokemonFormGroup.pokemon = relation(Pokemon, backref=backref('form_group',
456 uselist=False))
457 PokemonFormSprite.pokemon = relation(Pokemon, backref='form_sprites')
458
459 PokemonMove.pokemon = relation(Pokemon, backref='pokemon_moves')
460 PokemonMove.version_group = relation(VersionGroup)
461 PokemonMove.move = relation(Move, backref='pokemon_moves')
462 PokemonMove.method = relation(PokemonMoveMethod)
463
464 PokemonName.language = relation(Language)
465
466 PokemonStat.stat = relation(Stat)
467
468 Type.damage_efficacies = relation(TypeEfficacy,
469 primaryjoin=Type.id
470 ==TypeEfficacy.damage_type_id,
471 backref='damage_type')
472 Type.target_efficacies = relation(TypeEfficacy,
473 primaryjoin=Type.id
474 ==TypeEfficacy.target_type_id,
475 backref='target_type')
476
477 Version.version_group = relation(VersionGroup, backref='versions')
478 Version.generation = association_proxy('version_group', 'generation')
479
480 VersionGroup.generation = relation(Generation, backref='version_groups')