Fix song "10 bla" comes before "2 foo"

bzr-revno: 2113
This commit is contained in:
Raoul Snyman 2012-11-21 18:45:37 +00:00 committed by Tim Bentley
commit 12e7f7384d
2 changed files with 58 additions and 6 deletions

View File

@ -31,6 +31,8 @@ The :mod:`db` module provides the database and schema that is the backend for
the Songs plugin
"""
import re
from sqlalchemy import Column, ForeignKey, Table, types
from sqlalchemy.orm import mapper, relation, reconstructor
from sqlalchemy.sql.expression import func
@ -38,6 +40,7 @@ from PyQt4 import QtCore
from openlp.core.lib.db import BaseModel, init_db
class Author(BaseModel):
"""
Author model
@ -66,21 +69,33 @@ class Song(BaseModel):
Song model
"""
def __init__(self):
self.sort_string = ''
self.sort_key = ()
def _try_int(self, s):
"Convert to integer if possible."
try:
return int(s)
except:
return QtCore.QString(s.lower())
def _natsort_key(self, s):
"Used internally to get a tuple by which s is sorted."
return map(self._try_int, re.findall(r'(\d+|\D+)', s))
# This decorator tells sqlalchemy to call this method everytime
# any data on this object are updated.
# any data on this object is updated.
@reconstructor
def init_on_load(self):
"""
Precompute string to be used for sorting.
Precompute a tuple to be used for sorting.
Song sorting is performance sensitive operation.
To get maximum speed lets precompute the string
used for comparison.
"""
# Avoid the overhead of converting string to lowercase and to QString
self.sort_string = QtCore.QString(self.title.lower())
# with every call to sort().
self.sort_key = self._natsort_key(self.title)
class Topic(BaseModel):

View File

@ -50,6 +50,44 @@ from openlp.plugins.songs.lib.ui import SongStrings
log = logging.getLogger(__name__)
def natcmp(a, b):
"""
Natural string comparison which mimics the behaviour of Python's internal
cmp function.
"""
log.debug('a: %s; b: %s', a, b)
if len(a) <= len(b):
for i, key in enumerate(a):
if isinstance(key, int) and isinstance(b[i], int):
result = cmp(key, b[i])
elif isinstance(key, int) and not isinstance(b[i], int):
result = locale_direct_compare(QtCore.QString(str(key)), b[i])
elif not isinstance(key, int) and isinstance(b[i], int):
result = locale_direct_compare(key, QtCore.QString(str(b[i])))
else:
result = locale_direct_compare(key, b[i])
if result != 0:
return result
if len(a) == len(b):
return 0
else:
return -1
else:
for i, key in enumerate(b):
if isinstance(a[i], int) and isinstance(key, int):
result = cmp(a[i], key)
elif isinstance(a[i], int) and not isinstance(key, int):
result = locale_direct_compare(QtCore.QString(str(a[i])), key)
elif not isinstance(a[i], int) and isinstance(key, int):
result = locale_direct_compare(a[i], QtCore.QString(str(key)))
else:
result = locale_direct_compare(a[i], key)
if result != 0:
return result
return 1
class SongSearch(object):
"""
An enumeration for song search methods.
@ -260,8 +298,7 @@ class SongMediaItem(MediaManagerItem):
log.debug(u'display results Song')
self.saveAutoSelectId()
self.listView.clear()
searchresults.sort(
cmp=locale_direct_compare, key=lambda song: song.sort_string)
searchresults.sort(cmp=natcmp, key=lambda song: song.sort_key)
for song in searchresults:
# Do not display temporary songs
if song.temporary: