Example #1
0
def query_create_feedback(userId: Union[int, str], movieId: Union[int, str], rating: Union[int, str]):
    """
    Create a new feedback row for a movie from a user
    :param Union[int, str] userId: The user id to retrieve
    :param Union[int, str] movieId: The movie id to retrieve
    :param Union[int, str] rating: The rating to apply to the movie
    :return: JSON object of feedback id
    """
    
    con, cursor = db_connection()
    
    try:
        cursor.execute(f"INSERT INTO movie_feedback (rating, movie_id, user_id) VALUES ({rating}, {movieId}, {userId})")
        if cursor.rowcount == 1:
            con.commit()
            data = {
                "id": cursor.lastrowid
            }
            return data
        else:
            con.rollback()
            return None
    finally:
        cursor.close()
        con.close()
Example #2
0
def query_get_tag(id: Union[int, str]):
    """
    Returns a single tag by tag id
    :param Union[int, str] id: The tag id to retrieve
    :return: JSON object with tag id, name, and movie id
    """

    con, cursor = db_connection()
    try:
        cursor.execute("SELECT name, movie_id FROM tags WHERE id=%s", (id,))
        result = cursor.fetchmany(size=1)

        if len(result) != 1:
            return None
        else:
            name = result[0][0]
            movie_id = result[0][1]
            
            data = {
                "id": id,
                "name": name,
                "movie_id": movie_id
            }

            return data
    finally:
        con.close()
        cursor.close()
def query_create_feedback_tag(userId: Union[int, str], movieId: Union[int,
                                                                      str],
                              tagId: Union[int, str], rating: Union[int, str]):
    """
    Create a new feedback row for a movie's tag from a user
    :param Union[int, str] userId: The user id to retrieve
    :param Union[int, str] movieId: The movie id to retrieve
    :param Union[int, str] tagId: The tag id to retrieve
    :param Union[int, str] rating: The rating to apply to the tag
    :return: JSON object of feedback id
    """

    con, cursor = db_connection()

    try:
        cursor.execute(
            "INSERT INTO tag_feedback (rating, movie_id, user_id, tag_id) VALUES ({r}, {m}, {u}, {t})"
            .format(r=rating, m=movieId, u=userId, t=tagId))
        if cursor.rowcount == 1:
            con.commit()
            data = {"id": cursor.lastrowid}
            return data
        else:
            con.rollback()
            return None
    finally:
        cursor.close()
        con.close()
def query_get_feedback_tags(userId: Union[int, str], movieId: Union[int, str]):
    """
    Return the user’s feedback on tags of a specific movie
    :param Union[int, str] userId: The user id to retrieve
    :param Union[int, str] movieId: The movie id to retrieve
    :return: JSON object of feedbacks array containing movie id, tag id, and rating
    """

    con, cursor = db_connection()

    try:
        cursor.execute(
            "SELECT movie_feedback.movie_id, tag_feedback.tag_id, tag_feedback.rating FROM movie_feedback JOIN tag_feedback ON movie_feedback.movie_id = tag_feedback.movie_id WHERE movie_feedback.user_id={u} AND movie_feedback.movie_id={m}"
            .format(u=userId, m=movieId))
        result = cursor.fetchall()
        if cursor.rowcount > 0:
            data = {}
            feedbacks = []
            for row in result:
                feedbacks.append({
                    "movie_id": row[0],
                    "tag_id": row[1],
                    "rating": row[2]
                })
            data.update({"feedbacks": feedbacks})
            return data
        else:
            return None
    finally:
        cursor.close()
        con.close()
Example #5
0
def authenticate(email: str, token: str) -> Union[Dict, None]:
    """
    Validate the user has the ability to login with the given token.
    :param str email: Email address to attempt to validate against
    :param str token: OAuth token to attempt to validate
    :return: Authorization status of the request.
    """
    from server import app
    con, cursor = db_connection()

    try:
        token_first_name, token_last_name, token_email, token_expiration = _fetch_token_info(
            token)
        assert email == token_email

        verify_user(token_first_name, token_last_name, token_email)

        cursor.execute("SELECT id, isAdmin FROM users WHERE email=%s",
                       (token_email, ))
        result = cursor.fetchmany(size=1)

        user = {
            'id': result[0][0],
            'firstName': token_first_name,
            'lastName': token_last_name,
            'isAdmin': result[0][1] == 1,
            'email': token_email,
            'expiration': token_expiration
        }
        session['user'] = user
        app.open_session(request)

        print(
            f"Login success: {user['lastName']}, {user['firstName']} ({user['email']})"
        )
        return user
    except (AssertionError, ValueError):
        print(
            f'Warning: Invalid login attempt for {email} from {request.remote_addr}.'
        )
        return None
    finally:
        print('*' * 50)
        cursor.close()
        con.close()
