Add pokedex.util.get: helpers to get stuff out of the DB easily

This commit is contained in:
Petr Viktorin 2011-04-14 17:42:35 +03:00 committed by Eevee
parent 1f2df80a00
commit 2f014411e1
2 changed files with 250 additions and 0 deletions

View file

@ -0,0 +1,75 @@
# encoding: utf8
from nose.tools import *
import unittest
from pokedex.db import connect, tables
from pokedex.util import get
session = connect()
def test_get_item_identifier():
item = get.get(session, tables.Item, identifier='master-ball')
assert item.name == 'Master Ball'
def test_get_item_name():
item = get.get(session, tables.Item, name='Awakening')
assert item.name == 'Awakening'
def test_get_english_by_identifier():
language = get.get(session, tables.Language, 'en')
assert language.name == 'English'
def test_get_pokemon_baseform_identifier():
for identifier in 'burmy shaymin unown cresselia'.split():
poke = get.get(session, tables.Pokemon, identifier=identifier)
assert poke.identifier == identifier
assert poke.is_base_form
def test_get_pokemon_baseform_name():
for name in 'Burmy Shaymin Unown Cresselia'.split():
poke = get.get(session, tables.Pokemon, name=name)
assert poke.name == name
assert poke.is_base_form
def test_get_pokemon_baseform_name_explicit_language():
french = get.get(session, tables.Language, 'fr')
for name in 'Cheniti Shaymin Zarbi Cresselia'.split():
poke = get.get(session, tables.Pokemon, name=name, language=french)
assert poke.name_map[french] == name, poke.name_map[french]
assert poke.is_base_form
def test_get_pokemon_other_form_identifier():
for ii in 'wormadam/trash shaymin/sky shaymin/land'.split():
pokemon_identifier, form_identifier = ii.split('/')
poke = get.get(session, tables.Pokemon, identifier=pokemon_identifier, form_identifier=form_identifier)
assert poke.identifier == pokemon_identifier
if poke.form.unique_pokemon_id:
assert poke.form.identifier == form_identifier
def test_pokemon():
pokemon = get.pokemon(session)
assert pokemon[0].identifier == 'bulbasaur'
assert pokemon[-1].identifier == 'genesect'
def test_pokemon_by_name():
pokemon = get.pokemon(session, order=tables.Pokemon.name)
assert pokemon[0].identifier == 'abomasnow'
assert pokemon[-1].identifier == 'zweilous'
def test_types_french_order():
french = get.get(session, tables.Language, 'fr')
types = get.types(session, order=None)
types = list(get.order_by_name(types, tables.Type, language=french))
assert types[0].name_map[french] == 'Acier', types[0].name_map[french]
assert types[-1].name_map[french] == 'Vol', types[-1].name_map[french]
def test_moves():
moves = get.moves(session)
assert moves[0].identifier == 'absorb'
assert moves[-1].identifier == 'zen-headbutt'
def test_items():
items = get.items(session)
assert items[0].identifier == 'ability-urge'
assert items[-1].identifier == 'zoom-lens'

175
pokedex/util/get.py Normal file
View file

