import pickle import streamlit as st st.set_page_config(layout="wide") from streamlit_modal import Modal import numpy as np import bookdb import ai_services st.header("My Book Buddy 🐛") if "clicked_book" not in st.session_state: st.session_state["clicked_book"] = {} if "closestReadBook" not in st.session_state: st.session_state["closestReadBook"] = None if "suggestedBookText" not in st.session_state: st.session_state["suggestedBookText"] = "" if "upvoted_book_ids" not in st.session_state: st.session_state["upvoted_book_ids"] = [] if "downvoted_book_ids" not in st.session_state: st.session_state["downvoted_book_ids"] = [] if "recommendedBooksData" not in st.session_state: st.session_state["recommendedBooksData"] = None if "topCorrelatedReadersData" not in st.session_state: st.session_state["topCorrelatedReadersData"] = None if "numSimilarUsers" not in st.session_state: st.session_state["numSimilarUsers"] = 0 def update_display(displayData): st.session_state["upvoted_book_ids"] = displayData["upvotedBookIds"] st.session_state["downvoted_book_ids"] = displayData["downvotedBookIds"] st.session_state["numSimilarUsers"] = displayData["numSimilarUsers"] st.session_state["recommendedBooksData"] = displayData["recommendedBooksData"] st.session_state["topCorrelatedReadersData"] = displayData["topCorrelatedReadersData"] st.session_state["bookByRatingData"] = displayData["bookByRatingData"] def on_reset_votes(): st.session_state["upvoted_book_ids"] = [] st.session_state["downvoted_book_ids"] = [] st.session_state["numSimilarUsers"] = 0 st.session_state["recommendedBooksData"] = None st.session_state["topCorrelatedReadersData"] = None st.session_state["bookByRatingData"] = None def on_test_submit_votes(): upvotedBookIds = [104, 103, 102, 110, 113, 124, 129, 135, 141, 142, 155, 161, 165, 176, 181, 974, 4443, 1496, 1003, 2600] # TODO REMOVE downvotedBookIds = [126, 179, 183, 184, 187, 9076, 960, 5895, 777, 6902, 2084, 584] # TODO REMOVE update_display(bookdb.update_user_ratings(upvotedBookIds, downvotedBookIds)) def on_submit_votes(): upvoteBookTitles = st.session_state["multiselect_upvote"] downvoteBookTitles = st.session_state["multiselect_downvote"] if len(upvoteBookTitles) == 0 and len(downvoteBookTitles) == 0: st.warning("Please select at least one book to upvote or downvote") return booksToUpvote = bookdb.get_book_ids_by_title(upvoteBookTitles) booksToDownvote = bookdb.get_book_ids_by_title(downvoteBookTitles) # get the currently voted book ids upvotedBookIds = st.session_state["upvoted_book_ids"] downvotedBookIds = st.session_state["downvoted_book_ids"] if (len(upvotedBookIds) + len(booksToUpvote)) == 0 or (len(downvotedBookIds) + len(booksToDownvote)) == 0: st.warning("You must select at least one book to upvote and one book to downvote") return # append booksToUpvote to upvotedBookIds upvotedBookIds.extend(booksToUpvote) # append booksToDownvote to downvotedBookIds downvotedBookIds.extend(booksToDownvote) # remove any upvoted books from downvotedBookIds if they are in there downvotedBookIds = [x for x in downvotedBookIds if x not in upvotedBookIds] # clear the multiselects st.session_state.multiselect_upvote = [] st.session_state.multiselect_downvote = [] update_display(bookdb.update_user_ratings(upvotedBookIds, downvotedBookIds)) clickedBook = st.session_state["clicked_book"] if "clicked_book" in st.session_state else {} modal = Modal( f"📖 {clickedBook['title'] if 'title' in clickedBook else ''}", key="book-modal" ) def on_update_recommendations(): # iterate through the number of recommendations and check the selected radio button recommendations = st.session_state["recommendedBooksData"] bookIdsToUpvote = [] bookIdsToDownvote = [] for counter in range(len(recommendations)): radioKey = f"update_recommendation_{counter}" radioValue = st.session_state[radioKey] if radioValue == "👍": bookIdsToUpvote.append(recommendations[counter]["book_id"]) elif radioValue == "👎": bookIdsToDownvote.append(recommendations[counter]["book_id"]) # reset the radio button st.session_state[radioKey] = None if len(bookIdsToUpvote) == 0 and len(bookIdsToDownvote) == 0: st.warning("Please select at least one book to upvote or downvote") return # append booksToUpvote to upvotedBookIds upvotedBookIds = st.session_state["upvoted_book_ids"] downvotedBookIds = st.session_state["downvoted_book_ids"] upvotedBookIds.extend(bookIdsToUpvote) downvotedBookIds.extend(bookIdsToDownvote) # remove any upvoted books from downvotedBookIds if they are in there downvotedBookIds = [x for x in downvotedBookIds if x not in upvotedBookIds] update_display(bookdb.update_user_ratings(upvotedBookIds, downvotedBookIds)) if st.session_state["recommendedBooksData"] is not None: recommendations = st.session_state["recommendedBooksData"] st.subheader("Recommendations") recommendationsCol1, recommendationsCol2, recommendationsCol3 = st.columns(3) for counter in range(len(recommendations)): bookData = recommendations[counter] bookMetadata = bookdb.get_book_metadata_by_id(bookData["book_id"]) # print(bookMetadata) if counter % 3 == 0: container = recommendationsCol1.container(border=True) elif counter % 3 == 1: container = recommendationsCol2.container(border=True) else: container = recommendationsCol3.container(border=True) bookAuthor = bookData["authors"] bookTitle = bookData["title"] bookPubYear = bookData["original_publication_year"] containerCol1, containerCol2, containerCol3 = container.columns([5, 10, 2]) containerCol1.html(f"") textContainer = containerCol2.container() titleClicked = textContainer.button(f"{bookTitle}", use_container_width=True) if titleClicked: bookByRatingData = st.session_state["bookByRatingData"] upvotedBookIds = st.session_state["upvoted_book_ids"] targetBookId = bookData["book_id"] closestReadBookData = bookdb.find_closest_read_title(bookByRatingData, upvotedBookIds, targetBookId) targetBookData = bookdb.get_book_data_by_id(targetBookId) st.session_state["closestReadBook"] = closestReadBookData st.session_state["clicked_book"] = bookData st.session_state["suggestedBookText"] = ai_services.get_suggestion_text(closestReadBookData, targetBookData) modal.open() textContainer.markdown(f"*{bookAuthor}*") textContainer.markdown(f"Published: {int(bookPubYear)}") # containerCol2.markdown(textHTMLcontent, unsafe_allow_html=True) # containerCol2.markdown(f"[{bookTitle}](https://streamlit.io)\n\n*{bookAuthor}*\n\nPublished: {int(bookPubYear)}") # radioContainer = containerCol3.container() containerCol3.radio(label="Upvote/Downvote", label_visibility="hidden", options=["👍", "👎"], key=f"update_recommendation_{counter}", index=None) # radioContainer.button('Why This Book?', type="secondary", key=f"submit_{counter}") st.button('Update Recommendations', type="primary", on_click=on_update_recommendations) st.subheader("Your Ratings") displayCol1, displayCol2 = st.columns(2) if len(st.session_state["upvoted_book_ids"]) > 0: upvotedBookIds = st.session_state["upvoted_book_ids"] displayCol1.markdown(f"Upvoted: {len(upvotedBookIds)} book(s)") for bookId in upvotedBookIds: displayCol1.markdown(f' - {bookId}-{bookdb.get_book_title(bookId)}') if len(st.session_state["downvoted_book_ids"]) > 0: downvotedBookIds = st.session_state["downvoted_book_ids"] displayCol2.markdown(f'Downvoted: {len(downvotedBookIds)} book(s)') for bookId in downvotedBookIds: displayCol2.markdown(f' - {bookId}-{bookdb.get_book_title(bookId)}') st.button('Reset All Ratings', type="secondary", on_click=on_reset_votes) # st.write(f"Similar User Min Percent Shared Books = {round(bookdb.SIMILAR_USER_MIN_PERCENT_SHARED_BOOKS * 100)}%") # st.write(f"Similar User Min Correlation = {bookdb.SIMILAR_USER_MIN_CORRELATION}") # if "numSimilarUsers" in st.session_state: # st.write(f"{st.session_state['numSimilarUsers']} similar users") with st.form(key='upvote_form'): col1, col2 = st.columns(2) allBookTitles = bookdb.get_all_book_titles() myRatedBookTitles = bookdb.get_book_titles(st.session_state["upvoted_book_ids"] + st.session_state["downvoted_book_ids"]) # remove myRatedBookTitles from allBookTitles remainingBookTitles = [x for x in allBookTitles if x not in myRatedBookTitles] col1.multiselect( 'Upvote Books 👍', remainingBookTitles, key='multiselect_upvote' ) col2.multiselect( 'Downvote Books 👎', remainingBookTitles, key='multiselect_downvote' ) st.form_submit_button(label='Submit', type="primary", on_click=on_submit_votes) st.form_submit_button(label='Test', type="secondary", on_click=on_test_submit_votes) if st.session_state["topCorrelatedReadersData"] is not None: df = st.session_state["topCorrelatedReadersData"] st.subheader("Top Correlated Readers") st.dataframe(df, use_container_width=True) if modal.is_open(): with modal.container(): clickedBook = st.session_state["clicked_book"] clickedBookMetadata = bookdb.get_book_metadata_by_id(clickedBook["book_id"]) clickedBookDescription = clickedBookMetadata["description"] st.html(f"{clickedBookDescription}") aiSuggestContainer = st.container(border=True) closestReadBook = st.session_state["closestReadBook"] suggestedBookText = st.session_state["suggestedBookText"] aiSuggestContainer.html(f"

💫 Because you liked {closestReadBook['title']} by {closestReadBook['authors']}...

") aiSuggestContainer.html(f"

{suggestedBookText}

") buttonCol1, buttonCol2, buttonCol3, buttonCol4 = st.columns(4) clickedBookTitle = clickedBook["title"] clickedBookAuthor = clickedBook["authors"] clickedBookISBN = clickedBook["isbn"] outboundLinkSuffix = f"{clickedBookTitle} {clickedBookAuthor}" buttonCol1.link_button('🛒 Bookshop', f'https://bookshop.org/search?keywords={outboundLinkSuffix}') buttonCol2.link_button('📚 Biblio', f'https://www.biblio.com/search.php?stage=1&title={outboundLinkSuffix}') buttonCol3.link_button('🎧 Libro', f'https://libro.fm/search?utf8=%E2%9C%93&q={outboundLinkSuffix}') buttonCol4.link_button('💬 Hardcover', f'https://hardcover.app/search?q={outboundLinkSuffix}') st.write("Version 0.1.0")