예제 #1
0
def pins(user_name: str):
    """ Show user pin history """

    user = _get_user(user_name)
    user_data = {
        "name": user.musicbrainz_id,
        "id": user.id,
    }

    pins = get_pin_history_for_user(user_id=user.id, count=25, offset=0)
    pins = [dict(pin) for pin in fetch_track_metadata_for_items(pins)]
    total_count = get_pin_count_for_user(user_id=user.id)

    props = {
        "user": user_data,
        "pins": pins,
        "profile_url": url_for('user.profile', user_name=user_name),
        "total_count": total_count,
        "logged_in_user_follows_user": logged_in_user_follows_user(user),
    }

    return render_template("user/pins.html",
                           active_section="pins",
                           props=ujson.dumps(props),
                           user=user)
예제 #2
0
    def test_fetch_track_metadata_for_items(self):
        recordings = self.insert_recordings()
        models = [
            # these recordings test we find mapping metadata from mbids
            MsidMbidModel(recording_msid=recordings[0]["recording_msid"], recording_mbid=recordings[0]["recording_mbid"]),
            MsidMbidModel(recording_msid=recordings[1]["recording_msid"], recording_mbid=recordings[1]["recording_mbid"]),
            # this recording tests loading data from MsB when no mapping is available
            MsidMbidModel(recording_msid=recordings[2]["recording_msid"], recording_mbid=None),
            # this recording tests loading mapping metadata from msid but no mbid
            # (we actually have a mapped mbid in the corresponding recording but omit it here for testing this case)
            MsidMbidModel(recording_msid=recordings[3]["recording_msid"], recording_mbid=None)
        ]
        models = fetch_track_metadata_for_items(models)

        for model, recording in zip(models, recordings):
            metadata = model.track_metadata
            self.assertEqual(metadata["track_name"], recording["title"])
            self.assertEqual(metadata["artist_name"], recording["artist"])

            if recording["recording_mbid"] is None:  # 3rd recording which only present in MsB
                continue

            self.assertEqual(metadata["release_name"], recording["release"])
            self.assertEqual(metadata["additional_info"]["recording_mbid"], recording["recording_mbid"])
            self.assertEqual(metadata["additional_info"]["recording_msid"], recording["recording_msid"])
            self.assertEqual(metadata["additional_info"]["release_mbid"], recording["release_mbid"])
            self.assertEqual(metadata["additional_info"]["artist_mbids"], recording["artist_mbids"])
예제 #3
0
 def test_fetch_track_metadata_for_items_with_same_mbid(self):
     recording = self.insert_recordings()[0]
     models = [
         MsidMbidModel(recording_msid=recording["recording_msid"], recording_mbid=recording["recording_mbid"]),
         MsidMbidModel(recording_msid=recording["recording_msid"], recording_mbid=recording["recording_mbid"]),
     ]
     models = fetch_track_metadata_for_items(models)
     for model in models:
         metadata = model.track_metadata
         self.assertEqual(metadata["track_name"], recording["title"])
         self.assertEqual(metadata["artist_name"], recording["artist"])
         self.assertEqual(metadata["release_name"], recording["release"])
         self.assertEqual(metadata["additional_info"]["recording_mbid"], recording["recording_mbid"])
         self.assertEqual(metadata["additional_info"]["recording_msid"], recording["recording_msid"])
         self.assertEqual(metadata["additional_info"]["release_mbid"], recording["release_mbid"])
         self.assertEqual(metadata["additional_info"]["artist_mbids"], recording["artist_mbids"])
예제 #4
0
def get_feedback_for_user(user_id: int,
                          limit: int,
                          offset: int,
                          score: int = None,
                          metadata: bool = False) -> List[Feedback]:
    """ Get a list of recording feedback given by the user in descending order of their creation

        Args:
            user_id: the row ID of the user in the DB
            score: the score value by which the results are to be filtered. If 1 then returns the loved recordings,
                   if -1 returns hated recordings.
            limit: number of rows to be returned
            offset: number of feedback to skip from the beginning
            metadata: fetch metadata for the returned feedback recordings

        Returns:
            A list of Feedback objects
    """

    args = {"user_id": user_id, "limit": limit, "offset": offset}
    query = """ SELECT user_id
                     , "user".musicbrainz_id AS user_name
                     , recording_msid::text
                     , recording_mbid::text
                     , score
                     , recording_feedback.created
                  FROM recording_feedback
                  JOIN "user"
                    ON "user".id = recording_feedback.user_id
                 WHERE user_id = :user_id
    """

    if score:
        query += " AND score = :score"
        args["score"] = score

    query += """ ORDER BY recording_feedback.created DESC
                 LIMIT :limit OFFSET :offset """

    with db.engine.connect() as connection:
        result = connection.execute(sqlalchemy.text(query), args)
        feedback = [Feedback(**dict(row)) for row in result.fetchall()]

    if metadata and len(feedback) > 0:
        feedback = fetch_track_metadata_for_items(feedback)

    return feedback
