Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions conf/solr/conf/managed-schema.xml
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,9 @@
<field name="time_facet" type="string" stored="false" multiValued="true"/>
<field name="time_key" type="string" stored="false" multiValued="true"/>

<!-- only on edition -->
<field name="acquisition" type="string" multiValued="true"/>

<!-- Ratings -->
<field name="ratings_average" type="pfloat"/>
<field name="ratings_sortable" type="pfloat"/>
Expand Down
29 changes: 29 additions & 0 deletions openlibrary/book_providers.py
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,33 @@ def get_acquisitions(self, ed_or_solr: Edition | dict) -> list[Acquisition]:
]


class BetterWorldBooksProvider(AbstractBookProvider):
short_name = 'betterworldbooks'
long_name = 'Better World Books'
identifier_key = 'betterworldbooks'

def is_own_ocaid(self, ocaid: str) -> bool:
return False

def get_identifiers(self, ed_or_solr: Edition | dict) -> list[str]:
# basically just check if it has an isbn?
return (ed_or_solr.get('isbn_10') or []) + (ed_or_solr.get('isbn_13') or [])

def get_acquisitions(
self,
ed_or_solr: Edition | dict,
) -> list[Acquisition]:
return [
Acquisition(
access='buy',
format='web',
price=None,
url=f'https://www.betterworldbooks.com/product/detail/{self.get_best_identifier(ed_or_solr)}',
provider_name=self.short_name,
)
]


PROVIDER_ORDER: list[AbstractBookProvider] = [
# These providers act essentially as their own publishers, so link to the first when
# we're on an edition page
Expand All @@ -607,6 +634,8 @@ def get_acquisitions(self, ed_or_solr: Edition | dict) -> list[Acquisition]:
WikisourceProvider(),
# Then link to IA
InternetArchiveProvider(),
# Then link to purchase options
BetterWorldBooksProvider(),
]


Expand Down
1 change: 1 addition & 0 deletions openlibrary/plugins/worksearch/schemes/editions.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class EditionSearchScheme(SearchScheme):
"publish_year",
"language",
"publisher_facet",
"acquisition",
}
)
non_solr_fields = frozenset()
Expand Down
18 changes: 15 additions & 3 deletions openlibrary/plugins/worksearch/schemes/works.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import infogami
from openlibrary.plugins.upstream.utils import convert_iso_to_marc
from openlibrary.plugins.worksearch.schemes import SearchScheme
from openlibrary.plugins.worksearch.schemes.editions import EditionSearchScheme
from openlibrary.solr.query_utils import (
EmptyTreeError,
fully_escape_query,
Expand Down Expand Up @@ -238,9 +239,15 @@ class WorkSearchScheme(SearchScheme):

def is_search_field(self, field: str):
# New variable introduced to prevent rewriting the input.
if field.startswith(('work.', 'edition.')):
if field.startswith('work.'):
return self.is_search_field(field.partition(".")[2])
return super().is_search_field(field) or field.startswith('id_')
if field.startswith('edition.'):
return EditionSearchScheme().is_search_field(field.partition(".")[2])
return (
super().is_search_field(field)
or field.startswith('id_')
or EditionSearchScheme().is_search_field(field)
)

def transform_user_query(
self, user_query: str, q_tree: luqum.tree.Item
Expand Down Expand Up @@ -327,8 +334,12 @@ def remove_work_prefix(field: str) -> str:
# Removes the indicator prefix from queries with the 'work field' before appending them to parameters.
final_work_query = deepcopy(work_q_tree)
luqum_replace_field(final_work_query, remove_work_prefix)
EDITION_ONLY_FIELDS = {'acquisition'}
try:
luqum_remove_field(final_work_query, lambda f: f.startswith('edition.'))
luqum_remove_field(
final_work_query,
lambda f: f.startswith('edition.') or f in EDITION_ONLY_FIELDS,
)
except EmptyTreeError:
# If the whole tree is removed, we should just search for everything
final_work_query = luqum_parser('*:*')
Expand Down Expand Up @@ -398,6 +409,7 @@ def remove_work_prefix(field: str) -> str:
'ia_collection': 'ia_collection',
'ia_box_id': 'ia_box_id',
'public_scan_b': 'public_scan_b',
'acquisition': 'acquisition',
}

def convert_work_field_to_edition_field(
Expand Down
1 change: 1 addition & 0 deletions openlibrary/solr/solr_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ class SolrDocument(TypedDict):
time: Optional[list[str]]
time_facet: Optional[list[str]]
time_key: Optional[list[str]]
acquisition: Optional[list[str]]
ratings_average: Optional[float]
ratings_sortable: Optional[float]
ratings_count: Optional[int]
Expand Down
9 changes: 9 additions & 0 deletions openlibrary/solr/updater/edition.py
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,14 @@ def has_fulltext(self) -> bool:
def public_scan_b(self) -> bool:
return self.ebook_access == bp.EbookAccess.PUBLIC

def acquisition(self) -> list[str]:
"""Get acquisition methods for the edition."""
return [
f'{acq.access},{acq.format},{acq.url}'
for provider in self._providers
for acq in provider.get_acquisitions(self._edition)
]

def build(self) -> SolrDocument:
"""
Build the solr document for the given edition to store as a nested
Expand Down Expand Up @@ -339,6 +347,7 @@ def build(self) -> SolrDocument:
'ia_collection': self.ia_collection,
'ia_box_id': self.ia_box_id,
# Ebook access
'acquisition': self.acquisition(),
'ebook_access': self.ebook_access.to_solr_str(),
'ebook_provider': self.ebook_provider,
'has_fulltext': self.has_fulltext,
Expand Down
Loading