Add 'types' plugin for flexible field types

Conflicts:
	beets/library.py
This commit is contained in:
Thomas Scholtes 2014-09-11 23:48:17 +02:00
parent 475d4899ee
commit f112c9610c
3 changed files with 170 additions and 0 deletions

View file

@ -62,6 +62,8 @@ class PathQuery(dbcore.FieldQuery):
class DateType(types.Type):
# TODO representation should be `datetime` object
# TODO distinguish beetween date and time types
sql = u'REAL'
query = dbcore.query.DateQuery
null = 0.0

42
beetsplug/types.py Normal file
View file

@ -0,0 +1,42 @@
# This file is part of beets.
# Copyright 2014, Thomas Scholtes.
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
from beets.plugins import BeetsPlugin
from beets.dbcore import types
from beets.util.confit import ConfigValueError
from beets import library
class TypesPlugin(BeetsPlugin):
@property
def item_types(self):
if not self.config.exists():
return {}
mytypes = {}
for key, value in self.config.items():
if value.get() == 'int':
mytypes[key] = types.INTEGER
elif value.get() == 'float':
mytypes[key] = types.FLOAT
elif value.get() == 'bool':
mytypes[key] = types.BOOLEAN
elif value.get() == 'date':
mytypes[key] = library.DateType()
else:
raise ConfigValueError(
u"unknown type '{0}' for the '{1}' field"
.format(value, key))
return mytypes

126
test/test_types_plugin.py Normal file
View file

@ -0,0 +1,126 @@
# This file is part of beets.
# Copyright 2014, Thomas Scholtes.
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
import time
from datetime import datetime
from _common import unittest
from helper import TestHelper
from beets.util.confit import ConfigValueError
class TypesPluginTest(unittest.TestCase, TestHelper):
def setUp(self):
self.setup_beets()
self.load_plugins('types')
def tearDown(self):
self.unload_plugins()
self.teardown_beets()
def test_integer_modify_and_query(self):
self.config['types'] = {'myint': 'int'}
item = self.add_item(artist='aaa')
# Do not match unset values
out = self.list('myint:1..3')
self.assertEqual('', out)
self.modify('myint=2')
item.load()
self.assertEqual(item['myint'], 2)
# Match in range
out = self.list('myint:1..3')
self.assertIn('aaa', out)
def test_float_modify_and_query(self):
self.config['types'] = {'myfloat': 'float'}
item = self.add_item(artist='aaa')
self.modify('myfloat=-9.1')
item.load()
self.assertEqual(item['myfloat'], -9.1)
# Match in range
out = self.list('myfloat:-10..0')
self.assertIn('aaa', out)
def test_bool_modify_and_query(self):
self.config['types'] = {'mybool': 'bool'}
true = self.add_item(artist='true')
false = self.add_item(artist='false')
self.add_item(artist='unset')
# Set true
self.modify('mybool=1', 'artist:true')
true.load()
self.assertEqual(true['mybool'], True)
# Set false
self.modify('mybool=false', 'artist:false')
false.load()
self.assertEqual(false['mybool'], False)
# Query bools
out = self.list('mybool:true', '$artist $mybool')
self.assertEqual('true True', out)
out = self.list('mybool:false', '$artist $mybool')
# TODO this should not match the `unset` item
# self.assertEqual('false False', out)
out = self.list('mybool:', '$artist $mybool')
self.assertIn('unset $mybool', out)
def test_date_modify_and_query(self):
self.config['types'] = {'mydate': 'date'}
# FIXME parsing should also work with default time format
self.config['time_format'] = '%Y-%m-%d'
old = self.add_item(artist='prince')
new = self.add_item(artist='britney')
self.modify('mydate=1999-01-01', 'artist:prince')
old.load()
self.assertEqual(old['mydate'], mktime(1999, 01, 01))
self.modify('mydate=1999-12-30', 'artist:britney')
new.load()
self.assertEqual(new['mydate'], mktime(1999, 12, 30))
# Match in range
out = self.list('mydate:..1999-07', '$artist $mydate')
self.assertEqual('prince 1999-01-01', out)
# FIXME
self.skipTest('there is a timezone issue here')
out = self.list('mydate:1999-12-30', '$artist $mydate')
self.assertEqual('britney 1999-12-30', out)
def test_unknown_type_error(self):
self.config['types'] = {'flex': 'unkown type'}
with self.assertRaises(ConfigValueError):
self.run_command('ls')
def modify(self, *args):
return self.run_with_output('modify', '--yes', '--nowrite', *args)
def list(self, query, fmt='$artist - $album - $title'):
return self.run_with_output('ls', '-f', fmt, query).strip()
def mktime(*args):
return time.mktime(datetime(*args).timetuple())