From 206da41869ba70203b7aecdd49ddec0b5afbe149 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 5 Oct 2008 20:00:02 -0700 Subject: [PATCH] Implement #1068 (Multiple authors vs. Standards) --- src/calibre/ebooks/metadata/__init__.py | 18 +++++++++++++++ src/calibre/gui2/dialogs/epub.py | 15 ++++++++----- src/calibre/gui2/dialogs/epub.ui | 4 ++-- src/calibre/gui2/dialogs/lrf_single.py | 17 +++++++++----- src/calibre/gui2/dialogs/lrf_single.ui | 4 ++-- src/calibre/gui2/dialogs/metadata_bulk.py | 3 ++- src/calibre/gui2/dialogs/metadata_bulk.ui | 5 ++++- src/calibre/gui2/dialogs/metadata_single.py | 25 +++++++++++---------- src/calibre/gui2/dialogs/metadata_single.ui | 4 +--- src/calibre/gui2/library.py | 2 +- src/calibre/library/database.py | 7 ++++-- src/calibre/library/database2.py | 6 +++-- 12 files changed, 74 insertions(+), 36 deletions(-) diff --git a/src/calibre/ebooks/metadata/__init__.py b/src/calibre/ebooks/metadata/__init__.py index fd7622a5d2..26d52cc3f2 100644 --- a/src/calibre/ebooks/metadata/__init__.py +++ b/src/calibre/ebooks/metadata/__init__.py @@ -15,6 +15,24 @@ from calibre.constants import __version__ as VERSION from calibre import relpath from calibre.utils.config import OptionParser +def string_to_authors(raw): + raw = raw.replace('&&', u'\uffff') + authors = [a.strip().replace(u'\uffff', '&') for a in raw.split('&')] + return authors + +def authors_to_string(authors): + return ' & '.join([a.replace('&', '&&') for a in authors]) + +def author_to_author_sort(author): + tokens = author.split() + tokens = tokens[-1:] + tokens[:-1] + if len(tokens) > 1: + tokens[0] += ',' + return ' '.join(tokens) + +def authors_to_sort_string(authors): + return ' & '.join(map(author_to_author_sort, authors)) + def get_parser(extension): ''' Return an option parser with the basic metadata options already setup''' parser = OptionParser(usage='%prog [options] myfile.'+extension+'\n\nRead and write metadata from an ebook file.') diff --git a/src/calibre/gui2/dialogs/epub.py b/src/calibre/gui2/dialogs/epub.py index 78a2be0f51..c044eb0f01 100644 --- a/src/calibre/gui2/dialogs/epub.py +++ b/src/calibre/gui2/dialogs/epub.py @@ -7,8 +7,10 @@ __docformat__ = 'restructuredtext en' The GUI for conversion to EPUB. ''' import os + from PyQt4.Qt import QDialog, QSpinBox, QDoubleSpinBox, QComboBox, QLineEdit, \ QTextEdit, QCheckBox, Qt, QPixmap, QIcon, QListWidgetItem, SIGNAL +from lxml.etree import XPath from calibre.gui2.dialogs.choose_format import ChooseFormatDialog from calibre.gui2.dialogs.epub_ui import Ui_Dialog @@ -17,7 +19,8 @@ from calibre.ebooks.epub.from_any import SOURCE_FORMATS, config from calibre.ebooks.metadata import MetaInformation from calibre.ptempfile import PersistentTemporaryFile from calibre.ebooks.metadata.opf import OPFCreator -from lxml.etree import XPath +from calibre.ebooks.metadata import authors_to_string, string_to_authors + class Config(QDialog, Ui_Dialog): @@ -128,7 +131,10 @@ class Config(QDialog, Ui_Dialog): if self.row is not None: mi = self.db.get_metadata(self.id, index_is_id=True) self.title.setText(mi.title) - self.author.setText(', '.join(mi.authors)) + if mi.authors: + self.author.setText(authors_to_string(mi.authors)) + else: + self.author.setText('') self.publisher.setText(mi.publisher if mi.publisher else '') self.author_sort.setText(mi.author_sort if mi.author_sort else '') self.tags.setText(', '.join(mi.tags if mi.tags else [])) @@ -149,9 +155,8 @@ class Config(QDialog, Ui_Dialog): title = unicode(self.title.text()).strip() if not title: title = _('Unknown') - authors = [i.strip() for i in unicode(self.author.text()).strip().split(',')] - if not authors: - authors = [_('Unknown')] + authors = unicode(self.author.text()).strip() + authors = string_to_authors(authors) if authors else [_('Unknown')] return title, authors def get_metadata(self): diff --git a/src/calibre/gui2/dialogs/epub.ui b/src/calibre/gui2/dialogs/epub.ui index 3ecc0991e8..5aff8b5227 100644 --- a/src/calibre/gui2/dialogs/epub.ui +++ b/src/calibre/gui2/dialogs/epub.ui @@ -77,7 +77,7 @@ - 3 + 0 @@ -221,7 +221,7 @@ - Change the author(s) of this book. Multiple authors should be separated by a comma + Change the author(s) of this book. Multiple authors should be separated by an &. If the author name contains an &, use && to represent it. diff --git a/src/calibre/gui2/dialogs/lrf_single.py b/src/calibre/gui2/dialogs/lrf_single.py index 787df4d080..9f905c4458 100644 --- a/src/calibre/gui2/dialogs/lrf_single.py +++ b/src/calibre/gui2/dialogs/lrf_single.py @@ -14,6 +14,7 @@ from calibre.gui2.widgets import FontFamilyModel from calibre.ebooks.lrf import option_parser from calibre.ptempfile import PersistentTemporaryFile from calibre.constants import __appname__ +from calibre.ebooks.metadata import authors_to_string, string_to_authors, authors_to_sort_string font_family_model = None @@ -199,7 +200,11 @@ class LRFSingleDialog(QDialog, Ui_LRFSingleDialog): self.id = self.db.id(row) self.gui_title.setText(db.title(row)) au = self.db.authors(row) - self.gui_author.setText(au if au else '') + if au: + au = [a.strip().replace('|', ',') for a in au.split(',')] + self.gui_author.setText(authors_to_string(au)) + else: + self.gui_author.setText('') aus = self.db.author_sort(row) self.gui_author_sort.setText(aus if aus else '') pub = self.db.publisher(row) @@ -350,14 +355,16 @@ class LRFSingleDialog(QDialog, Ui_LRFSingleDialog): def write_metadata(self): title = qstring_to_unicode(self.gui_title.text()) self.db.set_title(self.id, title) - au = qstring_to_unicode(self.gui_author.text()).split(',') - if au: self.db.set_authors(self.id, au) + au = unicode(self.gui_author.text()) + if au: + self.db.set_authors(self.id, string_to_authors(au)) aus = qstring_to_unicode(self.gui_author_sort.text()) if not aus: t = self.db.authors(self.id, index_is_id=True) if not t: - t = 'Unknown' - aus = t.split(',')[0].strip() + t = _('Unknown') + aus = [a.strip().replace('|', ',') for a in t.split(',')] + aus = authors_to_sort_string(aus) self.db.set_author_sort(self.id, aus) self.db.set_publisher(self.id, qstring_to_unicode(self.gui_publisher.text())) self.db.set_tags(self.id, qstring_to_unicode(self.tags.text()).split(',')) diff --git a/src/calibre/gui2/dialogs/lrf_single.ui b/src/calibre/gui2/dialogs/lrf_single.ui index c0e17a1e0e..f08265fe9e 100644 --- a/src/calibre/gui2/dialogs/lrf_single.ui +++ b/src/calibre/gui2/dialogs/lrf_single.ui @@ -115,7 +115,7 @@ - 3 + 0 @@ -255,7 +255,7 @@ - Change the author(s) of this book. Multiple authors should be separated by a comma + Change the author(s) of this book. Multiple authors should be separated by an &. If the author name contains an &, use && to represent it. diff --git a/src/calibre/gui2/dialogs/metadata_bulk.py b/src/calibre/gui2/dialogs/metadata_bulk.py index 98ec115e51..fac7d7be85 100644 --- a/src/calibre/gui2/dialogs/metadata_bulk.py +++ b/src/calibre/gui2/dialogs/metadata_bulk.py @@ -9,6 +9,7 @@ from PyQt4.QtGui import QDialog from calibre.gui2 import qstring_to_unicode from calibre.gui2.dialogs.metadata_bulk_ui import Ui_MetadataBulkDialog from calibre.gui2.dialogs.tag_editor import TagEditor +from calibre.ebooks.metadata import string_to_authors class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog): def __init__(self, window, rows, db): @@ -51,7 +52,7 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog): for id in self.ids: au = qstring_to_unicode(self.authors.text()) if au: - au = au.split(',') + au = string_to_authors(au) self.db.set_authors(id, au) aus = qstring_to_unicode(self.author_sort.text()) if aus: diff --git a/src/calibre/gui2/dialogs/metadata_bulk.ui b/src/calibre/gui2/dialogs/metadata_bulk.ui index 334ce1b42b..b4a4106d87 100644 --- a/src/calibre/gui2/dialogs/metadata_bulk.ui +++ b/src/calibre/gui2/dialogs/metadata_bulk.ui @@ -52,7 +52,7 @@ - Change the author(s) of this book. Multiple authors should be separated by a comma + Change the author(s) of this book. Multiple authors should be separated by an &. If the author name contains an &, use && to represent it. @@ -84,6 +84,9 @@ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + rating + diff --git a/src/calibre/gui2/dialogs/metadata_single.py b/src/calibre/gui2/dialogs/metadata_single.py index 342bf25cd3..c6e26e86dc 100644 --- a/src/calibre/gui2/dialogs/metadata_single.py +++ b/src/calibre/gui2/dialogs/metadata_single.py @@ -17,6 +17,7 @@ from calibre.gui2.dialogs.fetch_metadata import FetchMetadata from calibre.gui2.dialogs.tag_editor import TagEditor from calibre.gui2.dialogs.password import PasswordDialog from calibre.ebooks import BOOK_EXTENSIONS +from calibre.ebooks.metadata import authors_to_sort_string, string_to_authors, authors_to_string from calibre.ebooks.metadata.library_thing import login, cover_from_isbn, LibraryThingError from calibre import islinux from calibre.utils.config import prefs @@ -155,7 +156,11 @@ class MetadataSingleDialog(QDialog, Ui_MetadataSingleDialog): isbn = '' self.isbn.setText(isbn) au = self.db.authors(row) - self.authors.setText(au if au else '') + if au: + au = [a.strip().replace('|', ',') for a in au.split(',')] + self.authors.setText(authors_to_string(au)) + else: + self.authors.setText('') aus = self.db.author_sort(row) self.author_sort.setText(aus if aus else '') pub = self.db.publisher(row) @@ -199,13 +204,8 @@ class MetadataSingleDialog(QDialog, Ui_MetadataSingleDialog): def deduce_author_sort(self): au = unicode(self.authors.text()) - tokens = au.split() - for x in (',', ';'): - if x in tokens: - tokens.remove(x) - if tokens: - tokens = [tokens[-1]+';'] + tokens[:-1] - self.author_sort.setText(u' '.join(tokens)) + authors = string_to_authors(au) + self.author_sort.setText(authors_to_sort_string(authors)) def swap_title_author(self): title = self.title.text() @@ -293,7 +293,7 @@ class MetadataSingleDialog(QDialog, Ui_MetadataSingleDialog): def fetch_metadata(self): isbn = qstring_to_unicode(self.isbn.text()) title = qstring_to_unicode(self.title.text()) - author = qstring_to_unicode(self.authors.text()).split(',')[0] + author = string_to_authors(unicode(self.authors.text()))[0] publisher = qstring_to_unicode(self.publisher.text()) if isbn or title or author or publisher: d = FetchMetadata(self, isbn, title, author, publisher, self.timeout) @@ -302,7 +302,7 @@ class MetadataSingleDialog(QDialog, Ui_MetadataSingleDialog): book = d.selected_book() if book: self.title.setText(book.title) - self.authors.setText(', '.join(book.authors)) + self.authors.setText(authors_to_string(book.authors)) if book.author_sort: self.author_sort.setText(book.author_sort) if book.publisher: self.publisher.setText(book.publisher) if book.isbn: self.isbn.setText(book.isbn) @@ -335,8 +335,9 @@ class MetadataSingleDialog(QDialog, Ui_MetadataSingleDialog): self.sync_formats() title = qstring_to_unicode(self.title.text()) self.db.set_title(self.id, title) - au = qstring_to_unicode(self.authors.text()).split(',') - if au: self.db.set_authors(self.id, au) + au = unicode(self.authors.text()) + if au: + self.db.set_authors(self.id, string_to_authors(au)) aus = qstring_to_unicode(self.author_sort.text()) if aus: self.db.set_author_sort(self.id, aus) diff --git a/src/calibre/gui2/dialogs/metadata_single.ui b/src/calibre/gui2/dialogs/metadata_single.ui index 3aeea0ed32..3f82f81039 100644 --- a/src/calibre/gui2/dialogs/metadata_single.ui +++ b/src/calibre/gui2/dialogs/metadata_single.ui @@ -98,7 +98,7 @@ - Change the author(s) of this book. Multiple authors should be separated by a comma + Change the author(s) of this book. Multiple authors should be separated by an &. If the author name contains an &, use && to represent it. @@ -548,8 +548,6 @@ - af_group_box - groupBox_4 diff --git a/src/calibre/gui2/library.py b/src/calibre/gui2/library.py index 520a08e36a..3c1b669d78 100644 --- a/src/calibre/gui2/library.py +++ b/src/calibre/gui2/library.py @@ -381,7 +381,7 @@ class BooksModel(QAbstractTableModel): elif col == 1: au = self.db.authors(row) if au: - au = au.split(',') + au = [a.strip().replace('|', ',') for a in au.split(',')] return QVariant("\n".join(au)) elif col == 2: size = self.db.max_size(row) diff --git a/src/calibre/library/database.py b/src/calibre/library/database.py index 84ae9ba8e9..4b2b515808 100644 --- a/src/calibre/library/database.py +++ b/src/calibre/library/database.py @@ -910,7 +910,10 @@ ALTER TABLE books ADD COLUMN isbn TEXT DEFAULT "" COLLATE NOCASE; return _('Unknown') def authors(self, index, index_is_id=False): - ''' Authors as a comma separated list or None''' + ''' + Authors as a comma separated list or None. + In the comma separated list, commas in author names are replaced by | symbols + ''' if not index_is_id: return self.data[index][2] try: @@ -1361,7 +1364,7 @@ ALTER TABLE books ADD COLUMN isbn TEXT DEFAULT "" COLLATE NOCASE; Convenience method to return metadata as a L{MetaInformation} object. ''' aum = self.authors(idx, index_is_id=index_is_id) - if aum: aum = aum.split(',') + if aum: aum = [a.strip().replace('|', ',') for a in aum.split(',')] mi = MetaInformation(self.title(idx, index_is_id=index_is_id), aum) mi.author_sort = self.author_sort(idx, index_is_id=index_is_id) mi.comments = self.comments(idx, index_is_id=index_is_id) diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index b8ad998b3a..55966dcde5 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -16,6 +16,7 @@ from PyQt4.QtGui import QApplication, QPixmap, QImage __app = None from calibre.library.database import LibraryDatabase +from calibre.ebooks.metadata import string_to_authors from calibre.constants import preferred_encoding copyfile = os.link if hasattr(os, 'link') else shutil.copyfile @@ -732,7 +733,7 @@ class LibraryDatabase2(LibraryDatabase): self.data.set(row, col, val) if column == 'authors': - val = val.split('&,') + val = string_to_authors(val) self.set_authors(id, val, notify=False) elif column == 'title': self.set_title(id, val, notify=False) @@ -742,6 +743,7 @@ class LibraryDatabase2(LibraryDatabase): self.set_rating(id, val) elif column == 'tags': self.set_tags(id, val.split(','), append=False, notify=False) + self.data.refresh_ids(self.conn, [id]) self.set_path(id, True) self.notify('metadata', [id]) @@ -783,7 +785,7 @@ class LibraryDatabase2(LibraryDatabase): for a in authors: if not a: continue - a = a.strip() + a = a.strip().replace(',', '|') if not isinstance(a, unicode): a = a.decode(preferred_encoding, 'replace') author = self.conn.execute('SELECT id from authors WHERE name=?', (a,)).fetchone()