Example #6
0
def query_get_movie(id: Union[int, str]):
    """
    Returns a single movie by movie id
    :param Union[int, str] id: The movie id to retrieve
    :return: JSON object with movie id, title, tags, and genres
    """

    con, cursor = db_connection()
    try:
        cursor.execute(
            '''SELECT distinct movies.id, movies.name, group_concat(DISTINCT tags.name ORDER BY tags.name ASC SEPARATOR ',') as 'Tags', '''
            '''group_concat(DISTINCT genre.genre ORDER BY genre.genre ASC SEPARATOR ',') as 'Genres' FROM FlickPick.movies '''
            '''LEFT JOIN tags ON tags.movie_id = movies.id '''
            '''LEFT JOIN genre ON genre.movie_id = movies.id '''
            '''WHERE movies.id = %s;''', (id, ))
        result = cursor.fetchall()

        if (len(result)) == 0:
            return None
        else:
            movie_id = result[0][0]
            movie_name = result[0][1]
            if result[0][2] is None:
                tags = [[]]
            else:
                tags = [tags[2].split(',') for tags in result]

            if result[0][3] is None:
                genres = [[]]
            else:
                genres = [genres[3].split(',') for genres in result]

            data = {
                "id": movie_id,
                "title": movie_name,
                "tags": tags[0],
                "genres": genres[0]
            }

            return data

    finally:
        con.close()
        cursor.close()
Example #7
0
def verify_user(firstName: str, lastName: str, email: str):
    """
    Check if the user signing in is a user in the database. If not,
    an entry is created in the user table.
    :param str firstName: First name of the user signing in
    :param str lastName: Last name of the user signing in
    :param str email: Email address of the user signing in
    :return: Authorization status of the request.
    """
    con, cursor = db_connection()

    try:
        cursor.execute("SELECT * FROM users WHERE email=%s", (email, ))
        result = cursor.fetchmany(size=1)

        if len(result) == 1:
            return {
                'id': result[0][0],
                'firstName': result[0][1],
                'lastName': result[0][2],
                'email': result[0][3],
                'isAdmin': result[0][4] == 1,
            }
        else:
            cursor.execute(
                "INSERT INTO users (firstName, lastName, email, isAdmin) VALUES (%s, %s, %s, 0)",
                (firstName, lastName, email))
            con.commit()

            return {
                'id': cursor.lastrowid,
                'firstName': firstName,
                'lastName': lastName,
                'email': email,
                'isAdmin': False,
            }
    except Exception as e:
        print(f'Exception in verify_user')
        print(e)
        return None
    finally:
        cursor.close()
        con.close()
def get_movie_autocomplete(name: str):
    """
    Get a list of auto-complete suggestions for a partial movie title
    :param str name: The movie title to find suggestions for
    :return: JSON object of movies array containing movie id, title, tags and genres
    """
    con, cursor = db_connection()

    name = unquote(name)

    try:
        if not is_user():
            return Response(json.dumps({}), mimetype='application/json', status=403)
        else:
            cursor.execute(
                f"SELECT movies.id, movies.name, group_concat(DISTINCT tags.name ORDER BY tags.name ASC SEPARATOR ',') as 'Tags',"
                f"group_concat(DISTINCT genre.genre ORDER BY genre.genre ASC SEPARATOR ',') as 'Genres' FROM FlickPick.movies "
                f"LEFT JOIN tags ON tags.movie_id = movies.id "
                f"LEFT JOIN genre ON genre.movie_id = movies.id "
                f"WHERE movies.name LIKE '{name}__%'"
                f"GROUP BY movies.id ORDER BY movies.id ASC")

            result = cursor.fetchall()

            if len(result) == 0:
                return []
            else:
                distances = [(distance(a[1], name), a[0], a[1], a[2], a[3]) for a in result]
                titles = list(
                    map(lambda movie: {'id': movie[1], 'title': movie[2], 'tags': movie[3], 'genres': movie[4]},
                        sorted(distances)))[:10]

                for x in titles:
                    x['tags'] = [] if x['tags'] is None else x['tags'].split(',')
                    x['genres'] = [] if x['genres'] is None else x['genres'].split(',')

            return Response(json.dumps(titles), mimetype='application/json', status=200)
    except Exception:
        return Response(json.dumps({}), mimetype='application/json', status=500)
    finally:
        cursor.close()
        con.close()
