import streamlit as st import pandas as pd import numpy as np from annoy import AnnoyIndex from functions.find_artist_data import * from functions.reorder_artist_data import * st.set_page_config( page_title="Demo app", page_icon="", layout="wide", initial_sidebar_state="expanded", menu_items={ 'About': "Contact: rain.bomberg@theorchard.com" } ) @st.cache # make it more efficient to load pickled files into memory def preparing_environment(): # Dataframes for this app can be recreated using project related notebooks. # Inside these notebooks you will find cells that will produce required parquet files. # load objects for just genre model main_artist_df = pd.read_parquet('algo_input_df.parquet') main_artist_df['search_string'] = main_artist_df['C_ARTIST_NAME'].str.lower() # load objects for showing related artists related_artist_df = pd.read_parquet('related_artists_df.parquet') return related_artist_df, main_artist_df # https://docs.streamlit.io/knowledge-base/deploy/authentication-without-sso def check_password(): """Returns `True` if the user had the correct password.""" def password_entered(): """Checks whether a password entered by the user is correct.""" if st.session_state["password"] == st.secrets["password"]: st.session_state["password_correct"] = True del st.session_state["password"] # don't store password else: st.session_state["password_correct"] = False if "password_correct" not in st.session_state: # First run, show input for password. st.text_input( "Password", type="password", on_change=password_entered, key="password" ) return False elif not st.session_state["password_correct"]: # Password not correct, show input + error. st.text_input( "Password", type="password", on_change=password_entered, key="password" ) st.error("😕 Password incorrect") return False else: # Password correct. return True def make_clickable(link): # target _blank to open new window # extract clickable text to display for your link return f'Open Spotify' if check_password(): # START OF ACTUAL APP CONTENT AFTER VALID PASSWORD HAS BEEN PROVIDED # Before starting script make sure that *.py file folder also contains required *.ann files. # These files can be created using project related notebooks. # Depending on what kind of similarities have been created, fill dictionaries accordingly. approaches = { 'a': ['C_GENRES'], 'b': ['C_GENRES', 'C_FAN_COUNTRY_CODE'], 'c': ['C_GENRES', 'C_FAN_COUNTRY_CODE', 'C_BAND', 'C_PRONOUN', 'C_GENDER'], } index_sizes = { 'a': 300, 'b': 200, 'c': 200, } descriptors = { 'a': 'This approach is based just on genre', 'b': 'This approach is based on genre and top 3 streaming countries', 'c': 'This approaches is based on genre, top 3 streaming locations and band, pronoun and gender indicators' } filenames = { 'a': 'a_test.ann', 'b': 'b_test.ann', 'c': 'c_test.ann', } for apr in approaches: globals()[f"approach_{apr}"] = AnnoyIndex(index_sizes.get(apr), 'angular') # using prefault should load file into memory but seems for MAC this feature is not usable globals()[f"approach_{apr}"].load(filenames.get(apr), prefault=True) nr_of_artists = 0 related_artists_df_for_pickle, algo_input_df = preparing_environment() st.markdown("

Similar artists demo


