mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
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:
parent
44fb01dc3f
commit
beb817b5cb
90
src/calibre/devices/metadata_serializer.py
Normal file
90
src/calibre/devices/metadata_serializer.py
Normal 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()
|
||||||
|
|
@ -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):
|
||||||
|
@ -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():
|
||||||
|
@ -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:
|
||||||
|
@ -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):
|
||||||
|
@ -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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user