Example #9
0
def query_update_feedback(feedbackId: Union[int, str], rating: Union[float, str]):
    """
    Replace a user's feedback of a specific movie
    :param Union[int, str] feedbackId: The feedback id to retrieve
    :param Union[int, str] rating: The rating to apply to the movie
    :return: Nothing
    """
    
    con, cursor = db_connection()
    
    try:
        cursor.execute("UPDATE movie_feedback SET rating={r} WHERE id={f}".format(r = rating, f = feedbackId))
        if cursor.rowcount == 1:
            con.commit()
            return True
        else:
            con.rollback()
            return False
    finally:
        cursor.close()
        con.close()
def get_tag_autocomplete(name: str, movieId: int):
    """
    Get a list of auto-complete suggestions for a partial tag. The same tag may exist across multiple movies,
    this method does not return every instance of a tag, only unique tags
    :param str name: The tag name to find suggestions for
    :param int movieId: The movie id to retrieve
    :return: JSON object of tags array containing tag name
    """
    con, cursor = db_connection()
    result_set = {}

    name = unquote(name)

    try:
        if not is_user():
            return Response(json.dumps({}), mimetype='application/json', status=403)
        else:
            cursor.execute(f"SELECT id, name FROM tags WHERE movie_id=%s AND name LIKE '{name}__%'", (movieId,))

            result = cursor.fetchall()

            for a in result:        # creates a unique set from result list
                result_set[a[1]] = a

            result_set = list(result_set.values())  # turns set back to a list

            if len(result_set) == 0:
                return []
            else:
                distances = [(distance(a[1], name), a[1], a[0]) for a in result_set]
                tag = list(map(lambda movie: {'id': movie[2], 'tag': movie[1]}, sorted(distances)))[:10]

                return Response({
                    "tags": tag
                }, mimetype='application/json', status=200)
    except Exception:
        return Response(json.dumps({}), mimetype='application/json', status=500)
    finally:
        cursor.close()
        con.close()
def query_get_feedback(userId: int, movieId: int):
    """
    Return the user’s feedback of the specified movie id
    :param Union[int, str] userId: The user id to retrieve
    :param Union[int, str] movieId: The movie id to retrieve
    :return: JSON object of movie id and rating
    """

    con, cursor = db_connection()

    try:
        cursor.execute(
            "SELECT rating FROM movie_feedback WHERE user_id={u} AND movie_id={m}"
            .format(u=userId, m=movieId))
        result = cursor.fetchone()
        if cursor.rowcount == 1:
            data = {"id": movieId, "rating": result[0]}
            return data
        else:
            return None
    finally:
        cursor.close()
        con.close()
def del_feedback(userId: int):
    """
    Delete all user feedback for tags and movies
    :param int userId: The user whose feedback is being deleted
    :return: Nothing
    """
    con, cursor = db_connection()

    try:
        if not isinstance(userId, int):
            return Response(json.dumps({}),
                            mimetype='application/json',
                            status=400)
        elif not is_current_user(userId):
            return Response(json.dumps({}),
                            mimetype='application/json',
                            status=401)
        else:
            cursor.execute(
                "DELETE m.*, t.* FROM movie_feedback m LEFT JOIN tag_feedback t ON m.user_id = t.user_id "
                "WHERE m.user_id = %s", (userId, ))
            if cursor.rowcount == 0:
                return Response(json.dumps({}),
                                mimetype='application/json',
                                status=404)
            else:
                con.commit()
                return Response(json.dumps({}),
                                mimetype='application/json',
                                status=200)
    except Exception:
        return Response(json.dumps({}),
                        mimetype='application/json',
                        status=500)
    finally:
        cursor.close()
        con.close()
