| import gradio as gr |
| import joblib |
| import pandas as pd |
| import numpy as np |
| import re |
| from transformers import pipeline, AutoTokenizer, AutoModelForSeq2SeqLM |
| from datetime import datetime |
|
|
| |
| try: |
| models = joblib.load('email_quality_models.pkl') |
| scaler = joblib.load('feature_scaler.pkl') |
| day_encoder = joblib.load('day_encoder.pkl') |
| feature_names = joblib.load('feature_names.pkl') |
| model_results = joblib.load('model_results.pkl') |
| print("β
Models loaded successfully!") |
| except Exception as e: |
| print(f"β Error loading models: {e}") |
|
|
| |
| sentiment = pipeline("sentiment-analysis") |
|
|
| |
| classification_labels = [ |
| "engaging", "promotional", "informative", "urgent", "personal", "spammy", |
| "announcement", "educational", "sales", "boring", "friendly", "exclusive" |
| ] |
| classifier = pipeline("zero-shot-classification", model="facebook/bart-large-mnli") |
|
|
| |
| try: |
| chatbot_tokenizer = AutoTokenizer.from_pretrained("google/flan-t5-large") |
| chatbot_model = AutoModelForSeq2SeqLM.from_pretrained("google/flan-t5-large") |
| print("β
Chatbot model loaded successfully!") |
| except Exception as e: |
| print(f"β Error loading chatbot model: {e}") |
| |
| try: |
| chatbot_tokenizer = AutoTokenizer.from_pretrained("google/flan-t5-base") |
| chatbot_model = AutoModelForSeq2SeqLM.from_pretrained("google/flan-t5-base") |
| print("β
Fallback chatbot model loaded successfully!") |
| except Exception as e2: |
| print(f"β Error loading fallback model: {e2}") |
|
|
| def extract_text_features(text): |
| if pd.isna(text) or text == '': |
| return { |
| 'length': 0, |
| 'word_count': 0, |
| 'exclamation_count': 0, |
| 'question_count': 0, |
| 'emoji_count': 0, |
| 'number_count': 0, |
| 'caps_ratio': 0 |
| } |
| return { |
| 'length': len(text), |
| 'word_count': len(text.split()), |
| 'exclamation_count': text.count('!'), |
| 'question_count': text.count('?'), |
| 'emoji_count': len(re.findall(r'[^\w\s,.]', text)), |
| 'number_count': len(re.findall(r'\d+', text)), |
| 'caps_ratio': sum(1 for c in text if c.isupper()) / len(text) if len(text) > 0 else 0 |
| } |
|
|
| def section_score(features): |
| |
| score = 50 |
| score += min(20, features['emoji_count'] * 10) |
| score += min(10, features['exclamation_count'] * 5) |
| score += min(10, features['question_count'] * 5) |
| if 20 <= features['length'] <= 60: |
| score += 10 |
| score = max(0, min(100, score)) |
| return score |
|
|
| def section_suggestion(section, features): |
| |
| if section == "subject": |
| if features['length'] > 50: |
| return "Try shortening your subject line for better impact." |
| if features['emoji_count'] == 0: |
| return "Add an emoji to make your subject line stand out." |
| if features['exclamation_count'] == 0: |
| return "Consider adding an exclamation mark for urgency." |
| return "Your subject line looks good!" |
| elif section == "preview": |
| if features['length'] < 20: |
| return "Add more detail to your preview text." |
| if features['emoji_count'] == 0: |
| return "Try adding an emoji to your preview text." |
| return "Your preview text is engaging!" |
| elif section == "body": |
| if features['word_count'] < 50: |
| return "Consider a longer, more detailed email body." |
| if features['exclamation_count'] == 0: |
| return "Try using an exclamation mark to highlight key points." |
| return "Your email body is well-structured!" |
| return "" |
|
|
| def predict_email_performance(subject, preview_text, body_text, day_of_week, send_time, target_metric): |
| try: |
| |
| subject_features = extract_text_features(subject) |
| preview_features = extract_text_features(preview_text) |
| body_features = extract_text_features(body_text) |
|
|
| |
| try: |
| send_hour = datetime.strptime(send_time, '%I:%M %p').hour |
| except: |
| send_hour = 9 |
|
|
| |
| try: |
| day_encoded = day_encoder.transform([day_of_week])[0] |
| except: |
| day_encoded = 0 |
|
|
| |
| features = [ |
| 500000, |
| send_hour, |
| day_encoded, |
| 0 |
| ] |
|
|
| |
| for feats in [subject_features, preview_features]: |
| for suffix in ['length', 'word_count', 'exclamation_count', 'question_count', 'emoji_count', 'number_count', 'caps_ratio']: |
| features.append(feats[suffix]) |
|
|
| |
| if len(features) > len(feature_names): |
| features = features[:len(feature_names)] |
| elif len(features) < len(feature_names): |
| features.extend([0] * (len(feature_names) - len(features))) |
|
|
| features_scaled = scaler.transform([features]) |
|
|
| |
| model = models[target_metric] |
| prediction = model.predict(features_scaled)[0] |
|
|
| |
| if target_metric == 'open_rate': |
| prediction = max(0, min(1, prediction)) * 100 |
| elif target_metric == 'click_rate': |
| prediction = max(0, min(0.5, prediction)) * 100 |
| else: |
| prediction = max(0, min(0.1, prediction)) * 100 |
|
|
| return prediction |
|
|
| except Exception as e: |
| print(f"Prediction error: {e}") |
| return 2.5 |
|
|
| def analyze_email_complete(subject, preview_text, body_text, day_of_week, send_time, target_metric): |
| |
| subject_features = extract_text_features(subject) |
| preview_features = extract_text_features(preview_text) |
| body_features = extract_text_features(body_text) |
|
|
| subject_score = section_score(subject_features) |
| preview_score = section_score(preview_features) |
| body_score = section_score(body_features) |
|
|
| |
| subject_sugg = section_suggestion("subject", subject_features) |
| preview_sugg = section_suggestion("preview", preview_features) |
| body_sugg = section_suggestion("body", body_features) |
|
|
| |
| performance_score = int(round(0.4 * subject_score + 0.3 * preview_score + 0.3 * body_score)) |
|
|
| |
| predicted_value = predict_email_performance(subject, preview_text, body_text, day_of_week, send_time, target_metric) |
|
|
| |
| text_for_sentiment = f"{subject}\n{preview_text}\n{body_text}" |
| sentiment_result = sentiment(text_for_sentiment)[0] |
|
|
| |
| classification_result = classifier(text_for_sentiment, classification_labels) |
|
|
| |
| metric_label = { |
| "open_rate": "Open Rate", |
| "click_rate": "Click Rate", |
| "unsubscribe_rate": "Unsubscribe Rate" |
| }[target_metric] |
|
|
| output = f""" |
| ## π Performance Score: {performance_score}/100 |
| |
| ### π― Predicted {metric_label}: {predicted_value:.2f}% |
| |
| ### βοΈ Section Scores & Suggestions |
| - **Subject Line:** {subject_score}/100 |
| _Suggestion: {subject_sugg}_ |
| - **Preview Text:** {preview_score}/100 |
| _Suggestion: {preview_sugg}_ |
| - **Body Text:** {body_score}/100 |
| _Suggestion: {body_sugg}_ |
| |
| ### π Sentiment Analysis |
| - **Sentiment:** {sentiment_result['label']} (confidence: {sentiment_result['score']:.2f}) |
| |
| ### π·οΈ Content Classification |
| """ |
|
|
| for i, (label, score) in enumerate(zip(classification_result['labels'][:6], classification_result['scores'][:6])): |
| output += f"- **{label.title()}**: {score:.2f}\n" |
|
|
| output += f""" |
| ### π Email Details |
| - **Subject Length:** {subject_features['length']} characters |
| - **Preview Length:** {preview_features['length']} characters |
| - **Body Word Count:** {body_features['word_count']} words |
| - **Send Time:** {send_time} on {day_of_week} |
| |
| --- |
| #### π¬ Ask the Email Optimization Chatbot below for advice! |
| """ |
|
|
| |
| context = { |
| "subject": subject, |
| "preview_text": preview_text, |
| "body_text": body_text, |
| "day_of_week": day_of_week, |
| "send_time": send_time, |
| "target_metric": target_metric, |
| "scores": { |
| "performance_score": performance_score, |
| "subject_score": subject_score, |
| "preview_score": preview_score, |
| "body_score": body_score, |
| "predicted_value": predicted_value |
| }, |
| "suggestions": { |
| "subject": subject_sugg, |
| "preview": preview_sugg, |
| "body": body_sugg |
| }, |
| "sentiment": sentiment_result, |
| "classification": classification_result |
| } |
|
|
| return output, context |
|
|
| def chatbot_response(user_message, history, context): |
| |
| if not context or not isinstance(context, dict): |
| return "Please analyze an email first, then ask your question here." |
|
|
| try: |
| |
| prompt = f"""You are an expert email marketing assistant. Here is the analysis of an email campaign: |
| |
| Subject: {context.get('subject', 'N/A')} |
| Preview: {context.get('preview_text', 'N/A')} |
| Body: {context.get('body_text', 'N/A')} |
| Day: {context.get('day_of_week', 'N/A')} |
| Send Time: {context.get('send_time', 'N/A')} |
| Target Metric: {context.get('target_metric', 'N/A')} |
| |
| Performance Score: {context.get('scores', {}).get('performance_score', 'N/A')}/100 |
| Subject Score: {context.get('scores', {}).get('subject_score', 'N/A')}/100 |
| Preview Score: {context.get('scores', {}).get('preview_score', 'N/A')}/100 |
| Body Score: {context.get('scores', {}).get('body_score', 'N/A')}/100 |
| Predicted Value: {context.get('scores', {}).get('predicted_value', 'N/A')}% |
| |
| Current Suggestions: |
| - Subject: {context.get('suggestions', {}).get('subject', 'N/A')} |
| - Preview: {context.get('suggestions', {}).get('preview', 'N/A')} |
| - Body: {context.get('suggestions', {}).get('body', 'N/A')} |
| |
| Sentiment: {context.get('sentiment', {}).get('label', 'N/A')} |
| Top Classifications: {', '.join(context.get('classification', {}).get('labels', [])[:3])} |
| |
| User question: {user_message} |
| |
| Give a specific, actionable answer based on the above analysis. Be concise and practical.""" |
|
|
| |
| inputs = chatbot_tokenizer(prompt, return_tensors="pt", max_length=1024, truncation=True) |
| outputs = chatbot_model.generate(**inputs, max_new_tokens=256, do_sample=True, temperature=0.7) |
| answer = chatbot_tokenizer.decode(outputs[0], skip_special_tokens=True) |
|
|
| |
| if prompt in answer: |
| answer = answer.replace(prompt, "").strip() |
|
|
| return answer if answer else "I'm sorry, I couldn't generate a response. Please try rephrasing your question." |
|
|
| except Exception as e: |
| print(f"Chatbot error: {e}") |
| return "I'm having trouble generating a response right now. Please try again." |
|
|
| |
| day_options = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'] |
|
|
| |
| with gr.Blocks() as demo: |
| gr.Markdown( |
| """ |
| # π Email Performance Predictor β Forks Over Knives |
| |
| Predict your emailβs open, click, and unsubscribe rates. |
| Get actionable, section-specific suggestions, content classification, and optimization advice from the chatbot below! |
| """ |
| ) |
| with gr.Row(): |
| with gr.Column(): |
| subject = gr.Textbox(label="π§ Subject Line", placeholder="Enter your email subject line") |
| preview_text = gr.Textbox(label="π Preview Text", placeholder="Enter preview text (optional)") |
| body_text = gr.Textbox(label="π Email Body", placeholder="Paste your email body here") |
| day_of_week = gr.Dropdown(choices=day_options, label="π
Day of Week", value="Thursday") |
| send_time = gr.Textbox(label="β° Send Time", placeholder="9:00 AM", value="9:00 AM") |
| target_metric = gr.Radio(choices=['open_rate', 'click_rate', 'unsubscribe_rate'], |
| label="π― Target Metric", value='click_rate') |
| analyze_btn = gr.Button("Analyze Email") |
| with gr.Column(): |
| analysis_output = gr.Markdown() |
|
|
| |
| state = gr.State() |
|
|
| |
| chatbot = gr.ChatInterface( |
| fn=chatbot_response, |
| additional_inputs=[state], |
| title="Email Optimization Chatbot", |
| description="Ask for advice on how to improve your email based on the analysis above." |
| ) |
|
|
| |
| analyze_btn.click( |
| analyze_email_complete, |
| inputs=[subject, preview_text, body_text, day_of_week, send_time, target_metric], |
| outputs=[analysis_output, state] |
| ) |
|
|
| if __name__ == "__main__": |
| demo.launch() |
|
|