Skip to content

Commit abe2785

Browse files
Copilotmekarpeles
andcommitted
Implement bulk availability API optimization for dynlinks
Co-authored-by: mekarpeles <978325+mekarpeles@users.noreply.github.com>
1 parent 3cf8177 commit abe2785

File tree

1 file changed

+85
-7
lines changed

1 file changed

+85
-7
lines changed

openlibrary/plugins/books/dynlinks.py

Lines changed: 85 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from infogami.utils.delegate import register_exception
1010
from openlibrary.core import helpers as h
1111
from openlibrary.core import ia
12+
from openlibrary.core import lending
1213
from openlibrary.core.imports import ImportItem
1314
from openlibrary.core.models import Edition
1415
from openlibrary.plugins.openlibrary.processors import urlsafe
@@ -182,6 +183,18 @@ def process(self, result):
182183
for a in w.get('authors', [])
183184
]
184185
self.authors = get_many_as_dict(author_keys)
186+
187+
# Collect all ocaids for bulk availability request
188+
ocaids = [doc.get('ocaid') for doc in result.values() if doc.get('ocaid')]
189+
190+
# Make bulk availability request if we have ocaids
191+
self.availability_map = {}
192+
if ocaids:
193+
try:
194+
self.availability_map = lending.get_availability('identifier', ocaids)
195+
except Exception:
196+
# Fall back to individual calls if bulk API fails
197+
self.availability_map = {}
185198

186199
return {k: self.process_doc(doc) for k, doc in result.items()}
187200

@@ -313,7 +326,12 @@ def row(r):
313326

314327
def ebook(doc):
315328
itemid = doc['ocaid']
316-
availability = get_ia_availability(itemid)
329+
# Use bulk availability data if available, otherwise fall back to individual call
330+
availability_data = self.availability_map.get(itemid)
331+
if availability_data:
332+
availability = map_availability_to_legacy_contract(availability_data)
333+
else:
334+
availability = get_ia_availability(itemid)
317335

318336
d = {
319337
"preview_url": "https://archive.org/details/" + itemid,
@@ -378,8 +396,11 @@ def get_authors(docs):
378396

379397

380398
def process_result_for_details(result):
381-
def f(bib_key, doc):
382-
d = process_doc_for_viewapi(bib_key, doc)
399+
def f(bib_key, doc, availability_map):
400+
# Get availability for this document's ocaid
401+
ocaid = doc.get('ocaid')
402+
availability = availability_map.get(ocaid) if ocaid else None
403+
d = process_doc_for_viewapi(bib_key, doc, availability)
383404

384405
if 'authors' in doc:
385406
doc['authors'] = [author_dict[a['key']] for a in doc['authors']]
@@ -388,11 +409,41 @@ def f(bib_key, doc):
388409
return d
389410

390411
author_dict = get_authors(result.values())
391-
return {k: f(k, doc) for k, doc in result.items()}
412+
413+
# Collect all ocaids for bulk availability request
414+
ocaids = [doc.get('ocaid') for doc in result.values() if doc.get('ocaid')]
415+
416+
# Make bulk availability request if we have ocaids
417+
availability_map = {}
418+
if ocaids:
419+
try:
420+
availability_map = lending.get_availability('identifier', ocaids)
421+
except Exception:
422+
# Fall back to individual calls if bulk API fails
423+
availability_map = {}
424+
425+
return {k: f(k, doc, availability_map) for k, doc in result.items()}
392426

393427

394428
def process_result_for_viewapi(result):
395-
return {k: process_doc_for_viewapi(k, doc) for k, doc in result.items()}
429+
# Collect all ocaids for bulk availability request
430+
ocaids = [doc.get('ocaid') for doc in result.values() if doc.get('ocaid')]
431+
432+
# Make bulk availability request if we have ocaids
433+
availability_map = {}
434+
if ocaids:
435+
try:
436+
availability_map = lending.get_availability('identifier', ocaids)
437+
except Exception:
438+
# Fall back to individual calls if bulk API fails
439+
availability_map = {}
440+
441+
return {
442+
k: process_doc_for_viewapi(
443+
k, doc, availability_map.get(doc.get('ocaid'))
444+
)
445+
for k, doc in result.items()
446+
}
396447

397448

398449
def get_ia_availability(itemid):
@@ -406,11 +457,38 @@ def get_ia_availability(itemid):
406457
return 'full'
407458

408459

409-
def process_doc_for_viewapi(bib_key, page):
460+
def map_availability_to_legacy_contract(availability_data):
461+
"""
462+
Map bulk availability API response to legacy contract values.
463+
464+
Args:
465+
availability_data: AvailabilityStatusV2 dict from bulk API
466+
467+
Returns:
468+
str: One of 'borrow', 'restricted', 'full', or 'noview' for missing data
469+
"""
470+
if not availability_data:
471+
return 'noview'
472+
473+
# Apply the same logic as get_ia_availability but using bulk API fields
474+
if availability_data.get('is_lendable'):
475+
return 'borrow'
476+
elif availability_data.get('is_printdisabled'):
477+
return 'restricted'
478+
else:
479+
return 'full'
480+
481+
482+
def process_doc_for_viewapi(bib_key, page, availability=None):
410483
url = get_url(page)
411484

412485
if 'ocaid' in page:
413-
preview = get_ia_availability(page['ocaid'])
486+
if availability is not None:
487+
# Use provided availability data from bulk API
488+
preview = map_availability_to_legacy_contract(availability)
489+
else:
490+
# Fallback to individual API call for backward compatibility
491+
preview = get_ia_availability(page['ocaid'])
414492
preview_url = 'https://archive.org/details/' + page['ocaid']
415493
else:
416494
preview = 'noview'

0 commit comments

Comments
 (0)