Justkushere commited on
Commit
9f41fd4
·
1 Parent(s): cc1a86b

Initial commit for HF Spaces deployment

Browse files
Files changed (4) hide show
  1. Dockerfile +15 -0
  2. app.py +117 -0
  3. requirements.txt +0 -0
  4. templates/index.html +140 -0
Dockerfile ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.10
2
+
3
+ RUN useradd -m -u 1000 user
4
+ USER user
5
+ ENV PATH="/home/user/.local/bin:$PATH"
6
+
7
+ WORKDIR /app
8
+
9
+ COPY --chown=user ./requirements.txt requirements.txt
10
+ RUN pip install --no-cache-dir --upgrade -r requirements.txt
11
+
12
+ COPY --chown=user . /app
13
+
14
+ # For Flask, we should use gunicorn or similar, not uvicorn (which is for FastAPI)
15
+ CMD ["gunicorn", "-b", "0.0.0.0:7860", "app:app"]
app.py ADDED
@@ -0,0 +1,117 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import matplotlib
2
+ matplotlib.use('Agg')
3
+
4
+ from flask import Flask, render_template, request, redirect, url_for
5
+ import torch
6
+ import pandas as pd
7
+ import matplotlib.pyplot as plt
8
+ import io
9
+ import base64
10
+ from wordcloud import WordCloud
11
+ from transformers import DistilBertTokenizer, DistilBertForSequenceClassification
12
+ from datasets import load_dataset
13
+
14
+ app = Flask(__name__)
15
+
16
+ # Global variables
17
+ product_data = None
18
+ tokenizer = None
19
+ model = None
20
+
21
+ def load_data_and_model():
22
+ global product_data, tokenizer, model
23
+
24
+ # Load dataset
25
+ dataset = load_dataset("LoganKells/amazon_product_reviews_video_games")
26
+ product_data = dataset["train"].to_pandas()
27
+
28
+ # Load model and tokenizer
29
+ tokenizer = DistilBertTokenizer.from_pretrained("distilbert-base-uncased-finetuned-sst-2-english")
30
+ model = DistilBertForSequenceClassification.from_pretrained("distilbert-base-uncased-finetuned-sst-2-english")
31
+
32
+ def generate_word_cloud(text):
33
+ if not text.strip():
34
+ return None
35
+
36
+ wordcloud = WordCloud(width=800, height=400, background_color='white').generate(text)
37
+ plt.figure(figsize=(8, 4))
38
+ plt.imshow(wordcloud, interpolation='bilinear')
39
+ plt.axis('off')
40
+
41
+ img_buffer = io.BytesIO()
42
+ plt.savefig(img_buffer, format='png')
43
+ img_buffer.seek(0)
44
+ img_base64 = base64.b64encode(img_buffer.getvalue()).decode('utf-8')
45
+ plt.close()
46
+
47
+ return img_base64
48
+
49
+ @app.route('/', methods=['GET', 'POST'])
50
+ def index():
51
+ global product_data, tokenizer, model
52
+
53
+ # Load data and model if not already loaded
54
+ if product_data is None or tokenizer is None or model is None:
55
+ load_data_and_model()
56
+
57
+ result = None
58
+ error = None
59
+
60
+ if request.method == 'POST':
61
+ if 'refresh' in request.form:
62
+ return redirect(url_for('index'))
63
+
64
+ if 'generate' in request.form:
65
+ product_code = request.form.get('product_code', '').strip()
66
+
67
+ # Validate product code (10 digits)
68
+ if not product_code or not product_code.isdigit() or len(product_code) != 10:
69
+ error = "Please enter a 10 digit number product code."
70
+ else:
71
+ # Get reviews for the product
72
+ product_reviews = product_data[product_data['asin'] == product_code]['reviewText'].tolist()
73
+
74
+ if not product_reviews:
75
+ error = "Product code not found."
76
+ else:
77
+ total_reviews = len(product_reviews)
78
+ positive_count = 0
79
+ negative_count = 0
80
+ positive_reviews_text = ""
81
+ negative_reviews_text = ""
82
+
83
+ # Perform sentiment analysis on each review
84
+ for review in product_reviews:
85
+ inputs = tokenizer(review, return_tensors="pt", truncation=True, padding=True)
86
+ with torch.no_grad():
87
+ logits = model(**inputs).logits
88
+ predicted_class_id = logits.argmax().item()
89
+ label = model.config.id2label[predicted_class_id]
90
+
91
+ if label == 'POSITIVE':
92
+ positive_count += 1
93
+ positive_reviews_text += review + " "
94
+ else:
95
+ negative_count += 1
96
+ negative_reviews_text += review + " "
97
+
98
+ positive_percentage = (positive_count / total_reviews) * 100
99
+ negative_percentage = (negative_count / total_reviews) * 100
100
+
101
+ # Generate word clouds
102
+ positive_wordcloud = generate_word_cloud(positive_reviews_text)
103
+ negative_wordcloud = generate_word_cloud(negative_reviews_text)
104
+
105
+ result = {
106
+ 'product_code': product_code,
107
+ 'total_reviews': total_reviews,
108
+ 'positive_percentage': positive_percentage,
109
+ 'negative_percentage': negative_percentage,
110
+ 'positive_wordcloud': positive_wordcloud,
111
+ 'negative_wordcloud': negative_wordcloud
112
+ }
113
+
114
+ return render_template('index.html', result=result, error=error)
115
+
116
+ if __name__ == '__main__':
117
+ app.run(debug=True)
requirements.txt ADDED
Binary file (2.03 kB). View file
 