def get_recording_pin_events(users_for_events: List[dict], min_ts: int,
                             max_ts: int,
                             count: int) -> List[APITimelineEvent]:
    """ Gets all recording pin events in the feed."""

    id_username_map = {
        user['id']: user['musicbrainz_id']
        for user in users_for_events
    }
    recording_pin_events_db = get_pins_for_feed(
        user_ids=(user['id'] for user in users_for_events),
        min_ts=min_ts,
        max_ts=max_ts,
        count=count,
    )
    recording_pin_events_db = fetch_track_metadata_for_items(
        recording_pin_events_db)

    events = []
    for pin in recording_pin_events_db:
        try:
            pinEvent = APIPinEvent(
                user_name=id_username_map[pin.user_id],
                blurb_content=pin.blurb_content,
                track_metadata=TrackMetadata(
                    artist_name=pin.track_metadata["artist_name"],
                    track_name=pin.track_metadata["track_name"],
                    release_name=None,
                    additional_info=AdditionalInfo(
                        recording_msid=pin.recording_msid,
                        recording_mbid=pin.recording_mbid,
                    )))
            events.append(
                APITimelineEvent(
                    id=pin.row_id,
                    event_type=UserTimelineEventType.RECORDING_PIN,
                    user_name=pinEvent.user_name,
                    created=pin.created.timestamp(),
                    metadata=pinEvent,
                ))
        except pydantic.ValidationError as e:
            current_app.logger.error('Validation error: ' + str(e),
                                     exc_info=True)
            continue
    return events
def get_pins_for_user_following(user_name):
    """
    Get a list containing the active pinned recordings for all users in a user's ``user_name``
    following list. The returned pinned recordings are sorted in descending order of the time they were pinned.
    The JSON returned by the API will look like the following:

    .. code-block:: json

        {
            "count": 1,
            "offset": 0,
            "pinned_recordings": [
                {
                "blurb_content": "Spectacular recording!",
                "created": 1624000841,
                "row_id": 1,
                "pinned_until": 1624605641,
                "recording_mbid": null,
                "recording_msid": "40ef0ae1-5626-43eb-838f-1b34187519bf",
                "track_metadata": {
                    "artist_name": "Rick Astley",
                    "track_name": "Never Gonna Give You Up"
                },
                "user_name": "-- the MusicBrainz ID of the user who pinned this recording --"
                },
                "-- more pinned recordings from different users here ---"
            ],
            "user_name": "-- the MusicBrainz ID of the original user --"
        }

    :param user_name: the MusicBrainz ID of the user whose followed user's pinned recordings are being requested.
    :type user_name: ``str``
    :param count: Optional, number of pinned recording items to return,
        Default: :data:`~webserver.views.api.DEFAULT_ITEMS_PER_GET`
        Max: :data:`~webserver.views.api.MAX_ITEMS_PER_GET`
    :type count: ``int``
    :param offset: Optional, number of pinned recording items to skip from the beginning, for pagination.
        Ex. An offset of 5 means the most recent pinned recordings from the first 5 users will be skipped, defaults to 0
    :type offset: ``int``
    :statuscode 200: Yay, you have data!
    :statuscode 400: Invalid query parameters. See error message for details.
    :statuscode 404: The requested user was not found.
    :resheader Content-Type: *application/json*
    """

    count = get_non_negative_param("count", default=DEFAULT_ITEMS_PER_GET)
    offset = get_non_negative_param("offset", default=0)

    count = min(count, MAX_ITEMS_PER_GET)

    user = db_user.get_by_mb_id(user_name)
    if user is None:
        raise APINotFound("Cannot find user: %s" % user_name)

    pinned_recordings = db_pinned_rec.get_pins_for_user_following(
        user_id=user["id"], count=count, offset=offset)
    pinned_recordings = fetch_track_metadata_for_items(pinned_recordings)
    pinned_recordings = [
        _pinned_recording_to_api(pin) for pin in pinned_recordings
    ]

    return jsonify({
        "pinned_recordings": pinned_recordings,
        "count": len(pinned_recordings),
        "offset": offset,
        "user_name": user_name,
    })
