Beispiel #1
0
    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)
Beispiel #2
0
    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 insert_user_artist_map(user_id: int, artist_map: UserArtistMapStatJson):
    """Inserts artist_map stats calculated from Spark into the database.

       If stats are already present for some user, they are updated to the new
       values passed.

       Args: user_id: the row id of the user,
             artist_map: the artist_map stats of the user
    """
    _insert_user_jsonb_data(user_id=user_id,
                            column='artist_map',
                            data=artist_map.dict(exclude_none=True))
Beispiel #4
0
    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())
        }
    })