Ejemplo n.º 1
0
def _playlist_resultset_to_model(connection, result, load_recordings):
    """Parse the result of an sql query to get playlists

    Fill in related data (username, created_for username) and collaborators
    """
    playlists = []
    user_id_map = {}
    for row in result:
        creator_id = row["creator_id"]
        if creator_id not in user_id_map:
            # TODO: Do this lookup in bulk
            user_id_map[creator_id] = db_user.get(creator_id)
        created_for_id = row["created_for_id"]
        if created_for_id and created_for_id not in user_id_map:
            user_id_map[created_for_id] = db_user.get(created_for_id)
        row = dict(row)
        row["creator"] = user_id_map[creator_id]["musicbrainz_id"]
        if created_for_id:
            row["created_for"] = user_id_map[created_for_id]["musicbrainz_id"]
        row["recordings"] = []
        playlist = model_playlist.Playlist.parse_obj(row)
        playlists.append(playlist)

    playlist_ids = [p.id for p in playlists]
    if playlist_ids:
        if load_recordings:
            playlist_recordings = get_recordings_for_playlists(connection, playlist_ids)
            for p in playlists:
                p.recordings = playlist_recordings.get(p.id, [])
        playlist_collaborator_ids = get_collaborators_for_playlists(connection, playlist_ids)
        for p in playlists:
            p.collaborator_ids = playlist_collaborator_ids.get(p.id, [])
            p.collaborators = get_collaborators_names_from_ids(p.collaborator_ids)

    return playlists
Ejemplo n.º 2
0
def get_by_mbid(
        playlist_id: str,
        load_recordings: bool = True) -> Optional[model_playlist.Playlist]:
    """Get a playlist given its mbid

    Arguments:
        playlist_id: the uuid of a playlist to get
        load_recordings: If true, load the recordings for the playlist too

    Raises:
        ValueError: if playlist_id isn't a valid uuid

    Returns:
        a ``model_playlist.Playlist``, or ``None`` if no such Playlist with the
        given id exists
    """
    query = sqlalchemy.text("""
        SELECT pl.id
             , pl.mbid
             , pl.creator_id
             , pl.name
             , pl.description
             , pl.public
             , pl.created
             , pl.last_updated
             , pl.copied_from_id
             , pl.created_for_id
             , pl.algorithm_metadata
             , copy.mbid as copied_from_mbid
          FROM playlist.playlist AS pl
     LEFT JOIN playlist.playlist AS copy
            ON pl.copied_from_id = copy.id
         WHERE pl.mbid = :mbid""")
    with ts.engine.connect() as connection:
        result = connection.execute(query, {"mbid": playlist_id})
        obj = result.fetchone()
        if not obj:
            return None
        obj = dict(obj)
        user_id = obj['creator_id']
        user = db_user.get(user_id)
        obj['creator'] = user['musicbrainz_id']
        if obj['created_for_id']:
            created_for_user = db_user.get(obj['created_for_id'])
            if created_for_user:
                obj['created_for'] = created_for_user['musicbrainz_id']

        if load_recordings:
            playlist_map = get_recordings_for_playlists(
                connection, [obj['id']])
            obj['recordings'] = playlist_map.get(obj['id'], [])
        else:
            obj['recordings'] = []
        playlist_collaborator_ids = get_collaborators_for_playlists(
            connection, [obj['id']])
        collaborator_ids_list = playlist_collaborator_ids.get(obj['id'], [])
        obj['collaborator_ids'] = collaborator_ids_list
        obj['collaborators'] = get_collaborators_names_from_ids(
            collaborator_ids_list)
        return model_playlist.Playlist.parse_obj(obj)
 def setUp(self):
     super(TestPostgresListenStore, self).setUp()
     self.log = logging.getLogger(__name__)
     self.logstore = init_postgres_connection(self.config.SQLALCHEMY_DATABASE_URI)
     # TODO: Does this use the normal or test DB??
     self.testuser_id = db_user.create("test")
     user = db_user.get(self.testuser_id)
     print(user)
     self.testuser_name = db_user.get(self.testuser_id)['musicbrainz_id']
Ejemplo n.º 4
0
    def test_delete_when_spotify_import_activated(self):
        user_id = db_user.create(11, 'kishore')
        user = db_user.get(user_id)
        self.assertIsNotNone(user)
        db_spotify.create_spotify(user_id, 'user token', 'refresh token', 0)

        db_user.delete(user_id)
        user = db_user.get(user_id)
        self.assertIsNone(user)
        token = db_spotify.get_token_for_user(user_id)
        self.assertIsNone(token)
