def reset_latest_import_timestamp(): form = FlaskForm() if form.validate_on_submit(): try: listens_importer.update_latest_listened_at( current_user.id, ExternalServiceType.LASTFM, 0) flash.info( "Latest import time reset, we'll now import all your data instead of stopping at your last imported listen." ) except DatabaseException: flash.error( "Something went wrong! Unable to reset latest import timestamp right now." ) return redirect(url_for("profile.info")) if form.csrf_token.errors: flash.error( 'Cannot reset import time due to error during authentication, please try again later.' ) return redirect(url_for('profile.info')) return render_template( "profile/resetlatestimportts.html", form=form, )
def test_update_latest_listened_at(self): spotify_user = db_spotify.get_user_import_details(self.user_id) self.assertIsNone(spotify_user['latest_listened_at']) self.assertIsNone(spotify_user['last_updated']) t = int(time.time()) db_import.update_latest_listened_at(self.user_id, ExternalServiceType.SPOTIFY, t) spotify_user = db_spotify.get_user_import_details(self.user_id) self.assertEqual(t, int(spotify_user['latest_listened_at'].strftime('%s'))) self.assertIsNotNone(spotify_user['last_updated'])
def update_latest_listen_ts(self, user_id: int, timestamp: int): """ Update the latest_listened_at field for user with specified ListenBrainz user ID. Args: user_id (int): the ListenBrainz row ID of the user timestamp (int): the unix timestamp of the latest listen imported for the user """ listens_importer.update_latest_listened_at(user_id, self.service, timestamp)
def delete_listens_history(user_id: int): """ Delete a user's listens from ListenBrainz completely. Args: user_id: the LB row ID of the user """ timescale_connection._ts.delete(user_id) listens_importer.update_latest_listened_at(user_id, ExternalServiceType.LASTFM, 0) db_stats.delete_user_stats(user_id)
def test_update_latest_import(self): user = db_user.get_or_create(3, 'updatelatestimportuser') val = int(time.time()) listens_importer.update_latest_listened_at(user['id'], ExternalServiceType.LASTFM, val) ts = listens_importer.get_latest_listened_at(user['id'], ExternalServiceType.LASTFM) self.assertEqual(int(ts.strftime('%s')), val) listens_importer.update_latest_listened_at(user['id'], ExternalServiceType.LASTFM, 0) ts = listens_importer.get_latest_listened_at(user['id'], ExternalServiceType.LASTFM) self.assertEqual(int(ts.strftime('%s')), 0)
def delete_listens_history(musicbrainz_id): """ Delete a user's listens from ListenBrainz completely. Args: musicbrainz_id (str): the MusicBrainz ID of the user Raises: NotFound if user isn't present in the database """ user = _get_user(musicbrainz_id) timescale_connection._ts.delete(user.musicbrainz_id) timescale_connection._ts.reset_listen_count(user.musicbrainz_id) listens_importer.update_latest_listened_at(user.id, ExternalServiceType.LASTFM, 0) db_stats.delete_user_stats(user.id)
def test_reset_import_timestamp(self): # we do a get request first to put the CSRF token in the flask global context # so that we can access it for using in the post request in the next step val = int(time.time()) listens_importer.update_latest_listened_at(self.user['id'], ExternalServiceType.LASTFM, val) self.temporary_login(self.user['login_id']) response = self.client.get(url_for('profile.reset_latest_import_timestamp')) self.assertTemplateUsed('profile/resetlatestimportts.html') self.assert200(response) response = self.client.post( url_for('profile.reset_latest_import_timestamp'), data={'csrf_token': g.csrf_token} ) self.assertStatus(response, 302) # should have redirected to the info page self.assertRedirects(response, url_for('profile.info')) ts = listens_importer.get_latest_listened_at(self.user['id'], ExternalServiceType.LASTFM) self.assertEqual(int(ts.strftime('%s')), 0)
def test_get_active_users_to_process(self): db_user.create(2, 'newspotifyuser') db_oauth.save_token( user_id=2, service=ExternalServiceType.SPOTIFY, access_token='token', refresh_token='refresh_token', token_expires_ts=int(time.time()), record_listens=True, scopes=['user-read-recently-played'] ) users = db_spotify.get_active_users_to_process() self.assertEqual(len(users), 2) self.assertEqual(users[0]['user_id'], 1) self.assertEqual(users[0]['musicbrainz_row_id'], 1) self.assertEqual(users[1]['user_id'], 2) self.assertEqual(users[1]['musicbrainz_row_id'], 2) # check order, the users should be sorted by latest_listened_at timestamp db_user.create(3, 'newnewspotifyuser') db_oauth.save_token( user_id=3, service=ExternalServiceType.SPOTIFY, access_token='tokentoken', refresh_token='newrefresh_token', token_expires_ts=int(time.time()), record_listens=True, scopes=['user-read-recently-played'] ) t = int(time.time()) db_import.update_latest_listened_at(2, ExternalServiceType.SPOTIFY, t + 20) db_import.update_latest_listened_at(1, ExternalServiceType.SPOTIFY, t + 10) users = db_spotify.get_active_users_to_process() self.assertEqual(len(users), 3) self.assertEqual(users[0]['user_id'], 2) self.assertEqual(users[1]['user_id'], 1) self.assertEqual(users[2]['user_id'], 3) db_import.update_import_status(2, ExternalServiceType.SPOTIFY, 'something broke') db_import.update_import_status(3, ExternalServiceType.SPOTIFY, 'oops.') users = db_spotify.get_active_users_to_process() self.assertEqual(len(users), 1) self.assertEqual(users[0]['user_id'], 1)
def latest_import(): """ Get and update the timestamp of the newest listen submitted in previous imports to ListenBrainz. In order to get the timestamp for a user, make a GET request to this endpoint. The data returned will be JSON of the following format: .. code-block:: json { "musicbrainz_id": "the MusicBrainz ID of the user", "latest_import": "the timestamp of the newest listen submitted in previous imports. Defaults to 0" } :param user_name: the MusicBrainz ID of the user whose data is needed :type user_name: ``str`` :statuscode 200: Yay, you have data! :resheader Content-Type: *application/json* In order to update the timestamp of a user, you'll have to provide a user token in the Authorization Header. User tokens can be found on https://listenbrainz.org/profile/ . The JSON that needs to be posted must contain a field named `ts` in the root with a valid unix timestamp. :reqheader Authorization: Token <user token> :reqheader Content-Type: *application/json* :statuscode 200: latest import timestamp updated :statuscode 400: invalid JSON sent, see error message for details. :statuscode 401: invalid authorization. See error message for details. :statuscode 404: user or service not found. See error message for details. """ if request.method == 'GET': user_name = request.args.get('user_name', '') service_name = request.args.get('service', 'lastfm') try: service = ExternalServiceType[service_name.upper()] except KeyError: raise APINotFound( "Service does not exist: {}".format(service_name)) user = db_user.get_by_mb_id(user_name) if user is None: raise APINotFound( "Cannot find user: {user_name}".format(user_name=user_name)) latest_import_ts = listens_importer.get_latest_listened_at( user["id"], service) return jsonify({ 'musicbrainz_id': user['musicbrainz_id'], 'latest_import': 0 if not latest_import_ts else int(latest_import_ts.strftime('%s')) }) elif request.method == 'POST': user = validate_auth_header() try: data = ujson.loads(request.get_data()) ts = int(data.get('ts', 0)) service_name = data.get('service', 'lastfm') service = ExternalServiceType[service_name.upper()] except (ValueError, KeyError): raise APIBadRequest('Invalid data sent') try: last_import_ts = listens_importer.get_latest_listened_at( user["id"], service) last_import_ts = 0 if not last_import_ts else int( last_import_ts.strftime('%s')) if ts > last_import_ts: listens_importer.update_latest_listened_at( user["id"], service, ts) except DatabaseException: current_app.logger.error("Error while updating latest import: ", exc_info=True) raise APIInternalServerError( 'Could not update latest_import, try again') # During unrelated tests _ts may be None -- improving this would be a great headache. # However, during the test of this code and while serving requests _ts is set. if _ts: _ts.set_listen_count_expiry_for_user(user['musicbrainz_id']) return jsonify({'status': 'ok'})