def get_following():
    request_data = request.json
    user_id = extract_integer(request_data, "userID")
    reader = Reader.query.filter_by(id=user_id).first()
    if not reader:
        raise ResourceNotFound("A user with the specified ID does not exist")

    follows = reader.follows
    if not follows:
        raise ResourceNotFound(
            "The user with the specified ID does not follow any users")

    n_recommend = extract_integer(request_data,
                                  "n_recommend",
                                  default_value=DEFAULT_NRECOMMEND)

    all_following_books = set()
    for followed_user in follows:
        all_following_books.update(set(get_all_books(followed_user)))

    all_following_books = list(all_following_books)

    all_following_books = remove_reader_overlap(reader.id, all_following_books)
    recommendations = sample_top_books(all_following_books, n_recommend)

    return jsonify(books_schema.dump(recommendations))
def get_readers_recently_read(username):
    reader = Reader.query.filter_by(username=username).first()
    if not reader:
        raise ResourceNotFound(
            f"A user with the username {username} does not exist")
    recently_read = get_recently_read(reader)
    return jsonify(collection_schema.dump(recently_read))
def get_followers(username):
    reader = Reader.query.filter_by(username=username).first()
    if not reader:
        raise ResourceNotFound("A user with the specified ID does not exist")

    followers = reader.followers
    return jsonify(readers_schema.dump(followers))
def get_genre():
    request_data = request.json
    n_recommend = extract_integer(request_data,
                                  "n_recommend",
                                  default_value=DEFAULT_NRECOMMEND)

    # Seed Book
    book_id = extract_integer(request_data, "bookID")
    seed_book = Book.query.filter_by(id=book_id).first()
    if not seed_book:
        raise ResourceNotFound("A book with this ID does not exist")

    # If a genre name is provided, use books from that genre
    if genre_name := request.json.get("genre"):
        genre = Genre.query.filter_by(name=genre_name).first()
        if not genre:
            raise ResourceNotFound("A genre with this name does not exist")
        all_genre_books = genre.books
def get_author():
    request_data = request.json
    n_recommend = extract_integer(request_data,
                                  "n_recommend",
                                  default_value=DEFAULT_NRECOMMEND)

    # Seed Book
    book_id = extract_integer(request_data, "bookID")
    seed_book = Book.query.filter_by(id=book_id).first()
    if not seed_book:
        raise ResourceNotFound("A book with this ID does not exist")

    # If an author's name is provided, use their books
    if author_name := request_data.get("author"):
        author = Author.query.filter_by(name=author_name).first()
        if not author:
            raise ResourceNotFound("An author with this name does not exist")
        all_author_books = author.books
def get_all_readers_books(username):
    reader = Reader.query.filter_by(username=username).first()
    if not reader:
        raise ResourceNotFound(
            f"A user with the username {username} does not exist")
    all_books = get_all_books(
        reader, sort_func=lambda membership: membership.book.title)
    all_collection = Collection(books=all_books, name="All", reader=reader)
    return jsonify(collection_schema.dump(all_collection))
def get_reader_by_id(id_):
    if not isinstance(id_, int) and not id_.isdigit():
        raise InvalidRequest(
            r"Id should be an integer or a string interpretable as an integer",
        )

    reader = Reader.query.filter_by(id=id_).first()
    if not reader:
        raise ResourceNotFound(f"A user with the id {id_} does not exist")
    return jsonify(reader_schema.dump(reader))
def get_content():
    request_data = request.json
    n_recommend = extract_integer(request_data,
                                  "n_recommend",
                                  default_value=DEFAULT_NRECOMMEND)

    recommender = ContentRecommender(ngram_range=(1, 1))

    # Get recommendations from single book or list of books
    book_id = extract_integer(request_data, "bookID", required=False)
    book_ids = request_data.get("bookIDs")

    # Recommending from single seed book ID
    if book_id:
        seed = Book.query.filter_by(id=book_id).first()
        if not seed:
            raise ResourceNotFound("A book with this ID does not exist")

    # Recommending from list of book IDs
    elif book_ids:
        seed = Book.query.filter(Book.id.in_(book_ids)).all()
        if not len(seed) == len(book_ids):
            raise ResourceNotFound(
                "One of more books with a provided bookID does not exist")
    else:
        raise InvalidRequest("One of 'bookID' or 'bookIDs' is a required key")

    try:
        most_similar_books = recommender.recommend(seed,
                                                   n_recommend=POOL_SIZE *
                                                   n_recommend)
    except ValueError:
        raise InvalidRequest(
            "The provided bookID(s) did not correspond to books")

    # If a user id is provided, remove overlap between a user's books and the genre books
    if user_id := extract_integer(request_data, "userID", required=False):
        most_similar_books = remove_reader_overlap(user_id, most_similar_books)
def delete_reader(id_):
    if not isinstance(id_, int) and not id_.isdigit():
        raise InvalidRequest(
            r"Id should be an integer or a string interpretable as an integer",
        )
    reader = Reader.query.filter_by(id=id_).first()

    # Check user exists
    if not reader:
        raise ResourceNotFound("A user with the specified ID does not exist")

    # Check we are not deleting an admin
    if "admin" in reader.roles:
        raise ForbiddenResource("Cannot delete an admin")

    # Delete the user
    db.session.delete(reader)
    db.session.commit()

    # Return the new state of all users in the db
    users = Reader.query.all()
    return jsonify(readers_schema.dump(users))
def follow():
    follow_data = request.json
    follower_username = follow_data.get("follower")
    reader_username = follow_data.get("user")

    if not (follower_username and reader_username):
        raise InvalidRequest(
            r"Request should be of the form {{follower: 'username', user: '******'}}",
        )

    # Only the follower can request to follow
    if follower_username != flask_praetorian.current_user().username:
        raise ForbiddenResource(
            "You do not have the correct authorisation to access this resource"
        )

    reader = Reader.query.filter_by(username=reader_username).first()
    follower = Reader.query.filter_by(username=follower_username).first()

    if not reader or not follower:
        raise ResourceNotFound(
            "Either the follower or the user to follow does not exist")

    # Add the follower relationship if it does not exist
    if request.method == "POST" and follower not in reader.followers:
        reader.followers.append(follower)
        db.session.add(reader)
        db.session.commit()

    # Delete the follower relationship if it does not exist
    if request.method == "DELETE" and follower in reader.followers:
        reader.followers.remove(follower)
        db.session.add(reader)
        db.session.commit()

    return jsonify(reader_schema.dump(follower))
예제 #11
0
def remove_reader_overlap(reader_id, book_list):
    reader = Reader.query.filter_by(id=reader_id).first()
    if not reader:
        raise ResourceNotFound("A user with the specified ID does not exist")
    reader_books = get_all_books(reader)
    return list(set(book_list) - set(reader_books))
def get_reader(username):
    reader = Reader.query.filter_by(username=username).first()
    if not reader:
        raise ResourceNotFound(
            f"A user with the username {username} does not exist")
    return jsonify(reader_schema.dump(reader))