From a5b37d01c94eb53bffaf14997205840f60692b71 Mon Sep 17 00:00:00 2001 From: Josh Brown Date: Sat, 17 Dec 2016 16:46:00 -0600 Subject: [PATCH 01/13] added zero subcommand --- beetsplug/zero.py | 54 ++++++++++++++++++++++++++++++++++- test/test_zero.py | 72 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 125 insertions(+), 1 deletion(-) diff --git a/beetsplug/zero.py b/beetsplug/zero.py index 1401b11dea..403def8079 100644 --- a/beetsplug/zero.py +++ b/beetsplug/zero.py @@ -21,7 +21,8 @@ from beets.plugins import BeetsPlugin from beets.mediafile import MediaFile from beets.importer import action -from beets.util import confit +from beets.util import confit, syspath +from beets.ui import Subcommand, decargs import six __author__ = 'baobab@heresiarch.info' @@ -41,6 +42,7 @@ def __init__(self): self.import_task_choice_event) self.config.add({ + 'auto': True, 'fields': [], 'keep_fields': [], 'update_database': False, @@ -75,6 +77,16 @@ def __init__(self): if key in self.patterns: del self.patterns[key] + def commands(self): + zero_command = Subcommand('zero', help='set fields to null') + + def zero_fields(lib, opts, args): + for item in lib.items(decargs(args)): + self.process_item(item) + + zero_command.func = zero_fields + return [zero_command] + def validate_config(self, mode): """Check whether fields in the configuration are valid. @@ -120,10 +132,39 @@ def match_patterns(cls, field, patterns): return True return False + def process_item(self, item): + if not self.patterns: + self._log.warning(u'no fields, nothing to do') + return + + for field, patterns in self.patterns.items(): + if field in item.keys(): + if not self.should_update(item, field): + continue + + value = item[field] + match = self.match_patterns(item[field], patterns) + + else: + value = '' + match = patterns is True + + if match: + self._log.warning(u'{0}: {1} -> None', field, value) + item[field] = None + if self.config['update_database']: + item.update({field: None}) + item.store() + + item.write() + def write_event(self, item, path, tags): """Set values in tags to `None` if the key and value are matched by `self.patterns`. """ + if not self.config['auto']: + return + if not self.patterns: self._log.warning(u'no fields, nothing to do') return @@ -141,3 +182,14 @@ def write_event(self, item, path, tags): tags[field] = None if self.config['update_database']: item[field] = None + + def should_update(self, item, field): + media = MediaFile(syspath(item.path)) + attributes = media.fields() + if field in attributes: + return True + + if self.config['update_database'] and item[field]: + return True + + return False diff --git a/test/test_zero.py b/test/test_zero.py index fb28330ee8..ca4a2dd674 100644 --- a/test/test_zero.py +++ b/test/test_zero.py @@ -121,6 +121,78 @@ def test_album_art(self): mediafile = MediaFile(syspath(item.path)) self.assertEqual(0, len(mediafile.images)) + def test_auto_false(self): + item = self.add_item_fixture(year=2000) + item.write() + mediafile = MediaFile(syspath(item.path)) + self.assertEqual(2000, mediafile.year) + + config['zero'] = { + 'fields': [u'year'], + 'update_database': True, + 'auto': False + } + self.load_plugins('zero') + + item.write() + mediafile = MediaFile(syspath(item.path)) + self.assertEqual(item['year'], 2000) + self.assertEqual(mediafile.year, 2000) + + def test_subcommand(self): + item = self.add_item_fixture( + year=2016, + day=13, + month=3, + comments=u'test comment' + ) + item.write() + item_id = item.id + config['zero'] = { + 'fields': [u'comments'], + 'update_database': True, + 'auto': False + } + self.load_plugins('zero') + self.run_command('zero') + + mediafile = MediaFile(syspath(item.path)) + item = self.lib.get_item(item_id) + + self.assertEqual(item['year'], 2016) + self.assertEqual(mediafile.year, 2016) + self.assertEqual(mediafile.comments, None) + self.assertEqual(item['comments'], u'') + + def test_subcommand_update_database_False(self): + item = self.add_item_fixture( + year=2016, + day=13, + month=3, + comments=u'test comment' + ) + item.write() + item_id = item.id + config['zero'] = { + 'fields': [u'comments'], + 'update_database': False, + 'auto': False + } + self.load_plugins('zero') + + z = ZeroPlugin() + z.debug = False + self.run_command('zero') + + mediafile = MediaFile(syspath(item.path)) + item = self.lib.get_item(item_id) + + self.assertEqual(item['year'], 2016) + self.assertEqual(mediafile.year, 2016) + self.assertEqual(item['comments'], u'test comment') + self.assertEqual(mediafile.comments, None) + + def suite(): return unittest.TestLoader().loadTestsFromName(__name__) From fec7e30fe18a441c4d5b18b640e122f92790f908 Mon Sep 17 00:00:00 2001 From: Josh Brown Date: Sat, 17 Dec 2016 17:17:55 -0600 Subject: [PATCH 02/13] added documentation for zero subcommand --- docs/plugins/zero.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/plugins/zero.rst b/docs/plugins/zero.rst index ecaa60a3d1..282bb4fc8f 100644 --- a/docs/plugins/zero.rst +++ b/docs/plugins/zero.rst @@ -18,6 +18,7 @@ Configuration Make a ``zero:`` section in your configuration file. You can specify the fields to nullify and the conditions for nullifying them: +* Set ``auto`` to ``yes`` to null fields automatically on import. Default ``yes`` * Set ``fields`` to a whitespace-separated list of fields to change. You can get the list of all available fields by running ``beet fields``. In addition, the ``images`` field allows you to remove any images @@ -42,3 +43,9 @@ If a custom pattern is not defined for a given field, the field will be nulled unconditionally. Note that the plugin currently does not zero fields when importing "as-is". + +Manually Triggering Zero +------------------------ + +The ``zero`` command will invoke the zero plugin on items matching a query. Use the +command ``beet zero [QUERY]`` From 148354c5c499bed665813a4631c5fd6f3e4e788a Mon Sep 17 00:00:00 2001 From: Josh Brown Date: Sat, 17 Dec 2016 17:30:07 -0600 Subject: [PATCH 03/13] additional zero tests --- test/test_zero.py | 54 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 49 insertions(+), 5 deletions(-) diff --git a/test/test_zero.py b/test/test_zero.py index ca4a2dd674..38fdb3726a 100644 --- a/test/test_zero.py +++ b/test/test_zero.py @@ -139,7 +139,7 @@ def test_auto_false(self): self.assertEqual(item['year'], 2000) self.assertEqual(mediafile.year, 2000) - def test_subcommand(self): + def test_subcommand_update_database_true(self): item = self.add_item_fixture( year=2016, day=13, @@ -164,7 +164,7 @@ def test_subcommand(self): self.assertEqual(mediafile.comments, None) self.assertEqual(item['comments'], u'') - def test_subcommand_update_database_False(self): + def test_subcommand_update_database_false(self): item = self.add_item_fixture( year=2016, day=13, @@ -179,9 +179,6 @@ def test_subcommand_update_database_False(self): 'auto': False } self.load_plugins('zero') - - z = ZeroPlugin() - z.debug = False self.run_command('zero') mediafile = MediaFile(syspath(item.path)) @@ -192,6 +189,53 @@ def test_subcommand_update_database_False(self): self.assertEqual(item['comments'], u'test comment') self.assertEqual(mediafile.comments, None) + def test_subcommand_query_include(self): + item = self.add_item_fixture( + year=2016, + day=13, + month=3, + comments=u'test comment' + ) + + item.write() + + config['zero'] = { + 'fields': [u'comments'], + 'update_database': False, + 'auto': False + } + + self.load_plugins('zero') + self.run_command('zero', 'year: 2016') + + mediafile = MediaFile(syspath(item.path)) + + self.assertEqual(mediafile.year, 2016) + self.assertEqual(mediafile.comments, None) + + def test_subcommand_query_exclude(self): + item = self.add_item_fixture( + year=2016, + day=13, + month=3, + comments=u'test comment' + ) + + item.write() + + config['zero'] = { + 'fields': [u'comments'], + 'update_database': False, + 'auto': False + } + + self.load_plugins('zero') + self.run_command('zero', 'year: 0000') + + mediafile = MediaFile(syspath(item.path)) + + self.assertEqual(mediafile.year, 2016) + self.assertEqual(mediafile.comments, u'test comment') def suite(): From c40a587e68d6dd6cb579183c4728d06a95090945 Mon Sep 17 00:00:00 2001 From: Josh Brown Date: Sat, 17 Dec 2016 17:49:45 -0600 Subject: [PATCH 04/13] update changelog --- docs/changelog.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/changelog.rst b/docs/changelog.rst index 40b044941b..b5ea8a62b6 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -6,6 +6,10 @@ Changelog Changelog goes here! +Features: + +* :doc:`/plugins/zero`: Added ``zero`` command to manually trigger the zero + plugin. 1.4.2 (December 16, 2016) ------------------------- From 510fe6f146f1be757d321fae639fadb769f41986 Mon Sep 17 00:00:00 2001 From: Josh Brown Date: Sun, 18 Dec 2016 14:27:42 -0600 Subject: [PATCH 05/13] refactor changes to zero plugin --- beetsplug/zero.py | 55 +++++++++++++++-------------------------------- 1 file changed, 17 insertions(+), 38 deletions(-) diff --git a/beetsplug/zero.py b/beetsplug/zero.py index 403def8079..73961134a3 100644 --- a/beetsplug/zero.py +++ b/beetsplug/zero.py @@ -21,7 +21,7 @@ from beets.plugins import BeetsPlugin from beets.mediafile import MediaFile from beets.importer import action -from beets.util import confit, syspath +from beets.util import confit from beets.ui import Subcommand, decargs import six @@ -94,13 +94,13 @@ def validate_config(self, mode): the section of the configuration to validate. """ for field in self.config[mode].as_str_seq(): - if field not in MediaFile.fields(): - self._log.error(u'invalid field: {0}', field) - continue if mode == 'fields' and field in ('id', 'path', 'album_id'): self._log.warning(u'field \'{0}\' ignored, zeroing ' u'it would be dangerous', field) continue + if field not in MediaFile.fields(): + self._log.error(u'invalid field: {0}', field) + continue def set_pattern(self, field): """Set a field in `self.patterns` to a string list corresponding to @@ -133,30 +133,12 @@ def match_patterns(cls, field, patterns): return False def process_item(self, item): - if not self.patterns: - self._log.warning(u'no fields, nothing to do') - return - - for field, patterns in self.patterns.items(): - if field in item.keys(): - if not self.should_update(item, field): - continue - - value = item[field] - match = self.match_patterns(item[field], patterns) - - else: - value = '' - match = patterns is True + tags = dict(item) - if match: - self._log.warning(u'{0}: {1} -> None', field, value) - item[field] = None - if self.config['update_database']: - item.update({field: None}) - item.store() - - item.write() + if self.set_fields(item, tags): + item.write(tags=tags) + if self.config['update_database']: + item.store(fields=tags) def write_event(self, item, path, tags): """Set values in tags to `None` if the key and value are matched @@ -165,9 +147,14 @@ def write_event(self, item, path, tags): if not self.config['auto']: return + self.set_fields(item, tags) + + def set_fields(self, item, tags): + fields_set = False + if not self.patterns: self._log.warning(u'no fields, nothing to do') - return + return False for field, patterns in self.patterns.items(): if field in tags: @@ -178,18 +165,10 @@ def write_event(self, item, path, tags): match = patterns is True if match: + fields_set = True self._log.debug(u'{0}: {1} -> None', field, value) tags[field] = None if self.config['update_database']: item[field] = None - def should_update(self, item, field): - media = MediaFile(syspath(item.path)) - attributes = media.fields() - if field in attributes: - return True - - if self.config['update_database'] and item[field]: - return True - - return False + return fields_set From 69a58ca842e9741a1125e5622e3d1c1c5b613ef9 Mon Sep 17 00:00:00 2001 From: Josh Brown Date: Sun, 18 Dec 2016 14:28:55 -0600 Subject: [PATCH 06/13] additional test coverage for zero plugin --- test/test_zero.py | 71 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/test/test_zero.py b/test/test_zero.py index 38fdb3726a..e7af5fee86 100644 --- a/test/test_zero.py +++ b/test/test_zero.py @@ -19,6 +19,7 @@ def setUp(self): self.setup_beets() def tearDown(self): + self.remove_mediafile_fixtures() self.teardown_beets() self.unload_plugins() @@ -237,6 +238,76 @@ def test_subcommand_query_exclude(self): self.assertEqual(mediafile.year, 2016) self.assertEqual(mediafile.comments, u'test comment') + def test_no_fields(self): + item = self.add_item_fixture(year=2016) + item.write() + mediafile = MediaFile(syspath(item.path)) + self.assertEqual(mediafile.year, 2016) + + item_id = item.id + config['zero'] = {} + self.load_plugins('zero') + self.run_command('zero') + + item = self.lib.get_item(item_id) + + self.assertEqual(item['year'], 2016) + self.assertEqual(mediafile.year, 2016) + + def test_whitelist_and_blacklist(self): + item = self.add_item_fixture(year=2016) + item.write() + mediafile = MediaFile(syspath(item.path)) + self.assertEqual(mediafile.year, 2016) + + item_id = item.id + config['zero'] = {'fields': [u'year'], + 'keep_fields': [u'comments']} + + self.load_plugins('zero') + self.run_command('zero') + + item = self.lib.get_item(item_id) + + self.assertEqual(item['year'], 2016) + self.assertEqual(mediafile.year, 2016) + + def test_keep_fields(self): + item = self.add_item_fixture(year=2016, comments=u'test comment') + config['zero'] = {'keep_fields': [u'year'], + 'fields': None, + 'update_database': True, + 'auto': True} + + tags = { + 'comments': u'test comment', + 'year': 2016, + } + self.load_plugins('zero') + z = ZeroPlugin() + z.write_event(item, item.path, tags) + self.assertEqual(tags['comments'], None) + self.assertEqual(tags['year'], 2016) + + def test_keep_fields_removes_preserved_tags(self): + config['zero'] = {'keep_fields': [u'year id'], + 'fields': None, + 'update_database': True, + 'auto': True} + + z = ZeroPlugin() + + self.assertNotIn('id', z.patterns) + + def test_field_preserved_tags(self): + config['zero'] = {'fields': [u'year id'], + 'update_database': True, + 'auto': True} + + z = ZeroPlugin() + + self.assertNotIn('id', z.patterns) + def suite(): return unittest.TestLoader().loadTestsFromName(__name__) From 4cc4fe1290a018486becff8a023cabad10214061 Mon Sep 17 00:00:00 2001 From: Josh Brown Date: Sat, 17 Dec 2016 16:46:00 -0600 Subject: [PATCH 07/13] added zero subcommand --- test/test_zero.py | 72 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/test/test_zero.py b/test/test_zero.py index 1223e5c9ed..e7eb95413c 100644 --- a/test/test_zero.py +++ b/test/test_zero.py @@ -108,6 +108,78 @@ def test_album_art(self): mediafile = MediaFile(syspath(path)) self.assertEqual(0, len(mediafile.images)) + def test_auto_false(self): + item = self.add_item_fixture(year=2000) + item.write() + mediafile = MediaFile(syspath(item.path)) + self.assertEqual(2000, mediafile.year) + + config['zero'] = { + 'fields': [u'year'], + 'update_database': True, + 'auto': False + } + self.load_plugins('zero') + + item.write() + mediafile = MediaFile(syspath(item.path)) + self.assertEqual(item['year'], 2000) + self.assertEqual(mediafile.year, 2000) + + def test_subcommand(self): + item = self.add_item_fixture( + year=2016, + day=13, + month=3, + comments=u'test comment' + ) + item.write() + item_id = item.id + config['zero'] = { + 'fields': [u'comments'], + 'update_database': True, + 'auto': False + } + self.load_plugins('zero') + self.run_command('zero') + + mediafile = MediaFile(syspath(item.path)) + item = self.lib.get_item(item_id) + + self.assertEqual(item['year'], 2016) + self.assertEqual(mediafile.year, 2016) + self.assertEqual(mediafile.comments, None) + self.assertEqual(item['comments'], u'') + + def test_subcommand_update_database_False(self): + item = self.add_item_fixture( + year=2016, + day=13, + month=3, + comments=u'test comment' + ) + item.write() + item_id = item.id + config['zero'] = { + 'fields': [u'comments'], + 'update_database': False, + 'auto': False + } + self.load_plugins('zero') + + z = ZeroPlugin() + z.debug = False + self.run_command('zero') + + mediafile = MediaFile(syspath(item.path)) + item = self.lib.get_item(item_id) + + self.assertEqual(item['year'], 2016) + self.assertEqual(mediafile.year, 2016) + self.assertEqual(item['comments'], u'test comment') + self.assertEqual(mediafile.comments, None) + + def suite(): return unittest.TestLoader().loadTestsFromName(__name__) From 5b8b61f0c0d3f10250f3e3622b7f08c2a4f6d219 Mon Sep 17 00:00:00 2001 From: Josh Brown Date: Sat, 17 Dec 2016 17:17:55 -0600 Subject: [PATCH 08/13] added documentation for zero subcommand --- docs/plugins/zero.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/plugins/zero.rst b/docs/plugins/zero.rst index ecaa60a3d1..282bb4fc8f 100644 --- a/docs/plugins/zero.rst +++ b/docs/plugins/zero.rst @@ -18,6 +18,7 @@ Configuration Make a ``zero:`` section in your configuration file. You can specify the fields to nullify and the conditions for nullifying them: +* Set ``auto`` to ``yes`` to null fields automatically on import. Default ``yes`` * Set ``fields`` to a whitespace-separated list of fields to change. You can get the list of all available fields by running ``beet fields``. In addition, the ``images`` field allows you to remove any images @@ -42,3 +43,9 @@ If a custom pattern is not defined for a given field, the field will be nulled unconditionally. Note that the plugin currently does not zero fields when importing "as-is". + +Manually Triggering Zero +------------------------ + +The ``zero`` command will invoke the zero plugin on items matching a query. Use the +command ``beet zero [QUERY]`` From ef802293502aa07398bae723e9347d2b295874b2 Mon Sep 17 00:00:00 2001 From: Josh Brown Date: Sat, 17 Dec 2016 17:30:07 -0600 Subject: [PATCH 09/13] additional zero tests --- test/test_zero.py | 54 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 49 insertions(+), 5 deletions(-) diff --git a/test/test_zero.py b/test/test_zero.py index e7eb95413c..4baacba905 100644 --- a/test/test_zero.py +++ b/test/test_zero.py @@ -126,7 +126,7 @@ def test_auto_false(self): self.assertEqual(item['year'], 2000) self.assertEqual(mediafile.year, 2000) - def test_subcommand(self): + def test_subcommand_update_database_true(self): item = self.add_item_fixture( year=2016, day=13, @@ -151,7 +151,7 @@ def test_subcommand(self): self.assertEqual(mediafile.comments, None) self.assertEqual(item['comments'], u'') - def test_subcommand_update_database_False(self): + def test_subcommand_update_database_false(self): item = self.add_item_fixture( year=2016, day=13, @@ -166,9 +166,6 @@ def test_subcommand_update_database_False(self): 'auto': False } self.load_plugins('zero') - - z = ZeroPlugin() - z.debug = False self.run_command('zero') mediafile = MediaFile(syspath(item.path)) @@ -179,6 +176,53 @@ def test_subcommand_update_database_False(self): self.assertEqual(item['comments'], u'test comment') self.assertEqual(mediafile.comments, None) + def test_subcommand_query_include(self): + item = self.add_item_fixture( + year=2016, + day=13, + month=3, + comments=u'test comment' + ) + + item.write() + + config['zero'] = { + 'fields': [u'comments'], + 'update_database': False, + 'auto': False + } + + self.load_plugins('zero') + self.run_command('zero', 'year: 2016') + + mediafile = MediaFile(syspath(item.path)) + + self.assertEqual(mediafile.year, 2016) + self.assertEqual(mediafile.comments, None) + + def test_subcommand_query_exclude(self): + item = self.add_item_fixture( + year=2016, + day=13, + month=3, + comments=u'test comment' + ) + + item.write() + + config['zero'] = { + 'fields': [u'comments'], + 'update_database': False, + 'auto': False + } + + self.load_plugins('zero') + self.run_command('zero', 'year: 0000') + + mediafile = MediaFile(syspath(item.path)) + + self.assertEqual(mediafile.year, 2016) + self.assertEqual(mediafile.comments, u'test comment') def suite(): From 30ae211e66c101aaeb3917253033b528d29a7bc5 Mon Sep 17 00:00:00 2001 From: Josh Brown Date: Sat, 17 Dec 2016 17:49:45 -0600 Subject: [PATCH 10/13] update changelog --- docs/changelog.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/changelog.rst b/docs/changelog.rst index 2720ffe193..9cdf298c0f 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -15,6 +15,10 @@ Fixes: * :doc:`/plugins/bpd`: Fix a crash on non-ASCII MPD commands. :bug:`2332` +Features: + +* :doc:`/plugins/zero`: Added ``zero`` command to manually trigger the zero + plugin. 1.4.2 (December 16, 2016) ------------------------- From 2903925e2f18f7e9cf3db59b9ad05a7182ffaeb7 Mon Sep 17 00:00:00 2001 From: Josh Brown Date: Mon, 26 Dec 2016 13:34:34 -0600 Subject: [PATCH 11/13] Rewrite zero plugin manual mode --- beetsplug/zero.py | 33 ++++++++++- test/test_zero.py | 141 ++++++++++++++++++++++++++++++++-------------- 2 files changed, 130 insertions(+), 44 deletions(-) diff --git a/beetsplug/zero.py b/beetsplug/zero.py index 5659207fe2..975a9944ff 100644 --- a/beetsplug/zero.py +++ b/beetsplug/zero.py @@ -23,6 +23,7 @@ from beets.plugins import BeetsPlugin from beets.mediafile import MediaFile from beets.importer import action +from beets.ui import Subcommand, decargs from beets.util import confit __author__ = 'baobab@heresiarch.info' @@ -39,6 +40,7 @@ def __init__(self): self.import_task_choice_event) self.config.add({ + 'auto': True, 'fields': [], 'keep_fields': [], 'update_database': False, @@ -63,6 +65,16 @@ def __init__(self): field not in ('id', 'path', 'album_id')): self._set_pattern(field) + def commands(self): + zero_command = Subcommand('zero', help='set fields to null') + + def zero_fields(lib, opts, args): + for item in lib.items(decargs(args)): + self.process_item(item) + + zero_command.func = zero_fields + return [zero_command] + def _set_pattern(self, field): """Set a field in `self.patterns` to a string list corresponding to the configuration, or `True` if the field has no specific @@ -93,24 +105,41 @@ def write_event(self, item, path, tags): """Set values in tags to `None` if the key and value are matched by `self.patterns`. """ + if self.config['auto']: + self.set_fields(item, tags) + + def set_fields(self, item, tags): + fields_set = False + if not self.fields_to_progs: self._log.warning(u'no fields, nothing to do') - return + return False for field, progs in self.fields_to_progs.items(): if field in tags: value = tags[field] - match = _match_progs(value, progs, self._log) + match = _match_progs(tags[field], progs, self._log) else: value = '' match = not progs if match: + fields_set = True self._log.debug(u'{0}: {1} -> None', field, value) tags[field] = None if self.config['update_database']: item[field] = None + return fields_set + + def process_item(self, item): + tags = dict(item) + + if self.set_fields(item, tags): + item.write(tags=tags) + if self.config['update_database']: + item.store(fields=tags) + def _match_progs(value, progs, log): """Check if field (as string) is matching any of the patterns in diff --git a/test/test_zero.py b/test/test_zero.py index 4baacba905..87e31c5394 100644 --- a/test/test_zero.py +++ b/test/test_zero.py @@ -105,26 +105,21 @@ def test_album_art(self): self.load_plugins('zero') item.write() - mediafile = MediaFile(syspath(path)) - self.assertEqual(0, len(mediafile.images)) + mf = MediaFile(syspath(path)) + self.assertEqual(0, len(mf.images)) def test_auto_false(self): + self.config['zero']['fields'] = ['year'] + self.config['zero']['update_database'] = True + self.config['zero']['auto'] = False + item = self.add_item_fixture(year=2000) item.write() - mediafile = MediaFile(syspath(item.path)) - self.assertEqual(2000, mediafile.year) - config['zero'] = { - 'fields': [u'year'], - 'update_database': True, - 'auto': False - } self.load_plugins('zero') - item.write() - mediafile = MediaFile(syspath(item.path)) + self.assertEqual(item['year'], 2000) - self.assertEqual(mediafile.year, 2000) def test_subcommand_update_database_true(self): item = self.add_item_fixture( @@ -135,20 +130,19 @@ def test_subcommand_update_database_true(self): ) item.write() item_id = item.id - config['zero'] = { - 'fields': [u'comments'], - 'update_database': True, - 'auto': False - } + self.config['zero']['fields'] = ['comments'] + self.config['zero']['update_database'] = True + self.config['zero']['auto'] = False + self.load_plugins('zero') self.run_command('zero') - mediafile = MediaFile(syspath(item.path)) + mf = MediaFile(syspath(item.path)) item = self.lib.get_item(item_id) self.assertEqual(item['year'], 2016) - self.assertEqual(mediafile.year, 2016) - self.assertEqual(mediafile.comments, None) + self.assertEqual(mf.year, 2016) + self.assertEqual(mf.comments, None) self.assertEqual(item['comments'], u'') def test_subcommand_update_database_false(self): @@ -160,21 +154,21 @@ def test_subcommand_update_database_false(self): ) item.write() item_id = item.id - config['zero'] = { - 'fields': [u'comments'], - 'update_database': False, - 'auto': False - } + + self.config['zero']['fields'] = ['comments'] + self.config['zero']['update_database'] = False + self.config['zero']['auto'] = False + self.load_plugins('zero') self.run_command('zero') - mediafile = MediaFile(syspath(item.path)) + mf = MediaFile(syspath(item.path)) item = self.lib.get_item(item_id) self.assertEqual(item['year'], 2016) - self.assertEqual(mediafile.year, 2016) + self.assertEqual(mf.year, 2016) self.assertEqual(item['comments'], u'test comment') - self.assertEqual(mediafile.comments, None) + self.assertEqual(mf.comments, None) def test_subcommand_query_include(self): item = self.add_item_fixture( @@ -186,19 +180,17 @@ def test_subcommand_query_include(self): item.write() - config['zero'] = { - 'fields': [u'comments'], - 'update_database': False, - 'auto': False - } + self.config['zero']['fields'] = ['comments'] + self.config['zero']['update_database'] = False + self.config['zero']['auto'] = False self.load_plugins('zero') self.run_command('zero', 'year: 2016') - mediafile = MediaFile(syspath(item.path)) + mf = MediaFile(syspath(item.path)) - self.assertEqual(mediafile.year, 2016) - self.assertEqual(mediafile.comments, None) + self.assertEqual(mf.year, 2016) + self.assertEqual(mf.comments, None) def test_subcommand_query_exclude(self): item = self.add_item_fixture( @@ -210,19 +202,84 @@ def test_subcommand_query_exclude(self): item.write() - config['zero'] = { - 'fields': [u'comments'], - 'update_database': False, - 'auto': False - } + self.config['zero']['fields'] = ['comments'] + self.config['zero']['update_database'] = False + self.config['zero']['auto'] = False self.load_plugins('zero') self.run_command('zero', 'year: 0000') + mf = MediaFile(syspath(item.path)) + + self.assertEqual(mf.year, 2016) + self.assertEqual(mf.comments, u'test comment') + + def test_no_fields(self): + item = self.add_item_fixture(year=2016) + item.write() mediafile = MediaFile(syspath(item.path)) + self.assertEqual(mediafile.year, 2016) + + item_id = item.id + + self.load_plugins('zero') + self.run_command('zero') + + item = self.lib.get_item(item_id) + self.assertEqual(item['year'], 2016) self.assertEqual(mediafile.year, 2016) - self.assertEqual(mediafile.comments, u'test comment') + + def test_whitelist_and_blacklist(self): + item = self.add_item_fixture(year=2016) + item.write() + mf = MediaFile(syspath(item.path)) + self.assertEqual(mf.year, 2016) + + item_id = item.id + self.config['zero']['fields'] = [u'year'] + self.config['zero']['keep_fields'] = [u'comments'] + + self.load_plugins('zero') + self.run_command('zero') + + item = self.lib.get_item(item_id) + + self.assertEqual(item['year'], 2016) + self.assertEqual(mf.year, 2016) + + def test_keep_fields(self): + item = self.add_item_fixture(year=2016, comments=u'test comment') + self.config['zero']['keep_fields'] = [u'year'] + self.config['zero']['fields'] = None + self.config['zero']['update_database'] = True + + tags = { + 'comments': u'test comment', + 'year': 2016, + } + self.load_plugins('zero') + z = ZeroPlugin() + z.write_event(item, item.path, tags) + self.assertEqual(tags['comments'], None) + self.assertEqual(tags['year'], 2016) + + def test_keep_fields_removes_preserved_tags(self): + self.config['zero']['keep_fields'] = [u'year'] + self.config['zero']['fields'] = None + self.config['zero']['update_database'] = True + + z = ZeroPlugin() + + self.assertNotIn('id', z.fields_to_progs) + + def test_fields_removes_preserved_tags(self): + self.config['zero']['fields'] = [u'year id'] + self.config['zero']['update_database'] = True + + z = ZeroPlugin() + + self.assertNotIn('id', z.fields_to_progs) def suite(): From 7833469261c9e15e922691beb8c698ec543c362e Mon Sep 17 00:00:00 2001 From: Josh Brown Date: Mon, 26 Dec 2016 14:13:01 -0600 Subject: [PATCH 12/13] add confirmation for empty query --- beetsplug/zero.py | 4 +++- test/test_zero.py | 40 +++++++++++++++++++++++++++++++++++----- 2 files changed, 38 insertions(+), 6 deletions(-) diff --git a/beetsplug/zero.py b/beetsplug/zero.py index 975a9944ff..d3c3a697fe 100644 --- a/beetsplug/zero.py +++ b/beetsplug/zero.py @@ -23,7 +23,7 @@ from beets.plugins import BeetsPlugin from beets.mediafile import MediaFile from beets.importer import action -from beets.ui import Subcommand, decargs +from beets.ui import Subcommand, decargs, input_yn from beets.util import confit __author__ = 'baobab@heresiarch.info' @@ -69,6 +69,8 @@ def commands(self): zero_command = Subcommand('zero', help='set fields to null') def zero_fields(lib, opts, args): + if not decargs(args) and not input_yn(u"Remove fields for all items? (Y/n)", True): + return for item in lib.items(decargs(args)): self.process_item(item) diff --git a/test/test_zero.py b/test/test_zero.py index 87e31c5394..025eaa5407 100644 --- a/test/test_zero.py +++ b/test/test_zero.py @@ -5,7 +5,7 @@ from __future__ import division, absolute_import, print_function import unittest -from test.helper import TestHelper +from test.helper import TestHelper, control_stdin from beets.library import Item from beetsplug.zero import ZeroPlugin @@ -135,7 +135,8 @@ def test_subcommand_update_database_true(self): self.config['zero']['auto'] = False self.load_plugins('zero') - self.run_command('zero') + with control_stdin('y'): + self.run_command('zero') mf = MediaFile(syspath(item.path)) item = self.lib.get_item(item_id) @@ -160,7 +161,8 @@ def test_subcommand_update_database_false(self): self.config['zero']['auto'] = False self.load_plugins('zero') - self.run_command('zero') + with control_stdin('y'): + self.run_command('zero') mf = MediaFile(syspath(item.path)) item = self.lib.get_item(item_id) @@ -223,7 +225,8 @@ def test_no_fields(self): item_id = item.id self.load_plugins('zero') - self.run_command('zero') + with control_stdin('y'): + self.run_command('zero') item = self.lib.get_item(item_id) @@ -241,7 +244,8 @@ def test_whitelist_and_blacklist(self): self.config['zero']['keep_fields'] = [u'comments'] self.load_plugins('zero') - self.run_command('zero') + with control_stdin('y'): + self.run_command('zero') item = self.lib.get_item(item_id) @@ -259,6 +263,7 @@ def test_keep_fields(self): 'year': 2016, } self.load_plugins('zero') + z = ZeroPlugin() z.write_event(item, item.path, tags) self.assertEqual(tags['comments'], None) @@ -281,6 +286,31 @@ def test_fields_removes_preserved_tags(self): self.assertNotIn('id', z.fields_to_progs) + def test_empty_query_n_response_no_changes(self): + item = self.add_item_fixture( + year=2016, + day=13, + month=3, + comments=u'test comment' + ) + item.write() + item_id = item.id + self.config['zero']['fields'] = ['comments'] + self.config['zero']['update_database'] = True + self.config['zero']['auto'] = False + + self.load_plugins('zero') + with control_stdin('n'): + self.run_command('zero') + + mf = MediaFile(syspath(item.path)) + item = self.lib.get_item(item_id) + + self.assertEqual(item['year'], 2016) + self.assertEqual(mf.year, 2016) + self.assertEqual(mf.comments, u'test comment') + self.assertEqual(item['comments'], u'test comment') + def suite(): return unittest.TestLoader().loadTestsFromName(__name__) From b6577b42267ab4c4fc0112ef6720cbbe4cda470d Mon Sep 17 00:00:00 2001 From: Josh Brown Date: Mon, 26 Dec 2016 14:42:10 -0600 Subject: [PATCH 13/13] refactor long line for PEP8 --- beetsplug/zero.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/beetsplug/zero.py b/beetsplug/zero.py index d3c3a697fe..6a3013069a 100644 --- a/beetsplug/zero.py +++ b/beetsplug/zero.py @@ -69,7 +69,9 @@ def commands(self): zero_command = Subcommand('zero', help='set fields to null') def zero_fields(lib, opts, args): - if not decargs(args) and not input_yn(u"Remove fields for all items? (Y/n)", True): + if not decargs(args) and not input_yn( + u"Remove fields for all items? (Y/n)", + True): return for item in lib.items(decargs(args)): self.process_item(item)