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)
    def test_unpin(self):
        pinned = self.pin_single_sample(self.user["id"], 0)
        db_pinned_rec.unpin(self.user["id"])
        self.assertIsNone(db_pinned_rec.get_current_pin_for_user(self.user["id"]))

        # test that the pinned_until value was updated
        unpinned = db_pinned_rec.get_pin_history_for_user(user_id=self.user["id"], count=50, offset=0)[0]
        self.assertGreater(pinned.pinned_until, unpinned.pinned_until)
    def test_get_current_pin_for_user(self):
        self.pin_single_sample(self.user["id"], 0)
        expected_pinned = db_pinned_rec.get_current_pin_for_user(self.user["id"])
        recieved_pinned = db_pinned_rec.get_pin_history_for_user(user_id=self.user["id"], count=50, offset=0)[0]
        self.assertEqual(recieved_pinned, expected_pinned)

        self.pin_single_sample(self.user["id"], 1)
        expected_pinned = db_pinned_rec.get_current_pin_for_user(self.user["id"])
        recieved_pinned = db_pinned_rec.get_current_pin_for_user(self.user["id"])
        self.assertEqual(recieved_pinned, expected_pinned)
    def test_delete(self):
        keptIndex = 0

        # insert two records and delete the newer one
        self.pin_single_sample(self.user["id"], keptIndex)
        self.pin_single_sample(self.user["id"], 1)
        old_pin_history = db_pinned_rec.get_pin_history_for_user(user_id=self.user["id"], count=50, offset=0)
        pin_to_delete = old_pin_history[0]
        db_pinned_rec.delete(pin_to_delete.row_id, self.user["id"])

        # test that only the older pin remained in the database
        pin_history = db_pinned_rec.get_pin_history_for_user(user_id=self.user["id"], count=50, offset=0)
        pin_remaining = pin_history[0]
        self.assertEqual(len(pin_history), len(old_pin_history) - 1)
        self.assertEqual(pin_remaining.blurb_content, self.pinned_rec_samples[keptIndex]["blurb_content"])

        # delete the remaining pin
        db_pinned_rec.delete(pin_remaining.row_id, self.user["id"])
        pin_history = db_pinned_rec.get_pin_history_for_user(user_id=self.user["id"], count=50, offset=0)
        self.assertFalse(pin_history)
    def test_get_pins_for_user_following(self):
        # user follows followed_user_1
        db_user_relationship.insert(self.user["id"],
                                    self.followed_user_1["id"], "follow")
        self.assertTrue(
            db_user_relationship.is_following_user(self.user["id"],
                                                   self.followed_user_1["id"]))

        # test that followed_pins contains followed_user_1's pinned recording
        self.pin_single_sample(self.followed_user_1["id"], 0)
        followed_pins = db_pinned_rec.get_pins_for_user_following(user_id=1,
                                                                  count=50,
                                                                  offset=0)
        self.assertEqual(len(followed_pins), 1)
        self.assertEqual(followed_pins[0].user_name, "followed_user_1")

        # test that pins from users that the user is not following are not included
        self.pin_single_sample(self.followed_user_2["id"], 0)
        self.assertEqual(len(followed_pins), 1)

        # test that followed_user_2's pin is included after user follows
        db_user_relationship.insert(self.user["id"],
                                    self.followed_user_2["id"], "follow")
        followed_pins = db_pinned_rec.get_pins_for_user_following(user_id=1,
                                                                  count=50,
                                                                  offset=0)
        self.assertEqual(len(followed_pins), 2)
        self.assertEqual(followed_pins[0].user_name, "followed_user_2")

        # test that list is returned in descending order of creation date
        self.assertGreater(followed_pins[0].created, followed_pins[1].created)
        self.assertEqual(followed_pins[1].user_name, "followed_user_1")

        # test the limit argument
        limit = 1
        limited_following_pins = db_pinned_rec.get_pins_for_user_following(
            user_id=self.user["id"], count=limit, offset=0)
        self.assertEqual(len(limited_following_pins), limit)

        limit = 999
        limited_following_pins = db_pinned_rec.get_pins_for_user_following(
            user_id=self.user["id"], count=limit, offset=0)
        self.assertEqual(len(limited_following_pins), 2)

        # test the offset argument
        offset = 1
        offset_following_pins = db_pinned_rec.get_pins_for_user_following(
            user_id=self.user["id"], count=50, offset=offset)
        self.assertEqual(len(offset_following_pins), 2 - offset)

        offset = 999
        offset_following_pins = db_pinned_rec.get_pin_history_for_user(
            user_id=self.user["id"], count=50, offset=offset)
        self.assertFalse(offset_following_pins)
    def test_get_pin_history_for_user(self):
        count = 4
        self.insert_test_data(self.user["id"], count)

        # test that pin history includes unpinned recordings
        pin_history = db_pinned_rec.get_pin_history_for_user(user_id=self.user["id"], count=50, offset=0)
        db_pinned_rec.unpin(user_id=self.user["id"])
        new_pin_history = db_pinned_rec.get_pin_history_for_user(user_id=self.user["id"], count=50, offset=0)
        self.assertEqual(len(new_pin_history), len(pin_history))

        # test that the list was returned in descending order of creation date
        self.assertGreater(pin_history[0].created, pin_history[1].created)

        # test the limit argument
        limit = 1
        limited_pin_history = db_pinned_rec.get_pin_history_for_user(user_id=self.user["id"], count=limit, offset=0)
        self.assertEqual(len(limited_pin_history), limit)

        limit = 999
        limited_pin_history = db_pinned_rec.get_pin_history_for_user(user_id=self.user["id"], count=limit, offset=0)
        self.assertEqual(len(limited_pin_history), count)

        # test the offset argument
        offset = 1
        offset_pin_history = db_pinned_rec.get_pin_history_for_user(user_id=self.user["id"], count=50, offset=offset)
        self.assertEqual(len(offset_pin_history), count - offset)

        offset = 999
        offset_pin_history = db_pinned_rec.get_pin_history_for_user(user_id=self.user["id"], count=50, offset=offset)
        self.assertFalse(offset_pin_history)
    def test_unpin_if_active_currently_pinned(self):
        original_pinned = self.pin_single_sample(self.user["id"], 0)
        new_pinned = self.pin_single_sample(self.user["id"], 1)
        original_unpinned = db_pinned_rec.get_pin_history_for_user(user_id=self.user["id"], count=50, offset=0)[1]

        # only the pinned_until value of the record should be updated
        self.assertEqual(original_unpinned.user_id, original_pinned.user_id)
        self.assertEqual(original_unpinned.recording_msid, original_pinned.recording_msid)
        self.assertEqual(original_unpinned.recording_mbid, original_pinned.recording_mbid)
        self.assertEqual(original_unpinned.blurb_content, original_pinned.blurb_content)
        self.assertEqual(original_unpinned.created, original_pinned.created)
        self.assertLess(original_unpinned.pinned_until, original_pinned.pinned_until)

        self.assertNotEqual(new_pinned, original_pinned)
    def test_get_pin_count_for_user(self):
        self.insert_test_data(self.user["id"])
        pin_history = db_pinned_rec.get_pin_history_for_user(user_id=self.user["id"], count=50, offset=0)
        pin_count = db_pinned_rec.get_pin_count_for_user(user_id=self.user["id"])
        self.assertEqual(pin_count, len(pin_history))

        # test that pin_count includes unpinned recordings
        db_pinned_rec.unpin(user_id=self.user["id"])
        pin_count = db_pinned_rec.get_pin_count_for_user(user_id=self.user["id"])
        self.assertEqual(pin_count, len(pin_history))

        # test that pin_count excludes deleted recordings
        pin_to_delete = pin_history[1]
        db_pinned_rec.delete(pin_to_delete.row_id, self.user["id"])
        pin_count = db_pinned_rec.get_pin_count_for_user(user_id=self.user["id"])
        self.assertEqual(pin_count, len(pin_history) - 1)
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_msid": "8c7b4641-e363-4598-ae70-7709840fb934",
                        "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_pins(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,
    })
 def test_pin(self):
     count = self.insert_test_data(self.user["id"])
     pin_history = db_pinned_rec.get_pin_history_for_user(user_id=self.user["id"], count=50, offset=0)
     self.assertEqual(len(pin_history), count)
    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]
                }
            })