begin type-based formatting

This commit is contained in:
Adrian Sampson 2014-01-21 21:02:40 -08:00
parent 342d5e1f8b
commit 7aa4d1e5ba
3 changed files with 122 additions and 142 deletions

View file

@ -26,47 +26,11 @@ from beets.util.functemplate import Template
from .query import MatchQuery
# Path element formatting for templating.
# FIXME remove this once we have type-based formatting.
def format_for_path(value, key=None):
"""Sanitize the value for inclusion in a path: replace separators
with _, etc. Doesn't guarantee that the whole path will be valid;
you should still call `util.sanitize_path` on the complete path.
"""
if isinstance(value, basestring):
if isinstance(value, str):
value = value.decode('utf8', 'ignore')
elif key in ('track', 'tracktotal', 'disc', 'disctotal'):
# Pad indices with zeros.
value = u'%02i' % (value or 0)
elif key == 'year':
value = u'%04i' % (value or 0)
elif key in ('month', 'day'):
value = u'%02i' % (value or 0)
elif key == 'bitrate':
# Bitrate gets formatted as kbps.
value = u'%ikbps' % ((value or 0) // 1000)
elif key == 'samplerate':
# Sample rate formatted as kHz.
value = u'%ikHz' % ((value or 0) // 1000)
elif key in ('added', 'mtime'):
# Times are formatted to be human-readable.
value = time.strftime(beets.config['time_format'].get(unicode),
time.localtime(value))
value = unicode(value)
elif value is None:
value = u''
else:
value = unicode(value)
return value
# Abstract base for model classes and their field types.
Type = namedtuple('Type', 'sql query')
Type = namedtuple('Type', 'sql query format')
class Model(object):
@ -364,9 +328,11 @@ class Model(object):
"""
value = self.get(key)
# FIXME this will get replaced with more sophisticated
# (type-based) formatting logic.
value = format_for_path(value, key)
# Format the value as a string according to its type, if any.
if key in self._fields:
value = self._fields[key].format(value)
elif not isinstance(value, unicode):
value = unicode(value)
if for_path:
sep_repl = beets.config['path_sep_replace'].get(unicode)

View file

@ -78,14 +78,27 @@ class SingletonQuery(dbcore.Query):
# Model field lists.
# Common types used in field definitions.
TYPES = {
int: Type('INTEGER', dbcore.query.NumericQuery),
float: Type('REAL', dbcore.query.NumericQuery),
datetime: Type('REAL', dbcore.query.NumericQuery),
unicode: Type('TEXT', dbcore.query.SubstringQuery),
bool: Type('INTEGER', dbcore.query.BooleanQuery),
}
PATH_TYPE = Type('BLOB', PathQuery)
ID_TYPE = Type('INTEGER PRIMARY KEY', dbcore.query.NumericQuery, unicode)
INT_TYPE = Type('INTEGER', dbcore.query.NumericQuery, unicode)
FLOAT_TYPE = Type('REAL', dbcore.query.NumericQuery,
lambda n: u'{0:.1f}'.format(n))
DATE_TYPE = Type(
'REAL',
dbcore.query.NumericQuery,
lambda d: time.strftime(beets.config['time_format'].get(unicode),
time.localtime(d))
)
STRING_TYPE = Type('TEXT', dbcore.query.SubstringQuery, unicode)
BOOL_TYPE = Type('INTEGER', dbcore.query.BooleanQuery, unicode)
PATH_TYPE = Type('BLOB', PathQuery, util.displayable_path)
def _padded_int(digits):
return Type('INTEGER', dbcore.query.NumericQuery,
lambda n: u'{0:0{1}d}'.format(n, digits))
def _scaled_int(suffix=u'', unit=1000):
return Type('INTEGER', dbcore.query.NumericQuery,
lambda n: u'{0}{1}'.format(n // unit, suffix))
# Fields in the "items" database table; all the metadata available for
# items in the library. These are used directly in SQL; they are
@ -96,68 +109,67 @@ PATH_TYPE = Type('BLOB', PathQuery)
# - Is the field writable?
# - Does the field reflect an attribute of a MediaFile?
ITEM_FIELDS = [
('id', Type('INTEGER PRIMARY KEY', dbcore.query.NumericQuery),
False, False),
('path', PATH_TYPE, False, False),
('album_id', TYPES[int], False, False),
('id', ID_TYPE, False, False),
('path', PATH_TYPE, False, False),
('album_id', INT_TYPE, False, False),
('title', TYPES[unicode], True, True),
('artist', TYPES[unicode], True, True),
('artist_sort', TYPES[unicode], True, True),
('artist_credit', TYPES[unicode], True, True),
('album', TYPES[unicode], True, True),
('albumartist', TYPES[unicode], True, True),
('albumartist_sort', TYPES[unicode], True, True),
('albumartist_credit', TYPES[unicode], True, True),
('genre', TYPES[unicode], True, True),
('composer', TYPES[unicode], True, True),
('grouping', TYPES[unicode], True, True),
('year', TYPES[int], True, True),
('month', TYPES[int], True, True),
('day', TYPES[int], True, True),
('track', TYPES[int], True, True),
('tracktotal', TYPES[int], True, True),
('disc', TYPES[int], True, True),
('disctotal', TYPES[int], True, True),
('lyrics', TYPES[unicode], True, True),
('comments', TYPES[unicode], True, True),
('bpm', TYPES[int], True, True),
('comp', TYPES[bool], True, True),
('mb_trackid', TYPES[unicode], True, True),
('mb_albumid', TYPES[unicode], True, True),
('mb_artistid', TYPES[unicode], True, True),
('mb_albumartistid', TYPES[unicode], True, True),
('albumtype', TYPES[unicode], True, True),
('label', TYPES[unicode], True, True),
('acoustid_fingerprint', TYPES[unicode], True, True),
('acoustid_id', TYPES[unicode], True, True),
('mb_releasegroupid', TYPES[unicode], True, True),
('asin', TYPES[unicode], True, True),
('catalognum', TYPES[unicode], True, True),
('script', TYPES[unicode], True, True),
('language', TYPES[unicode], True, True),
('country', TYPES[unicode], True, True),
('albumstatus', TYPES[unicode], True, True),
('media', TYPES[unicode], True, True),
('albumdisambig', TYPES[unicode], True, True),
('disctitle', TYPES[unicode], True, True),
('encoder', TYPES[unicode], True, True),
('rg_track_gain', TYPES[float], True, True),
('rg_track_peak', TYPES[float], True, True),
('rg_album_gain', TYPES[float], True, True),
('rg_album_peak', TYPES[float], True, True),
('original_year', TYPES[int], True, True),
('original_month', TYPES[int], True, True),
('original_day', TYPES[int], True, True),
('title', STRING_TYPE, True, True),
('artist', STRING_TYPE, True, True),
('artist_sort', STRING_TYPE, True, True),
('artist_credit', STRING_TYPE, True, True),
('album', STRING_TYPE, True, True),
('albumartist', STRING_TYPE, True, True),
('albumartist_sort', STRING_TYPE, True, True),
('albumartist_credit', STRING_TYPE, True, True),
('genre', STRING_TYPE, True, True),
('composer', STRING_TYPE, True, True),
('grouping', STRING_TYPE, True, True),
('year', _padded_int(4), True, True),
('month', _padded_int(2), True, True),
('day', _padded_int(2), True, True),
('track', _padded_int(2), True, True),
('tracktotal', _padded_int(2), True, True),
('disc', _padded_int(2), True, True),
('disctotal', _padded_int(2), True, True),
('lyrics', STRING_TYPE, True, True),
('comments', STRING_TYPE, True, True),
('bpm', INT_TYPE, True, True),
('comp', BOOL_TYPE, True, True),
('mb_trackid', STRING_TYPE, True, True),
('mb_albumid', STRING_TYPE, True, True),
('mb_artistid', STRING_TYPE, True, True),
('mb_albumartistid', STRING_TYPE, True, True),
('albumtype', STRING_TYPE, True, True),
('label', STRING_TYPE, True, True),
('acoustid_fingerprint', STRING_TYPE, True, True),
('acoustid_id', STRING_TYPE, True, True),
('mb_releasegroupid', STRING_TYPE, True, True),
('asin', STRING_TYPE, True, True),
('catalognum', STRING_TYPE, True, True),
('script', STRING_TYPE, True, True),
('language', STRING_TYPE, True, True),
('country', STRING_TYPE, True, True),
('albumstatus', STRING_TYPE, True, True),
('media', STRING_TYPE, True, True),
('albumdisambig', STRING_TYPE, True, True),
('disctitle', STRING_TYPE, True, True),
('encoder', STRING_TYPE, True, True),
('rg_track_gain', FLOAT_TYPE, True, True),
('rg_track_peak', FLOAT_TYPE, True, True),
('rg_album_gain', FLOAT_TYPE, True, True),
('rg_album_peak', FLOAT_TYPE, True, True),
('original_year', _padded_int(4), True, True),
('original_month', _padded_int(2), True, True),
('original_day', _padded_int(2), True, True),
('length', TYPES[float], False, True),
('bitrate', TYPES[int], False, True),
('format', TYPES[unicode], False, True),
('samplerate', TYPES[int], False, True),
('bitdepth', TYPES[int], False, True),
('channels', TYPES[int], False, True),
('mtime', TYPES[int], False, False),
('added', TYPES[datetime], False, False),
('length', FLOAT_TYPE, False, True),
('bitrate', _scaled_int(u'kbps'), False, True),
('format', STRING_TYPE, False, True),
('samplerate', _scaled_int(u'kHz'), False, True),
('bitdepth', INT_TYPE, False, True),
('channels', INT_TYPE, False, True),
('mtime', DATE_TYPE, False, False),
('added', DATE_TYPE, False, False),
]
ITEM_KEYS_WRITABLE = [f[0] for f in ITEM_FIELDS if f[3] and f[2]]
ITEM_KEYS_META = [f[0] for f in ITEM_FIELDS if f[3]]
@ -167,39 +179,39 @@ ITEM_KEYS = [f[0] for f in ITEM_FIELDS]
# The third entry in each tuple indicates whether the field reflects an
# identically-named field in the items table.
ALBUM_FIELDS = [
('id', Type('INTEGER PRIMARY KEY', dbcore.query.NumericQuery), False),
('artpath', PATH_TYPE, False),
('added', TYPES[datetime], True),
('id', ID_TYPE, False),
('artpath', PATH_TYPE, False),
('added', DATE_TYPE, True),
('albumartist', TYPES[unicode], True),
('albumartist_sort', TYPES[unicode], True),
('albumartist_credit', TYPES[unicode], True),
('album', TYPES[unicode], True),
('genre', TYPES[unicode], True),
('year', TYPES[int], True),
('month', TYPES[int], True),
('day', TYPES[int], True),
('tracktotal', TYPES[int], True),
('disctotal', TYPES[int], True),
('comp', TYPES[bool], True),
('mb_albumid', TYPES[unicode], True),
('mb_albumartistid', TYPES[unicode], True),
('albumtype', TYPES[unicode], True),
('label', TYPES[unicode], True),
('mb_releasegroupid', TYPES[unicode], True),
('asin', TYPES[unicode], True),
('catalognum', TYPES[unicode], True),
('script', TYPES[unicode], True),
('language', TYPES[unicode], True),
('country', TYPES[unicode], True),
('albumstatus', TYPES[unicode], True),
('media', TYPES[unicode], True),
('albumdisambig', TYPES[unicode], True),
('rg_album_gain', TYPES[float], True),
('rg_album_peak', TYPES[float], True),
('original_year', TYPES[int], True),
('original_month', TYPES[int], True),
('original_day', TYPES[int], True),
('albumartist', STRING_TYPE, True),
('albumartist_sort', STRING_TYPE, True),
('albumartist_credit', STRING_TYPE, True),
('album', STRING_TYPE, True),
('genre', STRING_TYPE, True),
('year', _padded_int(4), True),
('month', _padded_int(2), True),
('day', _padded_int(2), True),
('tracktotal', _padded_int(2), True),
('disctotal', _padded_int(2), True),
('comp', BOOL_TYPE, True),
('mb_albumid', STRING_TYPE, True),
('mb_albumartistid', STRING_TYPE, True),
('albumtype', STRING_TYPE, True),
('label', STRING_TYPE, True),
('mb_releasegroupid', STRING_TYPE, True),
('asin', STRING_TYPE, True),
('catalognum', STRING_TYPE, True),
('script', STRING_TYPE, True),
('language', STRING_TYPE, True),
('country', STRING_TYPE, True),
('albumstatus', STRING_TYPE, True),
('media', STRING_TYPE, True),
('albumdisambig', STRING_TYPE, True),
('rg_album_gain', FLOAT_TYPE, True),
('rg_album_peak', FLOAT_TYPE, True),
('original_year', _padded_int(4), True),
('original_month', _padded_int(2), True),
('original_day', _padded_int(2), True),
]
ALBUM_KEYS = [f[0] for f in ALBUM_FIELDS]
ALBUM_KEYS_ITEM = [f[0] for f in ALBUM_FIELDS if f[2]]

View file

@ -25,8 +25,10 @@ from beets import dbcore
# Fixture: concrete database and model classes. For migration tests, we
# have multiple models with different numbers of fields.
ID_TYPE = dbcore.Type('INTEGER PRIMARY KEY', dbcore.query.NumericQuery)
INT_TYPE = dbcore.Type('INTEGER', dbcore.query.NumericQuery)
ID_TYPE = dbcore.Type('INTEGER PRIMARY KEY', dbcore.query.NumericQuery,
unicode)
INT_TYPE = dbcore.Type('INTEGER', dbcore.query.NumericQuery,
unicode)
class TestModel1(dbcore.Model):
_table = 'test'