def test_update_collision(self): existing_book = { "username": "******", "work_id": "2", "edition_id": "2", "bookshelf_id": "1", } self.db.insert("bookshelves_books", **existing_book) assert len(list(self.db.select("bookshelves_books"))) == 2 Bookshelves.update_work_id(self.source_book['work_id'], existing_book['work_id']) assert len( list( self.db.select( "bookshelves_books", where={ "username": "******", "work_id": "2", "edition_id": "2" }, ))), "failed to update 1 to 2" assert not len( list( self.db.select( "bookshelves_books", where={ "username": "******", "work_id": "1", "edition_id": "1" }, ))), "old work_id 1 present"
def test_update_username(self): before_where = {"username": "******"} after_where = {"username": "******"} assert len( list(self.db.select("bookshelves_books", where=before_where))) == 2 Bookshelves.update_username("@kilgore_trout", "@anonymous") assert len( list(self.db.select("bookshelves_books", where=before_where))) == 0 assert len(list(self.db.select("bookshelves_books", where=after_where))) == 2 assert len(list(self.db.select("booknotes", where=before_where))) == 1 Booknotes.update_username("@kilgore_trout", "@anonymous") assert len(list(self.db.select("booknotes", where=before_where))) == 0 assert len(list(self.db.select("booknotes", where=after_where))) == 1 assert len(list(self.db.select("ratings", where=before_where))) == 1 Ratings.update_username("@kilgore_trout", "@anonymous") assert len(list(self.db.select("ratings", where=before_where))) == 0 assert len(list(self.db.select("ratings", where=after_where))) == 1 assert len(list(self.db.select("observations", where=before_where))) == 1 Observations.update_username("@kilgore_trout", "@anonymous") assert len(list(self.db.select("observations", where=before_where))) == 0 assert len(list(self.db.select("observations", where=after_where))) == 1
def add(cls, username, work_id, rating, edition_id=None): from openlibrary.core.bookshelves import Bookshelves oldb = db.get_db() work_id = int(work_id) data = {'work_id': work_id, 'username': username} if rating not in cls.VALID_STAR_RATINGS: return None # Vote implies user read book; Update reading log status as "Already Read" users_read_status_for_work = Bookshelves.get_users_read_status_of_work( username, work_id ) if users_read_status_for_work != Bookshelves.PRESET_BOOKSHELVES['Already Read']: Bookshelves.add( username, Bookshelves.PRESET_BOOKSHELVES['Already Read'], work_id, edition_id=edition_id, ) users_rating_for_work = cls.get_users_rating_for_work(username, work_id) if not users_rating_for_work: return oldb.insert( 'ratings', username=username, work_id=work_id, rating=rating, edition_id=edition_id, ) else: where = "work_id=$work_id AND username=$username" return oldb.update('ratings', where=where, rating=rating, vars=data)
def main(self, test=False): params = web.input(key='', test='') # Provide an escape hatch to let POST requests preview if test is False and params.test: test = True summary = {'key': params.key, 'redirect_chain': [], 'resolved_key': None} if params.key: redirect_chain = Work.get_redirect_chain(params.key) summary['redirect_chain'] = [ { "key": thing.key, "occurrences": {}, "updates": {} } for thing in redirect_chain ] summary['resolved_key'] = redirect_chain[-1].key for r in summary['redirect_chain']: olid = r['key'].split('/')[-1][2:-1] new_olid = summary['resolved_key'].split('/')[-1][2:-1] # count reading log entries r['occurrences']['readinglog'] = len( Bookshelves.get_works_shelves(olid)) r['occurrences']['ratings'] = len( Ratings.get_all_works_ratings(olid)) r['occurrences']['booknotes'] = len( Booknotes.get_booknotes_for_work(olid)) r['occurrences']['observations'] = len( Observations.get_observations_for_work(olid)) # track updates r['updates']['readinglog'] = Bookshelves.update_work_id( olid, new_olid, _test=test ) r['updates']['ratings'] = Ratings.update_work_id( olid, new_olid, _test=test ) r['updates']['booknotes'] = Booknotes.update_work_id( olid, new_olid, _test=test ) r['updates']['observations'] = Observations.update_work_id( olid, new_olid, _test=test ) return delegate.RawText( json.dumps(summary), content_type="application/json")
def get_already_read(self, page=1, limit=RESULTS_PER_PAGE): return self.process_logged_books( Bookshelves.get_users_logged_books( self.user.get_username(), bookshelf_id=Bookshelves.PRESET_BOOKSHELVES['Already Read'], page=page, limit=limit))
def test_delete_all_by_username(self): assert len(list(self.db.select("bookshelves_books"))) == 3 Bookshelves.delete_all_by_username("@kilgore_trout") assert len(list(self.db.select("bookshelves_books"))) == 1 assert len(list(self.db.select("booknotes"))) == 2 Booknotes.delete_all_by_username('@kilgore_trout') assert len(list(self.db.select("booknotes"))) == 1 assert len(list(self.db.select("ratings"))) == 2 Ratings.delete_all_by_username("@kilgore_trout") assert len(list(self.db.select("ratings"))) == 1 assert len(list(self.db.select("observations"))) == 2 Observations.delete_all_by_username("@kilgore_trout") assert len(list(self.db.select("observations"))) == 1
def get_users_read_status(self, username): if not username: return None work_id = self.key.split('/')[2][2:-1] status_id = Bookshelves.get_users_read_status_of_work( username, work_id) return status_id
def get_users_read_status(self, username): if not username: return None work_id = extract_numeric_id_from_olid(self.key) status_id = Bookshelves.get_users_read_status_of_work( username, work_id) return status_id
def reading_log_counts(self): counts = Bookshelves.count_total_books_logged_by_user_per_shelf( self.user.get_username()) return { 'want-to-read': counts.get(Bookshelves.PRESET_BOOKSHELVES['Want to Read'], 0), 'currently-reading': counts.get(Bookshelves.PRESET_BOOKSHELVES['Currently Reading'], 0), 'already-read': counts.get(Bookshelves.PRESET_BOOKSHELVES['Already Read'], 0) }
def get_num_users_by_bookshelf(self): work_id = extract_numeric_id_from_olid(self.key) num_users_by_bookshelf = Bookshelves.get_num_users_by_bookshelf_by_work_id(work_id) return { 'want-to-read': num_users_by_bookshelf.get(Bookshelves.PRESET_BOOKSHELVES['Want to Read'], 0), 'currently-reading': num_users_by_bookshelf.get(Bookshelves.PRESET_BOOKSHELVES['Currently Reading'], 0), 'already-read': num_users_by_bookshelf.get(Bookshelves.PRESET_BOOKSHELVES['Already Read'], 0) }
def get_already_read(self, page=1, limit=RESULTS_PER_PAGE): work_ids = [ '/works/OL%sW' % i['work_id'] for i in Bookshelves.get_users_logged_books( self.user.get_username(), bookshelf_id=Bookshelves.PRESET_BOOKSHELVES['Already Read'], page=page, limit=limit) ] return web.ctx.site.get_many(work_ids)
def get_want_to_read(self, page=1, limit=100): work_ids = [ '/works/OL%sW' % i['work_id'] for i in Bookshelves.get_users_logged_books( self.user.get_username(), bookshelf_id=Bookshelves.PRESET_BOOKSHELVES['Want to Read'], page=page, limit=limit) ] return web.ctx.site.get_many(work_ids)
def resolve_redirect_chain(cls, work_key: str, test: bool = False) -> dict[str, Any]: summary: dict[str, Any] = { 'key': work_key, 'redirect_chain': [], 'resolved_key': None, } redirect_chain = cls.get_redirect_chain(work_key) summary['redirect_chain'] = [{ "key": thing.key, "occurrences": {}, "updates": {} } for thing in redirect_chain] summary['resolved_key'] = redirect_chain[-1].key for r in summary['redirect_chain']: olid = r['key'].split('/')[-1][2:-1] # 'OL1234x' --> '1234' new_olid = summary['resolved_key'].split('/')[-1][2:-1] # count reading log entries r['occurrences']['readinglog'] = len( Bookshelves.get_works_shelves(olid)) r['occurrences']['ratings'] = len( Ratings.get_all_works_ratings(olid)) r['occurrences']['booknotes'] = len( Booknotes.get_booknotes_for_work(olid)) r['occurrences']['observations'] = len( Observations.get_observations_for_work(olid)) # track updates r['updates']['readinglog'] = Bookshelves.update_work_id(olid, new_olid, _test=test) r['updates']['ratings'] = Ratings.update_work_id(olid, new_olid, _test=test) r['updates']['booknotes'] = Booknotes.update_work_id(olid, new_olid, _test=test) r['updates']['observations'] = Observations.update_work_id( olid, new_olid, _test=test) return summary
def get_already_read( self, page=1, limit=RESULTS_PER_PAGE, sort='created', sort_order='desc' ): return self.process_logged_books( Bookshelves.get_users_logged_books( self.user.get_username(), bookshelf_id=Bookshelves.PRESET_BOOKSHELVES['Already Read'], page=page, limit=limit, sort=sort + ' ' + sort_order, ) )
def add_read_statuses(username, works): work_ids = [ extract_numeric_id_from_olid(work.key.split('/')[-1]) for work in works ] results = Bookshelves.get_users_read_status_of_works(username, work_ids) results_map = {} for result in results: results_map[f"OL{result['work_id']}W"] = result['bookshelf_id'] for work in works: work_olid = work.key.split('/')[-1] work['readinglog'] = results_map.get(work_olid, None) return works
def generate_reading_log(self, username): books = Bookshelves.get_users_logged_books(username, limit=10000) csv = [] csv.append('Work Id,Edition Id,Bookshelf\n') mapping = {1: 'Want to Read', 2: 'Currently Reading', 3: 'Already Read'} for book in books: row = [ 'OL{}W'.format(book['work_id']), 'OL{}M'.format(book['edition_id']) if book['edition_id'] else '', '{}\n'.format(mapping[book['bookshelf_id']]), ] csv.append(','.join(row)) return ''.join(csv)
def GET(self): user = accounts.get_current_user() username = user.key.split('/')[-1] books = Bookshelves.get_users_logged_books(username, limit=10000) csv = [] csv.append('Work Id,Edition Id,Bookshelf\n') mapping = {1:'Want to Read', 2:'Currently Reading', 3:'Already Read'} for book in books: row = [ 'OL{}W'.format(book['work_id']), 'OL{}M'.format(book['edition_id']) if book['edition_id'] else '', '{}\n'.format(mapping[book['bookshelf_id']]) ] csv.append(','.join(row)) web.header('Content-Type','text/csv') web.header('Content-disposition', 'attachment; filename=OpenLibrary_ReadingLog.csv') csv = ''.join(csv) return delegate.RawText(csv, content_type="text/csv")
def anonymize(self, test=False): # Generate new unique username for patron: # Note: Cannot test get_activation_link() locally uuid = (self.get_activation_link()['code'] if self.get_activation_link() else generate_uuid()) new_username = f'anonymous-{uuid}' results = {'new_username': new_username} # Delete all of the patron's book notes: results['booknotes_count'] = Booknotes.delete_all_by_username( self.username, _test=test) # Anonymize patron's username in OL DB tables: results['ratings_count'] = Ratings.update_username(self.username, new_username, _test=test) results['observations_count'] = Observations.update_username( self.username, new_username, _test=test) results['bookshelves_count'] = Bookshelves.update_username( self.username, new_username, _test=test) if not test: patron = self.get_user() email = self.email username = self.username # Remove patron from all usergroups: for grp in patron.usergroups: grp.remove_user(patron.key) # Set preferences to default: patron.save_preferences({'updates': 'no', 'public_readlog': 'no'}) # Clear patron's profile page: data = {'key': patron.key, 'type': '/type/delete'} patron.set_data(data) # Remove account information from store: del web.ctx.site.store[f'account/{username}'] del web.ctx.site.store[f'account/{username}/verify'] del web.ctx.site.store[f'account/{username}/password'] del web.ctx.site.store[f'account-email/{email}'] return results
def test_update_simple(self): assert len(list(self.db.select("bookshelves_books"))) == 1 Bookshelves.update_work_id(self.source_book['work_id'], "2")
def get_read_status(work_key, username): work_id = extract_numeric_id_from_olid(work_key.split('/')[-1]) return Bookshelves.get_users_read_status_of_work(username, work_id)
def get_users_read_status(self, username): if not username: return None work_id = self.key.split('/')[2][2:-1] status_id = Bookshelves.get_users_read_status_of_work(username, work_id) return status_id
def get_already_read(self, page=1, limit=100): work_ids = ['/works/OL%sW' % i['work_id'] for i in Bookshelves.get_users_logged_books( self.user.get_username(), bookshelf_id=Bookshelves.PRESET_BOOKSHELVES['Already Read'], page=page, limit=limit)] return web.ctx.site.get_many(work_ids)
class export_books(delegate.page): path = "/account/export" date_format = '%Y-%m-%d %H:%M:%S' @require_login def GET(self): i = web.input(type='') filename = '' user = accounts.get_current_user() username = user.key.split('/')[-1] if i.type == 'reading_log': data = self.generate_reading_log(username) filename = 'OpenLibrary_ReadingLog.csv' elif i.type == 'book_notes': data = self.generate_book_notes(username) filename = 'OpenLibrary_BookNotes.csv' elif i.type == 'reviews': data = self.generate_reviews(username) filename = 'OpenLibrary_Reviews.csv' elif i.type == 'lists': with elapsed_time("user.get_lists()"): lists = user.get_lists(limit=1000) with elapsed_time("generate_list_overview()"): data = self.generate_list_overview(lists) filename = 'Openlibrary_ListOverview.csv' elif i.type == 'ratings': data = self.generate_star_ratings(username) filename = 'OpenLibrary_Ratings.csv' web.header('Content-Type', 'text/csv') web.header('Content-disposition', f'attachment; filename={filename}') return delegate.RawText('' or data, content_type="text/csv") def generate_reading_log(self, username: str) -> str: from openlibrary.plugins.upstream.models import Work # Avoid a circular import bookshelf_map = { 1: 'Want to Read', 2: 'Currently Reading', 3: 'Already Read' } def get_subjects(work: Work, subject_type: str) -> str: return " | ".join( s.title.replace(",", ";") for s in work.get_subject_links(subject_type)) def format_reading_log(book: dict) -> dict: """ Adding, deleting, renaming, or reordering the fields of the dict returned below will automatically be reflected in the CSV that is generated. """ work_key = f"/works/OL{book['work_id']}W" work: Work = web.ctx.site.get(work_key) if not work: raise ValueError(f"No Work found for {work_key}.") if edition_id := book.get("edition_id") or "": edition_id = f"OL{edition_id}M" ratings = work.get_rating_stats() or {"average": "", "count": ""} ratings_average, ratings_count = ratings.values() return { "work_id": work_key.split("/")[-1], "title": work.title, "authors": " | ".join(work.get_author_names()), "first_publish_year": work.first_publish_year, "edition_id": edition_id, "edition_count": work.edition_count, "bookshelf": bookshelf_map[work.get_users_read_status(username)], "my_ratings": work.get_users_rating(username) or "", "ratings_average": ratings_average, "ratings_count": ratings_count, "has_ebook": work.has_ebook(), "subjects": get_subjects(work=work, subject_type="subject"), "subject_people": get_subjects(work=work, subject_type="person"), "subject_places": get_subjects(work=work, subject_type="place"), "subject_times": get_subjects(work=work, subject_type="time"), } books = Bookshelves.iterate_users_logged_books(username) return csv_string(books, format_reading_log)
def get_users_read_status(self, username): if not username: return None work_id = extract_numeric_id_from_olid(self.key) status_id = Bookshelves.get_users_read_status_of_work(username, work_id) return status_id