mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Implement #2679 (Maintain author order for multi-author books)
This commit is contained in:
parent
2253d90e4c
commit
22ec9df720
@ -561,6 +561,35 @@ class LibraryDatabase2(LibraryDatabase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade_version_6(self):
|
||||||
|
'Show authors in order'
|
||||||
|
self.conn.executescript('''
|
||||||
|
BEGIN TRANSACTION;
|
||||||
|
DROP VIEW meta;
|
||||||
|
CREATE VIEW meta AS
|
||||||
|
SELECT id, title,
|
||||||
|
(SELECT sortconcat(bal.id, name) FROM books_authors_link AS bal JOIN authors ON(author = authors.id) WHERE book = books.id) authors,
|
||||||
|
(SELECT name FROM publishers WHERE publishers.id IN (SELECT publisher from books_publishers_link WHERE book=books.id)) publisher,
|
||||||
|
(SELECT rating FROM ratings WHERE ratings.id IN (SELECT rating from books_ratings_link WHERE book=books.id)) rating,
|
||||||
|
timestamp,
|
||||||
|
(SELECT MAX(uncompressed_size) FROM data WHERE book=books.id) size,
|
||||||
|
(SELECT concat(name) FROM tags WHERE tags.id IN (SELECT tag from books_tags_link WHERE book=books.id)) tags,
|
||||||
|
(SELECT text FROM comments WHERE book=books.id) comments,
|
||||||
|
(SELECT name FROM series WHERE series.id IN (SELECT series FROM books_series_link WHERE book=books.id)) series,
|
||||||
|
series_index,
|
||||||
|
sort,
|
||||||
|
author_sort,
|
||||||
|
(SELECT concat(format) FROM data WHERE data.book=books.id) formats,
|
||||||
|
isbn,
|
||||||
|
path,
|
||||||
|
lccn,
|
||||||
|
pubdate,
|
||||||
|
flags
|
||||||
|
FROM books;
|
||||||
|
END TRANSACTION;
|
||||||
|
''')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def last_modified(self):
|
def last_modified(self):
|
||||||
''' Return last modified time as a UTC datetime object'''
|
''' Return last modified time as a UTC datetime object'''
|
||||||
|
@ -40,10 +40,10 @@ def convert_timestamp(val):
|
|||||||
if tz is not None:
|
if tz is not None:
|
||||||
h, m = map(int, tz.split(':'))
|
h, m = map(int, tz.split(':'))
|
||||||
delta = timedelta(minutes=mult*(60*h + m))
|
delta = timedelta(minutes=mult*(60*h + m))
|
||||||
tz = type('CustomTZ', (tzinfo,), {'utcoffset':lambda self, dt:delta,
|
tz = type('CustomTZ', (tzinfo,), {'utcoffset':lambda self, dt:delta,
|
||||||
'dst':lambda self,dt:timedelta(0)})()
|
'dst':lambda self,dt:timedelta(0)})()
|
||||||
|
|
||||||
val = datetime(year, month, day, hours, minutes, seconds, microseconds,
|
val = datetime(year, month, day, hours, minutes, seconds, microseconds,
|
||||||
tzinfo=tz)
|
tzinfo=tz)
|
||||||
if tz is not None:
|
if tz is not None:
|
||||||
val = datetime(*(val.utctimetuple()[:6]))
|
val = datetime(*(val.utctimetuple()[:6]))
|
||||||
@ -61,11 +61,11 @@ class Concatenate(object):
|
|||||||
def __init__(self, sep=','):
|
def __init__(self, sep=','):
|
||||||
self.sep = sep
|
self.sep = sep
|
||||||
self.ans = ''
|
self.ans = ''
|
||||||
|
|
||||||
def step(self, value):
|
def step(self, value):
|
||||||
if value is not None:
|
if value is not None:
|
||||||
self.ans += value + self.sep
|
self.ans += value + self.sep
|
||||||
|
|
||||||
def finalize(self):
|
def finalize(self):
|
||||||
if not self.ans:
|
if not self.ans:
|
||||||
return None
|
return None
|
||||||
@ -73,8 +73,23 @@ class Concatenate(object):
|
|||||||
return self.ans[:-len(self.sep)]
|
return self.ans[:-len(self.sep)]
|
||||||
return self.ans
|
return self.ans
|
||||||
|
|
||||||
|
class SortedConcatenate(object):
|
||||||
|
'''String concatenation aggregator for sqlite, sorted by supplied index'''
|
||||||
|
def __init__(self, sep=','):
|
||||||
|
self.sep = sep
|
||||||
|
self.ans = {}
|
||||||
|
|
||||||
|
def step(self, ndx, value):
|
||||||
|
if value is not None:
|
||||||
|
self.ans[ndx] = value
|
||||||
|
|
||||||
|
def finalize(self):
|
||||||
|
if len(self.ans) == 0:
|
||||||
|
return None
|
||||||
|
return self.sep.join(map(self.ans.get, sorted(self.ans.keys())))
|
||||||
|
|
||||||
class Connection(sqlite.Connection):
|
class Connection(sqlite.Connection):
|
||||||
|
|
||||||
def get(self, *args, **kw):
|
def get(self, *args, **kw):
|
||||||
ans = self.execute(*args)
|
ans = self.execute(*args)
|
||||||
if not kw.get('all', True):
|
if not kw.get('all', True):
|
||||||
@ -83,12 +98,12 @@ class Connection(sqlite.Connection):
|
|||||||
ans = [None]
|
ans = [None]
|
||||||
return ans[0]
|
return ans[0]
|
||||||
return ans.fetchall()
|
return ans.fetchall()
|
||||||
|
|
||||||
|
|
||||||
class DBThread(Thread):
|
class DBThread(Thread):
|
||||||
|
|
||||||
CLOSE = '-------close---------'
|
CLOSE = '-------close---------'
|
||||||
|
|
||||||
def __init__(self, path, row_factory):
|
def __init__(self, path, row_factory):
|
||||||
Thread.__init__(self)
|
Thread.__init__(self)
|
||||||
self.setDaemon(True)
|
self.setDaemon(True)
|
||||||
@ -98,14 +113,15 @@ class DBThread(Thread):
|
|||||||
self.requests = Queue(1)
|
self.requests = Queue(1)
|
||||||
self.results = Queue(1)
|
self.results = Queue(1)
|
||||||
self.conn = None
|
self.conn = None
|
||||||
|
|
||||||
def connect(self):
|
def connect(self):
|
||||||
self.conn = sqlite.connect(self.path, factory=Connection,
|
self.conn = sqlite.connect(self.path, factory=Connection,
|
||||||
detect_types=sqlite.PARSE_DECLTYPES|sqlite.PARSE_COLNAMES)
|
detect_types=sqlite.PARSE_DECLTYPES|sqlite.PARSE_COLNAMES)
|
||||||
self.conn.row_factory = sqlite.Row if self.row_factory else lambda cursor, row : list(row)
|
self.conn.row_factory = sqlite.Row if self.row_factory else lambda cursor, row : list(row)
|
||||||
self.conn.create_aggregate('concat', 1, Concatenate)
|
self.conn.create_aggregate('concat', 1, Concatenate)
|
||||||
|
self.conn.create_aggregate('sortconcat', 2, SortedConcatenate)
|
||||||
self.conn.create_function('title_sort', 1, title_sort)
|
self.conn.create_function('title_sort', 1, title_sort)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
try:
|
try:
|
||||||
self.connect()
|
self.connect()
|
||||||
@ -124,7 +140,7 @@ class DBThread(Thread):
|
|||||||
self.unhandled_error = (err, traceback.format_exc())
|
self.unhandled_error = (err, traceback.format_exc())
|
||||||
|
|
||||||
class DatabaseException(Exception):
|
class DatabaseException(Exception):
|
||||||
|
|
||||||
def __init__(self, err, tb):
|
def __init__(self, err, tb):
|
||||||
tb = '\n\t'.join(('\tRemote'+tb).splitlines())
|
tb = '\n\t'.join(('\tRemote'+tb).splitlines())
|
||||||
msg = unicode(err) +'\n' + tb
|
msg = unicode(err) +'\n' + tb
|
||||||
@ -146,41 +162,41 @@ def proxy(fn):
|
|||||||
raise DatabaseException(*res)
|
raise DatabaseException(*res)
|
||||||
return res
|
return res
|
||||||
return run
|
return run
|
||||||
|
|
||||||
|
|
||||||
class ConnectionProxy(object):
|
class ConnectionProxy(object):
|
||||||
|
|
||||||
def __init__(self, proxy):
|
def __init__(self, proxy):
|
||||||
self.proxy = proxy
|
self.proxy = proxy
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
if self.proxy.unhandled_error is None:
|
if self.proxy.unhandled_error is None:
|
||||||
self.proxy.requests.put((self.proxy.CLOSE, [], {}))
|
self.proxy.requests.put((self.proxy.CLOSE, [], {}))
|
||||||
|
|
||||||
@proxy
|
@proxy
|
||||||
def get(self, query, all=True): pass
|
def get(self, query, all=True): pass
|
||||||
|
|
||||||
@proxy
|
@proxy
|
||||||
def commit(self): pass
|
def commit(self): pass
|
||||||
|
|
||||||
@proxy
|
@proxy
|
||||||
def execute(self): pass
|
def execute(self): pass
|
||||||
|
|
||||||
@proxy
|
@proxy
|
||||||
def executemany(self): pass
|
def executemany(self): pass
|
||||||
|
|
||||||
@proxy
|
@proxy
|
||||||
def executescript(self): pass
|
def executescript(self): pass
|
||||||
|
|
||||||
@proxy
|
@proxy
|
||||||
def create_aggregate(self): pass
|
def create_aggregate(self): pass
|
||||||
|
|
||||||
@proxy
|
@proxy
|
||||||
def create_function(self): pass
|
def create_function(self): pass
|
||||||
|
|
||||||
@proxy
|
@proxy
|
||||||
def cursor(self): pass
|
def cursor(self): pass
|
||||||
|
|
||||||
def connect(dbpath, row_factory=None):
|
def connect(dbpath, row_factory=None):
|
||||||
conn = ConnectionProxy(DBThread(dbpath, row_factory))
|
conn = ConnectionProxy(DBThread(dbpath, row_factory))
|
||||||
conn.proxy.start()
|
conn.proxy.start()
|
||||||
@ -188,4 +204,4 @@ def connect(dbpath, row_factory=None):
|
|||||||
time.sleep(0.01)
|
time.sleep(0.01)
|
||||||
if conn.proxy.unhandled_error[0] is not None:
|
if conn.proxy.unhandled_error[0] is not None:
|
||||||
raise DatabaseException(*conn.proxy.unhandled_error)
|
raise DatabaseException(*conn.proxy.unhandled_error)
|
||||||
return conn
|
return conn
|
||||||
|
Loading…
x
Reference in New Issue
Block a user