riazmo commited on
Commit
504405f
Β·
verified Β·
1 Parent(s): 221dd80

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +122 -41
app.py CHANGED
@@ -1,6 +1,7 @@
1
  """
2
  HuggingFace Spaces - Review Intelligence System (Streamlit)
3
  Complete app with URL input, progress tracking, and interactive dashboard
 
4
  """
5
 
6
  import streamlit as st
@@ -26,27 +27,87 @@ st.set_page_config(
26
  initial_sidebar_state="expanded"
27
  )
28
 
29
- # Custom CSS
30
  st.markdown("""
31
  <style>
32
  .main {
33
  padding: 0rem 1rem;
34
  }
 
 
35
  .stMetric {
36
- background-color: #f0f2f6;
37
- padding: 15px;
38
- border-radius: 5px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
  }
 
40
  .big-font {
41
  font-size: 24px !important;
42
  font-weight: bold;
43
  }
 
44
  .success-box {
45
- padding: 20px;
46
- border-radius: 10px;
47
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
48
  color: white;
49
  margin: 20px 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
  }
51
  </style>
52
  """, unsafe_allow_html=True)
@@ -143,6 +204,8 @@ def process_reviews_streamlit(app_store_urls: str, play_store_urls: str,
143
  reviews = pipeline.db.get_pending_reviews(limit=review_limit)
144
  total_reviews = len(reviews)
145
 
 
 
146
  processed_states = []
147
 
148
  for i, review in enumerate(reviews, 1):
@@ -155,9 +218,22 @@ def process_reviews_streamlit(app_store_urls: str, play_store_urls: str,
155
  state = create_initial_state(review)
156
  config = {"configurable": {"thread_id": f"review_{review.get('review_id')}"}}
157
  final_state = pipeline.review_graph.invoke(state, config=config)
158
- processed_states.append(dict(final_state))
 
 
 
 
 
 
 
 
 
 
159
  except Exception as e:
160
  st.warning(f"⚠️ Error processing review: {str(e)}")
 
 
 
161
  continue
162
 
163
  if len(processed_states) == 0:
@@ -220,32 +296,32 @@ def create_summary_section(scraped_count: int, results: List[Dict], insights: Di
220
  unsafe_allow_html=True
221
  )
222
 
223
- # Metrics
224
  col1, col2, col3, col4, col5 = st.columns(5)
225
 
226
  with col1:
227
- st.metric("Total Reviews", total, f"Scraped: {scraped_count}")
228
 
229
  with col2:
230
  pos_pct = (positive / total * 100) if total > 0 else 0
231
- st.metric("Positive", positive, f"{pos_pct:.1f}%")
232
 
233
  with col3:
234
  neg_pct = (negative / total * 100) if total > 0 else 0
235
- st.metric("Negative", negative, f"{neg_pct:.1f}%")
236
 
237
  with col4:
238
- st.metric("Critical Issues", critical, "🚨" if critical > 0 else "βœ…")
239
 
240
  with col5:
241
- delta_color = "inverse" if churn_risk > 30 else "normal"
242
- st.metric("Churn Risk", f"{churn_risk:.1f}%",
243
- "⚠️ High" if churn_risk > 30 else "βœ… Low")
244
 
245
  # Recommendations
246
- st.markdown("### πŸ’‘ Key Recommendations")
247
- for rec in insights.get('recommendations', []):
248
- st.info(rec)
 
249
 
250
 
251
  def create_sentiment_chart(insights: Dict):
@@ -254,7 +330,7 @@ def create_sentiment_chart(insights: Dict):
254
 
255
  labels = list(sentiment_dist.keys())
256
  values = list(sentiment_dist.values())
257
- colors = ['#2ca02c', '#ff7f0e', '#d62728']
258
 
259
  fig = go.Figure(data=[go.Pie(
260
  labels=labels,
@@ -282,7 +358,7 @@ def create_priority_chart(insights: Dict):
282
  priority_order = ['critical', 'high', 'medium', 'low']
283
  labels = [p for p in priority_order if p in priority_dist]
284
  values = [priority_dist.get(p, 0) for p in labels]
285
- colors = ['#d62728', '#ff7f0e', '#1f77b4', '#2ca02c']
286
 
287
  fig = go.Figure(data=[go.Bar(
288
  x=labels,
@@ -355,20 +431,24 @@ def create_emotion_chart(insights: Dict):
355
 
356
 
357
  def create_reviews_dataframe(results: List[Dict]) -> pd.DataFrame:
358
- """Create DataFrame for reviews table"""
 
 
 
359
 
360
  df_data = []
361
  for review in results:
 
362
  df_data.append({
363
  'Review ID': review.get('review_id', 'N/A')[:20],
364
  'Rating': review.get('rating', 0),
365
  'Review': (review.get('review_text', 'N/A') or '')[:100] + '...',
366
- 'Sentiment': review.get('stage3_final_sentiment', 'N/A'),
367
- 'Type': review.get('stage1_llm1_type', 'N/A'),
368
- 'Department': review.get('stage1_llm1_department', 'N/A'),
369
- 'Priority': review.get('stage1_llm1_priority', 'N/A'),
370
- 'Emotion': review.get('stage1_llm2_emotion', 'N/A'),
371
- 'Needs Review': '🚨 Yes' if review.get('stage3_needs_human_review') else 'βœ… No'
372
  })
373
 
374
  return pd.DataFrame(df_data)
@@ -383,7 +463,7 @@ def main():
383
 
384
  # Title
385
  st.title("🎯 Review Intelligence System")
386
- st.markdown("### Multi-Stage AI Analysis for App Store & Play Store Reviews")
387
  st.markdown("Powered by **LangGraph** + **HuggingFace** β€’ 4-Stage Processing Pipeline")
388
  st.markdown("---")
389
 
@@ -403,10 +483,8 @@ def main():
403
 
404
  # Main content - Input or Results
405
  if not st.session_state.processing_complete:
406
- # INPUT MODE
407
  show_input_form()
408
  else:
409
- # RESULTS MODE
410
  show_results_dashboard()
411
 
412
 
@@ -576,9 +654,10 @@ def show_results_dashboard():
576
  # Filter critical reviews
577
  critical_reviews = [
578
  r for r in results
579
- if (r.get('stage1_llm1_priority') == 'critical' or
580
- r.get('stage3_needs_human_review') or
581
- (r.get('stage3_final_sentiment') == 'NEGATIVE' and r.get('rating', 5) <= 2))
 
582
  ]
583
 
584
  if len(critical_reviews) == 0:
@@ -598,18 +677,20 @@ def show_results_dashboard():
598
  st.write(review.get('review_text', 'No text available'))
599
 
600
  st.markdown("**Reasoning:**")
601
- st.info(review.get('stage3_reasoning', 'No reasoning available'))
 
602
 
603
  with col2:
604
  st.markdown("**Classification:**")
605
- st.write(f"πŸ“Œ Type: {review.get('stage1_llm1_type', 'N/A')}")
606
- st.write(f"🏒 Department: {review.get('stage1_llm1_department', 'N/A')}")
607
- st.write(f"🎯 Priority: {review.get('stage1_llm1_priority', 'N/A')}")
608
- st.write(f"πŸ˜” Emotion: {review.get('stage1_llm2_emotion', 'N/A')}")
609
- st.write(f"πŸ’­ Sentiment: {review.get('stage3_final_sentiment', 'N/A')}")
610
 
611
  st.markdown("**Action:**")
612
- st.error(review.get('stage3_action_recommendation', 'No action specified'))
 
613
 
614
  # TAB 3: All Reviews
615
  with tab3:
@@ -748,4 +829,4 @@ def show_footer():
748
 
749
  if __name__ == "__main__":
750
  main()
751
- show_footer()
 
1
  """
2
  HuggingFace Spaces - Review Intelligence System (Streamlit)
3
  Complete app with URL input, progress tracking, and interactive dashboard
4
+ FIXED VERSION - Better UI contrast + Proper field mapping
5
  """
6
 
7
  import streamlit as st
 
27
  initial_sidebar_state="expanded"
28
  )
29
 
30
+ # FIXED Custom CSS - Better Contrast
31
  st.markdown("""
32
  <style>
33
  .main {
34
  padding: 0rem 1rem;
35
  }
36
+
37
+ /* FIXED: Metric cards with better contrast */
38
  .stMetric {
39
+ background: linear-gradient(135deg, #1e3a8a 0%, #3b82f6 100%);
40
+ padding: 20px;
41
+ border-radius: 10px;
42
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3);
43
+ border: 1px solid #60a5fa;
44
+ }
45
+
46
+ .stMetric label {
47
+ color: #dbeafe !important;
48
+ font-size: 14px !important;
49
+ font-weight: 600 !important;
50
+ text-transform: uppercase;
51
+ letter-spacing: 0.5px;
52
+ }
53
+
54
+ .stMetric [data-testid="stMetricValue"] {
55
+ color: #ffffff !important;
56
+ font-size: 36px !important;
57
+ font-weight: bold !important;
58
+ text-shadow: 0 2px 4px rgba(0,0,0,0.2);
59
+ }
60
+
61
+ .stMetric [data-testid="stMetricDelta"] {
62
+ color: #93c5fd !important;
63
+ font-size: 14px !important;
64
+ font-weight: 600 !important;
65
  }
66
+
67
  .big-font {
68
  font-size: 24px !important;
69
  font-weight: bold;
70
  }
71
+
72
  .success-box {
73
+ padding: 25px;
74
+ border-radius: 12px;
75
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
76
  color: white;
77
  margin: 20px 0;
78
+ box-shadow: 0 8px 16px rgba(0, 0, 0, 0.3);
79
+ }
80
+
81
+ .success-box h1 {
82
+ color: white !important;
83
+ text-shadow: 0 2px 4px rgba(0,0,0,0.2);
84
+ }
85
+
86
+ /* Info boxes */
87
+ .stAlert {
88
+ border-radius: 8px;
89
+ }
90
+
91
+ /* Better table styling */
92
+ .dataframe {
93
+ border: 1px solid #e2e8f0 !important;
94
+ }
95
+
96
+ /* Tab styling */
97
+ .stTabs [data-baseweb="tab-list"] {
98
+ gap: 8px;
99
+ }
100
+
101
+ .stTabs [data-baseweb="tab"] {
102
+ background-color: #1e293b;
103
+ border-radius: 8px 8px 0 0;
104
+ padding: 12px 24px;
105
+ color: #94a3b8;
106
+ }
107
+
108
+ .stTabs [aria-selected="true"] {
109
+ background-color: #3b82f6;
110
+ color: white;
111
  }
112
  </style>
113
  """, unsafe_allow_html=True)
 
204
  reviews = pipeline.db.get_pending_reviews(limit=review_limit)
205
  total_reviews = len(reviews)
206
 
207
+ print(f"πŸ“Š DEBUG: Found {total_reviews} reviews to process")
208
+
209
  processed_states = []
210
 
211
  for i, review in enumerate(reviews, 1):
 
218
  state = create_initial_state(review)
219
  config = {"configurable": {"thread_id": f"review_{review.get('review_id')}"}}
220
  final_state = pipeline.review_graph.invoke(state, config=config)
221
+
222
+ # Convert to dict
223
+ state_dict = dict(final_state)
224
+ processed_states.append(state_dict)
225
+
226
+ # DEBUG: Print what we got
227
+ print(f"βœ… Processed {review_id}:")
228
+ print(f" Type: {state_dict.get('classification_type', 'MISSING')}")
229
+ print(f" Dept: {state_dict.get('department', 'MISSING')}")
230
+ print(f" Sentiment: {state_dict.get('final_sentiment', 'MISSING')}")
231
+
232
  except Exception as e:
233
  st.warning(f"⚠️ Error processing review: {str(e)}")
234
+ print(f"❌ ERROR: {e}")
235
+ import traceback
236
+ print(traceback.format_exc())
237
  continue
238
 
239
  if len(processed_states) == 0:
 
296
  unsafe_allow_html=True
297
  )
298
 
299
+ # Metrics with better styling
300
  col1, col2, col3, col4, col5 = st.columns(5)
301
 
302
  with col1:
303
+ st.metric("πŸ“Š Total Reviews", total, f"Scraped: {scraped_count}")
304
 
305
  with col2:
306
  pos_pct = (positive / total * 100) if total > 0 else 0
307
+ st.metric("😊 Positive", positive, f"{pos_pct:.1f}%")
308
 
309
  with col3:
310
  neg_pct = (negative / total * 100) if total > 0 else 0
311
+ st.metric("😞 Negative", negative, f"{neg_pct:.1f}%")
312
 
313
  with col4:
314
+ st.metric("🚨 Critical", critical, "⚠️" if critical > 0 else "βœ…")
315
 
316
  with col5:
317
+ st.metric("πŸ“‰ Churn Risk", f"{churn_risk:.1f}%",
318
+ "πŸ”΄ High" if churn_risk > 30 else "🟒 Low")
 
319
 
320
  # Recommendations
321
+ if insights.get('recommendations'):
322
+ st.markdown("### πŸ’‘ Key Recommendations")
323
+ for rec in insights.get('recommendations', []):
324
+ st.info(rec)
325
 
326
 
327
  def create_sentiment_chart(insights: Dict):
 
330
 
331
  labels = list(sentiment_dist.keys())
332
  values = list(sentiment_dist.values())
333
+ colors = ['#10b981', '#f59e0b', '#ef4444']
334
 
335
  fig = go.Figure(data=[go.Pie(
336
  labels=labels,
 
358
  priority_order = ['critical', 'high', 'medium', 'low']
359
  labels = [p for p in priority_order if p in priority_dist]
360
  values = [priority_dist.get(p, 0) for p in labels]
361
+ colors = ['#dc2626', '#f59e0b', #3b82f6', '#10b981']
362
 
363
  fig = go.Figure(data=[go.Bar(
364
  x=labels,
 
431
 
432
 
433
  def create_reviews_dataframe(results: List[Dict]) -> pd.DataFrame:
434
+ """
435
+ FIXED: Create DataFrame with proper field mapping
436
+ Checks both state field names AND database field names
437
+ """
438
 
439
  df_data = []
440
  for review in results:
441
+ # FIXED: Check state fields FIRST, fall back to database fields
442
  df_data.append({
443
  'Review ID': review.get('review_id', 'N/A')[:20],
444
  'Rating': review.get('rating', 0),
445
  'Review': (review.get('review_text', 'N/A') or '')[:100] + '...',
446
+ 'Sentiment': review.get('final_sentiment', review.get('stage3_final_sentiment', 'N/A')),
447
+ 'Type': review.get('classification_type', review.get('stage1_llm1_type', 'N/A')),
448
+ 'Department': review.get('department', review.get('stage1_llm1_department', 'N/A')),
449
+ 'Priority': review.get('priority', review.get('stage1_llm1_priority', 'N/A')),
450
+ 'Emotion': review.get('emotion', review.get('stage1_llm2_emotion', 'N/A')),
451
+ 'Needs Review': '🚨 Yes' if review.get('needs_human_review', review.get('stage3_needs_human_review')) else 'βœ… No'
452
  })
453
 
454
  return pd.DataFrame(df_data)
 
463
 
464
  # Title
465
  st.title("🎯 Review Intelligence System")
466
+ st.markdown("### Multi-Stage AI Analysis Dashboard")
467
  st.markdown("Powered by **LangGraph** + **HuggingFace** β€’ 4-Stage Processing Pipeline")
468
  st.markdown("---")
469
 
 
483
 
484
  # Main content - Input or Results
485
  if not st.session_state.processing_complete:
 
486
  show_input_form()
487
  else:
 
488
  show_results_dashboard()
489
 
490
 
 
654
  # Filter critical reviews
655
  critical_reviews = [
656
  r for r in results
657
+ if (r.get('priority') == 'critical' or
658
+ r.get('stage1_llm1_priority') == 'critical' or
659
+ r.get('needs_human_review', r.get('stage3_needs_human_review')) or
660
+ (r.get('final_sentiment', r.get('stage3_final_sentiment')) == 'NEGATIVE' and r.get('rating', 5) <= 2))
661
  ]
662
 
663
  if len(critical_reviews) == 0:
 
677
  st.write(review.get('review_text', 'No text available'))
678
 
679
  st.markdown("**Reasoning:**")
680
+ reasoning = review.get('reasoning', review.get('stage3_reasoning', 'No reasoning available'))
681
+ st.info(reasoning)
682
 
683
  with col2:
684
  st.markdown("**Classification:**")
685
+ st.write(f"πŸ“Œ Type: {review.get('classification_type', review.get('stage1_llm1_type', 'N/A'))}")
686
+ st.write(f"🏒 Department: {review.get('department', review.get('stage1_llm1_department', 'N/A'))}")
687
+ st.write(f"🎯 Priority: {review.get('priority', review.get('stage1_llm1_priority', 'N/A'))}")
688
+ st.write(f"πŸ˜” Emotion: {review.get('emotion', review.get('stage1_llm2_emotion', 'N/A'))}")
689
+ st.write(f"πŸ’­ Sentiment: {review.get('final_sentiment', review.get('stage3_final_sentiment', 'N/A'))}")
690
 
691
  st.markdown("**Action:**")
692
+ action = review.get('action_recommendation', review.get('stage3_action_recommendation', 'No action specified'))
693
+ st.error(action)
694
 
695
  # TAB 3: All Reviews
696
  with tab3:
 
829
 
830
  if __name__ == "__main__":
831
  main()
832
+ show_footer()