Skip to content

Commit

Permalink
Closes internetarchive#5080. Adds basic ability to search/filter one'…
Browse files Browse the repository at this point in the history
…s reading log.
  • Loading branch information
scottbarnes committed Oct 4, 2022
1 parent fb6aee6 commit 5bcc6ec
Show file tree
Hide file tree
Showing 6 changed files with 88 additions and 23 deletions.
33 changes: 32 additions & 1 deletion openlibrary/core/bookshelves.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ def get_users_logged_books(
limit: int = 100,
page: int = 1, # Not zero-based counting!
sort: Literal['created asc', 'created desc'] = 'created desc',
q: str = "",
) -> list[dict]:
"""Returns a list of Reading Log database records for books which
the user has logged. Records are described in core/schema.py
Expand All @@ -188,6 +189,9 @@ def get_users_logged_books(
"""
oldb = db.get_db()
page = int(page or 1)
# Filtering by title/author needs a larger limit as as the results are
# used to query Solr to get the title/author to do the actual filtering.
limit = 1000 if q else limit
data = {
'username': username,
'limit': limit,
Expand All @@ -213,7 +217,34 @@ def get_users_logged_books(
# XXX Removing limit, offset, etc from data looks like a bug
# unrelated / not fixing in this PR.
data = {'username': username}
return list(oldb.query(query, vars=data))
logged_books = list(oldb.query(query, vars=data))

# Filtering won't occur unless q >= 3, as limited in mybooks.my_books_view().
if q:
from openlibrary.plugins.worksearch.code import do_search

def filter_key(key: str) -> int:
"""
Get the int() representation of an OL work key.
filter_key("/works/OL123W")
>>> 123
"""
return int("".join(char for char in key if char.isdigit()))

work_ids = ['/works/OL%sW' % i['work_id'] for i in logged_books]
solr_query = {'q': f'{q} key:({" OR ".join(work_ids)})'}
solr_resp = do_search(param=solr_query, page=1, rows=100, sort=None)
solr_work_keys_in_oldb_form = {
filter_key(doc["key"]) for doc in solr_resp.docs
}
filtered_books = [
book
for book in logged_books
if book["work_id"] in solr_work_keys_in_oldb_form
]
return filtered_books
else:
return logged_books

@classmethod
def iterate_users_logged_books(cls, username: str) -> Iterable[dict]:
Expand Down
2 changes: 1 addition & 1 deletion openlibrary/core/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -445,7 +445,7 @@ def edition_count(self):
def get_lists(self, limit=50, offset=0, sort=True):
return self._get_lists(limit=limit, offset=offset, sort=sort)

def get_users_rating(self, username):
def get_users_rating(self, username: str) -> int | None:
if not username:
return None
work_id = extract_numeric_id_from_olid(self.key)
Expand Down
5 changes: 3 additions & 2 deletions openlibrary/core/ratings.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,12 +111,13 @@ def get_all_works_ratings(cls, work_id) -> list:
return list(oldb.query(query, vars={'work_id': int(work_id)}))

@classmethod
def get_users_rating_for_work(cls, username, work_id):
def get_users_rating_for_work(cls, username: str, work_id: str | int) -> int | None:
"""work_id must be convetrible to int."""
oldb = db.get_db()
data = {'username': username, 'work_id': int(work_id)}
query = 'SELECT * from ratings where username=$username AND work_id=$work_id'
results = list(oldb.query(query, vars=data))
rating = results[0].rating if results else None
rating: int | None = results[0].rating if results else None
return rating

@classmethod
Expand Down
48 changes: 35 additions & 13 deletions openlibrary/plugins/upstream/mybooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,13 @@ class my_books_view(delegate.page):
path = r"/people/([^/]+)/books/([a-zA-Z_-]+)"

def GET(self, username, key):
i = web.input(page=1, sort='desc')
return MyBooksTemplate(username, key).render(page=i.page, sort=i.sort)
i = web.input(page=1, sort='desc', q=None)
# Limit reading log filtering to queries of 3+ characters because filtering the
# reading log requires comparing potentially large queries, which can be expensive
# and are not paginated.
if i.q:
i.q = i.q if len(i.q) >= 3 else None
return MyBooksTemplate(username, key).render(page=i.page, sort=i.sort, q=i.q)


class public_my_books_json(delegate.page):
Expand Down Expand Up @@ -189,7 +194,7 @@ def __init__(self, username, key):
self.lists = self.readlog.lists
self.counts = self.readlog.reading_log_counts

def render(self, page=1, sort='desc', list=None):
def render(self, page=1, sort='desc', list=None, q=None):
if not self.user:
return render.notfound("User %s" % self.username, create=False)
logged_in_user = accounts.get_current_user()
Expand Down Expand Up @@ -221,7 +226,7 @@ def render(self, page=1, sort='desc', list=None):
elif self.key in self.READING_LOG_KEYS:
data = add_availability(
self.readlog.get_works(
self.key, page=page, sort='created', sort_order=sort
self.key, page=page, sort='created', sort_order=sort, q=q
),
mode="openlibrary_work",
)
Expand All @@ -239,6 +244,7 @@ def render(self, page=1, sort='desc', list=None):
)

if data is not None:

return render['account/books'](
data,
self.key,
Expand All @@ -249,6 +255,7 @@ def render(self, page=1, sort='desc', list=None):
public=is_public,
owners_page=is_logged_in_user,
sort_order=sort,
q=q,
)

raise web.seeother(self.user.key)
Expand Down Expand Up @@ -359,9 +366,15 @@ def get_waitlisted_editions(self):
editions[i].waitlist_record = keyed_waitlists[editions[i].ocaid]
return editions

def process_logged_books(self, logged_books):
def process_logged_books(self, logged_books: list[web.storage]):
"""
Turn logged_books into <Work> items, while adding the logge_date
and logged_edition, as that information is stored in a separate
database from that which holds returns works.
"""
work_ids = ['/works/OL%sW' % i['work_id'] for i in logged_books]
works = web.ctx.site.get_many(work_ids)

for i in range(len(works)):
# insert the logged edition (if present) and logged date
works[i].logged_date = logged_books[i]['created']
Expand All @@ -373,7 +386,7 @@ def process_logged_books(self, logged_books):
return works

def get_want_to_read(
self, page=1, limit=RESULTS_PER_PAGE, sort='created', sort_order='desc'
self, page=1, limit=RESULTS_PER_PAGE, sort='created', sort_order='desc', q=None
):
return self.process_logged_books(
Bookshelves.get_users_logged_books(
Expand All @@ -382,11 +395,12 @@ def get_want_to_read(
page=page,
limit=limit,
sort=sort + ' ' + sort_order,
)
q=q,
),
)

def get_currently_reading(
self, page=1, limit=RESULTS_PER_PAGE, sort='created', sort_order='desc'
self, page=1, limit=RESULTS_PER_PAGE, sort='created', sort_order='desc', q=None
):
return self.process_logged_books(
Bookshelves.get_users_logged_books(
Expand All @@ -395,11 +409,12 @@ def get_currently_reading(
page=page,
limit=limit,
sort=sort + ' ' + sort_order,
)
q=q,
),
)

def get_already_read(
self, page=1, limit=RESULTS_PER_PAGE, sort='created', sort_order='desc'
self, page=1, limit=RESULTS_PER_PAGE, sort='created', sort_order='desc', q=None
):
return self.process_logged_books(
Bookshelves.get_users_logged_books(
Expand All @@ -408,19 +423,26 @@ def get_already_read(
page=page,
limit=limit,
sort=sort + ' ' + sort_order,
)
q=q,
),
)

def get_works(
self, key, page=1, limit=RESULTS_PER_PAGE, sort='created', sort_order='desc'
self,
key,
page=1,
limit=RESULTS_PER_PAGE,
sort='created',
sort_order='desc',
q=None,
):
"""
:rtype: list of openlibrary.plugins.upstream.models.Work
"""
key = key.lower()
if key in self.KEYS:
return self.KEYS[key](
page=page, limit=limit, sort=sort, sort_order=sort_order
page=page, limit=limit, sort=sort, sort_order=sort_order, q=q
)
else: # must be a list or invalid page!
# works = web.ctx.site.get_many([ ... ])
Expand Down
7 changes: 4 additions & 3 deletions openlibrary/templates/account/books.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
$def with (items, key, counts, lists=None, user=None, logged_in_user=None, public=False, sort_order='desc', owners_page=False)
$def with (items, key, counts, lists=None, user=None, logged_in_user=None, public=False, sort_order='desc', owners_page=False, q=None)

$# Displays a user's reading log
$# :param list items:
Expand All @@ -7,6 +7,7 @@
$# :param list? lists:
$# :param user:
$# :param bool public:
$# :param str q: search term.

$ component_times = {}
$ component_times['TotalTime'] = time()
Expand Down Expand Up @@ -106,9 +107,9 @@ <h1 class="details-title">
$elif key == 'lists':
$:render_template('lists/lists', items, lists, show_header=False)
$elif key == 'already-read':
$:render_template('account/reading_log', items, key, counts[key], owners_page, current_page, sort_order=sort_order, user=user, include_ratings=owners_page)
$:render_template('account/reading_log', items, key, counts[key], owners_page, current_page, sort_order=sort_order, user=user, include_ratings=owners_page, q=q)
$elif key in {'currently-reading', 'want-to-read', 'sponsorships'}:
$:render_template('account/reading_log', items, key, counts[key], owners_page, current_page, sort_order=sort_order, user=user)
$:render_template('account/reading_log', items, key, counts[key], owners_page, current_page, sort_order=sort_order, user=user, q=q)
$else:
$:render_template('type/list/view', items, check_owner=False, owns_page=True)
</div>
Expand Down
16 changes: 13 additions & 3 deletions openlibrary/templates/account/reading_log.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
$def with (items, key, total_items, owners_page, current_page, sort_order='desc', user=None, include_ratings=False)
$def with (items, key, total_items, owners_page, current_page, sort_order='desc', user=None, include_ratings=False, q=None)

$ username = user.key.split('/')[-1]
$ meta_photo_url = "https://archive.org/services/img/%s" % get_internet_archive_id(user.key)
Expand All @@ -24,14 +24,24 @@
$add_metatag(property="og:description", content=og_description)
$add_metatag(property="og:image", content=meta_photo_url)

<form method="GET" class="olform pagesearchbox">
<input type="text" minlength="3" placeholder="Search by Title or Author" name="q" value="$(query_param('q', ''))"/>
<input type="submit"/>
</form>

<div class="mybooks-list">
$# If filtering results, override total_items to match the number of filtered results from bookshelves.get_users_logged_books().
$# This overrides pagination and will return all total_items on one page.
$if q:
$ total_items = len(items)
<span class="search-results-stats">$total_items hits</span>
<span class="mybooks-tools"><img src="/images/icons/icon_sort.png" alt="$_('Sorting by')" style="margin-right:10px;" width="9" height="11">
$if sort_order == 'desc':
<strong class="lightgreen">$_("Date Added (newest)")</strong>
|
<a href="$ctx.path?sort=asc">$_("Date Added (oldest)")</a>
<a href="$ctx.path?sort=asc&q=$q">$_("Date Added (oldest)")</a>
$else:
<a href="$ctx.path?sort=desc">$_("Date Added (newest)")</a>
<a href="$ctx.path?sort=desc&q=$q">$_("Date Added (newest)")</a>
|
<strong class="lightgreen">$_("Date Added (oldest)")</strong>
</span>
Expand Down

0 comments on commit 5bcc6ec

Please sign in to comment.