mirror of
https://github.com/veekun/pokedex.git
synced 2024-08-20 18:16:34 +00:00
Support filtering by strings (Pokemon.name, Pokemon.names['fr'], etc.)
This commit is contained in:
parent
414a272c21
commit
bc2707f6c4
2 changed files with 111 additions and 18 deletions
|
@ -15,6 +15,14 @@ Columns have a info dictionary with these keys:
|
||||||
- identifier: A fan-made identifier in the [-_a-z0-9]* format. Not intended
|
- identifier: A fan-made identifier in the [-_a-z0-9]* format. Not intended
|
||||||
for translation.
|
for translation.
|
||||||
- latex: A formula in LaTeX syntax.
|
- latex: A formula in LaTeX syntax.
|
||||||
|
|
||||||
|
A localizable text column is visible as two properties:
|
||||||
|
The plural-name property (e.g. Pokemon.names) is a language-to-name dictionary:
|
||||||
|
bulbasaur.names['en'] == "Bulbasaur" and bulbasaur.names['de'] == "Bisasam".
|
||||||
|
You can use Pokemon.names['en'] to filter a query.
|
||||||
|
The singular-name property returns the name in the default language, English.
|
||||||
|
For example bulbasaur.name == "Bulbasaur"
|
||||||
|
Setting pokedex.db.tables.default_lang changes the default language.
|
||||||
"""
|
"""
|
||||||
# XXX: Check if "gametext" is set correctly everywhere
|
# XXX: Check if "gametext" is set correctly everywhere
|
||||||
|
|
||||||
|
@ -30,7 +38,9 @@ from sqlalchemy.orm import (
|
||||||
)
|
)
|
||||||
from sqlalchemy.orm.session import Session
|
from sqlalchemy.orm.session import Session
|
||||||
from sqlalchemy.orm.collections import attribute_mapped_collection
|
from sqlalchemy.orm.collections import attribute_mapped_collection
|
||||||
|
from sqlalchemy.ext.associationproxy import association_proxy
|
||||||
from sqlalchemy.sql import and_
|
from sqlalchemy.sql import and_
|
||||||
|
from sqlalchemy.sql.expression import ColumnOperators
|
||||||
from sqlalchemy.types import *
|
from sqlalchemy.types import *
|
||||||
from inspect import isclass
|
from inspect import isclass
|
||||||
|
|
||||||
|
@ -1789,6 +1799,8 @@ for table in list(table_classes):
|
||||||
|
|
||||||
### Add text/prose tables
|
### Add text/prose tables
|
||||||
|
|
||||||
|
default_lang = u'en'
|
||||||
|
|
||||||
def makeTextTable(object_table, name_plural, name_singular, columns, lazy):
|
def makeTextTable(object_table, name_plural, name_singular, columns, lazy):
|
||||||
# With "Language", we'd have two language_id. So, rename one to 'lang'
|
# With "Language", we'd have two language_id. So, rename one to 'lang'
|
||||||
safe_name = object_table.__singlename__
|
safe_name = object_table.__singlename__
|
||||||
|
@ -1801,6 +1813,8 @@ def makeTextTable(object_table, name_plural, name_singular, columns, lazy):
|
||||||
class Strings(object):
|
class Strings(object):
|
||||||
__tablename__ = tablename
|
__tablename__ = tablename
|
||||||
__singlename__ = singlename
|
__singlename__ = singlename
|
||||||
|
_attrname = name_plural
|
||||||
|
_language_identifier = association_proxy('language', 'identifier')
|
||||||
|
|
||||||
for name, plural, column in columns:
|
for name, plural, column in columns:
|
||||||
column.name = name
|
column.name = name
|
||||||
|
@ -1833,35 +1847,71 @@ def makeTextTable(object_table, name_plural, name_singular, columns, lazy):
|
||||||
))
|
))
|
||||||
Strings.object = getattr(Strings, safe_name)
|
Strings.object = getattr(Strings, safe_name)
|
||||||
|
|
||||||
# Link the tables themselves, so we can get to them
|
# Link the tables themselves, so we can get them if needed
|
||||||
Strings.object_table = object_table
|
Strings.object_table = object_table
|
||||||
setattr(object_table, name_singular + '_table', Strings)
|
setattr(object_table, name_singular + '_table', Strings)
|
||||||
|
|
||||||
for colname, pluralname, column in columns:
|
for colname, pluralname, column in columns:
|
||||||
# Provide a relation with all the names, and an English accessor
|
# Provide a property with all the names, and an English accessor
|
||||||
# for backwards compatibility
|
# for backwards compatibility
|
||||||
def scope(colname, pluralname, column):
|
setattr(object_table, pluralname, StringProperty(
|
||||||
def get_strings(self):
|
object_table, Strings, colname,
|
||||||
return dict(
|
))
|
||||||
(l, getattr(t, colname))
|
setattr(object_table, colname, DefaultLangProperty(pluralname))
|
||||||
for l, t in getattr(self, name_plural).items()
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_english_string(self):
|
|
||||||
try:
|
|
||||||
return get_strings(self)['en']
|
|
||||||
except KeyError:
|
|
||||||
raise AttributeError(colname)
|
|
||||||
|
|
||||||
setattr(object_table, pluralname, property(get_strings))
|
|
||||||
setattr(object_table, colname, property(get_english_string))
|
|
||||||
scope(colname, pluralname, column)
|
|
||||||
|
|
||||||
if colname == 'name':
|
if colname == 'name':
|
||||||
object_table.name_table = Strings
|
object_table.name_table = Strings
|
||||||
|
|
||||||
return Strings
|
return Strings
|
||||||
|
|
||||||
|
class StringProperty(object):
|
||||||
|
def __init__(self, cls, stringclass, colname):
|
||||||
|
self.cls = cls
|
||||||
|
self.colname = colname
|
||||||
|
self.stringclass = stringclass
|
||||||
|
|
||||||
|
def __get__(self, instance, cls):
|
||||||
|
if instance:
|
||||||
|
return dict(
|
||||||
|
(l, getattr(t, self.colname))
|
||||||
|
for l, t
|
||||||
|
in getattr(instance, self.stringclass._attrname).items()
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __getitem__(self, lang):
|
||||||
|
return StringExpression(self, lang)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return '<StringDict %s.%s>' % (self.cls, self.colname)
|
||||||
|
|
||||||
|
class StringExpression(ColumnOperators):
|
||||||
|
def __init__(self, prop, lang):
|
||||||
|
self.prop = prop
|
||||||
|
self.column = getattr(prop.stringclass, prop.colname)
|
||||||
|
self.lang_column = prop.stringclass._language_identifier
|
||||||
|
if isinstance(lang, basestring):
|
||||||
|
self.lang = lang
|
||||||
|
else:
|
||||||
|
self.lang = lang.identifier
|
||||||
|
|
||||||
|
def operate(self, op, *values, **kwargs):
|
||||||
|
return getattr(self.prop.cls, self.prop.stringclass._attrname).any(and_(
|
||||||
|
self.lang_column == self.lang,
|
||||||
|
op(self.column, *values, **kwargs),
|
||||||
|
))
|
||||||
|
|
||||||
|
class DefaultLangProperty(object):
|
||||||
|
def __init__(self, colname):
|
||||||
|
self.colname = colname
|
||||||
|
|
||||||
|
def __get__(self, instance, cls):
|
||||||
|
if instance:
|
||||||
|
return getattr(instance, self.colname)[default_lang]
|
||||||
|
else:
|
||||||
|
return getattr(cls, self.colname)[default_lang]
|
||||||
|
|
||||||
for table in list(table_classes):
|
for table in list(table_classes):
|
||||||
# Find all the language-specific columns, keeping them in the order they
|
# Find all the language-specific columns, keeping them in the order they
|
||||||
# were defined
|
# were defined
|
||||||
|
|
43
pokedex/tests/test_strings.py
Normal file
43
pokedex/tests/test_strings.py
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
# Encoding: UTF-8
|
||||||
|
|
||||||
|
from nose.tools import *
|
||||||
|
|
||||||
|
from pokedex.db import tables, connect
|
||||||
|
|
||||||
|
class TestStrings(object):
|
||||||
|
def setup(self):
|
||||||
|
self.connection = connect()
|
||||||
|
|
||||||
|
def test_filter(self):
|
||||||
|
q = self.connection.query(tables.Pokemon).filter(
|
||||||
|
tables.Pokemon.name == u"Marowak")
|
||||||
|
assert q.one().identifier == 'marowak'
|
||||||
|
|
||||||
|
def test_gt(self):
|
||||||
|
# Assuming that the identifiers are just lowercase names
|
||||||
|
q1 = self.connection.query(tables.Pokemon).filter(
|
||||||
|
tables.Pokemon.name > u"Xatu").order_by(
|
||||||
|
tables.Pokemon.id)
|
||||||
|
q2 = self.connection.query(tables.Pokemon).filter(
|
||||||
|
tables.Pokemon.identifier > u"xatu").order_by(
|
||||||
|
tables.Pokemon.id)
|
||||||
|
assert q1.all() == q2.all()
|
||||||
|
|
||||||
|
def test_languages(self):
|
||||||
|
q = self.connection.query(tables.Pokemon).filter(
|
||||||
|
tables.Pokemon.name == u"Mightyena")
|
||||||
|
pkmn = q.one()
|
||||||
|
for lang, name in (
|
||||||
|
('en', u'Mightyena'),
|
||||||
|
('ja', u'グラエナ'),
|
||||||
|
('roomaji', u'Guraena'),
|
||||||
|
('fr', u'Grahyèna'),
|
||||||
|
):
|
||||||
|
assert pkmn.names[lang] == name
|
||||||
|
|
||||||
|
@raises(KeyError)
|
||||||
|
def test_bad_lang(self):
|
||||||
|
q = self.connection.query(tables.Pokemon).filter(
|
||||||
|
tables.Pokemon.name == u"Mightyena")
|
||||||
|
pkmn = q.one()
|
||||||
|
pkmn.names["identifier of a language that doesn't exist"]
|
Loading…
Reference in a new issue