Matteo08 commited on
Commit
b3cc6b6
·
verified ·
1 Parent(s): 2e78c23

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +166 -0
app.py ADDED
@@ -0,0 +1,166 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import pandas as pd
3
+ import matplotlib.pyplot as plt
4
+ import matplotlib
5
+ matplotlib.use("Agg")
6
+ import seaborn as sns
7
+ import requests
8
+ import io
9
+ from pathlib import Path
10
+ from vaderSentiment.vaderSentiment import SentimentIntensityAnalyzer
11
+
12
+ # ─────────────────────────────────────────
13
+ # SECTION 1 — Charger les données pré-calculées
14
+ # ─────────────────────────────────────────
15
+ try:
16
+ df_pricing = pd.read_csv("artifacts/pricing_decisions.csv")
17
+ df_sales = pd.read_csv("artifacts/dashboard_data.csv")
18
+ ARTIFACTS_OK = True
19
+ except Exception:
20
+ ARTIFACTS_OK = False
21
+ df_pricing = pd.DataFrame()
22
+ df_sales = pd.DataFrame()
23
+
24
+ # ─────────────────────────────────────────
25
+ # SECTION 2 — Analyse en temps réel (VADER)
26
+ # ─────────────────────────────────────────
27
+ analyzer = SentimentIntensityAnalyzer()
28
+
29
+ def get_sentiment_label(text):
30
+ score = analyzer.polarity_scores(text)["compound"]
31
+ if score >= 0.05:
32
+ return "positive"
33
+ elif score <= -0.05:
34
+ return "negative"
35
+ else:
36
+ return "neutral"
37
+
38
+ def pricing_decision(avg_units, positive_ratio, negative_ratio):
39
+ if avg_units >= 120 and positive_ratio >= 0.6:
40
+ return "📈 Increase Price"
41
+ elif avg_units <= 60 and negative_ratio >= 0.4:
42
+ return "📉 Decrease Price"
43
+ else:
44
+ return "➡️ Keep Price"
45
+
46
+ def analyze_book(title, reviews_text, avg_units_sold):
47
+ if not title or not reviews_text:
48
+ return "⚠️ Please enter a title and at least one review.", "", None
49
+
50
+ # Analyse sentiment de chaque review
51
+ lines = [r.strip() for r in reviews_text.strip().split("\n") if r.strip()]
52
+ labels = [get_sentiment_label(line) for line in lines]
53
+
54
+ total = len(labels)
55
+ positive_ratio = labels.count("positive") / total
56
+ negative_ratio = labels.count("negative") / total
57
+ neutral_ratio = labels.count("neutral") / total
58
+
59
+ decision = pricing_decision(avg_units_sold, positive_ratio, negative_ratio)
60
+
61
+ # Résumé texte
62
+ summary = f"""
63
+ 📚 **{title}**
64
+
65
+ 🔢 Reviews analysées : {total}
66
+ 😊 Positive : {positive_ratio:.0%}
67
+ 😐 Neutral : {neutral_ratio:.0%}
68
+ 😞 Negative : {negative_ratio:.0%}
69
+ 📦 Avg units sold : {avg_units_sold}
70
+
71
+ 💡 **Pricing Decision : {decision}**
72
+ """
73
+
74
+ # Graphique en camembert
75
+ fig, ax = plt.subplots(figsize=(4, 4))
76
+ colors = ["#4CAF50", "#FFC107", "#F44336"]
77
+ ax.pie(
78
+ [positive_ratio, neutral_ratio, negative_ratio],
79
+ labels=["Positive", "Neutral", "Negative"],
80
+ autopct="%1.0f%%",
81
+ colors=colors,
82
+ startangle=90
83
+ )
84
+ ax.set_title(f"Sentiment — {title}")
85
+ plt.tight_layout()
86
+
87
+ return summary, "\n".join(f"{l} → {s}" for l, s in zip(lines, labels)), fig
88
+
89
+ # ─────────────────────────────────────────
90
+ # SECTION 3 — Interface Gradio
91
+ # ─────────────────────────────────────────
92
+ with gr.Blocks(title="📚 Book Price Decider", theme=gr.themes.Soft()) as app:
93
+
94
+ gr.Markdown("# 📚 Book Price Decider — Group A4")
95
+ gr.Markdown("Sentiment analysis + ARIMA-based pricing decisions for books.")
96
+
97
+ with gr.Tabs():
98
+
99
+ # ── Tab 1 : Dashboard pré-calculé ──────────────────
100
+ with gr.Tab("📊 Dashboard"):
101
+ gr.Markdown("### Pre-computed results from the analysis notebooks")
102
+
103
+ if ARTIFACTS_OK:
104
+ with gr.Row():
105
+ gr.Image(value="artifacts/sales_trends.png",
106
+ label="Sales Trends")
107
+ gr.Image(value="artifacts/sentiment_distribution.png",
108
+ label="Sentiment Distribution")
109
+ gr.Dataframe(value=df_pricing, label="Pricing Decisions Table")
110
+ else:
111
+ gr.Markdown(
112
+ "⚠️ No artifacts found yet. "
113
+ "Run the notebooks and upload the `artifacts/` folder."
114
+ )
115
+
116
+ # ── Tab 2 : Analyse en temps réel ──────────────────
117
+ with gr.Tab("🔮 Analyze a New Book"):
118
+ gr.Markdown("### Enter book info to get a live pricing recommendation")
119
+
120
+ with gr.Row():
121
+ title_input = gr.Textbox(label="Book Title", placeholder="e.g. The Great Gatsby")
122
+ units_input = gr.Number(label="Avg Monthly Units Sold", value=100)
123
+
124
+ reviews_input = gr.Textbox(
125
+ label="Paste reviews here (one per line)",
126
+ lines=6,
127
+ placeholder="This book was amazing!\nNot what I expected.\nDecent read overall."
128
+ )
129
+
130
+ analyze_btn = gr.Button("🚀 Analyze & Decide", variant="primary")
131
+
132
+ with gr.Row():
133
+ summary_output = gr.Markdown(label="Summary")
134
+ details_output = gr.Textbox(label="Review-by-review labels", lines=6)
135
+
136
+ chart_output = gr.Plot(label="Sentiment Chart")
137
+
138
+ analyze_btn.click(
139
+ fn=analyze_book,
140
+ inputs=[title_input, reviews_input, units_input],
141
+ outputs=[summary_output, details_output, chart_output]
142
+ )
143
+
144
+ # ── Tab 3 : À propos ───────────────────────────────
145
+ with gr.Tab("ℹ️ About"):
146
+ gr.Markdown("""
147
+ ## About this app
148
+
149
+ This app is part of the **AI for Big Data Management** group project at ESCP Business School.
150
+
151
+ ### Pipeline
152
+ 1. **Real-world data** scraped from Books to Scrape
153
+ 2. **Synthetic data** generated to enrich with reviews & sales history
154
+ 3. **VADER sentiment analysis** on customer reviews
155
+ 4. **ARIMA forecasting** on sales time series
156
+ 5. **Rule-based pricing decisions** combining sentiment + sales volume
157
+ 6. **This Hugging Face app** as the final automation layer
158
+
159
+ ### Team — Group A4
160
+ - Project Manager
161
+ - Data Analyst
162
+ - UX Designer(s)
163
+ - Content Specialist
164
+ """)
165
+
166
+ app.launch()