def test_insert_user_artist_map(self): """ Test if daily activity stats are inserted correctly """ with open(self.path_to_data_file('user_artist_map_db.json')) as f: artist_map_data = json.load(f) db_stats.insert_user_artist_map( user_id=self.user['id'], artist_map=UserArtistMapStatJson(**{'all_time': artist_map_data})) result = db_stats.get_user_artist_map(user_id=self.user['id'], stats_range='all_time') self.assertDictEqual(result.all_time.dict(), artist_map_data)
def test_insert_user_stats_mult_ranges_artist_map(self): """ Test if multiple time range data is inserted correctly """ with open(self.path_to_data_file('user_artist_map_db.json')) as f: artist_map_data = json.load(f) db_stats.insert_user_artist_map( user_id=self.user['id'], artist_map=UserArtistMapStatJson(**{'year': artist_map_data})) db_stats.insert_user_artist_map( self.user['id'], UserArtistMapStatJson(**{'all_time': artist_map_data})) result = db_stats.get_user_artist_map(1, 'all_time') self.assertDictEqual(result.all_time.dict(), artist_map_data) result = db_stats.get_user_artist_map(1, 'year') self.assertDictEqual(result.year.dict(), artist_map_data)
def insert_test_data(self): """ Insert test data into the database """ with open(self.path_to_data_file('user_top_artists_db.json')) as f: user_artists = json.load(f) with open(self.path_to_data_file('user_top_releases_db.json')) as f: releases = json.load(f) with open(self.path_to_data_file('user_top_recordings_db.json')) as f: recordings = json.load(f) with open(self.path_to_data_file('user_listening_activity_db.json')) as f: listening_activity = json.load(f) with open(self.path_to_data_file('user_daily_activity_db.json')) as f: daily_activity = json.load(f) with open(self.path_to_data_file('user_artist_map_db.json')) as f: artist_map = json.load(f) with open(self.path_to_data_file('sitewide_top_artists_db.json')) as f: sitewide_artists = json.load(f) db_stats.insert_user_artists(self.user['id'], UserArtistStatJson(**{'all_time': user_artists})) db_stats.insert_user_releases(self.user['id'], UserReleaseStatJson(**{'all_time': releases})) db_stats.insert_user_recordings(self.user['id'], UserRecordingStatJson(**{'all_time': recordings})) db_stats.insert_user_listening_activity( self.user['id'], UserListeningActivityStatJson(**{'all_time': listening_activity})) db_stats.insert_user_daily_activity(self.user['id'], UserDailyActivityStatJson(**{'all_time': daily_activity})) db_stats.insert_user_artist_map(self.user['id'], UserArtistMapStatJson(**{'all_time': artist_map})) db_stats.insert_sitewide_artists('all_time', SitewideArtistStatJson(**sitewide_artists)) return { 'user_artists': user_artists, 'user_releases': releases, 'user_recordings': recordings, 'user_listening_activity': listening_activity, 'user_daily_activity': daily_activity, 'user_artist_map': artist_map, 'sitewide_artists': sitewide_artists }
def get_artist_map(user_name: str): """ Get the artist map for user ``user_name``. The artist map shows the number of artists the user has listened to from different countries of the world. A sample response from the endpoint may look like:: { "payload": { "from_ts": 1587945600, "last_updated": 1592807084, "artist_map": [ { "country": "USA", "artist_count": 34 }, { "country": "GBR", "artist_count": 69 }, { "country": "IND", "artist_count": 32 } ], "stats_range": "all_time" "to_ts": 1589155200, "user_id": "ishaanshah" } } .. note:: - This endpoint is currently in beta - We cache the results for this query for a week to improve page load times, if you want to request fresh data you can use the ``force_recalculate`` flag. :param range: Optional, time interval for which statistics should be returned, possible values are ``week``, ``month``, ``year``, ``all_time``, defaults to ``all_time`` :type range: ``str`` :param force_recalculate: Optional, recalculate the data instead of returning the cached result. :type range: ``bool`` :statuscode 200: Successful query, you have data! :statuscode 204: Statistics for the user haven't been calculated, empty response will be returned :statuscode 400: Bad request, check ``response['error']`` for more details :statuscode 404: User not found :resheader Content-Type: *application/json* """ user = db_user.get_by_mb_id(user_name) if user is None: raise APINotFound("Cannot find user: {}".format(user_name)) stats_range = request.args.get('range', default='all_time') if not _is_valid_range(stats_range): raise APIBadRequest("Invalid range: {}".format(stats_range)) recalculate_param = request.args.get('force_recalculate', default='false') if recalculate_param.lower() not in ['true', 'false']: raise APIBadRequest( "Invalid value of force_recalculate: {}".format(recalculate_param)) force_recalculate = recalculate_param.lower() == 'true' # Check if stats are present in DB, if not calculate them calculated = not force_recalculate stats = db_stats.get_user_artist_map(user['id'], stats_range) if stats is None or getattr(stats, stats_range) is None: calculated = False # Check if the stats present in DB have been calculated in the past week, if not recalculate them stale = False if calculated: last_updated = getattr(stats, stats_range).last_updated if (datetime.now() - datetime.fromtimestamp(last_updated) ).days >= STATS_CALCULATION_INTERVAL: stale = True if stale or not calculated: artist_stats = db_stats.get_user_artists(user['id'], stats_range) # If top artists are missing, return the stale stats if present, otherwise return 204 if artist_stats is None or getattr(artist_stats, stats_range) is None: if stale: result = stats else: raise APINoContent('') else: # Calculate the data artist_msids = defaultdict(lambda: 0) artist_mbids = defaultdict(lambda: 0) top_artists = getattr(artist_stats, stats_range).artists for artist in top_artists: if artist.artist_msid is not None: artist_msids[artist.artist_msid] += artist.listen_count else: for artist_mbid in artist.artist_mbids: artist_mbids[artist_mbid] += artist.listen_count country_code_data = _get_country_codes(artist_msids, artist_mbids) result = UserArtistMapStatJson( **{ stats_range: { "artist_map": country_code_data, "from_ts": int( getattr(artist_stats, stats_range).from_ts), "to_ts": int(getattr(artist_stats, stats_range).to_ts), "last_updated": int(datetime.now().timestamp()) } }) # Store in DB for future use try: db_stats.insert_user_artist_map(user['id'], result) except Exception as err: current_app.logger.error( "Error while inserting artist map stats for {user}. Error: {err}. Data: {data}" .format(user=user_name, err=err, data=result), exc_info=True) else: result = stats return jsonify({ "payload": { "user_id": user_name, "range": stats_range, **(getattr(result, stats_range).dict()) } })