Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor branch, improve UI (two columns) and visibility #21

Merged
merged 8 commits into from
Oct 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 9 additions & 7 deletions src/recommenderapp/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,18 @@
Module for routing all calls from the frontend
"""

from utils import send_email_to_user, beautify_feedback_data
from flask_cors import CORS
import json
import sys

from flask import Flask, jsonify, render_template, request
from flask_cors import CORS
from search import Search
import sys
import json
from utils import beautify_feedback_data, send_email_to_user

sys.path.append("../../")
#pylint: disable=wrong-import-position
from src.prediction_scripts.item_based import recommend_for_new_user
# pylint: disable=wrong-import-position
# pylint: enable=wrong-import-position
#pylint: enable=wrong-import-position


app = Flask(__name__)
Expand Down Expand Up @@ -69,7 +71,7 @@ def feedback():


@app.route("/sendMail", methods=["POST"])
def sendMail():
def send_mail():
"""
Handles user feedback submission and mails the results.
"""
Expand Down
215 changes: 66 additions & 149 deletions src/recommenderapp/templates/landing_page.html
Original file line number Diff line number Diff line change
@@ -1,177 +1,94 @@
<!DOCTYPE html>
<html>
<head>
<link
rel="stylesheet"
type="text/css"
href="{{ url_for('static', filename='stylesheet.css') }}"
/>
<!-- Include stylesheets and scripts -->
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='stylesheet.css') }}">
<title>PopcornPicks🍿</title>
<script src="{{ url_for('static', filename='script.js') }}"></script>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link
rel="stylesheet"
href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css"
/>
<link
href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3"
crossorigin="anonymous"
/>
<script
src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"
integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p"
crossorigin="anonymous"
></script>
<meta name="viewport" content="width device-width, initial-scale=1" />
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css" />
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous" />
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
</head>

<body>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark topNavBar">
<nav class="navbar navbar-expand-lg navbar-dark bg-dark topNavBar fixed-top">
<div class="container-fluid">
<a class="navbar-brand" href="#">PopcornPicks🍿</a>
<button
class="navbar-toggler"
type="button"
data-bs-toggle="collapse"
data-bs-target="#navbarSupportedContent"
aria-controls="navbarSupportedContent"
aria-expanded="false"
aria-label="Toggle navigation"
>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<!-- <div class="collapse navbar-collapse" id="navbarSupportedContent">
<div class="d-flex">
<a class="nav-link" href="#">Link</a>
</div>
</div> -->
</div>
</nav>
<div class="heading1">
<div class="d-flex">
<h2>🎬Pick a Movie!🎬</h2>
</div>
<div class="d-flex">
<input
class="form-control mr-sm-2"
type="search"
placeholder="Search for a Movie"
aria-label="Search"
id="searchBox"
/>
<div class="container" style="margin-top: 60px;">
<div class="heading1">
<!-- Heading for picking a movie -->
<h2><center>🎬 Pick a Movie! 🎬</center></h2>
</div>
</div>

<!-- <div style="width: 600px; margin: auto">
<h2>Please select movies for training</h2>
<p style="width: 560px; margin: auto">
<label>Search Here</label>&nbsp;&nbsp;<input
type="text"
name="search"
id="searchBox"
/>
</p>
</div> -->
<div style="width: 600px; margin: auto">
<h2>Selected movie :</h2>
<ul class="list-group" id="selectedMovies"></ul>
</div>
<div style="width: 600px; margin: auto">
<input
type="button"
class="btn btn-primary"
name="predict"
id="predict"
value="Predict"
/>
</div>
<div style="width: 600px; margin: auto">
<h2>Predicted Movies</h2>
<form class="recos" id="recos">
<ul class="list-group" id="predictedMovies"></ul>
</form>
</div>
<div style="width: 600px; margin: auto">
<input
type="button"
class="btn btn-primary"
name="feedback"
data-toggle="modal"
id="feedback"
value="Give Feedback"
data-target="#exampleModalCenter"
/>
<!--TODO Button trigger modal-->
<button
type="button"
data-bs-toggle="modal"
data-bs-target="#exampleModalCenter"
>
button
</button>
<div
class="modal fade"
id="exampleModalCenter"
tabindex="-1"
role="dialog"
aria-labelledby="exampleModalCenterTitle"
aria-hidden="true"
>
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLongTitle">
Modal title
</h5>
<button
type="button"
class="close"
data-bs-dismiss="modal"
aria-label="Close"
>
<span aria-hidden="true">&times;</span>
</button>

<div class="row" style="margin-top: 25px;">
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Try using rem instead of px, instead of row, col (Table) use flexBox.

Don't add styles in HTML, define a class and move it to stylesheet.css

<div class="col-md-6">
<!-- Left Column (Selected Movie(s) and Search) -->
<div class="row">
<div class="col-md-10">
<!-- Selected Movie(s) section -->
<h2>Selected Movie(s):</h2>
<input class="form-control mr-sm-2" type="search" placeholder="Search for a Movie" aria-label="Search" id="searchBox" />
<ul class="list-group" id="selectedMovies"></ul>
</div>
<div class="col-md-2">
<!-- Predict button -->
<input type="button" class="btn btn-primary" name="predict" id="predict" value="Predict" style="margin-top: 47.5px;" />
</div>
</div>
</div>
<div class="col-md-1">
<!-- Empty column for spacing -->
</div>
<div class="col-md-5">
<!-- Right Column (Recommended Movies) -->
<div class="row">
<div class="col-md-12">
<!-- Recommended Movies section -->
<h2>Recommended Movies:</h2>
<form class="recos" id="recos">
<ul class="list-group" id="predictedMovies"></ul>
</form>
</div>
<div class="modal-body">...</div>
<div class="modal-footer">
<button
type="button"
class="btn btn-secondary"
data-bs-dismiss="modal"
>
Close
</button>
<button type="button" class="btn btn-primary">
Save changes
</button>
</div>
<div class="row">
<div class="col-md-12">
<!-- Feedback button (modal) -->
<input type="button" class="btn btn-primary" name="feedback" data-toggle="modal" id="feedback" value="Give Feedback" data-target="#exampleModalCenter">
</div>
</div>
</div>
</div>
</div>
<input class="c-checkbox" type="checkbox" id="checkbox" />
<div id="dataCollected" style="width: 600px; margin: auto; display: none">
<h1>Thanks!! Your response was stored.</h1>
<input
type="button"
id="refreshPage"
class="btn btn-danger"
name="refreshPage"
value="Take another attempt"
/>
<div class="container" style="margin-top: 20px;">
<div class="row">
<div class="col-md-12">
<!-- Checkbox section -->
<input class="c-checkbox" type="checkbox" id="checkbox" />
</div>
</div>
</div>
<div class="container" style="margin-top: 20px;">
<div class="row">
<div class="col-md-8">
<!-- Data collected section -->
<div id="dataCollected" style="display: none;">
<h1>Thanks!! Your response was stored.</h1>
<input type="button" id="refreshPage" class="btn btn-danger" name="refreshPage" value="Take another attempt" />
</div>
</div>
</div>
</div>
<br /><br /><br />
</body>
<script
src="https://code.jquery.com/jquery-3.5.1.min.js"
crossorigin="anonymous"
></script>
<script
src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js"
crossorigin="anonymous"
></script>
<script src="https://code.jquery.com/jquery-3.5.1.min.js" crossorigin="anonymous"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js" crossorigin="anonymous"></script>
<script src="{{ url_for('static', filename='script.js') }}"></script>
</html>
67 changes: 63 additions & 4 deletions src/recommenderapp/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,45 @@
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart

import pandas as pd
import constants as c

def create_colored_tags(genres):
"""
Utitilty function to create colored tags for different
movie genres
"""
# Define colors for specific genres
genre_colors = {
'Musical': '#FF1493', # DeepPink
'Sci-Fi': '#00CED1', # DarkTurquoise
'Mystery': '#8A2BE2', # BlueViolet
'Thriller': '#FF4500', # OrangeRed
'Horror': '#FF0000', # Red
'Documentary': '#228B22', # ForestGreen
'Fantasy': '#FF8C00', # DarkOrange
'Adventure': '#FFD700', # Gold
'Children': '#32CD32', # LimeGreen
'Film-Noir': '#000000', # Black
'Comedy': '#FFD700', # Gold
'Crime': '#8B0000', # DarkRed
'Drama': '#8B008B', # DarkMagenta
'Western': '#FF6347', # Tomato
'IMAX': '#7FFFD4', # Aquamarine
'Action': '#FF4500', # OrangeRed
'War': '#B22222', # FireBrick
'(no genres listed)': '#A9A9A9', # DarkGray
'Romance': '#FF69B4', # HotPink
'Animation': '#20B2AA' # LightSeaGreen
}
tags = []
for genre in genres:
color = genre_colors.get(genre, '#CCCCCC') # Default color if not found
tag = f'<span style="background-color: {color}; color: #FFFFFF; \
padding: 5px; border-radius: 5px;">{genre}</span>'
tags.append(tag)
return ' '.join(tags)

def beautify_feedback_data(data):
"""
Utility function to beautify the feedback json containing predicted movies for sending in email
Expand All @@ -33,6 +70,19 @@ def beautify_feedback_data(data):

