Implement merging of annotations in rapydscript

This commit is contained in:
Kovid Goyal 2020-06-30 13:55:56 +05:30
parent 5075fc2d36
commit 631a907773
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
3 changed files with 129 additions and 0 deletions

View File

@ -0,0 +1,93 @@
# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>
from __python__ import bound_methods, hash_literals
from read_book.cfi import create_cfi_cmp, cfi_sort_key
no_cfi = '/99999999'
def bookmark_get_cfi(b):
if b.pos_type is 'epubcfi':
return b.pos[8:-1]
def highlight_get_cfi(hl):
cfi = hl.start_cfi
if cfi:
return cfi[8:-1]
def sort_annot_list(annots, get_cfi_func):
key_map = {no_cfi: cfi_sort_key(no_cfi)}
for annot in annots:
cfi = get_cfi_func(annot)
if cfi and not key_map[cfi]:
key_map[cfi] = cfi_sort_key(cfi)
cfi_cmp = create_cfi_cmp(key_map)
annots.sort(def (a, b):
acfi = get_cfi_func(a) or no_cfi
bcfi = get_cfi_func(b) or no_cfi
return cfi_cmp(acfi, bcfi)
)
def annots_descending_cmp(a, b):
return -1 if a.timestamp > b.timestamp else (1 if a.timestamp < b.timestamp else 0)
def merge_annots_with_identical_field(annots_a, annots_b, field, cfi_getter_func):
title_groups = {}
changed = False
all_annots = annots_a.concat(annots_b)
for a in all_annots:
q = title_groups[a[field]]
if not q:
q = title_groups[a[field]] = v'[]'
q.push(a)
for tg in Object.values(title_groups):
tg.sort(annots_descending_cmp)
seen = {}
ans = v'[]'
for a in all_annots:
title = a[field]
if not seen[title]:
seen[title] = True
candidates = title_groups[title]
if not changed and candidates.length > 1 and candidates[0].timestamp is not candidates[1].timestamp:
changed = True
ans.push(title_groups[title][0])
if ans.length is not annots_a.length or ans.length is not annots_b.length:
changed = True
if changed:
sort_annot_list(ans, cfi_getter_func)
return changed, ans
field_map = {'bookmark': 'title', 'highlight': 'uuid'}
getter_map = {'bookmark': bookmark_get_cfi, 'highlight': highlight_get_cfi}
def merge_annot_lists(a, b, field):
key_field = field_map[field]
return merge_annots_with_identical_field(a, b, key_field, getter_map[field])
def merge_annotation_maps(a, b):
updated = False
ans = {}
for field in field_map:
a_items = a[field] or v'[]'
b_items = b[field] or v'[]'
if not a_items.length:
ans[field] = b_items
continue
if not b_items.length:
ans[field] = a_items
continue
changed, ans[field] = merge_annot_lists(a_items, b_items, field)
if changed:
updated = True
return updated, ans

View File

@ -8,6 +8,7 @@ import initialize # noqa: unused-import
import test_date # noqa: unused-import import test_date # noqa: unused-import
import test_utils # noqa: unused-import import test_utils # noqa: unused-import
import read_book.test_cfi # noqa: unused-import import read_book.test_cfi # noqa: unused-import
import test_annotations # noqa: unused-import
from testing import registered_tests, reset_dom from testing import registered_tests, reset_dom

View File

@ -0,0 +1,35 @@
# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>
from __python__ import bound_methods, hash_literals
from read_book.annotations import merge_annot_lists
from testing import assert_equal, test, assert_true
def bm(title, bmid, year=20, first_cfi_number=1):
return {
'title': title, 'id': bmid, 'timestamp': str.format('20{}-06-29T03:21:48.895323+00:00', year),
'pos_type': 'epubcfi', 'pos': str.format('epubcfi(/{}/4/8)', first_cfi_number)
}
def hl(uuid, hlid, year=20, first_cfi_number=1):
return {
'uuid': uuid, 'id': hlid, 'timestamp': str.format('20{}-06-29T03:21:48.895323+00:00', year),
'start_cfi': str.format('epubcfi(/{}/4/8)', first_cfi_number)
}
@test
def merging_annotations():
for atype in 'bookmark highlight'.split(' '):
f = bm if atype == 'bookmark' else hl
a = [f('one', 1, 20, 2), f('two', 2, 20, 4), f('a', 3, 20, 16),]
b = [f('one', 10, 30, 2), f('two', 20, 10, 4), f('b', 30, 20, 8),]
changed, c = merge_annot_lists(a, b, atype)
assert_true(changed)
def get_id(x):
return x.id
assert_equal(list(map(get_id, c)), [10, 2, 30, 3])