diff --git a/openlibrary/core/lending.py b/openlibrary/core/lending.py index e92c3dd7e42..47e64e26b5c 100644 --- a/openlibrary/core/lending.py +++ b/openlibrary/core/lending.py @@ -47,6 +47,7 @@ config_ia_availability_api_v1_url = None config_ia_availability_api_v2_url = None config_ia_access_secret = None +config_ia_domain = None config_ia_ol_shared_key = None config_ia_ol_xauth_s3 = None config_ia_s3_auth_url = None @@ -74,10 +75,11 @@ def setup(config): config_ia_ol_metadata_write_s3, config_ia_xauth_api_url, \ config_http_request_timeout, config_ia_s3_auth_url, \ config_ia_users_loan_history, config_ia_loan_api_developer_key, \ - config_ia_civicrm_api + config_ia_civicrm_api, config_ia_domain config_loanstatus_url = config.get('loanstatus_url') config_bookreader_host = config.get('bookreader_host', 'archive.org') + config_ia_domain = config.get('ia_base_url', 'https://archive.org') config_ia_loan_api_url = config.get('ia_loan_api_url') config_ia_availability_api_v1_url = config.get('ia_availability_api_v1_url') config_ia_availability_api_v2_url = config.get('ia_availability_api_v2_url') diff --git a/openlibrary/core/models.py b/openlibrary/core/models.py index c75b5fa8c11..62c42206ed3 100644 --- a/openlibrary/core/models.py +++ b/openlibrary/core/models.py @@ -18,6 +18,8 @@ from openlibrary.core.helpers import private_collection_in from openlibrary.core.bookshelves import Bookshelves from openlibrary.core.ratings import Ratings +from openlibrary.utils.isbn import to_isbn_13, isbn_13_to_isbn_10 +from openlibrary.core.vendors import create_edition_from_amazon_metadata # relative imports from lists.model import ListMixin, Seed @@ -369,6 +371,30 @@ def get_ia_download_link(self, suffix): if filename: return "https://archive.org/download/%s/%s" % (self.ocaid, filename) + @classmethod + def get_by_isbn(cls, isbn): + """Attempts to fetch an edition by isbn, or if no edition is found, + attempts to import from amazon + """ + isbn13 = to_isbn_13(isbn) + isbn10 = isbn_13_to_isbn_10(isbn) + + # Attempt to fetch book from OL + for isbn in [isbn13, isbn10]: + if isbn: + matches = web.ctx.site.things({ + "type": "/type/edition", 'isbn_%s' % len(isbn): isbn + }) + if matches: + return web.ctx.site.get(matches[0]) + + # Attempt to create from amazon, then fetch from OL + key = next( + create_edition_from_amazon_metadata(isbn) + for isbn in [isbn13, isbn10]) + if key: + return web.ctx.site.get(key) + def is_ia_scan(self): metadata = self.get_ia_meta_fields() # all IA scans will have scanningcenter field set diff --git a/openlibrary/core/sponsorships.py b/openlibrary/core/sponsorships.py index f88a42a8598..5ff426a4ac4 100644 --- a/openlibrary/core/sponsorships.py +++ b/openlibrary/core/sponsorships.py @@ -1,18 +1,32 @@ import json import urllib import requests +import web from infogami import config from infogami.utils.view import public from openlibrary import accounts -from openlibrary.core.lending import get_work_availability -from openlibrary.core.vendors import get_betterworldbooks_metadata +from openlibrary.core import models +from openlibrary.core.lending import get_work_availability, config_ia_domain +from openlibrary.core.vendors import ( + get_betterworldbooks_metadata, create_edition_from_amazon_metadata) from openlibrary.accounts import get_internet_archive_id from openlibrary.core.lending import config_ia_civicrm_api +from openlibrary.utils.isbn import to_isbn_13 + +try: + from booklending_utils.sponsorship import eligibility_check +except ImportError: + def eligibility_check(edition): + """For testing if Internet Archive book sponsorship check unavailable""" + work = edition.works[0] + authors = [w.author.name for w in work.authors] + if authors: + return True CIVI_ISBN = 'custom_52' CIVI_USERNAME = 'custom_51' CIVI_CONTEXT = 'custom_53' -PRICE_LIMIT = 50.00 +PRICE_LIMIT_CENTS = 5000 def get_sponsored_editions(user): @@ -78,27 +92,67 @@ def get_sponsorships_by_contact_id(contact_id, isbn=None): } for t in txs] +def isbn_qualifies_for_sponsorship(isbn): + """Checks possible isbn10 + isbn13 variations to""" + edition = models.Edition.get_by_isbn(isbn) + if edition: + return qualifies_for_sponsorship(edition) + @public def qualifies_for_sponsorship(edition): - # User must be logged in and in /usergroup/sponsors list + # defaults + dwhi = None + eligibility = False + price = None + work = edition.works and edition.works[0] + edition.isbn13 = to_isbn_13(edition.isbn_13 and edition.isbn_13[0] or + edition.isbn_10 and edition.isbn_10[0]) req_fields = all(edition.get(x) for x in [ - 'publishers', 'title', 'publish_date', 'covers', 'number_of_pages' + 'publishers', 'title', 'publish_date', 'covers', + 'number_of_pages', 'isbn13' ]) - isbn = (edition.isbn_13 and edition.isbn_13[0] or - edition.isbn_10 and edition.isbn_10[0]) - if work and req_fields and isbn: - work_id = work.key.split("/")[-1] - num_pages = int(edition.get('number_of_pages')) - availability = get_work_availability(work_id) - dwwi = availability.get(work_id, {}).get('status', 'error') == 'error' - if dwwi: - bwb_price = get_betterworldbooks_metadata(isbn).get('price_amt') - if bwb_price: - scan_price = 3.0 + (.12 * num_pages) - total_price = scan_price + float(bwb_price) - return total_price <= PRICE_LIMIT - return False + if not (work and req_fields and edition.isbn13): + return { + 'do_we_have_it': dwhi, + 'is_eligibile': eligibility, + 'price': price, + 'error': ("Open Library is missing book metadata " + "necessary for sponsorship"), + } + + work_id = work.key.split("/")[-1] + num_pages = int(edition.get('number_of_pages')) + availability = get_work_availability(work_id) + dwhi = (availability.get(work_id, {}).get('status', 'error') != 'error') and availability + if not dwhi: + bwb_price = get_betterworldbooks_metadata( + edition.isbn13).get('price_amt') + if bwb_price: + SETUP_COST_CENTS = 300 + PAGE_COST_CENTS = 12 + scan_price_cents = SETUP_COST_CENTS + (PAGE_COST_CENTS * num_pages) + book_cost_cents = int(float(bwb_price) * 100) + total_price_cents = scan_price_cents + book_cost_cents + price = { + 'book_cost_cents': book_cost_cents, + 'scan_price_cents': scan_price_cents, + 'total_price_cents': total_price_cents + } + if total_price_cents <= PRICE_LIMIT_CENTS: + eligibility = eligibility_check(edition) + params = { + 'campaign': 'pilot', + 'type': 'sponsorship', + 'context': 'ol', + 'isbn': edition.isbn13 + } + return { + 'is_eligible': eligibility, + 'do_we_have_it': dwhi, + 'price': price, + 'url': config_ia_domain + '/donate?' + urllib.urlencode(params) + } def get_all_sponsors(): diff --git a/openlibrary/macros/LoanStatus.html b/openlibrary/macros/LoanStatus.html index f44ec084dde..b21772ea0ad 100644 --- a/openlibrary/macros/LoanStatus.html +++ b/openlibrary/macros/LoanStatus.html @@ -81,17 +81,17 @@ Read $elif (not page.get('ocaid') or page.is_access_restricted()) and editions_page: - $if user and (input(sponsorship=None) or user.is_sponsor()) and qualifies_for_sponsorship(page): + $ sponsorship = qualifies_for_sponsorship(page) + $if (input(sponsorship=None) or user and user.is_sponsor()) and sponsorship.get('is_eligible'): $ isbn = (page.isbn_13 and page.isbn_13[0] or page.isbn_10 and page.isbn_10[0]) - $ user_id = user.key.split("/")[-1] - $ donate_url = "https://archive.org/donate?context=ol&userid=%s&campaign=pilot&type=book-sponsorship&isbn=%s" % (user_id, isbn) - Sponsor eBook