Example #13
0
def del_user(id: int):
    """
    Delete a user by id
    :param int id: The user id to delete
    :return: Nothing
    """
    if not isinstance(id, int):
        return Response(json.dumps({}),
                        mimetype='application/json',
                        status=400)

    con, cursor = db_connection()
    try:
        if not (is_admin() or is_current_user(id)):
            return Response(json.dumps({}),
                            mimetype='application/json',
                            status=401)
        else:
            cursor.execute("DELETE FROM users WHERE id=%s", (id, ))
            if cursor.rowcount == 1:
                con.commit()
                return Response(json.dumps({}),
                                mimetype='application/json',
                                status=200)
            else:
                return Response(json.dumps({}),
                                mimetype='application/json',
                                status=404)
    except Exception as e:
        print(f'Error in del_user')
        print(e)
        return Response(json.dumps({}),
                        mimetype='application/json',
                        status=500)
    finally:
        cursor.close()
        con.close()
Example #14
0
def check_update(firstName: str, lastName: str, email: str):
    """
    Check if the user signing in has updated personal details and update values
    in the database if need be.
    NOTE: Only firstName and lastName can be updated
    :param str firstName: First name of the user signing in
    :param str lastName: Last name of the user signing in
    :param str email: Email address of the user signing in
    :return: Authorization status of the request.
    """
    con, cursor = db_connection()

    try:
        cursor.execute("SELECT firstName, lastName FROM users WHERE email=%s",
                       (email, ))
        result = cursor.fetchmany(size=1)

        if result[0][0] != firstName:
            cursor.execute("UPDATE users SET firstName=%s WHERE email=%s", (
                firstName,
                email,
            ))

        if result[0][1] != lastName:
            cursor.execute("UPDATE users SET lastName=%s WHERE email=%s", (
                lastName,
                email,
            ))

        con.commit()
    except Exception as e:
        print(f"Error in check_update")
        print(e)
    finally:
        cursor.close()
        con.close()
def get_recommendations(user_id: int):
    """
    Retrieves the top 10 best movies to fit a user's current taste.
    :param int user_id: The user to make a recommendation for
    :return: A list of recommended movies and all of their data
    """
    if not (is_admin() or is_current_user(user_id)):
        return Response(json.dumps({}),
                        mimetype='application/json',
                        status=401)
    else:
        connection, cursor = db_connection()

        limit = 10

        def magnitude(a: np.ndarray):
            return np.linalg.norm(a)

        def similarity(a: np.ndarray, b: np.ndarray):
            return np.dot(a, b) / (magnitude(a) * magnitude(b))

        with open(f"model/mf/{model_name}_d.mat.pickle", 'rb') as file:
            ratings_mat = pickle.loads(file.read())

        predict_mat = np.load(f"model/mf/{model_name}_r.npy")

        # TODO: Slow. Should be loaded from disk.
        cursor.execute("SELECT MAX(movie_id) FROM movie_feedback;")
        model_size = cursor.fetchone()[0]

        cursor.execute(
            f"SELECT movie_id-1 FROM movie_feedback WHERE user_id={user_id};")
        user_ratings = cursor.fetchall()

        user_ratings = np.array(user_ratings).flat
        user_ratings_dense = np.zeros(model_size)
        np.put(user_ratings_dense, user_ratings, [1])
        user_ratings = set([int(rating) + 1 for rating in user_ratings])

        # Calculate which users are the most similar to the requested user
        similarities = [(idx, similarity(user_ratings_dense,
                                         row.todense().flat))
                        for idx, row in enumerate(ratings_mat)]
        similarities = sorted(similarities, key=lambda x: x[1], reverse=True)

        # Calculate the candidates based on the top 5 most similar users
        # Some movies will overlap, we'll weight those stronger
        candidates = np.zeros_like(user_ratings_dense)
        for sim_id, sim_val in similarities[:5]:
            candidates += ratings_mat[sim_id -
                                      1].todense().flat * predict_mat[sim_id -
                                                                      1]

        candidates = list(
            filter(
                lambda x: x not in user_ratings,
                map(
                    lambda x: x[0],
                    sorted(enumerate(candidates),
                           key=lambda x: x[1],
                           reverse=True))))
        candidates = list(map(query_get_movie, candidates[:limit]))
        return json.dumps(candidates)