Ejemplo n.º 5
0
    def test_delete_when_spotify_import_activated(self):
        user_id = db_user.create(11, 'kishore')
        user = db_user.get(user_id)
        self.assertIsNotNone(user)
        db_oauth.save_token(user_id, ExternalServiceType.SPOTIFY, 'user token',
                            'refresh token', 0, True,
                            ['user-read-recently-played'])

        db_user.delete(user_id)
        user = db_user.get(user_id)
        self.assertIsNone(user)
        token = db_oauth.get_token(user_id, ExternalServiceType.SPOTIFY)
        self.assertIsNone(token)
 def setUp(self):
     super(TestInfluxListenStore, self).setUp()
     self.log = logging.getLogger(__name__)
     self.logstore = init_influx_connection(
         self.log, {
             'REDIS_HOST': config.REDIS_HOST,
             'REDIS_PORT': config.REDIS_PORT,
             'INFLUX_HOST': config.INFLUX_HOST,
             'INFLUX_PORT': config.INFLUX_PORT,
             'INFLUX_DB_NAME': config.INFLUX_DB_NAME,
         })
     self.testuser_id = db_user.create("test")
     user = db_user.get(self.testuser_id)
     print(user)
     self.testuser_name = db_user.get(self.testuser_id)['musicbrainz_id']
Ejemplo n.º 7
0
    def test_ts_filters(self, timescale):
        """Check that max_ts and min_ts are passed to timescale """
        user = User.from_dbrow(db_user.get(1)).to_dict()
        timescale.return_value = ([], 0, 0)

        # If no parameter is given, use current time as the to_ts
        self.client.get(url_for('user.profile', user_name='iliekcomputers'))
        req_call = mock.call(user, limit=25, from_ts=None)
        timescale.assert_has_calls([req_call])
        timescale.reset_mock()

        # max_ts query param -> to_ts timescale param
        self.client.get(url_for('user.profile', user_name='iliekcomputers'),
                        query_string={'max_ts': 1520946000})
        req_call = mock.call(user, limit=25, to_ts=1520946000)
        timescale.assert_has_calls([req_call])
        timescale.reset_mock()

        # min_ts query param -> from_ts timescale param
        self.client.get(url_for('user.profile', user_name='iliekcomputers'),
                        query_string={'min_ts': 1520941000})
        req_call = mock.call(user, limit=25, from_ts=1520941000)
        timescale.assert_has_calls([req_call])
        timescale.reset_mock()

        # If max_ts and min_ts set, only max_ts is used
        self.client.get(url_for('user.profile', user_name='iliekcomputers'),
                        query_string={
                            'min_ts': 1520941000,
                            'max_ts': 1520946000
                        })
        req_call = mock.call(user, limit=25, to_ts=1520946000)
        timescale.assert_has_calls([req_call])
    def setUp(self):
        super(TestAPICompatUserClass, self).setUp()
        self.log = logging.getLogger(__name__)
        self.logstore = init_influx_connection(
            self.log, {
                'INFLUX_HOST': config.INFLUX_HOST,
                'INFLUX_PORT': config.INFLUX_PORT,
                'INFLUX_DB_NAME': config.INFLUX_DB_NAME,
                'REDIS_HOST': config.REDIS_HOST,
                'REDIS_PORT': config.REDIS_PORT,
            })

        # Create a user
        uid = db_user.create("test_api_compat_user")
        self.assertIsNotNone(db_user.get(uid))
        with db.engine.connect() as connection:
            result = connection.execute(
                text("""
                SELECT *
                  FROM "user"
                 WHERE id = :id
            """), {
                    "id": uid,
                })
            row = result.fetchone()
            self.user = User(row['id'], row['created'], row['musicbrainz_id'],
                             row['auth_token'])

        # Insert some listens
        date = datetime(2015, 9, 3, 0, 0, 0)
        self.log.info("Inserting test data...")
        test_data = generate_data(date, 100, self.user.name)
        self.logstore.insert(test_data)
        self.log.info("Test data inserted")
def submit_listens():
    """ Submit listens received from clients into the listenstore after validating them. """

    try:
        session = _get_session(request.form.get('s', ''))
    except BadRequest:
        return 'BADSESSION\n', 401

    listens = []
    index = 0
    while True:
        listen = _to_native_api(request.form, append_key='[{}]'.format(index))
        if listen is None:
            break
        else:
            listens.append(listen)
            index += 1

    if not listens:
        return 'FAILED Invalid data submitted!\n', 400

    user = db_user.get(session.user_id)
    insert_payload(listens, user)

    return 'OK\n'
