mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
When sorting fails first try fallback by sorting bad values as the first value
This is closer to the python 2 behavior
This commit is contained in:
parent
8b3b008c3c
commit
7fe84df779
@ -6,36 +6,45 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2011, Kovid Goyal <kovid@kovidgoyal.net>'
|
__copyright__ = '2011, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
import os, traceback, random, shutil, operator, sys
|
import operator
|
||||||
|
import os
|
||||||
|
import random
|
||||||
|
import shutil
|
||||||
|
import sys
|
||||||
|
import traceback
|
||||||
|
from collections import MutableSet, Set, defaultdict
|
||||||
|
from functools import partial, wraps
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from collections import defaultdict, Set, MutableSet
|
|
||||||
from functools import wraps, partial
|
|
||||||
from polyglot.builtins import iteritems, itervalues, unicode_type, zip, string_or_bytes, cmp
|
|
||||||
from time import time
|
from time import time
|
||||||
|
|
||||||
from calibre import isbytestring, as_unicode
|
from calibre import as_unicode, isbytestring
|
||||||
from calibre.constants import iswindows, preferred_encoding
|
from calibre.constants import iswindows, preferred_encoding
|
||||||
from calibre.customize.ui import run_plugins_on_import, run_plugins_on_postimport, run_plugins_on_postadd
|
from calibre.customize.ui import (
|
||||||
|
run_plugins_on_import, run_plugins_on_postadd, run_plugins_on_postimport
|
||||||
|
)
|
||||||
from calibre.db import SPOOL_SIZE, _get_next_series_num_for_list
|
from calibre.db import SPOOL_SIZE, _get_next_series_num_for_list
|
||||||
from calibre.db.annotations import merge_annotations
|
from calibre.db.annotations import merge_annotations
|
||||||
from calibre.db.categories import get_categories
|
from calibre.db.categories import get_categories
|
||||||
from calibre.db.locking import create_locks, DowngradeLockError, SafeReadLock
|
from calibre.db.errors import NoSuchBook, NoSuchFormat
|
||||||
from calibre.db.errors import NoSuchFormat, NoSuchBook
|
from calibre.db.fields import IDENTITY, InvalidLinkTable, create_field
|
||||||
from calibre.db.fields import create_field, IDENTITY, InvalidLinkTable
|
from calibre.db.lazy import FormatMetadata, FormatsList, ProxyMetadata
|
||||||
|
from calibre.db.locking import DowngradeLockError, SafeReadLock, create_locks
|
||||||
from calibre.db.search import Search
|
from calibre.db.search import Search
|
||||||
from calibre.db.tables import VirtualTable
|
from calibre.db.tables import VirtualTable
|
||||||
|
from calibre.db.utils import type_safe_sort_key_function
|
||||||
from calibre.db.write import get_series_values, uniq
|
from calibre.db.write import get_series_values, uniq
|
||||||
from calibre.db.lazy import FormatMetadata, FormatsList, ProxyMetadata
|
|
||||||
from calibre.ebooks import check_ebook_format
|
from calibre.ebooks import check_ebook_format
|
||||||
from calibre.ebooks.metadata import string_to_authors, author_to_author_sort
|
from calibre.ebooks.metadata import author_to_author_sort, string_to_authors
|
||||||
from calibre.ebooks.metadata.book.base import Metadata
|
from calibre.ebooks.metadata.book.base import Metadata
|
||||||
from calibre.ebooks.metadata.opf2 import metadata_to_opf
|
from calibre.ebooks.metadata.opf2 import metadata_to_opf
|
||||||
from calibre.ptempfile import (base_dir, PersistentTemporaryFile,
|
from calibre.ptempfile import PersistentTemporaryFile, SpooledTemporaryFile, base_dir
|
||||||
SpooledTemporaryFile)
|
|
||||||
from calibre.utils.config import prefs, tweaks
|
from calibre.utils.config import prefs, tweaks
|
||||||
from calibre.utils.date import now as nowf, utcnow, UNDEFINED_DATE
|
from calibre.utils.date import UNDEFINED_DATE, now as nowf, utcnow
|
||||||
from calibre.utils.icu import sort_key
|
from calibre.utils.icu import sort_key
|
||||||
from calibre.utils.localization import canonicalize_lang
|
from calibre.utils.localization import canonicalize_lang
|
||||||
|
from polyglot.builtins import (
|
||||||
|
cmp, iteritems, itervalues, string_or_bytes, unicode_type, zip
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def api(f):
|
def api(f):
|
||||||
@ -975,12 +984,17 @@ class Cache(object):
|
|||||||
fields = uniq(fields, operator.itemgetter(0))
|
fields = uniq(fields, operator.itemgetter(0))
|
||||||
|
|
||||||
if len(fields) == 1:
|
if len(fields) == 1:
|
||||||
|
keyfunc = sort_key_func(fields[0][0])
|
||||||
|
reverse = not fields[0][1]
|
||||||
try:
|
try:
|
||||||
return sorted(ids_to_sort, key=sort_key_func(fields[0][0]),
|
return sorted(ids_to_sort, key=keyfunc, reverse=reverse)
|
||||||
reverse=not fields[0][1])
|
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
print('Failed to sort database on field:', fields[0][0], 'with error:', err, file=sys.stderr)
|
print('Failed to sort database on field:', fields[0][0], 'with error:', err, file=sys.stderr)
|
||||||
return sorted(ids_to_sort, reverse=not fields[0][1])
|
try:
|
||||||
|
return sorted(ids_to_sort, key=type_safe_sort_key_function(keyfunc), reverse=reverse)
|
||||||
|
except Exception as err:
|
||||||
|
print('Failed to type-safe sort database on field:', fields[0][0], 'with error:', err, file=sys.stderr)
|
||||||
|
return sorted(ids_to_sort, reverse=reverse)
|
||||||
sort_key_funcs = tuple(sort_key_func(field) for field, order in fields)
|
sort_key_funcs = tuple(sort_key_func(field) for field, order in fields)
|
||||||
orders = tuple(1 if order else -1 for _, order in fields)
|
orders = tuple(1 if order else -1 for _, order in fields)
|
||||||
Lazy = object() # Lazy load the sort keys for sub-sort fields
|
Lazy = object() # Lazy load the sort keys for sub-sort fields
|
||||||
@ -2204,9 +2218,9 @@ class Cache(object):
|
|||||||
def embed_metadata(self, book_ids, only_fmts=None, report_error=None, report_progress=None):
|
def embed_metadata(self, book_ids, only_fmts=None, report_error=None, report_progress=None):
|
||||||
''' Update metadata in all formats of the specified book_ids to current metadata in the database. '''
|
''' Update metadata in all formats of the specified book_ids to current metadata in the database. '''
|
||||||
field = self.fields['formats']
|
field = self.fields['formats']
|
||||||
from calibre.ebooks.metadata.opf2 import pretty_print
|
|
||||||
from calibre.customize.ui import apply_null_metadata
|
from calibre.customize.ui import apply_null_metadata
|
||||||
from calibre.ebooks.metadata.meta import set_metadata
|
from calibre.ebooks.metadata.meta import set_metadata
|
||||||
|
from calibre.ebooks.metadata.opf2 import pretty_print
|
||||||
if only_fmts:
|
if only_fmts:
|
||||||
only_fmts = {f.lower() for f in only_fmts}
|
only_fmts = {f.lower() for f in only_fmts}
|
||||||
|
|
||||||
@ -2356,8 +2370,8 @@ class Cache(object):
|
|||||||
|
|
||||||
@write_api
|
@write_api
|
||||||
def restore_annotations(self, book_id, annotations):
|
def restore_annotations(self, book_id, annotations):
|
||||||
from calibre.utils.iso8601 import parse_iso8601
|
|
||||||
from calibre.utils.date import EPOCH
|
from calibre.utils.date import EPOCH
|
||||||
|
from calibre.utils.iso8601 import parse_iso8601
|
||||||
umap = defaultdict(list)
|
umap = defaultdict(list)
|
||||||
for adata in annotations:
|
for adata in annotations:
|
||||||
key = adata['user_type'], adata['user'], adata['format']
|
key = adata['user_type'], adata['user'], adata['format']
|
||||||
@ -2373,8 +2387,8 @@ class Cache(object):
|
|||||||
|
|
||||||
@write_api
|
@write_api
|
||||||
def merge_annotations_for_book(self, book_id, fmt, annots_list, user_type='local', user='viewer'):
|
def merge_annotations_for_book(self, book_id, fmt, annots_list, user_type='local', user='viewer'):
|
||||||
from calibre.utils.iso8601 import parse_iso8601
|
|
||||||
from calibre.utils.date import EPOCH
|
from calibre.utils.date import EPOCH
|
||||||
|
from calibre.utils.iso8601 import parse_iso8601
|
||||||
amap = self._annotations_map_for_book(book_id, fmt, user_type=user_type, user=user)
|
amap = self._annotations_map_for_book(book_id, fmt, user_type=user_type, user=user)
|
||||||
merge_annotations(annots_list, amap)
|
merge_annotations(annots_list, amap)
|
||||||
alist = []
|
alist = []
|
||||||
|
@ -405,3 +405,25 @@ def atof(string):
|
|||||||
d = d.decode('utf-8', 'ignore') or '.'
|
d = d.decode('utf-8', 'ignore') or '.'
|
||||||
number_separators = t, d
|
number_separators = t, d
|
||||||
return float(string.replace(number_separators[1], '.').replace(number_separators[0], ''))
|
return float(string.replace(number_separators[1], '.').replace(number_separators[0], ''))
|
||||||
|
|
||||||
|
|
||||||
|
def type_safe_sort_key_function(keyfunc=None):
|
||||||
|
if keyfunc is None:
|
||||||
|
keyfunc = lambda x: x
|
||||||
|
sentinel = object()
|
||||||
|
first_value = sentinel
|
||||||
|
|
||||||
|
def key(x):
|
||||||
|
nonlocal first_value
|
||||||
|
ans = keyfunc(x)
|
||||||
|
if first_value is sentinel:
|
||||||
|
first_value = ans
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
ans < first_value
|
||||||
|
first_value < ans
|
||||||
|
except TypeError:
|
||||||
|
ans = first_value
|
||||||
|
return ans
|
||||||
|
|
||||||
|
return key
|
||||||
|
Loading…
x
Reference in New Issue
Block a user