return categorized_data_dict

def create_movie_genres(movie_genre_df):
"""
Utility function for creating a dictionary for movie-genres mapping
"""
# Create a dictionary to map movies to their genres
movie_to_genres = {}

# Iterating on all movies to create the map
for row in movie_genre_df.iterrows():
movie = row[1]['title']
genres = row[1]['genres'].split('|')
movie_to_genres[movie] = genres
return movie_to_genres
def send_email_to_user(recipient_email, categorized_data):
"""
Utility function to send movie recommendations to user over email
Expand All @@ -53,12 +103,21 @@ def send_email_to_user(recipient_email, categorized_data):
message['From'] = sender_email
message['To'] = recipient_email
message['Subject'] = subject

# Load the CSV file into a DataFrame
movie_genre_df = pd.read_csv('../../data/movies.csv')
# Creating movie-genres map
movie_to_genres = create_movie_genres(movie_genre_df)
# Create the email message with HTML content
html_content = c.EMAIL_HTML_CONTENT.format(
'\n'.join(f'<li>{movie}</li>' for movie in categorized_data['Liked']),
'\n'.join(f'<li>{movie}</li>' for movie in categorized_data['Disliked']),
'\n'.join(f'<li>{movie}</li>' for movie in categorized_data['Yet to Watch']))
'\n'.join(f'<li>{movie} \
{create_colored_tags(movie_to_genres.get(movie, ["Unknown Genre"]))}</li><br>' \
for movie in categorized_data['Liked']),
'\n'.join(f'<li>{movie} \
{create_colored_tags(movie_to_genres.get(movie, ["Unknown Genre"]))}</li><br>' \
for movie in categorized_data['Disliked']),
'\n'.join(f'<li>{movie} \
{create_colored_tags(movie_to_genres.get(movie, ["Unknown Genre"]))}</li><br>' \
for movie in categorized_data['Yet to Watch']))

# Attach the HTML email body
message.attach(MIMEText(html_content, 'html'))
Expand Down