Ejemplo n.º 10
0
def get_recordings_for_playlists(connection, playlist_ids: List[int]):
    """"""

    query = sqlalchemy.text("""
        SELECT id
             , playlist_id
             , position
             , mbid
             , added_by_id
             , created
          FROM playlist.playlist_recording
         WHERE playlist_id IN :playlist_ids
      ORDER BY playlist_id, position
    """)
    result = connection.execute(query, {"playlist_ids": tuple(playlist_ids)})
    user_id_map = {}
    playlist_recordings_map = collections.defaultdict(list)
    for row in result:
        added_by_id = row["added_by_id"]
        if added_by_id not in user_id_map:
            # TODO: Do this lookup in bulk
            user_id_map[added_by_id] = db_user.get(added_by_id)
        row = dict(row)
        row["added_by"] = user_id_map[added_by_id]["musicbrainz_id"]
        playlist_recording = model_playlist.PlaylistRecording.parse_obj(row)
        playlist_recordings_map[playlist_recording.playlist_id].append(playlist_recording)
    for playlist_id in playlist_ids:
        if playlist_id not in playlist_recordings_map:
            playlist_recordings_map[playlist_id] = []
    return dict(playlist_recordings_map)
def process_one_user(user):
    """ Get recently played songs for this user and submit them to ListenBrainz.

    Args:
        user (spotify.Spotify): the user whose plays are to be imported.

    Raises:
        spotify.SpotifyAPIError: if we encounter errors from the Spotify API.
        spotify.SpotifyListenBrainzError: if we encounter a rate limit, even after retrying.
                                          or if we get errors while submitting the data to ListenBrainz

    """
    if user.token_expired:
        try:
            user = spotify.refresh_user_token(user)
        except spotify.SpotifyAPIError:
            current_app.logger.error('Could not refresh user token from spotify', exc_info=True)
            raise

    listenbrainz_user = db_user.get(user.user_id)
    try:
        recently_played = get_user_recently_played(user)
    except (spotify.SpotifyListenBrainzError, spotify.SpotifyAPIError) as e:
        raise

    # convert listens to ListenBrainz format and validate them
    listens = []
    if 'items' in recently_played:
        current_app.logger.debug('Received %d listens from Spotify for %s', len(recently_played['items']),  str(user))
        for item in recently_played['items']:
            listen = _convert_spotify_play_to_listen(item)
            try:
                validate_listen(listen, LISTEN_TYPE_IMPORT)
                listens.append(listen)
            except BadRequest:
                current_app.logger.error('Could not validate listen for user %s: %s', str(user), json.dumps(listen, indent=3), exc_info=True)
                # TODO: api_utils exposes werkzeug exceptions, if it's a more generic module it shouldn't be web-specific

    # if we don't have any new listens, return
    if len(listens) == 0:
        return

    latest_listened_at = max(listen['listened_at'] for listen in listens)
    # try to submit listens to ListenBrainz
    retries = 10
    while retries >= 0:
        try:
            current_app.logger.debug('Submitting %d listens for user %s', len(listens), str(user))
            insert_payload(listens, listenbrainz_user, listen_type=LISTEN_TYPE_IMPORT)
            current_app.logger.debug('Submitted!')
            break
        except (InternalServerError, ServiceUnavailable) as e:
            retries -= 1
            current_app.logger.info('ISE while trying to import listens for %s: %s', str(user), str(e))
            if retries == 0:
                raise spotify.SpotifyListenBrainzError('ISE while trying to import listens: %s', str(e))

    # we've succeeded so update the last_updated field for this user
    spotify.update_latest_listened_at(user.user_id, latest_listened_at)
    spotify.update_last_updated(user.user_id)
Ejemplo n.º 12
0
def submit_listens():
    """ Submit listens received from clients into the listenstore after validating them. """

    try:
        session = _get_session(request.form.get('s', ''))
    except BadRequest:
        return 'BADSESSION\n', 401

    listens = []
    index = 0
    while True:
        listen = _to_native_api(request.form, append_key='[{}]'.format(index))
        if listen is None:
            break
        else:
            listens.append(listen)
            index += 1

    if not listens:
        return 'FAILED Invalid data submitted!\n', 400

    user = db_user.get(session.user_id)
    insert_payload(listens, user)

    return 'OK\n'
