def search(isAutocomplete): searchStr = request.args.get("query", type=str) if not searchStr: return api_helpers.error_response("Invalid value for parameter 'query'") searchStr = searchStr.replace('&', 'and') # when creating query table, we substitute this too kind = request.args.get("kind", type=str, default="all") if kind not in SearchKind.__members__: return api_helpers.error_response( "Invalid value for parameter 'kind' must be in %s" % [k.name for k in SearchKind] ) searchKind = SearchKind[kind] (limit, offset) = get_pagination_vars() results = {} if searchStr: db = get_db_read_replica() with db.scoped_session() as session: # Set similarity threshold to be used by % operator in queries. session.execute(sqlalchemy.text(f"select set_limit({minSearchSimilarity});")) if (searchKind in [SearchKind.all, SearchKind.tracks]): results['tracks'] = track_search_query(session, searchStr, limit, offset, False, isAutocomplete) results['saved_tracks'] = track_search_query(session, searchStr, limit, offset, True, isAutocomplete) if (searchKind in [SearchKind.all, SearchKind.users]): results['users'] = user_search_query(session, searchStr, limit, offset, False, isAutocomplete) results['followed_users'] = user_search_query(session, searchStr, limit, offset, True, isAutocomplete) if (searchKind in [SearchKind.all, SearchKind.playlists]): results['playlists'] = playlist_search_query( session, searchStr, limit, offset, False, False, isAutocomplete ) results['saved_playlists'] = playlist_search_query( session, searchStr, limit, offset, False, True, isAutocomplete ) if (searchKind in [SearchKind.all, SearchKind.albums]): results['albums'] = playlist_search_query(session, searchStr, limit, offset, True, False, isAutocomplete) results['saved_albums'] = playlist_search_query( session, searchStr, limit, offset, True, True, isAutocomplete ) return api_helpers.success_response(results)
def milestones_followers(): db = get_db_read_replica() if "user_id" not in request.args: return api_helpers.error_response({'msg': 'Please provider user ids'}, 500) try: user_id_str_list = request.args.getlist("user_id") user_ids = [] user_ids = [int(y) for y in user_id_str_list] except ValueError as e: logger.error("Invalid value found in user id list", esc_info=True) return api_helpers.error_response({'msg': e}, 500) with db.scoped_session() as session: follower_counts = (session.query( Follow.followee_user_id, func.count(Follow.followee_user_id)).filter( Follow.is_current == True, Follow.is_delete == False, Follow.followee_user_id.in_(user_ids)).group_by( Follow.followee_user_id).all()) follower_count_dict = \ {user_id: follower_count for ( user_id, follower_count) in follower_counts} return api_helpers.success_response(follower_count_dict)
def user_signals(): handle = request.args.get("handle") if not handle: return error_response("Please pass in a handle") try: response = get_user_signals(handle) return success_response(response) except Exception as e: return error_response(f"Request failed: {e}")
def validate_search_args(args): searchStr = args.get("query") if not searchStr: return api_helpers.error_response( "Invalid value for parameter 'query'", 400) kind = args.get("kind", "all") if kind not in SearchKind.__members__: return api_helpers.error_response( "Invalid value for parameter 'kind' must be in %s" % [k.name for k in SearchKind], 400) return None
def get_playlist_repost_intersection_users(repost_playlist_id, follower_user_id): users = [] db = get_db() with db.scoped_session() as session: # ensure playlist_id exists playlist_entry = session.query(Playlist).filter( Playlist.playlist_id == repost_playlist_id, Playlist.is_current == True).first() if playlist_entry is None: return api_helpers.error_response( 'Resource not found for provided playlist id', 404) query = (session.query(User).filter( User.is_current == True, User.is_ready == True, User.user_id.in_( session.query(Repost.user_id).filter( Repost.repost_item_id == repost_playlist_id, Repost.repost_type != RepostType.track, Repost.is_current == True, Repost.is_delete == False).intersect( session.query(Follow.followee_user_id).filter( Follow.follower_user_id == follower_user_id, Follow.is_current == True, Follow.is_delete == False))))) users = paginate_query(query).all() users = helpers.query_result_to_list(users) return api_helpers.success_response(users)
def get_top_followee_windowed_route(type, window): """ Gets a windowed (over a certain timerange) view into the "top" of a certain type amongst followees. Requires an account. This endpoint is useful in generating views like: - New releases Args: type: (string) The `type` (same as repost/save type) to query from. Currently only track is supported. window: (string) The window from now() to look back over. Supports all standard SqlAlchemy interval notation (week, month, year, etc.). limit?: (number) default=25, max=100 """ args = to_dict(request.args) if 'limit' in request.args: args['limit'] = min(request.args.get('limit', type=int), 100) else: args['limit'] = 25 if "with_users" in request.args: args["with_users"] = parse_bool_param(request.args.get("with_users")) try: tracks = get_top_followee_windowed(type, window, args) return api_helpers.success_response(tracks) except exceptions.ArgumentError as e: return api_helpers.error_response(str(e), 400)
def get_saves_route(save_type): try: user_id = get_current_user_id() save_results = get_saves(save_type, user_id) return api_helpers.success_response(save_results) except exceptions.ArgumentError as e: return api_helpers.error_response(str(e), 400)
def get_top_followee_saves_route(type): """ Gets a global view into the most saved of `type` amongst followees. Requires an account. This endpoint is useful in generating views like: - Most favorited Args: type: (string) The `type` (same as repost/save type) to query from. Currently only track is supported. limit?: (number) default=25, max=100 """ args = to_dict(request.args) if "limit" in request.args: args["limit"] = min(request.args.get("limit", type=int), 100) else: args["limit"] = 25 if "with_users" in request.args: args["with_users"] = parse_bool_param(request.args.get("with_users")) user_id = get_current_user_id() args["user_id"] = user_id try: tracks = get_top_followee_saves(type, args) return api_helpers.success_response(tracks) except exceptions.ArgumentError as e: return api_helpers.error_response(str(e), 400)
def get_top_playlists_route(type): """ An endpoint to retrieve the "top" of a certain demographic of playlists or albums. This endpoint is useful in generating views like: - Top playlists - Top Albums - Top playlists of a certain mood - Top playlists of a certain mood from people you follow Args: type: (string) The `type` (same as repost/save type) to query from. limit?: (number) default=16, max=100 mood?: (string) default=None filter?: (string) Optional filter to include (supports 'followees') default=None """ args = to_dict(request.args) if 'limit' in request.args: args['limit'] = min(request.args.get('limit', type=int), 100) else: args['limit'] = 16 if 'mood' in request.args: args['mood'] = request.args.get('mood') else: args['mood'] = None if "with_users" in request.args: args["with_users"] = parse_bool_param(request.args.get("with_users")) try: playlists = get_top_playlists(type, args) return api_helpers.success_response(playlists) except exceptions.ArgumentError as e: return api_helpers.error_response(str(e), 400)
def get_track_listen_milestone_data(): try: # Assign value only if not None or empty string data = get_track_listen_milestones(100) return api_helpers.success_response(data) except exceptions.ArgumentError as e: return api_helpers.error_response(str(e), 400)
def get_playlist_repost_intersection_users_route(repost_playlist_id, follower_user_id): try: users = get_playlist_repost_intersection_users( repost_playlist_id, follower_user_id) return api_helpers.success_response(users) except exceptions.NotFoundError as e: return api_helpers.error_response(str(e), 404)
def get_ursm_content_nodes(): try: # Assign value only if not None or empty string owner_wallet = request.args.get("owner_wallet") or None cnodes = get_ursm_cnodes(owner_wallet) return api_helpers.success_response(cnodes) except exceptions.ArgumentError as e: return api_helpers.error_response(str(e), 400)
def get_sol_play_tx(): try: # Assign value only if not None or empty string tx_sig = request.args.get("tx_sig") or None sig = get_sol_play(tx_sig) return api_helpers.success_response(sig) except exceptions.ArgumentError as e: return api_helpers.error_response(str(e), 400)
def get_creator_node_users(): try: if "creator_node_endpoint" not in request.args: raise exceptions.ArgumentError("Missing creator_node_endpoint") cnode_url = request.args.get("creator_node_endpoint") users = get_users_cnode(cnode_url) return api_helpers.success_response(users) except exceptions.ArgumentError as e: return api_helpers.error_response(str(e), 400)
def get_remixes_of_route(track_id): args = to_dict(request.args) if "with_users" in request.args: args["with_users"] = parse_bool_param(request.args.get("with_users")) try: remixes = get_remixes_of(track_id, args) return api_helpers.success_response(remixes) except exceptions.ArgumentError as e: return api_helpers.error_response(str(e), 400)
def check_transaction_status(): """ Returns whether a transaction 'PASSED' | 'FAILED' | 'NOT_FOUND' based on all 3 query params 'blocknumber', 'blockhash', and 'transactionhash' """ blocknumber = request.args.get("blocknumber", type=int) blockhash = request.args.get("blockhash") transactionhash = request.args.get("transactionhash") if blocknumber is None or blockhash is None or transactionhash is None: return error_response( "Please pass in required query parameters 'blocknumber', 'blockhash', and 'transactionhash'", 400, ) try: transaction_status = get_transaction_status( blocknumber, blockhash, transactionhash ) except Exception as e: return error_response(e) return success_response(transaction_status)
def get_user_history_route(user_id): try: (limit, offset) = get_pagination_vars() args = { "user_id": user_id, "limit": limit, "offset": offset, } user_history = get_user_history(args) return api_helpers.success_response(user_history) except exceptions.ArgumentError as e: return api_helpers.error_response(str(e), 400)
def milestones_followers(): db = get_db_read_replica() if "user_id" not in request.args: return api_helpers.error_response({"msg": "Please provider user ids"}, 500) try: user_id_str_list = request.args.getlist("user_id") user_ids = [] user_ids = [int(y) for y in user_id_str_list] except ValueError as e: logger.error("Invalid value found in user id list", esc_info=True) return api_helpers.error_response({"msg": e}, 500) with db.scoped_session() as session: follower_counts = ( session.query(AggregateUser.user_id, AggregateUser.follower_count) .filter(AggregateUser.user_id.in_(user_ids)) .all() ) follower_count_dict = dict(follower_counts) return api_helpers.success_response(follower_count_dict)
def block_confirmation(): blockhash = request.args.get("blockhash") blocknumber = request.args.get("blocknumber", type=int) response = {"block_found": False, "block_passed": False} bad_request = True if blockhash is not None and blocknumber is not None: bad_request = False logger.info(f"block_confirmation | ARGS: {blockhash, blocknumber}") try: response = get_block_confirmation(blockhash, blocknumber) except Exception as e: return error_response(e) return success_response(response, 400 if bad_request else 200)
def get_savers_for_playlist_route(save_playlist_id): try: current_user_id = get_current_user_id(required=False) (limit, offset) = get_pagination_vars() args = { 'save_playlist_id': save_playlist_id, 'current_user_id': current_user_id, 'limit': limit, 'offset': offset } user_results = get_savers_for_playlist(args) return api_helpers.success_response(user_results) except exceptions.NotFoundError as e: return api_helpers.error_response(str(e), 404)
def get_savers_for_track_route(save_track_id): try: current_user_id = get_current_user_id(required=False) (limit, offset) = get_pagination_vars() args = { "save_track_id": save_track_id, "current_user_id": current_user_id, "limit": limit, "offset": offset, } user_results = get_savers_for_track(args) return api_helpers.success_response(user_results) except exceptions.NotFoundError as e: return api_helpers.error_response(str(e), 404)
def get_remixes_of_route(track_id): args = to_dict(request.args) args["track_id"] = track_id args["current_user_id"] = get_current_user_id(required=False) limit, offset = get_pagination_vars() args["limit"] = limit args["offset"] = offset if "with_users" in request.args: args["with_users"] = parse_bool_param(request.args.get("with_users")) try: remixes = get_remixes_of(args) return api_helpers.success_response(remixes) except exceptions.ArgumentError as e: return api_helpers.error_response(str(e), 400)
def get(self, app_name): """Get the app name metrics""" args = metrics_app_name_parser.parse_args() if args.get('limit') is None: args['limit'] = 48 else: args['limit'] = min(args.get('limit'), 48) try: args['start_time'] = datetime.utcfromtimestamp(args['start_time']) except: return api_helpers.error_response( 'Poorly formated start_time parameter', 400) app_name_metrics = get_app_name_metrics(app_name, args) response = success_response(app_name_metrics) return response
def get_reposters_for_track(repost_track_id): user_results = [] db = get_db() with db.scoped_session() as session: # ensure track_id exists track_entry = session.query(Track).filter( Track.track_id == repost_track_id, Track.is_current == True ).first() if track_entry is None: return api_helpers.error_response('Resource not found for provided track id', 404) follower_count_subquery = ( session.query( Follow.followee_user_id, func.count(Follow.followee_user_id).label(response_name_constants.follower_count) ) .filter( Follow.is_current == True, Follow.is_delete == False ) .group_by(Follow.followee_user_id) .order_by(response_name_constants.follower_count + " desc") ) follower_count_subquery = paginate_query(follower_count_subquery).subquery() query = ( session.query(User, follower_count_subquery.c.follower_count) .join(Repost, User.user_id == Repost.user_id) .outerjoin(follower_count_subquery, follower_count_subquery.c.followee_user_id == User.user_id) .filter( User.is_current == True, User.is_ready == True, Repost.repost_item_id == repost_track_id, Repost.repost_type == RepostType.track, Repost.is_current == True, Repost.is_delete == False ) ) user_results = query.all() if user_results: users, follower_counts = zip(*user_results) user_results = helpers.query_result_to_list(users) for i, user in enumerate(user_results): user[response_name_constants.follower_count] = follower_counts[i] or 0 return api_helpers.success_response(user_results)
def search_tags(): search_str = request.args.get("query", type=str) current_user_id = get_current_user_id(required=False) if not search_str: raise exceptions.ArgumentError("Invalid value for parameter 'query'") user_tag_count = request.args.get("user_tag_count", type=str) if not user_tag_count: user_tag_count = "2" kind = request.args.get("kind", type=str, default="all") validSearchKinds = [SearchKind.all, SearchKind.tracks, SearchKind.users] try: searchKind = SearchKind[kind] if searchKind not in validSearchKinds: raise Exception except Exception: return api_helpers.error_response( "Invalid value for parameter 'kind' must be in %s" % [k.name for k in validSearchKinds], 400) results = {} (limit, offset) = get_pagination_vars() like_tags_str = str.format('%{}%', search_str) db = get_db_read_replica() with db.scoped_session() as session: if (searchKind in [SearchKind.all, SearchKind.tracks]): track_res = sqlalchemy.text(f""" select distinct(track_id) from ( select strip(to_tsvector(tracks.tags)) as tagstrip, track_id from tracks where (tags like :like_tags_query) and (is_current is true) and (is_delete is false) and (is_unlisted is false) and (stem_of is NULL) order by updated_at desc ) as t where tagstrip @@ to_tsquery(:query); """) track_ids = session.execute(track_res, { "query": search_str, "like_tags_query": like_tags_str }).fetchall() # track_ids is list of tuples - simplify to 1-D list track_ids = [i[0] for i in track_ids] tracks = (session.query(Track).filter( Track.is_current == True, Track.is_delete == False, Track.is_unlisted == False, Track.stem_of == None, Track.track_id.in_(track_ids), ).all()) tracks = helpers.query_result_to_list(tracks) track_play_counts = get_track_play_counts(track_ids) tracks = populate_track_metadata(session, track_ids, tracks, current_user_id) for track in tracks: track_id = track["track_id"] track[response_name_constants. play_count] = track_play_counts.get(track_id, 0) play_count_sorted_tracks = \ sorted( tracks, key=lambda i: i[response_name_constants.play_count], reverse=True) # Add pagination parameters to track and user results play_count_sorted_tracks = \ play_count_sorted_tracks[slice(offset, offset + limit, 1)] results['tracks'] = play_count_sorted_tracks if (searchKind in [SearchKind.all, SearchKind.users]): user_res = sqlalchemy.text(f""" select * from ( select count(track_id), owner_id from ( select strip(to_tsvector(tracks.tags)) as tagstrip, track_id, owner_id from tracks where (tags like :like_tags_query) and (is_current is true) and (is_unlisted is false) and (stem_of is NULL) order by updated_at desc ) as t where tagstrip @@ to_tsquery(:query) group by owner_id order by count desc ) as usr where usr.count >= :user_tag_count; """) user_ids = session.execute( user_res, { "query": search_str, "like_tags_query": like_tags_str, "user_tag_count": user_tag_count }).fetchall() # user_ids is list of tuples - simplify to 1-D list user_ids = [i[1] for i in user_ids] users = (session.query(User).filter( User.is_current == True, User.user_id.in_(user_ids)).all()) users = helpers.query_result_to_list(users) users = populate_user_metadata(session, user_ids, users, current_user_id) followee_sorted_users = \ sorted( users, key=lambda i: i[response_name_constants.follower_count], reverse=True) followee_sorted_users = \ followee_sorted_users[slice(offset, offset + limit, 1)] results['users'] = followee_sorted_users # Add personalized results for a given user if current_user_id: if (searchKind in [SearchKind.all, SearchKind.tracks]): # Query saved tracks for the current user that contain this tag saves_query = (session.query(Save.save_item_id).filter( Save.is_current == True, Save.is_delete == False, Save.save_type == SaveType.track, Save.user_id == current_user_id, Save.save_item_id.in_(track_ids)).all()) saved_track_ids = [i[0] for i in saves_query] saved_tracks = (session.query(Track).filter( Track.is_current == True, Track.is_delete == False, Track.is_unlisted == False, Track.stem_of == None, Track.track_id.in_(saved_track_ids), ).all()) saved_tracks = helpers.query_result_to_list(saved_tracks) for saved_track in saved_tracks: saved_track_id = saved_track["track_id"] saved_track[response_name_constants.play_count] = \ track_play_counts.get(saved_track_id, 0) saved_tracks = \ populate_track_metadata( session, saved_track_ids, saved_tracks, current_user_id) # Sort and paginate play_count_sorted_saved_tracks = \ sorted( saved_tracks, key=lambda i: i[response_name_constants.play_count], reverse=True) play_count_sorted_saved_tracks = \ play_count_sorted_saved_tracks[slice( offset, offset + limit, 1)] results['saved_tracks'] = play_count_sorted_saved_tracks if (searchKind in [SearchKind.all, SearchKind.users]): # Query followed users that have referenced this tag followed_user_query = (session.query( Follow.followee_user_id).filter( Follow.is_current == True, Follow.is_delete == False, Follow.follower_user_id == current_user_id, Follow.followee_user_id.in_(user_ids)).all()) followed_user_ids = [i[0] for i in followed_user_query] followed_users = (session.query(User).filter( User.is_current == True, User.user_id.in_(followed_user_ids)).all()) followed_users = helpers.query_result_to_list(followed_users) followed_users = \ populate_user_metadata( session, followed_user_ids, followed_users, current_user_id ) followed_users_followee_sorted = \ sorted( followed_users, key=lambda i: i[response_name_constants.follower_count], reverse=True) followed_users_followee_sorted = \ followed_users_followee_sorted[slice( offset, offset + limit, 1)] results['followed_users'] = followed_users_followee_sorted return api_helpers.success_response(results)
def get_previously_private_playlists_route(): try: playlists = get_previously_private_playlists(to_dict(request.args)) return api_helpers.success_response(playlists) except exceptions.ArgumentError as e: return api_helpers.error_response(str(e), 400)
def get_previously_unlisted_tracks_route(): try: tracks = get_previously_unlisted_tracks(to_dict(request.args)) return api_helpers.success_response(tracks) except exceptions.ArgumentError as e: return api_helpers.error_response(str(e), 400)
def handle_404(_error): # pylint: disable=W0612 return api_helpers.error_response(["Route does not exist"], 404)
def get_users_account_route(): try: user = get_users_account(to_dict(request.args)) return api_helpers.success_response(user) except exceptions.ArgumentError as e: return api_helpers.error_response(str(e), 400)
def get_max_id_route(type): try: latest = get_max_id(type) return api_helpers.success_response(latest) except exceptions.ArgumentError as e: return api_helpers.error_response(str(e), 400)