@ -0,0 +1,175 @@
"""Provides simple functions for common queries
These include identifier- and name-based lookup, filtering out base forms
of pokemon, ordering by name, and getting canonical "pokedex" lists (i.e.
ordered and without cruft like alternate pokemon forms or Shadow moves)
"""
from sqlalchemy.orm import aliased
from pokedex.db import tables
### Getters
def get(session, table, identifier=None, name=None, id=None,
form_identifier=None, form_name=None, language=None, is_pokemon=None):
"""Get one object from the database.
session: The session to use (from pokedex.db.connect())
table: The table to select from (such as pokedex.db.tables.Move)
identifier: Identifier of the object
name: The name of the object
id: The ID number of the object
form_identifier: For pokemon, identifier of the form
form_name: For pokemon, name of the form
language: A Language to use for name and form_name
is_pokemon: If true, specifies that the table should be treated as a
pokemon table (handling forms specially). If None and table is the
(unaliased) Pokemon, it is set to True. Otherwise, the pokemon forms
aren't handled.
All conditions must match, so it's not a good idea to specify more than one
of identifier/name/id at once.
If zero or more than one objects matching the criteria are found, the
appropriate SQLAlchemy exception is raised.
Exception: for pokemon, selects the form base unless form_* is given.
"""
if is_pokemon is None:
is_pokemon = (table is tables.Pokemon)
query = session.query(table)
if identifier is not None:
query = query.filter_by(identifier=identifier)
if name is not None:
query = filter_name(query, table, name, language)
if id is not None:
query = query.filter_by(id=id)
if form_identifier is not None or form_name is not None:
if is_pokemon:
query = query.join(table.unique_form)
if form_identifier is not None:
query = query.filter(tables.PokemonForm.identifier ==
form_identifier)
if form_name is not None:
query = filter_name(query, table, form_name, language)
else:
raise ValueError(
"form_identifier and form_name only make sense for pokemon")
elif is_pokemon:
query = filter_base_forms(query)
return query.one()
### Helpers
def filter_name(query, table, name, language):
"""Filter a query by name, return the resulting query
query: The query to filter
table: The table of named objects
name: The name to look for. May be a tuple of alternatives.
language: The language for "name", or None for the session default
"""
if language is None:
query = query.filter(table.name == name)
else:
names_table = table.names_table
query = query.join(names_table)
query = query.filter(names_table.foreign_id == table.id)
query = query.filter(names_table.local_language_id == language.id)
if isinstance(name, tuple):
query = query.filter(names_table.name in name)
else:
query = query.filter(names_table.name == name)
return query
def filter_base_forms(query):
"""Filter only base forms of pokemon, and return the resulting query
"""
query = query.filter(tables.Pokemon.forms.any())
return query
def order_by_name(query, table, language=None, *extra_languages):
"""Order a query by name.
query: The query to order
table: Table of the named objects
language: The language to order names by. If None, use the
connection default.
extra_languages: Extra languages to order by, should the translations for
`language` be incomplete (or ambiguous).
Uses the identifier as a fallback ordering.
"""
if language is None:
query = query.outerjoin(table.names_local)
query = query.order_by(table.names_table.name)
else:
extra_languages = (language, ) + extra_languages
for language in extra_languages:
names_table = aliased(table.names_table)
query = query.outerjoin(names_table)
query = query.filter(names_table.foreign_id == table.id)
query = query.filter(names_table.local_language_id == language.id)
query = query.order_by(names_table.name)
query = query.order_by(table.identifier)
return query
_name = object()
def get_all(session, table, order=_name):
"""Shortcut to get an ordered query from table.
session: The session to use
table: The table to select from
order: A clause to order by, or None for no ordering.
The default is to order by name; this can also be specified explicitly
with the table's name property (e.g. tables.Pokemon.name). Be aware
that the query's order_by will not order by name this way.
"""
query = session.query(table)
if order is table.name or order is _name:
query = order_by_name(query, table)
elif order is not None:
query = query.order_by(order)
return query
### Shortcuts
def pokemon(session, order=tables.Pokemon.id):
"""Return a query for all base form pokemon, ordered by id by default
See get_all for the session and order arguments (but note the default for
pokemon is to order by id).
"""
query = get_all(session, tables.Pokemon, order=order)
query = query.filter(tables.Pokemon.forms.any())
return query
def moves(session, order=_name):
"""Return a query for moves in the mainline games (i.e. no Shadow moves)
See get_all for the session and order arguments.
"""
return get_all(session, tables.Move, order=order).filter(tables.Move.id < 10000)
def types(session, order=_name):
"""Return a query for sane types (i.e. not ???, Shadow)
See get_all for the session and order arguments.
"""
return get_all(session, tables.Type, order=order).filter(tables.Type.id < 10000)
def items(session, order=_name):
"""Return a query for items
See get_all for the session and order arguments.
"""
return get_all(session, tables.Item, order=order)