-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathapp.py
More file actions
311 lines (245 loc) · 10.8 KB
/
app.py
File metadata and controls
311 lines (245 loc) · 10.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
import streamlit as st
import pandas as pd
from fpdf import FPDF
from langchain_google_genai import ChatGoogleGenerativeAI
from langchain.prompts import PromptTemplate
from dotenv import load_dotenv
import os
load_dotenv()
# --- CONFIGURATION ---
st.set_page_config(page_title="AdPulse | Automated Insights", page_icon="📊", layout="centered")
# --- Gemini API KEY from ENV ---
api_key = os.getenv("GEMINI_API_KEY")
# --- CORE LOGIC (PANDAS) ---
def analyze_campaign_data(df):
"""
Performs deterministic calculations on the raw data.
"""
try:
# 1. Clean & Validate
required_cols = ['Impressions', 'Clicks', 'Spend', 'Conversions']
if not all(col in df.columns for col in required_cols):
return None, "Missing required columns. Please upload a valid AdTech CSV."
# 2. Aggregations
total_spend = df['Spend'].sum()
total_clicks = df['Clicks'].sum()
total_conversions = df['Conversions'].sum()
total_impressions = df['Impressions'].sum()
# 3. Derived Metrics (KPIs)
ctr = (total_clicks / total_impressions * 100) if total_impressions > 0 else 0
cpa = total_spend / total_conversions if total_conversions > 0 else 0
cpc = total_spend / total_clicks if total_clicks > 0 else 0
# 4. Identify Winners & Losers
df['ROAS_Proxy'] = df['Conversions'] / df['Spend'] # Simple Return on Ad Spend proxy
top_campaign = df.loc[df['Conversions'].idxmax()]['Campaign_Name']
most_efficient = df.loc[df['ROAS_Proxy'].idxmax()]['Platform']
stats = {
"Total Spend": f"${total_spend:,.2f}",
"Total Conversions": f"{total_conversions:,}",
"Global CTR": f"{ctr:.2f}%",
"Avg CPA": f"${cpa:.2f}",
"Avg CPC": f"${cpc:.2f}",
"Top Campaign": top_campaign,
"Best Platform": most_efficient
}
return stats, None
except Exception as e:
return None, f"Data Processing Error: {str(e)}"
# --- AI INSIGHT GENERATOR ---
def generate_executive_summary(stats, df_head):
"""
Basically google gemini api is used to generate insights of the csv data.
"""
if not api_key:
return "⚠️ GEMINI_API_KEY is missing. Please check your .env file."
try:
# Using gemini-2.5-flash for speed and efficiency in the project
llm = ChatGoogleGenerativeAI(
model="gemini-2.5-flash",
temperature=0.5,
google_api_key=api_key,
convert_system_message_to_human=True # act as a helper for some langchain versions, good to have
)
template = """
You are a Senior Data Analyst at a top AdTech firm.
Write a concise, 3-paragraph executive summary for the Marketing Director based on the following weekly performance data.
KEY METRICS:
{metrics}
CONTEXT:
The highest performing platform is {best_platform}.
INSTRUCTIONS:
1. Start with an overall performance assessment.
2. Highlight the 'Top Campaign' and why it succeeded.
3. Provide one actionable recommendation for next week to lower the CPA.
Keep the tone professional, objective, and action-oriented.
"""
prompt = PromptTemplate(template=template, input_variables=["metrics", "best_platform"])
# Format metrics for the prompt
metrics_str = "\n".join([f"- {k}: {v}" for k,v in stats.items()])
response = llm.predict(prompt.format(metrics=metrics_str, best_platform=stats['Best Platform']))
return response
except Exception as e:
return f"AI Service Unavailable: {str(e)}"
# --- PDF GENERATION OF THE FINAL ANALYSIS---
class PDFReport(FPDF):
def header(self):
# 1. Top Dashboard Bar – GroundTruth style teal
# Approx brand teal: rgb(0, 191, 165)
self.set_fill_color(0, 191, 165)
self.rect(0, 0, 210, 25, 'F')
# 2. Logo / Title Area
self.set_y(8)
self.set_font('Arial', 'B', 16)
self.set_text_color(255, 255, 255) # White text
self.cell(10) # Left padding
self.cell(0, 10, 'ADMINISTRATIVE DASHBOARD | WEEKLY PERFORMANCE', 0, 1, 'L')
# 3. Sub-header (Date/Context)
self.set_font('Arial', '', 9)
# Slightly lighter text, still on teal
self.set_text_color(230, 255, 248)
self.cell(10)
self.cell(0, 0, 'Generated by AdPulse AI Engine', 0, 1, 'L')
self.ln(15)
def footer(self):
# Footer Bar – light mint background like website bg
self.set_y(-15)
# Soft mint / aqua strip
self.set_fill_color(220, 247, 240) # light mint
self.rect(0, 282, 210, 15, 'F')
self.set_font('Arial', 'I', 8)
# Dark teal-ish grey text
self.set_text_color(60, 90, 80)
self.cell(0, 10, f'Confidential Internal Report | Page {self.page_no()}', 0, 0, 'C')
def create_pdf(stats, analysis_text):
def safe_text(text):
return text.encode('latin-1', 'replace').decode('latin-1')
pdf = PDFReport()
pdf.set_auto_page_break(auto=True, margin=15)
pdf.add_page()
# --- SECTION 1: KPI CARDS ---
pdf.set_font("Arial", 'B', 12)
# Dark teal for headings
pdf.set_text_color(6, 64, 60)
pdf.cell(0, 10, "1. HIGH-LEVEL METRICS", ln=True)
pdf.ln(2)
col_width = 60
row_height = 25
spacing = 5
metrics_row_1 = [
("Total Spend", stats.get("Total Spend", "$0")),
("Conversions", stats.get("Total Conversions", "0")),
("Avg CPA", stats.get("Avg CPA", "$0"))
]
metrics_row_2 = [
("Global CTR", stats.get("Global CTR", "0%")),
("Avg CPC", stats.get("Avg CPC", "$0")),
("Best Platform", stats.get("Best Platform", "N/A"))
]
def draw_card(x, y, title, value):
# Card border + fill with GroundTruth-like mint
pdf.set_draw_color(210, 240, 232) # mint border
pdf.set_fill_color(242, 252, 249) # very light mint fill
pdf.rect(x, y, col_width, row_height, 'DF')
# Card label (small, muted)
pdf.set_xy(x + 2, y + 2)
pdf.set_font("Arial", '', 8)
pdf.set_text_color(90, 120, 110) # grey-green label
pdf.cell(col_width, 5, safe_text(title.upper()), 0, 0, 'L')
# Card value (bold, dark teal)
pdf.set_xy(x, y + 10)
pdf.set_font("Arial", 'B', 14)
pdf.set_text_color(6, 64, 60)
if len(str(value)) > 15:
pdf.set_font("Arial", 'B', 10)
pdf.cell(col_width, 10, safe_text(str(value)), 0, 0, 'C')
start_x = 10
start_y = pdf.get_y()
for i, (key, val) in enumerate(metrics_row_1):
draw_card(start_x + (i * (col_width + spacing)), start_y, key, val)
start_y += row_height + spacing
for i, (key, val) in enumerate(metrics_row_2):
draw_card(start_x + (i * (col_width + spacing)), start_y, key, val)
pdf.set_y(start_y + row_height + 10)
# --- SECTION 2: TOP CAMPAIGN ---
# Light mint banner like hero section background
pdf.set_fill_color(224, 248, 241)
pdf.rect(10, pdf.get_y(), 190, 15, 'F')
pdf.set_xy(15, pdf.get_y() + 4)
pdf.set_font("Arial", 'B', 10)
pdf.set_text_color(6, 64, 60) # dark teal
label = "TOP CAMPAIGN (WINNER):"
label_width = pdf.get_string_width(label) + 2
pdf.cell(label_width, 8, label, 0, 0)
pdf.set_font("Arial", '', 10)
pdf.cell(0, 8, safe_text(stats.get("Top Campaign", "N/A")), 0, 1)
pdf.ln(10)
# --- SECTION 3: AI EXECUTIVE SUMMARY ---
pdf.set_font("Arial", 'B', 12)
pdf.set_text_color(6, 64, 60)
pdf.cell(0, 10, "2. AI STRATEGIC NARRATIVE", ln=True)
start_text_y = pdf.get_y() + 5
pdf.set_font("Arial", size=10)
# Neutral dark text (like site body copy)
pdf.set_text_color(55, 71, 79)
pdf.set_xy(15, start_text_y)
pdf.multi_cell(180, 6, safe_text(analysis_text))
end_text_y = pdf.get_y()
# Left accent line in brand teal
pdf.set_line_width(1)
pdf.set_draw_color(0, 191, 165)
pdf.line(12, start_text_y, 12, end_text_y)
return pdf.output(dest='S').encode('latin-1', 'ignore')
# --- MAIN APPLICATION UI ---
def main():
st.title("📊 AdPulse: Automated Insight Engine")
st.markdown("""
**Transform raw campaign data into executive decisions in seconds.**
Upload your weekly CSV to generate a PDF report with AI-driven analysis.
""")
with st.expander("ℹ️ NOTE FOR EVALUATORS (Click to Expand)", expanded=True):
st.markdown("""
**I have included multiple test scenarios in the `custom_csv/` folder to demonstrate AI adaptability:**
1. `data_tiktok_win.csv`: Shows AI identifying a high-volume, low-cost winner.
2. `data_b2b_win.csv`: Shows AI understanding high-CPA logic for B2B (LinkedIn).
And many more so you can test the solution's reliability across contexts.
*Please upload these files to see the narrative change dynamically.*
""")
# 1. File Upload
uploaded_file = st.file_uploader("Upload CSV Data", type=['csv'])
if not uploaded_file:
st.info("👆 Please upload a CSV file to begin analysis.")
# Optional: Add a link to sample data if you host it
# st.markdown("[Download Sample CSV](https://github.com/...)")
return
# 2. Process Data
if uploaded_file:
df = pd.read_csv(uploaded_file)
st.write("### 🔍 Data Preview")
st.dataframe(df.head(5))
if st.button("🚀 Generate Executive Report"):
with st.spinner("Analyzing performance metrics with Gemini..."):
# Step A: Numeric Analysis
stats, error = analyze_campaign_data(df)
if error:
st.error(error)
return
# Step B: AI Narrative
summary = generate_executive_summary(stats, df.head())
# Step C: Display Results
st.success("Analysis Complete!")
# Metrics Display
col1, col2, col3 = st.columns(3)
col1.metric("Total Spend", stats['Total Spend'])
col2.metric("Conversions", stats['Total Conversions'])
col3.metric("CPA", stats['Avg CPA'])
with st.expander("Read Executive Summary"):
st.write(summary)
pdf_bytes = create_pdf(stats, summary)
st.download_button(
label="📥 Download Official PDF Report",
data=pdf_bytes,
file_name="Executive_Ad_Report.pdf",
mime="application/pdf"
)
if __name__ == "__main__":
main()