def render(self, page=1, sort='desc', list=None): if not self.user: return render.notfound("User %s" % self.username, create=False) logged_in_user = accounts.get_current_user() is_logged_in_user = (logged_in_user and logged_in_user.key.split('/')[-1] == self.username) is_public = self.user.preferences().get('public_readlog', 'no') == 'yes' data = None if is_logged_in_user and self.key in self.ALL_KEYS: self.counts.update(PatronBooknotes.get_counts(self.username)) sponsorships = get_sponsored_editions(self.user) self.counts['sponsorships'] = len(sponsorships) if self.key == 'sponsorships': data = add_availability( web.ctx.site.get_many([ '/books/%s' % doc['openlibrary_edition'] for doc in sponsorships ])) if sponsorships else 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), mode="openlibrary_work", ) elif self.key == 'list': data = list else: data = self._prepare_data(logged_in_user) elif self.key in self.READING_LOG_KEYS and is_public: data = add_availability( self.readlog.get_works(self.key, page=page, sort='created', sort_order=sort), mode="openlibrary_work", ) if data is not None: return render['account/books']( data, self.key, self.counts, logged_in_user=logged_in_user, user=self.user, lists=self.lists, public=is_public, owners_page=is_logged_in_user, ) raise web.seeother(self.user.key)
def work_search(query, sort=None, page=1, offset=0, limit=100): """ params: query: dict sort: str editions|old|new|scans """ sorts = { 'editions': 'edition_count desc', 'old': 'first_publish_year asc', 'new': 'first_publish_year desc', 'scans': 'ia_count desc' } query['wt'] = 'json' try: (reply, solr_select, q_list) = run_solr_query(query, rows=limit, page=page, sort=sorts.get(sort), offset=offset, fields="*") response = json.loads(reply)['response'] or '' except (ValueError, IOError) as e: logger.error("Error in processing search API.") response = dict(start=0, numFound=0, docs=[], error=str(e)) # backward compatibility response['num_found'] = response['numFound'] response['docs'] = add_availability(response['docs']) return response
def inject_availability(subject_results): works = add_availability(subject_results.works) for work in works: ocaid = work.ia if work.ia else None availability = work.get('availability', {}).get('status') subject_results.works = works return subject_results
def GET(self): i = web.input(q='', page=1, limit=100, subject='', work_id='', _type='', sorts='') sorts = i.sorts.split(',') page = int(i.page) limit = int(i.limit) url = lending.compose_ia_url(query=i.q, limit=limit, page=page, subject=i.subject, work_id=i.work_id, _type=i._type, sorts=sorts) result = { 'query': url, 'works': [ work.dict() for work in lending.add_availability( lending.get_available(url=url)) ] } return delegate.RawText(simplejson.dumps(result), content_type="application/json")
def inject_availability(subject_results): works = add_availability(subject_results.works) for work in works: ocaid = work.ia if work.ia else None availability = work.get('availability', {}).get('status') subject_results.works = works return subject_results
def work_search(query, sort=None, page=1, offset=0, limit=100, fields='*', facet=True, spellcheck_count=None): """ params: query: dict sort: str editions|old|new|scans """ query['wt'] = 'json' if sort: sort = process_sort(sort) try: (reply, solr_select, q_list) = run_solr_query(query, rows=limit, page=page, sort=sort, offset=offset, fields=fields, facet=facet, spellcheck_count=spellcheck_count) response = json.loads(reply)['response'] or '' except (ValueError, IOError) as e: logger.error("Error in processing search API.") response = dict(start=0, numFound=0, docs=[], error=str(e)) # backward compatibility response['num_found'] = response['numFound'] if fields == '*' or 'availability' in fields: response['docs'] = add_availability(response['docs']) return response
def works_by_author(akey, sort='editions', page=1, rows=100, has_fulltext=False): # called by merge_author_works q = 'author_key:' + akey offset = rows * (page - 1) fields = [ 'key', 'author_name', 'author_key', 'title', 'subtitle', 'edition_count', 'ia', 'cover_edition_key', 'has_fulltext', 'first_publish_year', 'public_scan_b', 'lending_edition_s', 'lending_identifier_s', 'ia_collection_s', 'cover_i' ] fl = ','.join(fields) fq = 'has_fulltext:true' if has_fulltext else '' # ebooks_only solr_select = solr_select_url + "?fq=type:work&q.op=AND&q=%s&fq=%s&start=%d&rows=%d&fl=%s&wt=json" % ( q, fq, offset, rows, fl) facet_fields = [ "author_facet", "language", "publish_year", "publisher_facet", "subject_facet", "person_facet", "place_facet", "time_facet" ] if sort == 'editions': solr_select += '&sort=edition_count+desc' elif sort.startswith('old'): solr_select += '&sort=first_publish_year+asc' elif sort.startswith('new'): solr_select += '&sort=first_publish_year+desc' elif sort.startswith('title'): solr_select += '&sort=title+asc' solr_select += "&facet=true&facet.mincount=1&f.author_facet.facet.sort=count&f.publish_year.facet.limit=-1&facet.limit=25&" + '&'.join( "facet.field=" + f for f in facet_fields) reply = parse_json_from_solr_query(solr_select) if reply is None: return web.storage( num_found=0, works=[], years=[], get_facet=[], sort=sort, ) # TODO: Deep JSON structure defense - for now, let it blow up so easier to detect facets = reply['facet_counts']['facet_fields'] works = [work_object(w) for w in reply['response']['docs']] def get_facet(f, limit=None): return list(web.group(facets[f][:limit * 2] if limit else facets[f], 2)) return web.storage( num_found=int(reply['response']['numFound']), works=add_availability(works), years=[(int(k), v) for k, v in get_facet('publish_year')], get_facet=get_facet, sort=sort, )
def readonline_carousel(): """Return template code for books pulled from search engine. TODO: If problems, use stock list. """ try: data = random_ebooks() if len(data) > 30: data = lending.add_availability(random.sample(data, 30)) data = [d for d in data if d['availability']['is_readable']] return storify(data) except Exception: logger.error("Failed to compute data for readonline_carousel", exc_info=True) return None
def work_search( query, sort=None, page=1, offset=0, limit=100, fields='*', facet=True, spellcheck_count=None, ): """ params: query: dict sort: str editions|old|new|scans """ # Ensure we don't mutate the `query` passed in by reference query = copy.deepcopy(query) query['wt'] = 'json' if sort: sort = process_sort(sort) # deal with special /lists/ key queries query['q'], page, offset, limit = rewrite_list_editions_query( query['q'], page, offset, limit ) try: (reply, solr_select, q_list) = run_solr_query( query, rows=limit, page=page, sort=sort, offset=offset, fields=fields, facet=facet, spellcheck_count=spellcheck_count, ) response = json.loads(reply)['response'] or '' except (ValueError, OSError) as e: logger.error("Error in processing search API.") response = dict(start=0, numFound=0, docs=[], error=str(e)) # backward compatibility response['num_found'] = response['numFound'] if fields == '*' or 'availability' in fields: response['docs'] = add_availability(response['docs']) return response
def work_search( query: dict, sort: str = None, page: int = 1, offset: int = 0, limit: int = 100, fields: str = '*', facet: bool = True, spellcheck_count: int = None, ) -> dict: """ :param sort: key of SORTS dict at the top of this file """ # Ensure we don't mutate the `query` passed in by reference query = copy.deepcopy(query) query['wt'] = 'json' if sort: sort = process_sort(sort) # deal with special /lists/ key queries query['q'], page, offset, limit = rewrite_list_query( query['q'], page, offset, limit) try: (reply, solr_select, q_list) = run_solr_query( query, rows=limit, page=page, sort=sort, offset=offset, fields=fields, facet=facet, spellcheck_count=spellcheck_count, ) assert reply, "Received None response from run_solr_query" response = reply['response'] except (ValueError, OSError, AssertionError) as e: logger.error("Error in processing search API.") response = dict(start=0, numFound=0, docs=[], error=str(e)) # backward compatibility response['num_found'] = response['numFound'] if fields == '*' or 'availability' in fields: response['docs'] = add_availability(response['docs']) return response
def GET(self, username, key='loans'): """check if user's reading log is public""" i = web.input(page=1, sort='desc') user = web.ctx.site.get('/people/%s' % username) if not user: return render.notfound("User %s" % username, create=False) is_public = user.preferences().get('public_readlog', 'no') == 'yes' logged_in_user = accounts.get_current_user() is_logged_in_user = (logged_in_user and logged_in_user.key.split('/')[-1] == username) if is_public or is_logged_in_user: readlog = ReadingLog(user=user) sponsorships = get_sponsored_editions(user) if key == 'sponsorships': books = (web.ctx.site.get( web.ctx.site.things({ 'type': '/type/edition', 'isbn_%s' % len(s['isbn']): s['isbn'] })[0]) for s in sponsorships) elif key == 'notes' and is_logged_in_user: books = PatronBooknotes(user).get_notes(page=int(i.page)) elif key == 'observations' and is_logged_in_user: books = PatronBooknotes(user).get_observations( page=int(i.page)) else: books = add_availability(readlog.get_works(key, page=i.page, sort='created', sort_order=i.sort), mode="openlibrary_work") booknotes_counts = PatronBooknotes.get_counts(username) return render['account/books']( books, key, sponsorship_count=len(sponsorships), reading_log_counts=readlog.reading_log_counts, lists=readlog.lists, user=user, logged_in_user=logged_in_user, public=is_public, sort_order=str(i.sort), booknotes_counts=booknotes_counts) raise web.seeother(user.key)
def GET(self): i = web.input(q='', page=1, limit=100, subject='', work_id='', _type='', sorts='') sorts = i.sorts.split(',') page = int(i.page) limit = int(i.limit) url = lending.compose_ia_url( query=i.q, limit=limit, page=page, subject=i.subject, work_id=i.work_id, _type=i._type, sorts=sorts) result = { 'query': url, 'works': [ work.dict() for work in lending.add_availability( lending.get_available(url=url) ) ] } return delegate.RawText( simplejson.dumps(result), content_type="application/json")
def works_by_author(akey, sort='editions', page=1, rows=100, has_fulltext=False, query=None): # called by merge_author_works q = 'author_key:' + akey if query: q = query offset = rows * (page - 1) params = [ ('fq', 'author_key:' + akey), ('fq', 'type:work'), ('q', q), ('start', offset), ('rows', rows), ( 'fl', ','.join([ 'key', 'author_name', 'author_key', 'title', 'subtitle', 'edition_count', 'ia', 'cover_edition_key', 'has_fulltext', 'language', 'first_publish_year', 'public_scan_b', 'lending_edition_s', 'lending_identifier_s', 'ia_collection_s', 'id_project_gutenberg', 'id_librivox', 'id_standard_ebooks', 'id_openstax', 'cover_i', ]), ), ('wt', 'json'), ('q.op', 'AND'), ('facet', 'true'), ('facet.mincount', 1), ('f.author_facet.facet.sort', 'count'), ('f.publish_year.facet.limit', -1), ('facet.limit', 25), ] if has_fulltext: params.append(('fq', 'has_fulltext:true')) if sort == "editions": params.append(('sort', 'edition_count desc')) elif sort.startswith('old'): params.append(('sort', 'first_publish_year asc')) elif sort.startswith('new'): params.append(('sort', 'first_publish_year desc')) elif sort.startswith('title'): params.append(('sort', 'title asc')) facet_fields = [ "author_facet", "language", "publish_year", "publisher_facet", "subject_facet", "person_facet", "place_facet", "time_facet", ] for f in facet_fields: params.append(("facet.field", f)) reply = parse_json_from_solr_query(solr_select_url, params) if reply is None: return web.storage( num_found=0, works=[], years=[], get_facet=[], sort=sort, ) # TODO: Deep JSON structure defense - for now, let it blow up so easier to detect facets = reply['facet_counts']['facet_fields'] works = [work_object(w) for w in reply['response']['docs']] def get_facet(f, limit=None): return list(web.group(facets[f][:limit * 2] if limit else facets[f], 2)) return web.storage( num_found=int(reply['response']['numFound']), works=add_availability(works), years=[(int(k), v) for k, v in get_facet('publish_year')], get_facet=get_facet, sort=sort, )
def get_subject(self, key, details=False, offset=0, limit=DEFAULT_RESULTS, sort='first_publish_year desc', **filters): meta = self.get_meta(key) q = self.make_query(key, filters) subject_type = meta.name name = meta.path.replace("_", " ") if details: kw = self.query_optons_for_details() else: kw = {} from search import work_search result = work_search(q, offset=offset, limit=limit, sort=sort, **kw) if not result: return None for w in result.docs: w.ia = w.ia and w.ia[0] or None w['checked_out'] = False if not w.get('public_scan') and w.ia and w.get( 'lending_identifier'): doc = web.ctx.site.store.get("ebooks/" + w['lending_identifier']) or {} w['checked_out'] = doc.get("borrowed") == "true" # XXX-Anand: Oct 2013 # Somewhere something is broken, work keys are coming as OL1234W/works/ # Quick fix it solve that issue. if w.key.endswith("/works/"): w.key = "/works/" + w.key.replace("/works/", "") subject = Subject(key=key, name=name, subject_type=subject_type, work_count=result['num_found'], works=add_availability(result['docs'])) if details: subject.ebook_count = dict(result.facets["has_fulltext"]).get( "true", 0) #subject.ebook_count = self.get_ebook_count(meta.name, q[meta.facet_key], q.get('publish_year')) subject.subjects = result.facets["subject_facet"] subject.places = result.facets["place_facet"] subject.people = result.facets["person_facet"] subject.times = result.facets["time_facet"] subject.authors = result.facets["author_facet"] subject.publishers = result.facets["publisher_facet"] subject.languages = result.facets['language'] # Ignore bad dates when computing publishing_history # year < 1000 or year > current_year+1 are considered bad dates current_year = datetime.datetime.utcnow().year subject.publishing_history = [[ year, count ] for year, count in result.facets["publish_year"] if 1000 < year <= current_year + 1] # strip self from subjects and use that to find exact name for i, s in enumerate(subject[meta.key]): if "key" in s and s.key.lower() == key.lower(): subject.name = s.name subject[meta.key].pop(i) break return subject