mirror of
https://github.com/kovidgoyal/calibre.git
synced 2026-05-27 09:12:34 -04:00
A new template function reading_progress() to get read progress for the specified book
This commit is contained in:
+15
-8
@@ -3300,14 +3300,21 @@ class Cache:
|
||||
report_progress(i+1, len(book_ids), mi)
|
||||
|
||||
@read_api
|
||||
def get_last_read_positions(self, book_id, fmt, user):
|
||||
fmt = fmt.upper()
|
||||
ans = []
|
||||
for device, cfi, epoch, pos_frac in self.backend.execute(
|
||||
'SELECT device,cfi,epoch,pos_frac FROM last_read_positions WHERE book=? AND format=? AND user=?',
|
||||
(book_id, fmt, user)):
|
||||
ans.append({'device':device, 'cfi': cfi, 'epoch':epoch, 'pos_frac':pos_frac})
|
||||
return ans
|
||||
def get_last_read_positions(self, book_id, fmt='', user='', order_by='', limit=0):
|
||||
q = 'SELECT device,cfi,epoch,pos_frac,format,user FROM last_read_positions WHERE book=?'
|
||||
bindings = [book_id]
|
||||
if fmt:
|
||||
q += ' AND format=?'
|
||||
bindings.append(fmt.upper())
|
||||
if user:
|
||||
q += ' AND user=?'
|
||||
bindings.append(user)
|
||||
if order_by in ('pos_frac', 'epoch'):
|
||||
q += f' ORDER BY {order_by} DESC'
|
||||
if limit:
|
||||
q += f' LIMIT {int(limit)}'
|
||||
return tuple({'device':device, 'cfi': cfi, 'epoch':epoch, 'pos_frac':pos_frac, 'format': format, 'user': user}
|
||||
for device, cfi, epoch, pos_frac, format, user in self.backend.execute(q, tuple(bindings)))
|
||||
|
||||
@write_api
|
||||
def set_last_read_position(self, book_id, fmt, user='_', device='_', cfi=None, epoch=None, pos_frac=0):
|
||||
|
||||
@@ -813,7 +813,8 @@ class ReadingTest(BaseTest):
|
||||
epoch = time()
|
||||
cache.set_last_read_position(1, 'EPUB', 'user', 'device', 'cFi', epoch, 0.3)
|
||||
self.assertFalse(cache.get_last_read_positions(1, 'x', 'u'))
|
||||
self.assertEqual(cache.get_last_read_positions(1, 'ePuB', 'user'), [{'epoch':epoch, 'device':'device', 'cfi':'cFi', 'pos_frac':0.3}])
|
||||
self.assertEqual(cache.get_last_read_positions(1, 'ePuB', 'user'), ({
|
||||
'epoch':epoch, 'device':'device', 'cfi':'cFi', 'pos_frac':0.3, 'format': 'EPUB', 'user': 'user'},))
|
||||
cache.set_last_read_position(1, 'EPUB', 'user', 'device')
|
||||
self.assertFalse(cache.get_last_read_positions(1, 'ePuB', 'user'))
|
||||
# }}}
|
||||
@@ -836,12 +837,22 @@ class ReadingTest(BaseTest):
|
||||
db = self.init_cache(self.library_path)
|
||||
db.create_custom_column('mult', 'CC1', 'composite', True, display={'composite_template': 'b,a,c'})
|
||||
db.create_custom_column('pages', 'Pages', 'int', False)
|
||||
|
||||
# need an empty metadata object to pass to the formatter
|
||||
db = self.init_legacy(self.library_path)
|
||||
db.new_api.set_field('#pages', {1: '11', 2: '22', 3: '33'})
|
||||
mi = db.get_metadata(1)
|
||||
|
||||
db.set_pages(2, 100, format='FMT1')
|
||||
db.set_pages(2, 100, format='FMT2')
|
||||
db.set_last_read_position(2, 'FMT1', pos_frac=0.25, cfi='epubcfi(/2)', epoch=2)
|
||||
db.set_last_read_position(2, 'FMT2', pos_frac=0.35, cfi='epubcfi(/2)', epoch=1)
|
||||
db.close()
|
||||
db = self.init_cache(self.library_path)
|
||||
db.set_field('#pages', {1: '11', 2: '22', 3: '33'})
|
||||
mi = db.get_metadata(2)
|
||||
# test reading_progress
|
||||
def trp(expected, args=''):
|
||||
self.assertEqual(expected, formatter.safe_format(f'{{id:reading_progress({args})}}', {}, 'TEMPLATE ERROR', mi))
|
||||
trp('25 / 100')
|
||||
trp('25%', ',percent')
|
||||
trp('25%', '_,percent')
|
||||
trp('0.35', ',pos_frac,furthest')
|
||||
trp('0.25', ',pos_frac,furthest,fmt1')
|
||||
# test width_from_pages
|
||||
v = formatter.safe_format('{#pages:width_from_pages(2,2,0.5)}', {}, 'TEMPLATE ERROR', mi)
|
||||
self.assertEqual(v, '1.0')
|
||||
|
||||
@@ -3267,6 +3267,80 @@ This function works only in the GUI and the content server.
|
||||
raise ValueError(str(e))
|
||||
|
||||
|
||||
class BuiltinReadingProgress(BuiltinFormatterFunction):
|
||||
name = 'reading_progress'
|
||||
arg_count = -1
|
||||
category = DB_FUNCS
|
||||
def __doc__getter__(self): return translate_ffml(
|
||||
r'''
|
||||
``reading_progress(book_id, [user, output_fmt, which, fmt])`` -- returns the reading progress, in the specified ''
|
||||
output format.[/]The ``user`` parameter defaults to match any user. Use the value ``local`` to match reading progress in
|
||||
the calibre e-book viewer. Use ``_`` to match reading progress for anonymous users of the Content server viewer.
|
||||
Any other value matches the correcsponding username as used in the Content server.
|
||||
|
||||
The ``output_fmt`` paramter controls the format of the text returned by this function. It takes three values:
|
||||
[LIST]
|
||||
[*] ``page_count`` - the default outputs ``pages read / total pages``. If page counting is not enabled outputs percent read.
|
||||
[*] ``percent`` - outputs percent read
|
||||
[*] ``pos_frac`` - outputs a fraction between zero and one.
|
||||
[/LIST]
|
||||
|
||||
The ``which`` parameter controls how the specific reading progress record for the specified ``user`` is selected.
|
||||
There can be more than one record if the no user is specified or if the book has been read in multiple formats.
|
||||
It accepts two values:
|
||||
[LIST]
|
||||
[*] ``most_recent`` - the progress of the most recent reader of the book (the default value)
|
||||
[*] ``furthest`` - the furthest progress of all matching records
|
||||
[/LIST]
|
||||
|
||||
The ``fmt`` parameter controls which book format is used. The default is to return records for all formats, the specific
|
||||
record is then selected by the ``which`` parameter.
|
||||
|
||||
Some examples:
|
||||
[CODE]
|
||||
{id:reading_progress()} -- the reading progress as pages read / total pages
|
||||
for the most recent reading session of this book
|
||||
{id:reading_progress(,percent)} -- same as above, but as a percentage
|
||||
{id:reading_progress(,pos_frac,furthest)} -- same as above, but as a fraction and using the
|
||||
furthest progress on this book.
|
||||
{id:reading_progress(bob,pos_frac,furthest,EPUB)} -- for the user "bob" and the "EPUB format
|
||||
[/CODE]
|
||||
''')
|
||||
|
||||
def evaluate(self, formatter, kwargs, mi, locals, book_id, *args):
|
||||
if len(args) > 4:
|
||||
raise ValueError(_('Incorrect number of arguments for function {0}').format('reading_progress'))
|
||||
book_id = int(book_id)
|
||||
for_user, output_fmt, which, fmt = '', 'page_count', 'most_recent', ''
|
||||
match len(args):
|
||||
case 4:
|
||||
for_user, output_fmt, which, fmt = args
|
||||
case 3:
|
||||
for_user, output_fmt, which = args
|
||||
case 2:
|
||||
for_user, output_fmt = args
|
||||
case 1:
|
||||
for_user = args[0]
|
||||
order_by = 'pos_frac' if which in ('furthest', 'farthest') else 'epoch'
|
||||
pos_frac = 0
|
||||
with (db := self.get_database(mi, formatter=formatter).new_api).safe_read_lock:
|
||||
if records := db._get_last_read_positions(book_id, fmt=fmt, user=for_user, order_by=order_by, limit=1):
|
||||
pos_frac = records[0]['pos_frac']
|
||||
fmt = records[0]['format']
|
||||
match output_fmt:
|
||||
case 'percent':
|
||||
return f'{pos_frac:.0%}'
|
||||
case 'page_count':
|
||||
page_count = 0
|
||||
if pages := db._get_pages(book_id):
|
||||
page_count = pages.pages
|
||||
if page_count > 0:
|
||||
return f'{int(pos_frac * page_count)} / {page_count}'
|
||||
return f'{pos_frac:.0%}'
|
||||
case _:
|
||||
return str(pos_frac)
|
||||
|
||||
|
||||
class BuiltinIsDarkMode(BuiltinFormatterFunction):
|
||||
name = 'is_dark_mode'
|
||||
arg_count = 0
|
||||
|
||||
Reference in New Issue
Block a user