Build enough of custom_column metadata support to implement reserved column names.

Fix search to not go pink when the timer expires, and to not be always pink for devices.
This commit is contained in:
Charles Haley 2010-05-21 19:19:51 +01:00
parent 44fb01dc3f
commit beb817b5cb
6 changed files with 104 additions and 28 deletions

View File

@ -0,0 +1,90 @@
'''
Created on 21 May 2010
@author: charles
'''
from calibre.constants import filesystem_encoding, preferred_encoding
from calibre import isbytestring
import json
class MetadataSerializer(object):
SERIALIZED_ATTRS = [
'lpath', 'title', 'authors', 'mime', 'size', 'tags', 'author_sort',
'title_sort', 'comments', 'category', 'publisher', 'series',
'series_index', 'rating', 'isbn', 'language', 'application_id',
'book_producer', 'lccn', 'lcc', 'ddc', 'rights', 'publication_type',
'uuid',
]
def to_json(self):
json = {}
for attr in self.SERIALIZED_ATTRS:
val = getattr(self, attr)
if isbytestring(val):
enc = filesystem_encoding if attr == 'lpath' else preferred_encoding
val = val.decode(enc, 'replace')
elif isinstance(val, (list, tuple)):
val = [x.decode(preferred_encoding, 'replace') if
isbytestring(x) else x for x in val]
json[attr] = val
return json
def read_json(self, cache_file):
with open(cache_file, 'rb') as f:
js = json.load(f, encoding='utf-8')
return js
def write_json(self, js, cache_file):
with open(cache_file, 'wb') as f:
json.dump(js, f, indent=2, encoding='utf-8')
def string_to_value(self, string, col_metadata, column_label=None):
'''
if column_label is none, col_metadata must be a dict containing custom
column metadata for one column. If column_label is not none, then
col_metadata must be a dict of custom column metadata, with column
labels as keys. Metadata for standard columns is always assumed to be in
the col_metadata dict. If column_label is not standard and is not in
col_metadata, check if it matches a custom column. If so, use that
column metadata. See get_column_metadata below.
'''
pass
def value_to_display(self, value, col_metadata, column_label=None):
pass
def value_to_string (self, value, col_metadata, column_label=None):
pass
def get_column_metadata(self, column_label = None, from_book=None):
'''
if column_label is None, then from_book must not be None. Returns the
complete set of custom column metadata for that book.
If column_label is not None, return the column metadata for the given
column. This works even if the label is for a built-in column. If
from_book is None, then column_label must be a current custom column
label or a standard label. If from_book is not None, then the column
metadata from that metadata set is returned if it exists, otherwise the
standard metadata for that column is returned. If neither is found,
return {}
'''
pass
def get_custom_column_labels(self, book):
'''
returns a list of custom column attributes in the book metadata.
'''
pass
def get_standard_column_labels(self):
'''
returns a list of standard attributes that should be in any book's
metadata
'''
pass
metadata_serializer = MetadataSerializer()

View File

@ -9,20 +9,14 @@ import os, re, time, sys
from calibre.ebooks.metadata import MetaInformation from calibre.ebooks.metadata import MetaInformation
from calibre.devices.mime import mime_type_ext from calibre.devices.mime import mime_type_ext
from calibre.devices.interface import BookList as _BookList from calibre.devices.interface import BookList as _BookList
from calibre.constants import filesystem_encoding, preferred_encoding from calibre.devices.metadata_serializer import MetadataSerializer
from calibre.constants import preferred_encoding
from calibre import isbytestring from calibre import isbytestring
class Book(MetaInformation): class Book(MetaInformation, MetadataSerializer):
BOOK_ATTRS = ['lpath', 'size', 'mime', 'device_collections'] BOOK_ATTRS = ['lpath', 'size', 'mime', 'device_collections']
JSON_ATTRS = [
'lpath', 'title', 'authors', 'mime', 'size', 'tags', 'author_sort',
'title_sort', 'comments', 'category', 'publisher', 'series',
'series_index', 'rating', 'isbn', 'language', 'application_id',
'book_producer', 'lccn', 'lcc', 'ddc', 'rights', 'publication_type',
'uuid',
]
def __init__(self, prefix, lpath, size=None, other=None): def __init__(self, prefix, lpath, size=None, other=None):
from calibre.ebooks.metadata.meta import path_to_ext from calibre.ebooks.metadata.meta import path_to_ext
@ -82,19 +76,6 @@ class Book(MetaInformation):
val = getattr(other, attr, None) val = getattr(other, attr, None)
setattr(self, attr, val) setattr(self, attr, val)
def to_json(self):
json = {}
for attr in self.JSON_ATTRS:
val = getattr(self, attr)
if isbytestring(val):
enc = filesystem_encoding if attr == 'lpath' else preferred_encoding
val = val.decode(enc, 'replace')
elif isinstance(val, (list, tuple)):
val = [x.decode(preferred_encoding, 'replace') if
isbytestring(x) else x for x in val]
json[attr] = val
return json
class BookList(_BookList): class BookList(_BookList):
def supports_collections(self): def supports_collections(self):

