Skip to content

Commit

Permalink
Move edition resolution + availability attach to get_items_and_add_av…
Browse files Browse the repository at this point in the history
…ailability()
  • Loading branch information
scottbarnes committed Feb 3, 2024
1 parent 1bcea0a commit 939a621
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 42 deletions.
34 changes: 30 additions & 4 deletions openlibrary/core/lending.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Module for providing core functionality of lending on Open Library.
"""
from typing import Literal, TypedDict, cast
from typing import TYPE_CHECKING, Literal, TypedDict, cast

import web
import datetime
Expand All @@ -21,6 +21,11 @@
from . import ia
from . import helpers as h


if TYPE_CHECKING:
from openlibrary.plugins.upstream.models import Edition


logger = logging.getLogger(__name__)

S3_LOAN_URL = 'https://%s/services/loans/loan/'
Expand Down Expand Up @@ -504,15 +509,36 @@ def get_availability_of_ocaid(ocaid):
return get_availability('identifier', [ocaid])


def get_availability_of_ocaids(
ocaids: list[str],
) -> dict[str, AvailabilityStatusV2]:
def get_availability_of_ocaids(ocaids: list[str]) -> dict[str, AvailabilityStatusV2]:
"""
Retrieves availability based on ocaids/archive.org identifiers
"""
return get_availability('identifier', ocaids)


def get_items_and_add_availability(ocaids: list[str]) -> dict[str, "Edition"]:
"""
Get Editions from OCAIDs and attach their availabiliity.
Returns a dict of the form: `{"ocaid1": edition1, "ocaid2": edition2, ...}`
"""
ocaid_availability = get_availability_of_ocaids(ocaids=ocaids)
editions = web.ctx.site.get_many(
[
f"/books/{item.get('openlibrary_edition')}"
for item in ocaid_availability.values()
if item.get('openlibrary_edition')
]
)

# Attach availability
for edition in editions:
if edition.ocaid in ocaids:
edition.availability = ocaid_availability.get(edition.ocaid)

return {edition.ocaid: edition for edition in editions if edition.ocaid}


def is_loaned_out(identifier):
"""Returns True if the given identifier is loaned out.
Expand Down
4 changes: 2 additions & 2 deletions openlibrary/macros/IABook.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
$def with (doc, ia_base_url="https://archive.org")
$ ocaid = doc.get('identifier')
$ ocaid = doc.get('ocaid')
<li class="searchResultItem">
<span class="bookcover">
$ cover = "%s/services/img/%s" % (ia_base_url, ocaid)
Expand All @@ -15,7 +15,7 @@
<div class="details">
<div class="resultTitle">
<h3 itemprop="name" class="booktitle">
<a itemprop="url" href="$ia_base_url/details/$ocaid" class="results">Borrowed from Internet Archive: $doc.get('identifier')</a>
<a itemprop="url" href="$ia_base_url/details/$ocaid" class="results">Borrowed from Internet Archive: $ocaid</a>
</h3>
</div>
</div>
Expand Down
65 changes: 29 additions & 36 deletions openlibrary/plugins/upstream/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
from openlibrary.core.booknotes import Booknotes
from openlibrary.core.bookshelves import Bookshelves
from openlibrary.core.lending import (
get_availability_of_ocaids,
get_items_and_add_availability,
s3_loan_api,
)
from openlibrary.core.observations import Observations
Expand All @@ -45,6 +45,7 @@
from openlibrary.plugins.upstream import borrow, forms, utils
from openlibrary.utils.dateutil import elapsed_time


logger = logging.getLogger("openlibrary.account")

CONFIG_IA_DOMAIN: Final = config.get('ia_base_url', 'https://archive.org')
Expand Down Expand Up @@ -1115,7 +1116,13 @@ def GET(self):
username = user['key'].split('/')[-1]
mb = MyBooksTemplate(username, key='loan_history')
loan_history_data = get_loan_history_data(page=page, mb=mb)
# Ensure all `docs` are `dicts`, as some are `Edition`s.
loan_history_data['docs'] = [
loan.dict() if not isinstance(loan, dict) else loan
for loan in loan_history_data['docs']
]
web.header('Content-Type', 'application/json')

return delegate.RawText(json.dumps({"loans_history": loan_history_data}))


Expand Down Expand Up @@ -1184,7 +1191,7 @@ def process_goodreads_csv(i):
return books, books_wo_isbns


def get_loan_history_data(page: int, mb: "MyBooksTemplate") -> dict[str, str | int]:
def get_loan_history_data(page: int, mb: "MyBooksTemplate") -> dict[str, Any]:
"""
Retrieve IA loan history data for page `page` of the patron's history.
Expand Down Expand Up @@ -1219,52 +1226,38 @@ def get_loan_history_data(page: int, mb: "MyBooksTemplate") -> dict[str, str | i
loan_history.pop()

ocaids = [loan_record['identifier'] for loan_record in loan_history]
ocaid_availability = get_availability_of_ocaids(ocaids)
loan_history_map = {
loan_record['identifier']: loan_record for loan_record in loan_history
}
editions = web.ctx.site.get_many(
[
f"/books/{item.get('openlibrary_edition')}"
for item in ocaid_availability.values()
if item.get('openlibrary_edition')
]
)

# Create 'placeholder' editions for items in the Internet Archive loan
# history, but absent from Open Library.
ia_only_loans = [
loan
for loan in ocaid_availability.values()
if not loan.get('openlibrary_edition')
]

# Attach availability and loan history info (for sorting) to both editions and
# ia-only items.
for ed in editions:
if ed.ocaid in ocaids:
ed.availability = ocaid_availability.get(ed.ocaid)
ed.last_loan_date = loan_history_map[ed.ocaid].get('updatedate')

for ia_only in ia_only_loans:
# ia_only['loan'] isn't set because `LoanStatus.html` reads it as a
# current loan. No apparenty way to distinguish between current and
# past loans from this API call.
loan = loan_history_map[ia_only['identifier']]
ia_only['last_loan_date'] = loan.get('updatedate', '')
# Get editions and attach their loan history.
editions_map = get_items_and_add_availability(ocaids=ocaids)
for edition in editions_map.values():
edition_loan_history = loan_history_map.get(edition.get('ocaid'))
edition['last_loan_date'] = (
edition_loan_history.get('updatedate') if edition_loan_history else ''
)

# Create 'placeholders' dicts for items in the Internet Archive loan history,
# but absent from Open Library, and then add loan history.
# ia_only['loan'] isn't set because `LoanStatus.html` reads it as a current
# loan. No apparenty way to distinguish between current and past loans with
# this API call.
ia_only_loans = [{'ocaid': ocaid} for ocaid in ocaids if ocaid not in editions_map]
for ia_only_loan in ia_only_loans:
loan_data = loan_history_map[ia_only_loan['ocaid']]
ia_only_loan['last_loan_date'] = loan_data.get('updatedate', '')
# Determine the macro to load for loan-history items only.
ia_only['ia_only'] = True # type: ignore[typeddict-unknown-key]
ia_only_loan['ia_only'] = True # type: ignore[typeddict-unknown-key]

editions_and_ia_loans = editions + ia_only_loans
editions_and_ia_loans = list(editions_map.values()) + ia_only_loans
editions_and_ia_loans.sort(
key=lambda item: item.get('last_loan_date', ''), reverse=True
)

result = {
return {
'docs': editions_and_ia_loans,
'show_next': show_next,
'limit': limit,
'page': page,
}

return result

0 comments on commit 939a621

Please sign in to comment.