We don’t have this book yet. You can add it to our Lending Library with a \$50 tax deductible donation. - Learn More + Learn More

$else:
diff --git a/openlibrary/plugins/openlibrary/api.py b/openlibrary/plugins/openlibrary/api.py index d79de272b64..a61f546cd77 100644 --- a/openlibrary/plugins/openlibrary/api.py +++ b/openlibrary/plugins/openlibrary/api.py @@ -15,6 +15,7 @@ from openlibrary.utils import extract_numeric_id_from_olid from openlibrary.plugins.worksearch.subjects import get_subject from openlibrary.core import ia, db, models, lending, helpers as h +from openlibrary.core.sponsorships import isbn_qualifies_for_sponsorship from openlibrary.core.vendors import ( get_amazon_metadata, create_edition_from_amazon_metadata, get_betterworldbooks_metadata) @@ -242,6 +243,13 @@ def get_works_data(self, author, limit, offset): "entries": works } +class sponsorship_eligibility_check(delegate.page): + path = r'/sponsorship/eligibility/(.*)' + + @jsonapi + def GET(self, isbn): + return simplejson.dumps(isbn_qualifies_for_sponsorship(isbn)) + class price_api(delegate.page): path = r'/prices' diff --git a/openlibrary/utils/isbn.py b/openlibrary/utils/isbn.py index 2f3cc0fbaa1..6bd4da55eca 100644 --- a/openlibrary/utils/isbn.py +++ b/openlibrary/utils/isbn.py @@ -47,6 +47,10 @@ def isbn_10_to_isbn_13(isbn_10): isbn_13 = '978' + isbn_10[:-1] return isbn_13 + check_digit_13(isbn_13) +def to_isbn_13(isbn): + isbn = normalize_isbn(isbn) + return isbn and (isbn if len(isbn) == 13 else isbn_10_to_isbn_13(isbn)) + def opposite_isbn(isbn): # ISBN10 -> ISBN13 and ISBN13 -> ISBN10 for f in isbn_13_to_isbn_10, isbn_10_to_isbn_13: alt = f(canonical(isbn))