Ejemplo n.º 13
0
    def setUp(self):
        super(TestAPICompatUserClass, self).setUp()
        self.log = logging.getLogger(__name__)
        self.logstore = init_influx_connection(
            self.log, {
                'INFLUX_HOST': config.INFLUX_HOST,
                'INFLUX_PORT': config.INFLUX_PORT,
                'INFLUX_DB_NAME': config.INFLUX_DB_NAME,
                'REDIS_HOST': config.REDIS_HOST,
                'REDIS_PORT': config.REDIS_PORT,
                'REDIS_NAMESPACE': config.REDIS_NAMESPACE,
            })

        # Create a user
        uid = db_user.create("test_api_compat_user")
        self.assertIsNotNone(db_user.get(uid))
        with db.engine.connect() as connection:
            result = connection.execute(
                text("""
                SELECT *
                  FROM "user"
                 WHERE id = :id
            """), {
                    "id": uid,
                })
            row = result.fetchone()
            self.user = User(row['id'], row['created'], row['musicbrainz_id'],
                             row['auth_token'])
Ejemplo n.º 14
0
def update_playlist(playlist: model_playlist.Playlist):
    """Update playlist metadata (Name, description, public flag)

    Arguments:

    """

    query = sqlalchemy.text("""
        UPDATE playlist.playlist
           SET name = :name
             , description = :description
             , public = :public
         WHERE id = :id
    """)
    with ts.engine.connect() as connection:
        params = playlist.dict(include={'id', 'name', 'description', 'public'})
        connection.execute(query, params)
        # Unconditionally add collaborators, this allows us to delete all collaborators
        # if [] is passed in.
        # TODO: Optimise this by getting collaborators from the database and only updating
        #  if what has passed is different to what exists
        add_playlist_collaborators(connection, playlist.id, playlist.collaborator_ids)
        collaborator_ids = get_collaborators_for_playlists(connection, [playlist.id])
        collaborator_ids = collaborator_ids.get(playlist.id, [])
        collaborators = []
        # TODO: Look this up in one query
        for user_id in collaborator_ids:
            user = db_user.get(user_id)
            if user:
                collaborators.append(user["musicbrainz_id"])
        playlist.collaborators = collaborators
        playlist.last_updated = set_last_updated(connection, playlist.id)
        return playlist
    def setUp(self):
        super(TestAPICompatUserClass, self).setUp()
        self.log = logging.getLogger(__name__)
        self.logstore = init_influx_connection(self.log, {
            'INFLUX_HOST': config.INFLUX_HOST,
            'INFLUX_PORT': config.INFLUX_PORT,
            'INFLUX_DB_NAME': config.INFLUX_DB_NAME,
            'REDIS_HOST': config.REDIS_HOST,
            'REDIS_PORT': config.REDIS_PORT,
            'REDIS_NAMESPACE': config.REDIS_NAMESPACE,
        })

        # Create a user
        uid = db_user.create(1, "test_api_compat_user")
        self.assertIsNotNone(db_user.get(uid))
        with db.engine.connect() as connection:
            result = connection.execute(text("""
                SELECT *
                  FROM "user"
                 WHERE id = :id
            """), {
                "id": uid,
            })
            row = result.fetchone()
            self.user = User(row['id'], row['created'], row['musicbrainz_id'], row['auth_token'])
Ejemplo n.º 16
0
def handle_new_releases_of_top_artists(message):
    user_id = message["user_id"]
    # need to check whether user exists before inserting otherwise possible FK error.
    user = db_user.get(user_id)
    if not user:
        return
    year_in_music.insert_new_releases_of_top_artists(user_id, message["data"])
Ejemplo n.º 17
0
def handle_recommendations(data):
    """ Take recommended recordings for a user and save it in the db.
    """
    user_id = data['user_id']
    user = db_user.get(user_id)
    if not user:
        current_app.logger.info(f"Generated recommendations for a user that doesn't exist in the Postgres database: {user_id}")
        return

    current_app.logger.debug("inserting recommendation for {}".format(user["musicbrainz_id"]))
    recommendations = data['recommendations']

    try:
        db_recommendations_cf_recording.insert_user_recommendation(
            user_id,
            UserRecommendationsJson(**{
                'top_artist': recommendations['top_artist'],
                'similar_artist': recommendations['similar_artist']
            })
        )
    except ValidationError:
        current_app.logger.error(f"""ValidationError while inserting recommendations for user with musicbrainz_id:
                                 {user["musicbrainz_id"]}. \nData: {json.dumps(data, indent=3)}""")

    current_app.logger.debug("recommendation for {} inserted".format(user["musicbrainz_id"]))
