Implement #1068 (Multiple authors vs. Standards)

This commit is contained in:
Kovid Goyal 2008-10-05 20:00:02 -07:00
parent 0d8425469e
commit 206da41869
12 changed files with 74 additions and 36 deletions

View File

@ -15,6 +15,24 @@ from calibre.constants import __version__ as VERSION
from calibre import relpath from calibre import relpath
from calibre.utils.config import OptionParser 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): def get_parser(extension):
''' Return an option parser with the basic metadata options already setup''' ''' 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.') parser = OptionParser(usage='%prog [options] myfile.'+extension+'\n\nRead and write metadata from an ebook file.')

View File

@ -7,8 +7,10 @@ __docformat__ = 'restructuredtext en'
The GUI for conversion to EPUB. The GUI for conversion to EPUB.
''' '''
import os import os
from PyQt4.Qt import QDialog, QSpinBox, QDoubleSpinBox, QComboBox, QLineEdit, \ from PyQt4.Qt import QDialog, QSpinBox, QDoubleSpinBox, QComboBox, QLineEdit, \
QTextEdit, QCheckBox, Qt, QPixmap, QIcon, QListWidgetItem, SIGNAL QTextEdit, QCheckBox, Qt, QPixmap, QIcon, QListWidgetItem, SIGNAL
from lxml.etree import XPath
from calibre.gui2.dialogs.choose_format import ChooseFormatDialog from calibre.gui2.dialogs.choose_format import ChooseFormatDialog
from calibre.gui2.dialogs.epub_ui import Ui_Dialog 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.ebooks.metadata import MetaInformation
from calibre.ptempfile import PersistentTemporaryFile from calibre.ptempfile import PersistentTemporaryFile
from calibre.ebooks.metadata.opf import OPFCreator 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): class Config(QDialog, Ui_Dialog):
@ -128,7 +131,10 @@ class Config(QDialog, Ui_Dialog):
if self.row is not None: if self.row is not None:
mi = self.db.get_metadata(self.id, index_is_id=True) mi = self.db.get_metadata(self.id, index_is_id=True)
self.title.setText(mi.title) 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.publisher.setText(mi.publisher if mi.publisher else '')
self.author_sort.setText(mi.author_sort if mi.author_sort else '') self.author_sort.setText(mi.author_sort if mi.author_sort else '')
self.tags.setText(', '.join(mi.tags if mi.tags 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() title = unicode(self.title.text()).strip()
if not title: if not title:
title = _('Unknown') title = _('Unknown')
authors = [i.strip() for i in unicode(self.author.text()).strip().split(',')] authors = unicode(self.author.text()).strip()
if not authors: authors = string_to_authors(authors) if authors else [_('Unknown')]
authors = [_('Unknown')]
return title, authors return title, authors
def get_metadata(self): def get_metadata(self):

View File

@ -77,7 +77,7 @@
<item> <item>
<widget class="QStackedWidget" name="stack" > <widget class="QStackedWidget" name="stack" >
<property name="currentIndex" > <property name="currentIndex" >
<number>3</number> <number>0</number>
</property> </property>
<widget class="QWidget" name="metadata_page" > <widget class="QWidget" name="metadata_page" >
<layout class="QGridLayout" name="gridLayout_4" > <layout class="QGridLayout" name="gridLayout_4" >
@ -221,7 +221,7 @@
</sizepolicy> </sizepolicy>
</property> </property>
<property name="toolTip" > <property name="toolTip" >
<string>Change the author(s) of this book. Multiple authors should be separated by a comma</string> <string>Change the author(s) of this book. Multiple authors should be separated by an &amp;. If the author name contains an &amp;, use &amp;&amp; to represent it.</string>
</property> </property>
</widget> </widget>
</item> </item>

View File

@ -14,6 +14,7 @@ from calibre.gui2.widgets import FontFamilyModel
from calibre.ebooks.lrf import option_parser from calibre.ebooks.lrf import option_parser
from calibre.ptempfile import PersistentTemporaryFile from calibre.ptempfile import PersistentTemporaryFile
from calibre.constants import __appname__ from calibre.constants import __appname__
from calibre.ebooks.metadata import authors_to_string, string_to_authors, authors_to_sort_string
font_family_model = None font_family_model = None
@ -199,7 +200,11 @@ class LRFSingleDialog(QDialog, Ui_LRFSingleDialog):
self.id = self.db.id(row) self.id = self.db.id(row)
self.gui_title.setText(db.title(row)) self.gui_title.setText(db.title(row))
au = self.db.authors(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) aus = self.db.author_sort(row)
self.gui_author_sort.setText(aus if aus else '') self.gui_author_sort.setText(aus if aus else '')
pub = self.db.publisher(row) pub = self.db.publisher(row)
@ -350,14 +355,16 @@ class LRFSingleDialog(QDialog, Ui_LRFSingleDialog):
def write_metadata(self): def write_metadata(self):
title = qstring_to_unicode(self.gui_title.text()) title = qstring_to_unicode(self.gui_title.text())
self.db.set_title(self.id, title) self.db.set_title(self.id, title)
au = qstring_to_unicode(self.gui_author.text()).split(',') au = unicode(self.gui_author.text())
if au: self.db.set_authors(self.id, au) if au:
self.db.set_authors(self.id, string_to_authors(au))
aus = qstring_to_unicode(self.gui_author_sort.text()) aus = qstring_to_unicode(self.gui_author_sort.text())
if not aus: if not aus:
t = self.db.authors(self.id, index_is_id=True) t = self.db.authors(self.id, index_is_id=True)
if not t: if not t:
t = 'Unknown' t = _('Unknown')
aus = t.split(',')[0].strip() 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_author_sort(self.id, aus)
self.db.set_publisher(self.id, qstring_to_unicode(self.gui_publisher.text())) 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(',')) self.db.set_tags(self.id, qstring_to_unicode(self.tags.text()).split(','))

View File

@ -115,7 +115,7 @@
<item row="0" column="0" > <item row="0" column="0" >
<widget class="QStackedWidget" name="stack" > <widget class="QStackedWidget" name="stack" >
<property name="currentIndex" > <property name="currentIndex" >
<number>3</number> <number>0</number>
</property> </property>
<widget class="QWidget" name="metadata_page" > <widget class="QWidget" name="metadata_page" >
<layout class="QHBoxLayout" > <layout class="QHBoxLayout" >
@ -255,7 +255,7 @@
</sizepolicy> </sizepolicy>
</property> </property>
<property name="toolTip" > <property name="toolTip" >
<string>Change the author(s) of this book. Multiple authors should be separated by a comma</string> <string>Change the author(s) of this book. Multiple authors should be separated by an &amp;. If the author name contains an &amp;, use &amp;&amp; to represent it.</string>
</property> </property>
</widget> </widget>
</item> </item>

View File

@ -9,6 +9,7 @@ from PyQt4.QtGui import QDialog
from calibre.gui2 import qstring_to_unicode from calibre.gui2 import qstring_to_unicode
from calibre.gui2.dialogs.metadata_bulk_ui import Ui_MetadataBulkDialog from calibre.gui2.dialogs.metadata_bulk_ui import Ui_MetadataBulkDialog
from calibre.gui2.dialogs.tag_editor import TagEditor from calibre.gui2.dialogs.tag_editor import TagEditor
from calibre.ebooks.metadata import string_to_authors
class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog): class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
def __init__(self, window, rows, db): def __init__(self, window, rows, db):
@ -51,7 +52,7 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
for id in self.ids: for id in self.ids:
au = qstring_to_unicode(self.authors.text()) au = qstring_to_unicode(self.authors.text())
if au: if au:
au = au.split(',') au = string_to_authors(au)
self.db.set_authors(id, au) self.db.set_authors(id, au)
aus = qstring_to_unicode(self.author_sort.text()) aus = qstring_to_unicode(self.author_sort.text())
if aus: if aus:

View File

@ -52,7 +52,7 @@
<item row="0" column="1" colspan="2" > <item row="0" column="1" colspan="2" >
<widget class="QLineEdit" name="authors" > <widget class="QLineEdit" name="authors" >
<property name="toolTip" > <property name="toolTip" >
<string>Change the author(s) of this book. Multiple authors should be separated by a comma</string> <string>Change the author(s) of this book. Multiple authors should be separated by an &amp;. If the author name contains an &amp;, use &amp;&amp; to represent it.</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -84,6 +84,9 @@
<property name="alignment" > <property name="alignment" >
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property> </property>
<property name="buddy" >
<cstring>rating</cstring>
</property>
</widget> </widget>
</item> </item>
<item row="2" column="1" colspan="2" > <item row="2" column="1" colspan="2" >

View File

@ -17,6 +17,7 @@ from calibre.gui2.dialogs.fetch_metadata import FetchMetadata
from calibre.gui2.dialogs.tag_editor import TagEditor from calibre.gui2.dialogs.tag_editor import TagEditor
from calibre.gui2.dialogs.password import PasswordDialog from calibre.gui2.dialogs.password import PasswordDialog
from calibre.ebooks import BOOK_EXTENSIONS 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.ebooks.metadata.library_thing import login, cover_from_isbn, LibraryThingError
from calibre import islinux from calibre import islinux
from calibre.utils.config import prefs from calibre.utils.config import prefs
@ -155,7 +156,11 @@ class MetadataSingleDialog(QDialog, Ui_MetadataSingleDialog):
isbn = '' isbn = ''
self.isbn.setText(isbn) self.isbn.setText(isbn)
au = self.db.authors(row) 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) aus = self.db.author_sort(row)
self.author_sort.setText(aus if aus else '') self.author_sort.setText(aus if aus else '')
pub = self.db.publisher(row) pub = self.db.publisher(row)
@ -199,13 +204,8 @@ class MetadataSingleDialog(QDialog, Ui_MetadataSingleDialog):
def deduce_author_sort(self): def deduce_author_sort(self):
au = unicode(self.authors.text()) au = unicode(self.authors.text())
tokens = au.split() authors = string_to_authors(au)
for x in (',', ';'): self.author_sort.setText(authors_to_sort_string(authors))
if x in tokens:
tokens.remove(x)
if tokens:
tokens = [tokens[-1]+';'] + tokens[:-1]
self.author_sort.setText(u' '.join(tokens))
def swap_title_author(self): def swap_title_author(self):
title = self.title.text() title = self.title.text()
@ -293,7 +293,7 @@ class MetadataSingleDialog(QDialog, Ui_MetadataSingleDialog):
def fetch_metadata(self): def fetch_metadata(self):
isbn = qstring_to_unicode(self.isbn.text()) isbn = qstring_to_unicode(self.isbn.text())
title = qstring_to_unicode(self.title.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()) publisher = qstring_to_unicode(self.publisher.text())
if isbn or title or author or publisher: if isbn or title or author or publisher:
d = FetchMetadata(self, isbn, title, author, publisher, self.timeout) d = FetchMetadata(self, isbn, title, author, publisher, self.timeout)
@ -302,7 +302,7 @@ class MetadataSingleDialog(QDialog, Ui_MetadataSingleDialog):
book = d.selected_book() book = d.selected_book()
if book: if book:
self.title.setText(book.title) 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.author_sort: self.author_sort.setText(book.author_sort)
if book.publisher: self.publisher.setText(book.publisher) if book.publisher: self.publisher.setText(book.publisher)
if book.isbn: self.isbn.setText(book.isbn) if book.isbn: self.isbn.setText(book.isbn)
@ -335,8 +335,9 @@ class MetadataSingleDialog(QDialog, Ui_MetadataSingleDialog):
self.sync_formats() self.sync_formats()
title = qstring_to_unicode(self.title.text()) title = qstring_to_unicode(self.title.text())
self.db.set_title(self.id, title) self.db.set_title(self.id, title)
au = qstring_to_unicode(self.authors.text()).split(',') au = unicode(self.authors.text())
if au: self.db.set_authors(self.id, au) if au:
self.db.set_authors(self.id, string_to_authors(au))
aus = qstring_to_unicode(self.author_sort.text()) aus = qstring_to_unicode(self.author_sort.text())
if aus: if aus:
self.db.set_author_sort(self.id, aus) self.db.set_author_sort(self.id, aus)

View File

@ -98,7 +98,7 @@
<item row="1" column="1" > <item row="1" column="1" >
<widget class="QLineEdit" name="authors" > <widget class="QLineEdit" name="authors" >
<property name="toolTip" > <property name="toolTip" >
<string>Change the author(s) of this book. Multiple authors should be separated by a comma</string> <string>Change the author(s) of this book. Multiple authors should be separated by an &amp;. If the author name contains an &amp;, use &amp;&amp; to represent it.</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -548,8 +548,6 @@
</widget> </widget>
</item> </item>
</layout> </layout>
<zorder>af_group_box</zorder>
<zorder>groupBox_4</zorder>
</widget> </widget>
</widget> </widget>
</item> </item>

View File

@ -381,7 +381,7 @@ class BooksModel(QAbstractTableModel):
elif col == 1: elif col == 1:
au = self.db.authors(row) au = self.db.authors(row)
if au: if au:
au = au.split(',') au = [a.strip().replace('|', ',') for a in au.split(',')]
return QVariant("\n".join(au)) return QVariant("\n".join(au))
elif col == 2: elif col == 2:
size = self.db.max_size(row) size = self.db.max_size(row)

View File

@ -910,7 +910,10 @@ ALTER TABLE books ADD COLUMN isbn TEXT DEFAULT "" COLLATE NOCASE;
return _('Unknown') return _('Unknown')
def authors(self, index, index_is_id=False): 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: if not index_is_id:
return self.data[index][2] return self.data[index][2]
try: 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. Convenience method to return metadata as a L{MetaInformation} object.
''' '''
aum = self.authors(idx, index_is_id=index_is_id) 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 = 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.author_sort = self.author_sort(idx, index_is_id=index_is_id)
mi.comments = self.comments(idx, index_is_id=index_is_id) mi.comments = self.comments(idx, index_is_id=index_is_id)

View File

@ -16,6 +16,7 @@ from PyQt4.QtGui import QApplication, QPixmap, QImage
__app = None __app = None
from calibre.library.database import LibraryDatabase from calibre.library.database import LibraryDatabase
from calibre.ebooks.metadata import string_to_authors
from calibre.constants import preferred_encoding from calibre.constants import preferred_encoding
copyfile = os.link if hasattr(os, 'link') else shutil.copyfile copyfile = os.link if hasattr(os, 'link') else shutil.copyfile
@ -732,7 +733,7 @@ class LibraryDatabase2(LibraryDatabase):
self.data.set(row, col, val) self.data.set(row, col, val)
if column == 'authors': if column == 'authors':
val = val.split('&,') val = string_to_authors(val)
self.set_authors(id, val, notify=False) self.set_authors(id, val, notify=False)
elif column == 'title': elif column == 'title':
self.set_title(id, val, notify=False) self.set_title(id, val, notify=False)
@ -742,6 +743,7 @@ class LibraryDatabase2(LibraryDatabase):
self.set_rating(id, val) self.set_rating(id, val)
elif column == 'tags': elif column == 'tags':
self.set_tags(id, val.split(','), append=False, notify=False) self.set_tags(id, val.split(','), append=False, notify=False)
self.data.refresh_ids(self.conn, [id])
self.set_path(id, True) self.set_path(id, True)
self.notify('metadata', [id]) self.notify('metadata', [id])
@ -783,7 +785,7 @@ class LibraryDatabase2(LibraryDatabase):
for a in authors: for a in authors:
if not a: if not a:
continue continue
a = a.strip() a = a.strip().replace(',', '|')
if not isinstance(a, unicode): if not isinstance(a, unicode):
a = a.decode(preferred_encoding, 'replace') a = a.decode(preferred_encoding, 'replace')
author = self.conn.execute('SELECT id from authors WHERE name=?', (a,)).fetchone() author = self.conn.execute('SELECT id from authors WHERE name=?', (a,)).fetchone()