Make IndexingProgress useable outside the GUI modules

This commit is contained in:
Kovid Goyal 2022-08-06 13:13:23 +05:30
parent 86d180be4c
commit 81bf05e10b
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
3 changed files with 76 additions and 76 deletions

View File

@ -4,7 +4,7 @@
__license__ = 'GPL v3' __license__ = 'GPL v3'
__copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>' __copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
import os, errno, sys, re import os, errno, sys, re, time
from locale import localeconv from locale import localeconv
from collections import OrderedDict, namedtuple from collections import OrderedDict, namedtuple
from polyglot.builtins import iteritems, itervalues, string_or_bytes from polyglot.builtins import iteritems, itervalues, string_or_bytes
@ -426,3 +426,76 @@ def type_safe_sort_key_function(keyfunc=None):
return ans return ans
return key 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')

View File

@ -3,8 +3,6 @@
# License: GPL v3 Copyright: 2022, Kovid Goyal <kovid at kovidgoyal.net> # License: GPL v3 Copyright: 2022, Kovid Goyal <kovid at kovidgoyal.net>
import os import os
import time
from collections import deque
from qt.core import ( from qt.core import (
QCheckBox, QDialog, QDialogButtonBox, QHBoxLayout, QIcon, QLabel, QPushButton, QCheckBox, QDialog, QDialogButtonBox, QHBoxLayout, QIcon, QLabel, QPushButton,
QRadioButton, QVBoxLayout, QWidget, pyqtSignal QRadioButton, QVBoxLayout, QWidget, pyqtSignal
@ -13,60 +11,12 @@ from qt.core import (
from calibre import detect_ncpus from calibre import detect_ncpus
from calibre.db.cache import Cache from calibre.db.cache import Cache
from calibre.db.listeners import EventType from calibre.db.listeners import EventType
from calibre.db.utils import IndexingProgress
from calibre.gui2.dialogs.confirm_delete import confirm from calibre.gui2.dialogs.confirm_delete import confirm
from calibre.gui2.fts.utils import get_db from calibre.gui2.fts.utils import get_db
from calibre.gui2.jobs import human_readable_interval
from calibre.gui2.ui import get_gui 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): class ScanProgress(QWidget):
switch_to_search_panel = pyqtSignal() switch_to_search_panel = pyqtSignal()

View File

@ -30,6 +30,7 @@ from calibre.gui2.progress_indicator import ProgressIndicator
from calibre.gui2.threaded_jobs import ThreadedJobServer, ThreadedJob from calibre.gui2.threaded_jobs import ThreadedJobServer, ThreadedJob
from calibre.gui2.widgets2 import Dialog from calibre.gui2.widgets2 import Dialog
from calibre.utils.search_query_parser import SearchQueryParser, ParseException from calibre.utils.search_query_parser import SearchQueryParser, ParseException
from calibre.db.utils import human_readable_interval
from calibre.utils.icu import lower from calibre.utils.icu import lower
from polyglot.queue import Empty, Queue from polyglot.queue import Empty, Queue
@ -40,30 +41,6 @@ class AdaptSQP(SearchQueryParser):
pass 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): # {{{ class JobManager(QAbstractTableModel, AdaptSQP): # {{{
job_added = pyqtSignal(int) job_added = pyqtSignal(int)