Ejemplo n.º 18
0
def handle_missing_musicbrainz_data(data):
    """ Insert user missing musicbrainz data i.e data submitted to ListenBrainz but not MusicBrainz.
    """
    user_id = data['user_id']
    user = db_user.get(user_id)

    if not user:
        return

    current_app.logger.debug(f"Inserting missing musicbrainz data for {user['musicbrainz_id']}")

    missing_musicbrainz_data = data['missing_musicbrainz_data']
    source = data['source']

    try:
        db_missing_musicbrainz_data.insert_user_missing_musicbrainz_data(
            user['id'],
            UserMissingMusicBrainzDataJson(missing_musicbrainz_data=missing_musicbrainz_data),
            source
        )
    except ValidationError:
        current_app.logger.error(f"""
        ValidationError while inserting missing MusicBrainz data from source "{source}" for user with musicbrainz_id:
         {user["musicbrainz_id"]}. Data: {json.dumps(data, indent=3)}""", exc_info=True)

    current_app.logger.debug(f"Missing musicbrainz data for {user['musicbrainz_id']} inserted")
Ejemplo n.º 19
0
def insert_recordings(connection, playlist_id: int, recordings: List[model_playlist.WritablePlaylistRecording],
                      starting_position: int):
    """Insert recordings to an existing playlist. The position field will be computed based on the order
    of the provided recordings.

    Arguments:
        connection: an open database connection
        playlist_id: the playlist id to add the recordings to
        recordings: a list of recordings to add
        starting_position: The position number to set in the first recording. The first recording in a playlist is position 0
    """
    for position, recording in enumerate(recordings, starting_position):
        recording.playlist_id = playlist_id
        recording.position = position

    query = sqlalchemy.text("""
        INSERT INTO playlist.playlist_recording (playlist_id, position, mbid, added_by_id, created)
                                         VALUES (:playlist_id, :position, :mbid, :added_by_id, :created)
                                      RETURNING id, created""")
    return_recordings = []
    user_id_map = {}
    insert_ts = datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc)
    for recording in recordings:
        if not recording.created:
            recording.created = insert_ts
        result = connection.execute(query, recording.dict(include={'playlist_id', 'position', 'mbid', 'added_by_id', 'created'}))
        if recording.added_by_id not in user_id_map:
            # TODO: Do this lookup in bulk
            user_id_map[recording.added_by_id] = db_user.get(recording.added_by_id)
        row = result.fetchone()
        recording.id = row['id']
        recording.created = row['created']
        recording.added_by = user_id_map[recording.added_by_id]["musicbrainz_id"]
        return_recordings.append(model_playlist.PlaylistRecording.parse_obj(recording.dict()))
    return return_recordings
Ejemplo n.º 20
0
 def test_update_user_details(self):
     user_id = db_user.create(17, "barbazfoo", "*****@*****.**")
     db_user.update_user_details(user_id, "hello-world",
                                 "*****@*****.**")
     user = db_user.get(user_id, fetch_email=True)
     self.assertEqual(user["id"], user_id)
     self.assertEqual(user["musicbrainz_id"], "hello-world")
     self.assertEqual(user["email"], "*****@*****.**")
 def setUp(self):
     super().setUp()
     self.app = create_app()
     self.tempdir = tempfile.mkdtemp()
     self.runner = CliRunner()
     self.listenstore = timescale_connection._ts
     self.user_id = db_user.create(1, 'iliekcomputers')
     self.user_name = db_user.get(self.user_id)['musicbrainz_id']
Ejemplo n.º 22
0
def process_one_user(user):
    """ Get recently played songs for this user and submit them to ListenBrainz.

    Args:
        user (spotify.Spotify): the user whose plays are to be imported.

    Raises:
        spotify.SpotifyAPIError: if we encounter errors from the Spotify API.
        spotify.SpotifyListenBrainzError: if we encounter a rate limit, even after retrying.
                                          or if we get errors while submitting the data to ListenBrainz

    """
    if user.token_expired:
        try:
            user = spotify.refresh_user_token(user)
        except spotify.SpotifyAPIError:
            current_app.logger.error(
                'Could not refresh user token from spotify', exc_info=True)
            raise
        else:
            if user is None:
                current_app.logger.debug(
                    "%s has revoked spotify authorization", str(user))

    listenbrainz_user = db_user.get(user.user_id)

    currently_playing = get_user_currently_playing(user)
    if currently_playing is not None and 'item' in currently_playing:
        current_app.logger.debug('Received a currently playing track for %s',
                                 str(user))
        listens = parse_and_validate_spotify_plays([currently_playing['item']],
                                                   LISTEN_TYPE_PLAYING_NOW)
        submit_listens_to_listenbrainz(listenbrainz_user,
                                       listens,
                                       listen_type=LISTEN_TYPE_PLAYING_NOW)

    recently_played = get_user_recently_played(user)
    if recently_played is not None and 'items' in recently_played:
        listens = parse_and_validate_spotify_plays(recently_played['items'],
                                                   LISTEN_TYPE_IMPORT)
        current_app.logger.debug('Received %d tracks for %s', len(listens),
                                 str(user))

    # if we don't have any new listens, return
    if len(listens) == 0:
        return

    latest_listened_at = max(listen['listened_at'] for listen in listens)
    submit_listens_to_listenbrainz(listenbrainz_user,
                                   listens,
                                   listen_type=LISTEN_TYPE_IMPORT)

    # we've succeeded so update the last_updated field for this user
    spotify.update_latest_listened_at(user.user_id, latest_listened_at)
    spotify.update_last_updated(user.user_id)

    current_app.logger.info('imported %d listens for %s' %
                            (len(listens), str(user)))
