Move the rest of pokedex.util.get to pokedex.db.util
[zzz-pokedex.git] / pokedex / db / util.py
1 """Helpers for common ways to work with pokedex queries
2
3 These include identifier- and name-based lookup, filtering out base forms
4 of pokemon, and filtering/ordering by name.
5 """
6
7 from sqlalchemy.orm import aliased
8
9 from pokedex.db import tables
10
11 ### Getter
12
13 def get(session, table, identifier=None, name=None, id=None,
14 form_identifier=None, form_name=None, language=None, is_pokemon=None):
15 """Get one object from the database.
16
17 session: The session to use (from pokedex.db.connect())
18 table: The table to select from (such as pokedex.db.tables.Move)
19
20 identifier: Identifier of the object
21 name: The name of the object
22 id: The ID number of the object
23 form_identifier: For pokemon, identifier of the form
24 form_name: For pokemon, name of the form
25
26 language: A Language to use for name and form_name
27 is_pokemon: If true, specifies that the table should be treated as a
28 pokemon table (handling forms specially). If None and table is the
29 (unaliased) Pokemon, it is set to True. Otherwise, the pokemon forms
30 aren't handled.
31
32 All conditions must match, so it's not a good idea to specify more than one
33 of identifier/name/id at once.
34
35 If zero or more than one objects matching the criteria are found, the
36 appropriate SQLAlchemy exception is raised.
37 Exception: for pokemon, selects the form base unless form_* is given.
38 """
39
40 if is_pokemon is None:
41 is_pokemon = (table is tables.Pokemon)
42
43 query = session.query(table)
44
45 if identifier is not None:
46 query = query.filter_by(identifier=identifier)
47
48 if name is not None:
49 query = filter_name(query, table, name, language)
50
51 if id is not None:
52 query = query.filter_by(id=id)
53
54 if form_identifier is not None or form_name is not None:
55 if is_pokemon:
56 query = query.join(table.unique_form)
57 if form_identifier is not None:
58 query = query.filter(tables.PokemonForm.identifier ==
59 form_identifier)
60 if form_name is not None:
61 query = filter_name(query, table, form_name, language)
62 else:
63 raise ValueError(
64 "form_identifier and form_name only make sense for pokemon")
65 elif is_pokemon:
66 query = filter_base_forms(query)
67
68 return query.one()
69
70 ### Helpers
71
72 def filter_name(query, table, name, language):
73 """Filter a query by name, return the resulting query
74
75 query: The query to filter
76 table: The table of named objects
77 name: The name to look for. May be a tuple of alternatives.
78 language: The language for "name", or None for the session default
79 """
80 if language is None:
81 query = query.filter(table.name == name)
82 else:
83 names_table = table.names_table
84 query = query.join(names_table)
85 query = query.filter(names_table.foreign_id == table.id)
86 query = query.filter(names_table.local_language_id == language.id)
87 if isinstance(name, tuple):
88 query = query.filter(names_table.name in name)
89 else:
90 query = query.filter(names_table.name == name)
91 return query
92
93 def filter_base_forms(query):
94 """Filter only base forms of pokemon, and return the resulting query
95 """
96 query = query.filter(tables.Pokemon.forms.any())
97 return query
98
99 def order_by_name(query, table, language=None, *extra_languages):
100 """Order a query by name.
101
102 query: The query to order
103 table: Table of the named objects
104 language: The language to order names by. If None, use the
105 connection default.
106 extra_languages: Extra languages to order by, should the translations for
107 `language` be incomplete (or ambiguous).
108
109 Uses the identifier as a fallback ordering.
110 """
111 if language is None:
112 query = query.outerjoin(table.names_local)
113 query = query.order_by(table.names_table.name)
114 else:
115 extra_languages = (language, ) + extra_languages
116 for language in extra_languages:
117 names_table = aliased(table.names_table)
118 query = query.outerjoin(names_table)
119 query = query.filter(names_table.foreign_id == table.id)
120 query = query.filter(names_table.local_language_id == language.id)
121 query = query.order_by(names_table.name)
122 query = query.order_by(table.identifier)
123 return query