recommendation is now a "real" enumeration

This commit is contained in:
Adrian Sampson 2013-02-07 17:26:58 -08:00
parent ebcd944bd5
commit 151177ab95
4 changed files with 29 additions and 35 deletions

View file

@ -25,8 +25,7 @@ from beets.util import sorted_walk, ancestry, displayable_path
from .hooks import AlbumInfo, TrackInfo, AlbumMatch, TrackMatch
from .match import AutotagError
from .match import tag_item, tag_album
from .match import \
RECOMMEND_STRONG, RECOMMEND_MEDIUM, RECOMMEND_LOW, RECOMMEND_NONE
from .match import recommendation
# Global logger.
log = logging.getLogger('beets')

View file

@ -25,6 +25,7 @@ from unidecode import unidecode
from beets import plugins
from beets import config
from beets.util import levenshtein, plurality
from beets.util.enumeration import enum
from beets.autotag import hooks
# Distance parameters.
@ -71,11 +72,8 @@ SD_REPLACE = [
(r'&', 'and'),
]
# Recommendation constants.
RECOMMEND_STRONG = 'RECOMMEND_STRONG'
RECOMMEND_MEDIUM = 'RECOMMEND_MEDIUM'
RECOMMEND_LOW = 'RECOMMEND_LOW'
RECOMMEND_NONE = 'RECOMMEND_NONE'
# Recommendation enumeration.
recommendation = enum('none', 'low', 'medium', 'strong', name='recommendation')
# Artist signals that indicate "various artists". These are used at the
# album level to determine whether a given release is likely a VA
@ -322,37 +320,36 @@ def match_by_id(items):
log.debug('No album ID consensus.')
return None
def recommendation(results):
def _recommendation(results):
"""Given a sorted list of AlbumMatch or TrackMatch objects, return a
recommendation flag (RECOMMEND_STRONG, RECOMMEND_MEDIUM,
RECOMMEND_NONE) based on the results' distances.
recommendation based on the results' distances.
"""
if not results:
# No candidates: no recommendation.
rec = RECOMMEND_NONE
rec = recommendation.none
else:
min_dist = results[0].distance
if min_dist < config['match']['strong_rec_thresh'].as_number():
# Partial matches get downgraded to "medium".
if isinstance(results[0], hooks.AlbumMatch) and \
(results[0].extra_items or results[0].extra_tracks):
rec = RECOMMEND_MEDIUM
rec = recommendation.medium
else:
# Strong recommendation level.
rec = RECOMMEND_STRONG
rec = recommendation.strong
elif min_dist <= config['match']['medium_rec_thresh'].as_number():
# Medium recommendation level.
rec = RECOMMEND_MEDIUM
rec = recommendation.medium
elif len(results) == 1:
# Only a single candidate.
rec = RECOMMEND_LOW
rec = recommendation.low
elif results[1].distance - min_dist >= \
config['match']['rec_gap_thresh'].as_number():
# Gap between first two candidates is large.
rec = RECOMMEND_LOW
rec = recommendation.low
else:
# No conclusion.
rec = RECOMMEND_NONE
rec = recommendation.none
return rec
def _add_candidate(items, results, info):
@ -386,10 +383,7 @@ def tag_album(items, search_artist=None, search_album=None,
- The current album.
- A list of AlbumMatch objects. The candidates are sorted by
distance (i.e., best match first).
- A recommendation, one of RECOMMEND_STRONG, RECOMMEND_MEDIUM,
or RECOMMEND_NONE; indicating that the first candidate is
very likely, it is somewhat likely, or no conclusion could
be reached.
- A recommendation.
If search_artist and search_album or search_id are provided, then
they are used as search terms in place of the current metadata.
May raise an AutotagError if existing metadata is insufficient.
@ -410,13 +404,13 @@ def tag_album(items, search_artist=None, search_album=None,
id_info = match_by_id(items)
if id_info:
_add_candidate(items, candidates, id_info)
rec = recommendation(candidates.values())
rec = _recommendation(candidates.values())
log.debug('Album ID match recommendation is ' + str(rec))
if candidates and not config['import']['timid']:
# If we have a very good MBID match, return immediately.
# Otherwise, this match will compete against metadata-based
# matches.
if rec == RECOMMEND_STRONG:
if rec == recommendation.strong:
log.debug('ID match.')
return cur_artist, cur_album, candidates.values(), rec
@ -425,7 +419,7 @@ def tag_album(items, search_artist=None, search_album=None,
if candidates:
return cur_artist, cur_album, candidates.values(), rec
else:
return cur_artist, cur_album, [], RECOMMEND_NONE
return cur_artist, cur_album, [], recommendation.none
# Search terms.
if not (search_artist and search_album):
@ -448,7 +442,7 @@ def tag_album(items, search_artist=None, search_album=None,
# Sort and get the recommendation.
candidates = sorted(candidates.itervalues())
rec = recommendation(candidates)
rec = _recommendation(candidates)
return cur_artist, cur_album, candidates, rec
def tag_item(item, search_artist=None, search_title=None,
@ -473,8 +467,8 @@ def tag_item(item, search_artist=None, search_title=None,
candidates[track_info.track_id] = \
hooks.TrackMatch(dist, track_info)
# If this is a good match, then don't keep searching.
rec = recommendation(candidates.values())
if rec == RECOMMEND_STRONG and not config['import']['timid']:
rec = _recommendation(candidates.values())
if rec == recommendation.strong and not config['import']['timid']:
log.debug('Track ID match.')
return candidates.values(), rec
@ -483,7 +477,7 @@ def tag_item(item, search_artist=None, search_title=None,
if candidates:
return candidates.values(), rec
else:
return [], RECOMMEND_NONE
return [], recommendation.none
# Search terms.
if not (search_artist and search_title):
@ -498,5 +492,5 @@ def tag_item(item, search_artist=None, search_title=None,
# Sort by distance and return with recommendation.
log.debug('Found %i candidates.' % len(candidates))
candidates = sorted(candidates.itervalues())
rec = recommendation(candidates)
rec = _recommendation(candidates)
return candidates, rec

View file

@ -28,6 +28,7 @@ import beets
from beets import ui
from beets.ui import print_, input_, decargs
from beets import autotag
from beets.autotag import recommendation
from beets import plugins
from beets import importer
from beets.util import syspath, normpath, ancestry, displayable_path
@ -277,7 +278,7 @@ def _summary_judment(rec):
made.
"""
if config['import']['quiet']:
if rec == autotag.RECOMMEND_STRONG:
if rec == recommendation.strong:
return importer.action.APPLY
else:
action = config['import']['quiet_fallback'].as_choice({
@ -285,7 +286,7 @@ def _summary_judment(rec):
'asis': importer.action.ASIS,
})
elif rec == autotag.RECOMMEND_NONE:
elif rec == recommendation.none:
action = config['import']['none_rec_action'].as_choice({
'skip': importer.action.SKIP,
'asis': importer.action.ASIS,
@ -352,13 +353,13 @@ def choose_candidate(candidates, singleton, rec, cur_artist=None,
# Is the change good enough?
bypass_candidates = False
if rec != autotag.RECOMMEND_NONE:
if rec != recommendation.none:
match = candidates[0]
bypass_candidates = True
while True:
# Display and choose from candidates.
require = rec in (autotag.RECOMMEND_NONE, autotag.RECOMMEND_LOW)
require = rec <= recommendation.low
if not bypass_candidates:
# Display list of candidates.
@ -441,7 +442,7 @@ def choose_candidate(candidates, singleton, rec, cur_artist=None,
show_change(cur_artist, cur_album, match)
# Exact match => tag automatically if we're not in timid mode.
if rec == autotag.RECOMMEND_STRONG and not config['import']['timid']:
if rec == recommendation.strong and not config['import']['timid']:
return match
# Ask for confirmation.

View file

@ -455,7 +455,7 @@ class AutotagTest(_common.TestCase):
'path',
[_common.item()],
)
task.set_candidates('artist', 'album', [], autotag.RECOMMEND_NONE)
task.set_candidates('artist', 'album', [], autotag.recommendation.none)
session = _common.import_session(cli=True)
res = session.choose_match(task)
self.assertEqual(res, result)