def get_pins_for_user(user_name):
    """
    Get a list of all recordings ever pinned by a user with given ``user_name`` in descending order of the time
    they were originally pinned. The JSON returned by the API will look like the following:

    .. code-block:: json

        {
            "count": 10,
            "offset": 0,
            "pinned_recordings": [
                {
                    "blurb_content": "Awesome recording!",
                    "created": 1623997168,
                    "row_id": 10,
                    "pinned_until": 1623997485,
                    "recording_mbid": null,
                    "recording_msid": "fd7d9162-a284-4a10-906c-faae4f1e166b"
                    "track_metadata": {
                        "artist_name": "Rick Astley",
                        "track_name": "Never Gonna Give You Up"
                    }
                },
                "-- more pinned recording items here ---"
            ],
            "total_count": 10,
            "user_name": "-- the MusicBrainz ID of the user --"
        }

    :param user_name: the MusicBrainz ID of the user whose pin track history requested.
    :type user_name: ``str``
    :param count: Optional, number of pinned recording items to return,
        Default: :data:`~webserver.views.api.DEFAULT_ITEMS_PER_GET`
        Max: :data:`~webserver.views.api.MAX_ITEMS_PER_GET`
    :type count: ``int``
    :param offset: Optional, number of pinned recording items to skip from the beginning, for pagination.
        Ex. An offset of 5 means the most recent 5 pinned recordings from the user will be skipped, defaults to 0
    :type offset: ``int``
    :statuscode 200: Yay, you have data!
    :statuscode 400: Invalid query parameters. See error message for details.
    :statuscode 404: The requested user was not found.
    :resheader Content-Type: *application/json*
    """
    offset = get_non_negative_param("offset", default=0)
    count = get_non_negative_param("count", default=DEFAULT_ITEMS_PER_GET)

    count = min(count, MAX_ITEMS_PER_GET)

    user = db_user.get_by_mb_id(user_name)
    if user is None:
        raise APINotFound("Cannot find user: %s" % user_name)

    try:
        pinned_recordings = db_pinned_rec.get_pin_history_for_user(
            user_id=user["id"], count=count, offset=offset)
    except Exception as e:
        current_app.logger.error(
            "Error while retrieving pins for user: {}".format(e))
        raise APIInternalServerError("Something went wrong. Please try again.")

    pinned_recordings = fetch_track_metadata_for_items(pinned_recordings)
    pinned_recordings = [
        _pinned_recording_to_api(pin) for pin in pinned_recordings
    ]
    total_count = db_pinned_rec.get_pin_count_for_user(user_id=user["id"])

    return jsonify({
        "pinned_recordings": pinned_recordings,
        "total_count": total_count,
        "count": len(pinned_recordings),
        "offset": offset,
        "user_name": user_name,
    })