Ejemplo n.º 23
0
def get_collaborators_names_from_ids(collaborator_ids: List[int]):
    collaborators = []
    # TODO: Look this up in one query
    for user_id in collaborator_ids:
        user = db_user.get(user_id)
        if user:
            collaborators.append(user["musicbrainz_id"])
    collaborators.sort()
    return collaborators
Ejemplo n.º 24
0
    def test_fetch_email(self):
        musicbrainz_id = "one"
        email = "*****@*****.**"
        user_id = db_user.create(1, musicbrainz_id, email)
        self.assertNotIn("email", db_user.get(user_id))
        self.assertEqual(email,
                         db_user.get(user_id, fetch_email=True)["email"])

        token = db_user.get(user_id)["auth_token"]
        self.assertNotIn("email", db_user.get_by_token(token))
        self.assertEqual(
            email,
            db_user.get_by_token(token, fetch_email=True)["email"])

        self.assertNotIn("email", db_user.get_by_mb_id(musicbrainz_id))
        self.assertEqual(
            email,
            db_user.get_by_mb_id(musicbrainz_id, fetch_email=True)["email"])
Ejemplo n.º 25
0
 def setUp(self):
     super(SpotifyDatabaseTestCase, self).setUp()
     db_user.create(1, 'testspotifyuser')
     self.user = db_user.get(1)
     db_spotify.create_spotify(
         user_id=self.user['id'],
         user_token='token',
         refresh_token='refresh_token',
         token_expires_ts=int(time.time()),
     )
Ejemplo n.º 26
0
 def setUp(self):
     super(SpotifyDatabaseTestCase, self).setUp()
     db_user.create(1, 'testspotifyuser')
     self.user = db_user.get(1)
     db_spotify.create_spotify(
         user_id=self.user['id'],
         user_token='token',
         refresh_token='refresh_token',
         token_expires_ts=int(time.time()),
     )
Ejemplo n.º 27
0
    def test_delete(self, mock_publish_data_to_queue):
        self.temporary_login(self.user['id'])
        r = self.client.get(url_for('profile.delete'))
        self.assert200(r)

        r = self.client.post(url_for('profile.delete'), data={'token': self.user['auth_token']})
        mock_publish_data_to_queue.assert_called_once()
        self.assertRedirects(r, '/')
        user = db_user.get(self.user['id'])
        self.assertIsNone(user)
Ejemplo n.º 28
0
    def test_delete(self):
        self.temporary_login(self.user['id'])
        r = self.client.get(url_for('profile.delete'))
        self.assert200(r)

        r = self.client.post(url_for('profile.delete'),
                             data={'token': self.user['auth_token']})
        self.assertRedirects(r, '/')
        user = db_user.get(self.user['id'])
        self.assertIsNone(user)