", unsafe_allow_html=True) st.markdown("STEP 1: Using left side menu, select approach to use. Name of the approach shows what data " "features are used for finding similarity between artists.", unsafe_allow_html=True) st.markdown("STEP 2: Then search for artist of interest. You can also use partial name. " "Click on the select button. After that main screen will be filled with similar artists.", unsafe_allow_html=True) st.markdown("STEP 3: Results. These are sorted by descending order. Meaning that most similar artists " " are at the top.", unsafe_allow_html=True) st.markdown("STEP 4: Filters. You can narrow down inital results by applying additional filters." "These filters can be found at the bottom of left side menu.", unsafe_allow_html=True) st.markdown("STEP 5: Listening. By clicking on the link under in the Spotify ID column you will be" " redirected to the Spotify website where you can listen for that artist.
", unsafe_allow_html=True) with st.sidebar: # define filters inside left side of app window st.markdown("Specify inputs", unsafe_allow_html=True) algo_list = ['genre', 'genre, streaming location', 'genre, streaming location, band, gender, pronoun'] algo_selection = st.selectbox('Select algorithm:', algo_list) artist = st.text_input('Type part of artist name to search for:', '') artist = artist.lower() if artist: st.write("Search string used: ", artist, ". Top 5 results sorted by popularity:", unsafe_allow_html=True) df_result_search = algo_input_df[algo_input_df['search_string'].str.contains(artist, na=False)][ ['SPOTIFY_ARTIST_ID', 'C_ARTIST_NAME', 'C_POPULARITY']].sort_values(by=['C_POPULARITY'], ascending=False).head() for header, column in zip(['Artist', 'Popularity', ''], st.columns((2, 1, 1))): column.subheader(header) for i, row in df_result_search.iterrows(): col1, col2, col3 = st.columns((2, 1, 1)) col1.write(row['C_ARTIST_NAME']) col2.write(row['C_POPULARITY']) if col3.button("Select", key=row['SPOTIFY_ARTIST_ID']): st.session_state['spotify_id'] = row['SPOTIFY_ARTIST_ID'] spotify_id = st.session_state.get('spotify_id') # spotify_id = st.text_input('Copy here the spotify id:', '') # nr_of_artists = st.slider('How many similar artist to show?(max 50)', 5, 50, 10, 5) nr_of_artists = 30 # apply additional filters to first similarity results options_gender = algo_input_df['C_GENDER'].unique().tolist() multi_options_gender = st.multiselect( 'Choose artist gender', options_gender) options_pronoun = algo_input_df['C_PRONOUN'].unique().tolist() multi_options_pronoun = st.multiselect( 'Choose artist pronoun', options_pronoun) options_band = algo_input_df['C_BAND'].unique().tolist() multi_options_band = st.multiselect( 'Is artist band', options_band) country_filter = st.text_input('Type 2 letter country code:', '') country_filter = country_filter.upper() if artist and spotify_id: st.write("Current similarity is based on ", algo_selection, ".") # select what data to use if algo_selection == 'genre': selected_algo = 'a' elif algo_selection == 'genre, streaming location': selected_algo = 'b' else: selected_algo = 'c' try: artist_desc, artist_index = get_artist_data(algo_input_df, spotify_id) if selected_algo == 'a': nearest_artists, distances = approach_a.get_nns_by_item(artist_index, nr_of_artists, include_distances=True) if selected_algo == 'b': nearest_artists, distances = approach_b.get_nns_by_item(artist_index, nr_of_artists, include_distances=True) else: nearest_artists, distances = approach_c.get_nns_by_item(artist_index, nr_of_artists, include_distances=True) response_tag = algo_input_df.loc[nearest_artists][ ['C_ARTIST_NAME', 'SPOTIFY_ARTIST_ID', 'C_POPULARITY', 'C_FAN_COUNTRY_CODE', 'C_GENRES', 'C_PRONOUN', 'C_GENDER', 'C_BAND']] response_tag.columns = ['ARTIST NAME', 'SPOTIFY ID', 'POPULARITY', 'TOP COUNTRIES', 'GENRES', 'PRONOUN', 'GENDER', 'BAND'] st.json(artist_desc) st.write("Showing most similar artists.") # create hyperlink using Spotify id response_tag['SPOTIFY ID'] = response_tag['SPOTIFY ID'].apply(make_clickable) artists_from_algo = response_tag['ARTIST NAME'].tolist() # check for filters if multi_options_gender: response_tag = response_tag[response_tag["GENDER"].isin(multi_options_gender)] if multi_options_pronoun: response_tag = response_tag[response_tag["PRONOUN"].isin(multi_options_pronoun)] if multi_options_band: response_tag = response_tag[response_tag["BAND"].isin(multi_options_band)] if country_filter: response_tag = response_tag[response_tag["TOP COUNTRIES"].str.contains(country_filter, na=True)] st.write(country_filter) # Move 10 most popular artists at the top based on popularity from artist itself response_tag = reorder_artists(response_tag, artist_desc.get('popularity', 50)) response_tag = response_tag.to_html(escape=False) st.write(response_tag, unsafe_allow_html=True) except KeyError: st.markdown("Did not find matching Spotify artist.", unsafe_allow_html=True) st.markdown("
", unsafe_allow_html=True) st.write("Showing most similar artists according to Spotify:") try: rel_artists = related_artists_df_for_pickle[['ARTIST_NAME', 'FOLLOWERS_LATEST', 'RELATED_ARTIST_ID']][ related_artists_df_for_pickle['MAIN_ARTIST'] == spotify_id] # create hyperlink using Spotify id rel_artists['RELATED_ARTIST_ID'] = rel_artists['RELATED_ARTIST_ID'].apply(make_clickable) artist_from_spotify = rel_artists['ARTIST_NAME'].tolist() rel_artists = rel_artists.style.set_precision(0) rel_artists = rel_artists.to_html(escape=False) st.write(rel_artists, unsafe_allow_html=True) # st.dataframe(rel_artists.style.set_precision(0)) common_artists = set(artists_from_algo).intersection(artist_from_spotify) st.write(f"Number of artist matching from both sources: {common_artists}") except NameError: st.markdown("Did not find matching related artists from Spotify.", unsafe_allow_html=True)