abid-ai commited on
Commit
33909eb
Β·
verified Β·
1 Parent(s): 930587e

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +218 -0
app.py ADDED
@@ -0,0 +1,218 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import json
3
+ import plotly.express as px
4
+ import pandas as pd
5
+ from groq import Groq
6
+ from fpdf import FPDF
7
+ from youtube_comment_downloader import YoutubeCommentDownloader
8
+ import re
9
+ import os
10
+ import warnings
11
+
12
+ warnings.filterwarnings("ignore")
13
+
14
+ # ====================== CONFIG ======================
15
+ GROQ_API_KEY = os.getenv("GROQ_API_KEY")
16
+
17
+ # ====================== SYSTEM PROMPT ======================
18
+ SYSTEM_PROMPT = """
19
+ You are an expert social media sentiment and poll analysis AI.
20
+ Focus on Yes/No, Agree/Disagree, Support/Oppose, and sentiment.
21
+
22
+ Handle English + Urdu well.
23
+ Return ONLY valid JSON in this exact format:
24
+ {
25
+ "main_poll": {
26
+ "question": "Suggested poll question",
27
+ "yes_count": int,
28
+ "no_count": int,
29
+ "agree_count": int,
30
+ "disagree_count": int,
31
+ "support_count": int,
32
+ "oppose_count": int,
33
+ "neutral_count": int
34
+ },
35
+ "sentiment": {
36
+ "positive": float,
37
+ "negative": float,
38
+ "neutral": float
39
+ },
40
+ "top_themes": ["theme1", "theme2", ...],
41
+ "summary": "Short professional summary",
42
+ "labeled_comments": [
43
+ {"comment": "...", "opinion": "Yes|No|Agree|Disagree|Positive|Negative|Neutral|Mixed"}
44
+ ]
45
+ }
46
+ """
47
+
48
+ # ====================== HELPER FUNCTIONS ======================
49
+ def extract_youtube_id(url):
50
+ patterns = [
51
+ r'youtu\.be/([a-zA-Z0-9_-]+)',
52
+ r'v=([a-zA-Z0-9_-]+)',
53
+ r'/embed/([a-zA-Z0-9_-]+)',
54
+ r'/shorts/([a-zA-Z0-9_-]+)'
55
+ ]
56
+ for pattern in patterns:
57
+ match = re.search(pattern, url)
58
+ if match:
59
+ return match.group(1)
60
+ return None
61
+
62
+ def clean_youtube_url(url):
63
+ video_id = extract_youtube_id(url)
64
+ if video_id:
65
+ return f"https://www.youtube.com/watch?v={video_id}"
66
+ return url
67
+
68
+ def fetch_youtube_comments(url, limit=150):
69
+ try:
70
+ clean_url = clean_youtube_url(url)
71
+ downloader = YoutubeCommentDownloader()
72
+ comments = []
73
+ for comment in downloader.get_comments_from_url(clean_url, sort_by=0):
74
+ comments.append(comment['text'])
75
+ if len(comments) >= limit:
76
+ break
77
+ return comments
78
+ except Exception as e:
79
+ print(f"Fetch error: {e}")
80
+ return []
81
+
82
+ def analyze_comments_with_groq(comments, post_context=""):
83
+ try:
84
+ client = Groq(api_key=GROQ_API_KEY)
85
+ comments_text = "\n\n".join([f"Comment {i+1}: {c[:250]}" for i, c in enumerate(comments[:130])])
86
+
87
+ user_prompt = f"""
88
+ Post Context: {post_context}
89
+ Analyze these comments and return proper JSON:
90
+ {comments_text}
91
+ """
92
+
93
+ response = client.chat.completions.create(
94
+ model="llama-3.3-70b-versatile",
95
+ messages=[
96
+ {"role": "system", "content": SYSTEM_PROMPT},
97
+ {"role": "user", "content": user_prompt}
98
+ ],
99
+ temperature=0.3,
100
+ max_tokens=4000,
101
+ response_format={"type": "json_object"}
102
+ )
103
+ return json.loads(response.choices[0].message.content)
104
+ except Exception as e:
105
+ print("Groq Error:", str(e))
106
+ return None
107
+
108
+ def create_pdf_report(analysis_result, poll_question):
109
+ pdf = FPDF()
110
+ pdf.add_page()
111
+ pdf.set_font('Arial', 'B', 16)
112
+ pdf.cell(0, 10, 'CommentSurvey AI Report', 0, 1, 'C')
113
+ pdf.ln(10)
114
+
115
+ pdf.set_font('Arial', 'B', 12)
116
+ pdf.cell(0, 10, f'Poll Question: {poll_question}', 0, 1, 'L')
117
+ pdf.ln(5)
118
+
119
+ pdf.set_font('Arial', 'B', 12)
120
+ pdf.cell(0, 10, 'Summary:', 0, 1, 'L')
121
+ pdf.set_font('Arial', '', 11)
122
+ pdf.multi_cell(0, 6, analysis_result.get('summary', 'No summary available.'))
123
+ pdf.ln(10)
124
+
125
+ pdf.set_font('Arial', 'B', 12)
126
+ pdf.cell(0, 10, 'Top Themes:', 0, 1, 'L')
127
+ pdf.set_font('Arial', '', 11)
128
+ for theme in analysis_result.get('top_themes', [])[:8]:
129
+ pdf.cell(0, 6, f"β€’ {theme}", 0, 1, 'L')
130
+
131
+ pdf.output("CommentSurvey_Report.pdf")
132
+ return "CommentSurvey_Report.pdf"
133
+
134
+ # ====================== GRADIO INTERFACE ======================
135
+ with gr.Blocks(title="CommentSurvey AI", theme=gr.themes.Soft()) as demo:
136
+ gr.Markdown("# πŸ“Š CommentSurvey AI\n**Turn YouTube Comments into Smart Surveys & Insights**")
137
+
138
+ with gr.Row():
139
+ with gr.Column(scale=4):
140
+ url_input = gr.Textbox(
141
+ label="🌐 Paste YouTube Video Link",
142
+ placeholder="https://youtu.be/xxxxxxxxxx",
143
+ lines=1
144
+ )
145
+ analyze_btn = gr.Button("πŸš€ Analyze Comments", variant="primary", size="large")
146
+
147
+ with gr.Column(scale=2):
148
+ status = gr.Markdown("**Status:** Ready")
149
+
150
+ with gr.Tabs():
151
+ with gr.Tab("πŸ“Š Poll Results"):
152
+ poll_plot = gr.Plot()
153
+ poll_md = gr.Markdown()
154
+
155
+ with gr.Tab("😊 Sentiment Analysis"):
156
+ sentiment_plot = gr.Plot()
157
+
158
+ with gr.Tab("πŸ” Key Insights"):
159
+ insights_md = gr.Markdown()
160
+
161
+ with gr.Tab("πŸ“œ Raw Comments"):
162
+ raw_table = gr.Dataframe()
163
+
164
+ download_btn = gr.File(label="πŸ“₯ Download PDF Report")
165
+
166
+ def analyze(url):
167
+ try:
168
+ if not GROQ_API_KEY:
169
+ return None, "**❌ Groq API Key is missing!**", None, None, None, None
170
+
171
+ comments = fetch_youtube_comments(url)
172
+ if len(comments) == 0:
173
+ return None, "**❌ Could not fetch comments.** Try a different public video.", None, None, None, None
174
+
175
+ result = analyze_comments_with_groq(comments)
176
+
177
+ if not result:
178
+ return None, "**❌ AI Analysis failed.**", None, None, None, None
179
+
180
+ main = result.get('main_poll', {})
181
+ poll_values = [
182
+ main.get('yes_count',0) + main.get('agree_count',0) + main.get('support_count',0),
183
+ main.get('no_count',0) + main.get('disagree_count',0) + main.get('oppose_count',0),
184
+ main.get('neutral_count',0)
185
+ ]
186
+
187
+ fig_poll = px.pie(
188
+ names=['Yes/Agree/Support', 'No/Disagree/Oppose', 'Neutral'],
189
+ values=poll_values,
190
+ title="Main Poll Results",
191
+ hole=0.4
192
+ )
193
+
194
+ sent = result.get('sentiment', {})
195
+ fig_sent = px.bar(
196
+ x=['Positive', 'Negative', 'Neutral'],
197
+ y=[sent.get('positive',0), sent.get('negative',0), sent.get('neutral',0)],
198
+ title="Sentiment Breakdown"
199
+ )
200
+
201
+ raw_df = pd.DataFrame(result.get('labeled_comments', []))
202
+ pdf_path = create_pdf_report(result, main.get('question', 'Social Media Poll'))
203
+
204
+ summary_text = f"**Poll Question:** {main.get('question','N/A')}\n\n**Summary:** {result.get('summary','')}"
205
+
206
+ return result, f"**βœ… Success!** Analyzed {len(comments)} comments", fig_poll, fig_sent, summary_text, pdf_path
207
+
208
+ except Exception as e:
209
+ return None, f"**❌ Error:** {str(e)}", None, None, None, None
210
+
211
+ analyze_btn.click(
212
+ fn=analyze,
213
+ inputs=[url_input],
214
+ outputs=[gr.State(), status, poll_plot, sentiment_plot, insights_md, download_btn]
215
+ )
216
+
217
+ if __name__ == "__main__":
218
+ demo.launch()