Ejemplo n.º 29
0
def record_listens(request, data):
    """ Submit the listen in the lastfm format to be inserted in db.
        Accepts listens for both track.updateNowPlaying and track.scrobble methods.
    """
    output_format = data.get('format', 'xml')
    try:
        sk, api_key = data['sk'], data['api_key']
    except KeyError:
        raise InvalidAPIUsage(CompatError.INVALID_PARAMETERS, output_format=output_format)    # Invalid parameters

    session = Session.load(sk)
    if not session:
        if not Token.is_valid_api_key(api_key):
            raise InvalidAPIUsage(CompatError.INVALID_API_KEY, output_format=output_format)   # Invalid API_KEY
        raise InvalidAPIUsage(CompatError.INVALID_SESSION_KEY, output_format=output_format)   # Invalid Session KEY

    lookup = defaultdict(dict)
    for key, value in data.items():
        if key in ["sk", "token", "api_key", "method", "api_sig"]:
            continue
        matches = re.match('(.*)\[(\d+)\]', key)
        if matches:
            key = matches.group(1)
            number = matches.group(2)
        else:
            number = 0
        lookup[number][key] = value

    if request.form['method'].lower() == 'track.updatenowplaying':
        for i, listen in lookup.items():
            if 'timestamp' not in listen:
                listen['timestamp'] = calendar.timegm(datetime.now().utctimetuple())

    # Convert to native payload then submit 'em after validation.
    listen_type, native_payload = _to_native_api(lookup, data['method'], output_format)
    for listen in native_payload:
        validate_listen(listen, listen_type)

    user = db_user.get(session.user_id)
    augmented_listens = insert_payload(native_payload, user, listen_type=listen_type)

    # With corrections than the original submitted listen.
    doc, tag, text = Doc().tagtext()
    with tag('lfm', status='ok'):
        if listen_type == 'playing_now':
            doc.asis(create_response_for_single_listen(list(lookup.values())[0], augmented_listens[0], listen_type))
        else:
            accepted_listens = len(lookup.values())
            # Currently LB accepts all the listens and ignores none
            with tag('scrobbles', accepted=accepted_listens, ignored='0'):
                for original_listen, augmented_listen in zip(list(lookup.values()), augmented_listens):
                    doc.asis(create_response_for_single_listen(original_listen, augmented_listen, listen_type))

    return format_response('<?xml version="1.0" encoding="utf-8"?>\n' + yattag.indent(doc.getvalue()),
                           output_format)
 def setUp(self):
     super(OAuthDatabaseTestCase, self).setUp()
     db_user.create(1, 'testspotifyuser')
     self.user = db_user.get(1)
     db_oauth.save_token(user_id=self.user['id'],
                         service=ExternalServiceType.SPOTIFY,
                         access_token='token',
                         refresh_token='refresh_token',
                         token_expires_ts=int(time.time()),
                         record_listens=True,
                         scopes=['user-read-recently-played'])
Ejemplo n.º 31
0
    def test_delete(self):
        user_id = db_user.create(10, 'frank')

        user = db_user.get(user_id)
        self.assertIsNotNone(user)
        db_stats.insert_user_stats(
            user_id=user_id,
            artists={},
            recordings={},
            releases={},
            artist_count=2,
        )
        user_stats = db_stats.get_all_user_stats(user_id)
        self.assertIsNotNone(user_stats)

        db_user.delete(user_id)
        user = db_user.get(user_id)
        self.assertIsNone(user)
        user_stats = db_stats.get_all_user_stats(user_id)
        self.assertIsNone(user_stats)
    def test_delete(self):
        user_id = db_user.create(10, 'frank')

        user = db_user.get(user_id)
        self.assertIsNotNone(user)

        with open(self.path_to_data_file('user_top_artists_db.json')) as f:
            artists_data = ujson.load(f)
        db_stats.insert_user_artists(
            user_id=user_id,
            artists=UserArtistStatJson(**{'all_time': artists_data}),
        )
        user_stats = db_stats.get_user_artists(user_id, 'all_time')
        self.assertIsNotNone(user_stats)

        db_user.delete(user_id)
        user = db_user.get(user_id)
        self.assertIsNone(user)
        user_stats = db_stats.get_user_artists(user_id, 'all_time')
        self.assertIsNone(user_stats)
Ejemplo n.º 33
0
    def test_delete(self):
        user_id = db_user.create(10, 'frank')

        user = db_user.get(user_id)
        self.assertIsNotNone(user)

        with open(self.path_to_data_file('user_top_artists_db.json')) as f:
            artists_data = ujson.load(f)
        db_stats.insert_user_jsonb_data(
            user_id=user_id,
            stats_type='artists',
            stats=StatRange[EntityRecord](**artists_data),
        )
        user_stats = db_stats.get_user_stats(user_id, 'all_time', 'artists')
        self.assertIsNotNone(user_stats)

        db_user.delete(user_id)
        user = db_user.get(user_id)
        self.assertIsNone(user)
        user_stats = db_stats.get_user_stats(user_id, 'all_time', 'artists')
        self.assertIsNone(user_stats)
    def setUp(self):
        super(TestAPICompatTokenClass, self).setUp()
        self.log = logging.getLogger(__name__)

        # Create a user
        uid = db_user.create(1, "test")
        self.assertIsNotNone(db_user.get(uid))
        with db.engine.connect() as connection:
            result = connection.execute(text('SELECT * FROM "user" WHERE id = :id'),
                                        {"id": uid})
            row = result.fetchone()
            self.user = User(row['id'], row['created'], row['musicbrainz_id'], row['auth_token'])