View File

@ -17,6 +17,7 @@ from itertools import cycle
from calibre import prints, isbytestring from calibre import prints, isbytestring
from calibre.constants import filesystem_encoding from calibre.constants import filesystem_encoding
from calibre.devices.metadata_serializer import metadata_serializer as ms
from calibre.devices.usbms.cli import CLI from calibre.devices.usbms.cli import CLI
from calibre.devices.usbms.device import Device from calibre.devices.usbms.device import Device
from calibre.devices.usbms.books import BookList, Book from calibre.devices.usbms.books import BookList, Book
@ -260,8 +261,7 @@ class USBMS(CLI, Device):
os.makedirs(self.normalize_path(prefix)) os.makedirs(self.normalize_path(prefix))
js = [item.to_json() for item in booklists[listid] if js = [item.to_json() for item in booklists[listid] if
hasattr(item, 'to_json')] hasattr(item, 'to_json')]
with open(self.normalize_path(os.path.join(prefix, self.METADATA_CACHE)), 'wb') as f: ms.write_json(js, self.normalize_path(os.path.join(prefix, self.METADATA_CACHE)))
json.dump(js, f, indent=2, encoding='utf-8')
write_prefix(self._main_prefix, 0) write_prefix(self._main_prefix, 0)
write_prefix(self._card_a_prefix, 1) write_prefix(self._card_a_prefix, 1)
write_prefix(self._card_b_prefix, 2) write_prefix(self._card_b_prefix, 2)
@ -293,8 +293,7 @@ class USBMS(CLI, Device):
cache_file = cls.normalize_path(os.path.join(prefix, name)) cache_file = cls.normalize_path(os.path.join(prefix, name))
if os.access(cache_file, os.R_OK): if os.access(cache_file, os.R_OK):
try: try:
with open(cache_file, 'rb') as f: js = ms.read_json(cache_file)
js = json.load(f, encoding='utf-8')
for item in js: for item in js:
book = cls.book_class(prefix, item.get('lpath', None)) book = cls.book_class(prefix, item.get('lpath', None))
for key in item.keys(): for key in item.keys():

View File

@ -8,6 +8,7 @@ from functools import partial
from PyQt4.QtCore import SIGNAL from PyQt4.QtCore import SIGNAL
from PyQt4.Qt import QDialog, Qt, QListWidgetItem, QVariant from PyQt4.Qt import QDialog, Qt, QListWidgetItem, QVariant
from calibre.devices.metadata_serializer import metadata_serializer
from calibre.gui2.dialogs.config.create_custom_column_ui import Ui_QCreateCustomColumn from calibre.gui2.dialogs.config.create_custom_column_ui import Ui_QCreateCustomColumn
from calibre.gui2 import error_dialog from calibre.gui2 import error_dialog
@ -102,6 +103,8 @@ class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn):
return self.simple_error('', _('No lookup name was provided')) return self.simple_error('', _('No lookup name was provided'))
if not col_heading: if not col_heading:
return self.simple_error('', _('No column heading was provided')) return self.simple_error('', _('No column heading was provided'))
if col in metadata_serializer.SERIALIZED_ATTRS:
return self.simple_error('', _('The lookup name %s is reserved and cannot be used')%col)
bad_col = False bad_col = False
if col in self.parent.custcols: if col in self.parent.custcols:
if not self.editing_col or self.parent.custcols[col]['num'] != self.orig_column_number: if not self.editing_col or self.parent.custcols[col]['num'] != self.orig_column_number:

View File

@ -883,7 +883,7 @@ class DeviceBooksModel(BooksModel): # {{{
self.reset() self.reset()
self.last_search = text self.last_search = text
if self.last_search: if self.last_search:
self.searched.emit(False) self.searched.emit(True)
def sort(self, col, order, reset=True): def sort(self, col, order, reset=True):

View File

@ -136,12 +136,12 @@ class SearchBox2(QComboBox):
def text_edited_slot(self, text): def text_edited_slot(self, text):
if self.as_you_type: if self.as_you_type:
text = unicode(text) text = unicode(text)
self.prev_text = text
self.timer = self.startTimer(self.__class__.INTERVAL) self.timer = self.startTimer(self.__class__.INTERVAL)
def timerEvent(self, event): def timerEvent(self, event):
self.killTimer(event.timerId()) self.killTimer(event.timerId())
if event.timerId() == self.timer: if event.timerId() == self.timer:
self.timer = None
self.do_search() self.do_search()
@property @property
@ -190,6 +190,9 @@ class SearchBox2(QComboBox):
def set_search_string(self, txt): def set_search_string(self, txt):
self.normalize_state() self.normalize_state()
self.setEditText(txt) self.setEditText(txt)
if self.timer is not None: # Turn off any timers that got started in setEditText
self.killTimer(self.timer)
self.timer = None
self.search.emit(txt, False) self.search.emit(txt, False)
self.line_edit.end(False) self.line_edit.end(False)
self.initial_state = False self.initial_state = False