templates/index.html ADDED
@@ -0,0 +1,140 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Product Health Check</title>
7
+ <style>
8
+ body {
9
+ font-family: Arial, sans-serif;
10
+ max-width: 1200px;
11
+ margin: 0 auto;
12
+ padding: 20px;
13
+ }
14
+ h1 {
15
+ text-align: center;
16
+ margin-bottom: 30px;
17
+ }
18
+ .form-container {
19
+ display: flex;
20
+ flex-direction: column;
21
+ align-items: center;
22
+ margin-bottom: 30px;
23
+ }
24
+ .input-row {
25
+ display: flex;
26
+ width: 100%;
27
+ max-width: 600px;
28
+ margin-top: 10px;
29
+ }
30
+ .input-label {
31
+ display: block;
32
+ margin-bottom: 5px;
33
+ font-weight: bold;
34
+ }
35
+ input[type="text"] {
36
+ flex-grow: 1;
37
+ padding: 8px;
38
+ font-size: 16px;
39
+ border: 1px solid #ccc;
40
+ border-radius: 4px;
41
+ }
42
+ button {
43
+ margin-left: 10px;
44
+ padding: 8px 16px;
45
+ font-size: 16px;
46
+ background-color: #4caf50;
47
+ color: white;
48
+ border: none;
49
+ border-radius: 4px;
50
+ cursor: pointer;
51
+ }
52
+ button[name="refresh"] {
53
+ background-color: #f44336;
54
+ }
55
+ .error {
56
+ color: red;
57
+ margin: 15px 0;
58
+ text-align: center;
59
+ }
60
+ .results {
61
+ margin-top: 20px;
62
+ }
63
+ .results-header {
64
+ margin-bottom: 20px;
65
+ }
66
+ .results-columns {
67
+ display: flex;
68
+ gap: 20px;
69
+ }
70
+ .column {
71
+ flex: 1;
72
+ border: 1px solid #ddd;
73
+ border-radius: 4px;
74
+ padding: 15px;
75
+ }
76
+ .column h3 {
77
+ margin-top: 0;
78
+ }
79
+ img {
80
+ max-width: 100%;
81
+ height: auto;
82
+ }
83
+ </style>
84
+ </head>
85
+ <body>
86
+ <h1>Check Your Products Health</h1>
87
+
88
+ <div class="form-container">
89
+ <div class="input-label">
90
+ Enter Product Code (ASIN): ASIN is a 10 digit number
91
+ </div>
92
+ <form method="POST" class="input-row">
93
+ <input
94
+ type="text"
95
+ name="product_code"
96
+ value="{{ request.form.get('product_code', '') }}"
97
+ />
98
+ <button type="submit" name="generate" value="true">Generate</button>
99
+ <button type="submit" name="refresh" value="true">Refresh</button>
100
+ </form>
101
+ </div>
102
+
103
+ {% if error %}
104
+ <div class="error">{{ error }}</div>
105
+ {% endif %} {% if result %}
106
+ <div class="results">
107
+ <div class="results-header">
108
+ <h2>Product ID: {{ result.product_code }}</h2>
109
+ <p>Total Reviews: {{ result.total_reviews }}</p>
110
+ </div>
111
+
112
+ <div class="results-columns">
113
+ <div class="column">
114
+ <h3>
115
+ Positive Reviews: {{ "%.2f"|format(result.positive_percentage) }}%
116
+ </h3>
117
+ {% if result.positive_wordcloud %}
118
+ <img
119
+ src="data:image/png;base64,{{ result.positive_wordcloud }}"
120
+ alt="Positive Reviews Word Cloud"
121
+ />
122
+ {% endif %}
123
+ </div>
124
+
125
+ <div class="column">
126
+ <h3>
127
+ Negative Reviews: {{ "%.2f"|format(result.negative_percentage) }}%
128
+ </h3>
129
+ {% if result.negative_wordcloud %}
130
+ <img
131
+ src="data:image/png;base64,{{ result.negative_wordcloud }}"
132
+ alt="Negative Reviews Word Cloud"
133
+ />
134
+ {% endif %}
135
+ </div>
136
+ </div>
137
+ </div>
138
+ {% endif %}
139
+ </body>
140
+ </html>