def process_one_user(user):
    """ Get recently played songs for this user and submit them to ListenBrainz.

    Args:
        user (spotify.Spotify): the user whose plays are to be imported.

    Raises:
        spotify.SpotifyAPIError: if we encounter errors from the Spotify API.
        spotify.SpotifyListenBrainzError: if we encounter a rate limit, even after retrying.
                                          or if we get errors while submitting the data to ListenBrainz

    """
    if user.token_expired:
        user = spotify.refresh_user_token(user)

    listenbrainz_user = db_user.get(user.user_id)
    try:
        recently_played = get_user_recently_played(user)
    except (spotify.SpotifyListenBrainzError, spotify.SpotifyAPIError) as e:
        raise

    # convert listens to ListenBrainz format and validate them
    listens = []
    if 'items' in recently_played:
        current_app.logger.debug('Received %d listens from Spotify for %s', len(recently_played['items']),  str(user))
        for item in recently_played['items']:
            listen = _convert_spotify_play_to_listen(item)
            try:
                validate_listen(listen, LISTEN_TYPE_IMPORT)
                listens.append(listen)
            except BadRequest:
                current_app.logger.error('Could not validate listen for user %s: %s', str(user), json.dumps(listen, indent=3), exc_info=True)
                # TODO: api_utils exposes werkzeug exceptions, if it's a more generic module it shouldn't be web-specific

    latest_listened_at = max(listen['listened_at'] for listen in listens)
    # try to submit listens to ListenBrainz
    retries = 10
    while retries >= 0:
        try:
            current_app.logger.debug('Submitting %d listens for user %s', len(listens), str(user))
            insert_payload(listens, listenbrainz_user, listen_type=LISTEN_TYPE_IMPORT)
            current_app.logger.debug('Submitted!')
            break
        except (InternalServerError, ServiceUnavailable) as e:
            retries -= 1
            current_app.logger.info('ISE while trying to import listens for %s: %s', str(user), str(e))
            if retries == 0:
                raise spotify.SpotifyListenBrainzError('ISE while trying to import listens: %s', str(e))

    # we've succeeded so update the last_updated field for this user
    spotify.update_latest_listened_at(user.user_id, latest_listened_at)
    spotify.update_last_updated(user.user_id)
Ejemplo n.º 36
0
    def setUp(self):
        super(TestAPICompatTokenClass, self).setUp()
        self.log = logging.getLogger(__name__)

        # Create a user
        uid = db_user.create(1, "test")
        self.assertIsNotNone(db_user.get(uid))
        with db.engine.connect() as connection:
            result = connection.execute(
                text('SELECT * FROM "user" WHERE id = :id'), {"id": uid})
            row = result.fetchone()
            self.user = User(row['id'], row['created'], row['musicbrainz_id'],
                             row['auth_token'])
    def setUp(self):
        super(TestInfluxListenStore, self).setUp()
        self.log = logging.getLogger(__name__)

        # In order to do counting correctly, we need a clean DB to start with
        self.reset_influx_db()

        self.logstore = init_influx_connection(self.log, {
            'REDIS_HOST': config.REDIS_HOST,
            'REDIS_PORT': config.REDIS_PORT,
            'REDIS_NAMESPACE': config.REDIS_NAMESPACE,
            'INFLUX_HOST': config.INFLUX_HOST,
            'INFLUX_PORT': config.INFLUX_PORT,
            'INFLUX_DB_NAME': config.INFLUX_DB_NAME,
        })
        self.testuser_id = db_user.create(1, "test")
        self.testuser_name = db_user.get(self.testuser_id)['musicbrainz_id']
def submit_now_playing():
    """ Handle now playing notifications sent by clients """

    current_app.logger.info(json.dumps(request.form, indent=4))

    try:
        session = _get_session(request.form.get('s', ''))
    except BadRequest:
        return 'BADSESSION\n', 401

    listen = _to_native_api(request.form, append_key='')
    if listen is None:
        return 'FAILED Invalid data submitted!\n', 400


    listens = [listen]
    user = db_user.get(session.user_id)
    insert_payload(listens, user, LISTEN_TYPE_PLAYING_NOW)

    return 'OK\n'
Ejemplo n.º 39
0
def load_user(user_id):
    user = db_user.get(user_id)
    if user:
        return User.from_dbrow(user)
    else:
        return None