예제 #8
0
def profile(user_name):
    # Which database to use to showing user listens.
    db_conn = webserver.timescale_connection._ts
    # Which database to use to show playing_now stream.
    playing_now_conn = webserver.redis_connection._redis

    user = _get_user(user_name)
    # User name used to get user may not have the same case as original user name.
    user_name = user.musicbrainz_id

    # Getting data for current page
    max_ts = request.args.get("max_ts")
    if max_ts is not None:
        try:
            max_ts = int(max_ts)
        except ValueError:
            raise BadRequest("Incorrect timestamp argument max_ts: %s" %
                             request.args.get("max_ts"))

    min_ts = request.args.get("min_ts")
    if min_ts is not None:
        try:
            min_ts = int(min_ts)
        except ValueError:
            raise BadRequest("Incorrect timestamp argument min_ts: %s" %
                             request.args.get("min_ts"))

    args = {}
    if max_ts:
        args['to_ts'] = max_ts
    else:
        args['from_ts'] = min_ts
    data, min_ts_per_user, max_ts_per_user = db_conn.fetch_listens(
        user.to_dict(), limit=LISTENS_PER_PAGE, **args)

    listens = []
    for listen in data:
        listens.append(listen.to_api())

    # If there are no previous listens then display now_playing
    if not listens or listens[0]['listened_at'] >= max_ts_per_user:
        playing_now = playing_now_conn.get_playing_now(user.id)
        if playing_now:
            listens.insert(0, playing_now.to_api())

    already_reported_user = False
    if current_user.is_authenticated:
        already_reported_user = db_user.is_user_reported(
            current_user.id, user.id)

    pin = get_current_pin_for_user(user_id=user.id)
    if pin:
        pin = dict(fetch_track_metadata_for_items([pin])[0])

    props = {
        "user": {
            "id": user.id,
            "name": user.musicbrainz_id,
        },
        "listens": listens,
        "latest_listen_ts": max_ts_per_user,
        "oldest_listen_ts": min_ts_per_user,
        "profile_url": url_for('user.profile', user_name=user_name),
        "userPinnedRecording": pin,
        "web_sockets_server_url": current_app.config['WEBSOCKETS_SERVER_URL'],
        "logged_in_user_follows_user": logged_in_user_follows_user(user),
        "already_reported_user": already_reported_user,
    }

    return render_template("user/profile.html",
                           props=ujson.dumps(props),
                           user=user,
                           active_section='listens')
    def test_pinned_recording_with_metadata(self):
        recordings = [{
            "title": "Strangers",
            "artist": "Portishead",
            "release": "Dummy"
        }, {
            "title": "Wicked Game",
            "artist": "Tom Ellis",
            "release": "Lucifer"
        }]

        submitted_data = msb_db.insert_all_in_transaction(recordings)
        msids = [x["ids"]["recording_msid"] for x in submitted_data]

        with ts.engine.connect() as connection:
            query = """
                INSERT INTO mbid_mapping_metadata
                            (recording_mbid, release_mbid, release_name, artist_credit_id,
                             artist_mbids, artist_credit_name, recording_name)
                     VALUES ('076255b4-1575-11ec-ac84-135bf6a670e3',
                            '1fd178b4-1575-11ec-b98a-d72392cd8c97',
                            'Dummy',
                            65,
                            '{6a221fda-2200-11ec-ac7d-dfa16a57158f}'::UUID[],
                            'Portishead', 'Strangers')
            """
            connection.execute(sqlalchemy.text(query))

            query = """INSERT INTO mbid_mapping
                                   (recording_msid, recording_mbid, match_type, last_updated)
                            VALUES (:msid, '076255b4-1575-11ec-ac84-135bf6a670e3', 'exact_match', now())"""
            connection.execute(sqlalchemy.text(query), msid=msids[0])

        pinned_recs = [{
            "recording_msid": msids[0],
            "recording_mbid": "076255b4-1575-11ec-ac84-135bf6a670e3",
            "blurb_content": "Awesome recordings with mapped data"
        }, {
            "recording_msid": msids[1],
            "recording_mbid": None,
            "blurb_content": "Great recording but unmapped"
        }]

        for data in pinned_recs:
            db_pinned_rec.pin(
                WritablePinnedRecording(
                    user_id=self.user["id"],
                    recording_msid=data["recording_msid"],
                    recording_mbid=data["recording_mbid"],
                    blurb_content=data["blurb_content"],
                ))

        pins = db_pinned_rec.get_pin_history_for_user(self.user["id"], 5, 0)
        pins_with_metadata = fetch_track_metadata_for_items(pins)

        received = [x.dict() for x in pins_with_metadata]
        # pinned recs returned in reverse order of submitted because order newest to oldest
        self.assertEqual(received[0]["recording_msid"],
                         pinned_recs[1]["recording_msid"])
        self.assertEqual(received[0]["recording_mbid"],
                         pinned_recs[1]["recording_mbid"])
        self.assertEqual(received[0]["blurb_content"],
                         pinned_recs[1]["blurb_content"])
        self.assertEqual(
            received[0]["track_metadata"], {
                "track_name": "Wicked Game",
                "artist_name": "Tom Ellis",
                "additional_info": {
                    "recording_msid": msids[1]
                }
            })

        self.assertEqual(received[1]["recording_msid"],
                         pinned_recs[0]["recording_msid"])
        self.assertEqual(received[1]["recording_mbid"],
                         pinned_recs[0]["recording_mbid"])
        self.assertEqual(received[1]["blurb_content"],
                         pinned_recs[0]["blurb_content"])
        self.assertEqual(
            received[1]["track_metadata"], {
                "track_name": "Strangers",
                "artist_name": "Portishead",
                "release_name": "Dummy",
                "additional_info": {
                    "recording_mbid": "076255b4-1575-11ec-ac84-135bf6a670e3",
                    "release_mbid": "1fd178b4-1575-11ec-b98a-d72392cd8c97",
                    "artist_mbids": ["6a221fda-2200-11ec-ac7d-dfa16a57158f"],
                    "recording_msid": msids[0]
                }
            })