diff --git a/src/calibre/db/utils.py b/src/calibre/db/utils.py index af3c4192a9..f3a2076aaf 100644 --- a/src/calibre/db/utils.py +++ b/src/calibre/db/utils.py @@ -4,7 +4,7 @@ __license__ = 'GPL v3' __copyright__ = '2013, Kovid Goyal ' -import os, errno, sys, re +import os, errno, sys, re, time from locale import localeconv from collections import OrderedDict, namedtuple from polyglot.builtins import iteritems, itervalues, string_or_bytes @@ -426,3 +426,76 @@ def type_safe_sort_key_function(keyfunc=None): return ans return key + + +def human_readable_interval(secs): + secs = int(secs) + days = secs // 86400 + hours = secs // 3600 % 24 + minutes = secs // 60 % 60 + seconds = secs % 60 + parts = [] + if days > 0: + parts.append(_('{} days').format(days)) + if hours > 0: + parts.append(_('{} hours').format(hours)) + elif hours > 0: + parts.append(_('{} hours').format(hours)) + if minutes > 0: + parts.append(_('{} minutes').format(minutes)) + elif minutes > 0: + parts.append(_('{} minutes').format(minutes)) + if secs > 0: + parts.append(_('{} seconds').format(seconds)) + elif secs > 0: + parts.append(_('{} seconds').format(seconds)) + return ' '.join(parts) + + +class IndexingProgress: + + def __init__(self): + self.left = self.total = -1 + self.clear_rate_information() + + def __repr__(self): + return f'IndexingProgress(left={self.left}, total={self.total})' + + def clear_rate_information(self): + from collections import deque + self.done_events = deque() + + def update(self, left, total): + changed = (left, total) != (self.left, self.total) + if changed: + done_num = self.left - left + if done_num > 0 and self.left > -1: # initial event will have self.left == -1 + self.done_events.append((done_num, time.monotonic())) + if len(self.done_events) > 50: + self.done_events.popleft() + self.left, self.total = left, total + return changed + + @property + def complete(self): + return not self.left or not self.total + + @property + def almost_complete(self): + return self.complete or (self.left / self.total) < 0.1 + + @property + def time_left(self): + if self.left < 2: + return _('almost done') + if len(self.done_events) < 5: + return _('calculating time left') + try: + start_time = self.done_events[0][1] + end_time = self.done_events[-1][1] + num_done = sum(x[0] for x in self.done_events) - self.done_events[0][0] + rate = num_done / max(0.1, end_time - start_time) + seconds_left = self.left / rate + return _('~{} left').format(human_readable_interval(seconds_left)) + except Exception: + return _('calculating time left') diff --git a/src/calibre/gui2/fts/scan.py b/src/calibre/gui2/fts/scan.py index 0c2024f3a0..30f16ea721 100644 --- a/src/calibre/gui2/fts/scan.py +++ b/src/calibre/gui2/fts/scan.py @@ -3,8 +3,6 @@ # License: GPL v3 Copyright: 2022, Kovid Goyal import os -import time -from collections import deque from qt.core import ( QCheckBox, QDialog, QDialogButtonBox, QHBoxLayout, QIcon, QLabel, QPushButton, QRadioButton, QVBoxLayout, QWidget, pyqtSignal @@ -13,60 +11,12 @@ from qt.core import ( from calibre import detect_ncpus from calibre.db.cache import Cache from calibre.db.listeners import EventType +from calibre.db.utils import IndexingProgress from calibre.gui2.dialogs.confirm_delete import confirm from calibre.gui2.fts.utils import get_db -from calibre.gui2.jobs import human_readable_interval from calibre.gui2.ui import get_gui -class IndexingProgress: - - def __init__(self): - self.left = self.total = -1 - self.clear_rate_information() - - def __repr__(self): - return f'IndexingProgress(left={self.left}, total={self.total})' - - def clear_rate_information(self): - self.done_events = deque() - - def update(self, left, total): - changed = (left, total) != (self.left, self.total) - if changed: - done_num = self.left - left - if done_num > 0 and self.left > -1: # initial event will have self.left == -1 - self.done_events.append((done_num, time.monotonic())) - if len(self.done_events) > 50: - self.done_events.popleft() - self.left, self.total = left, total - return changed - - @property - def complete(self): - return not self.left or not self.total - - @property - def almost_complete(self): - return self.complete or (self.left / self.total) < 0.1 - - @property - def time_left(self): - if self.left < 2: - return _('almost done') - if len(self.done_events) < 5: - return _('calculating time left') - try: - start_time = self.done_events[0][1] - end_time = self.done_events[-1][1] - num_done = sum(x[0] for x in self.done_events) - self.done_events[0][0] - rate = num_done / max(0.1, end_time - start_time) - seconds_left = self.left / rate - return _('~{} left').format(human_readable_interval(seconds_left)) - except Exception: - return _('calculating time left') - - class ScanProgress(QWidget): switch_to_search_panel = pyqtSignal() diff --git a/src/calibre/gui2/jobs.py b/src/calibre/gui2/jobs.py index 43c94c4daf..b82e1c898a 100644 --- a/src/calibre/gui2/jobs.py +++ b/src/calibre/gui2/jobs.py @@ -30,6 +30,7 @@ from calibre.gui2.progress_indicator import ProgressIndicator from calibre.gui2.threaded_jobs import ThreadedJobServer, ThreadedJob from calibre.gui2.widgets2 import Dialog from calibre.utils.search_query_parser import SearchQueryParser, ParseException +from calibre.db.utils import human_readable_interval from calibre.utils.icu import lower from polyglot.queue import Empty, Queue @@ -40,30 +41,6 @@ class AdaptSQP(SearchQueryParser): pass -def human_readable_interval(secs): - secs = int(secs) - days = secs // 86400 - hours = secs // 3600 % 24 - minutes = secs // 60 % 60 - seconds = secs % 60 - parts = [] - if days > 0: - parts.append(_('{} days').format(days)) - if hours > 0: - parts.append(_('{} hours').format(hours)) - elif hours > 0: - parts.append(_('{} hours').format(hours)) - if minutes > 0: - parts.append(_('{} minutes').format(minutes)) - elif minutes > 0: - parts.append(_('{} minutes').format(minutes)) - if secs > 0: - parts.append(_('{} seconds').format(seconds)) - elif secs > 0: - parts.append(_('{} seconds').format(seconds)) - return ' '.join(parts) - - class JobManager(QAbstractTableModel, AdaptSQP): # {{{ job_added = pyqtSignal(int)