Skip to content

Instantly share code, notes, and snippets.

@si3mshady
Created September 7, 2024 19:31
Show Gist options
  • Save si3mshady/46d4170d08dda4004b841ab5830309f9 to your computer and use it in GitHub Desktop.
Save si3mshady/46d4170d08dda4004b841ab5830309f9 to your computer and use it in GitHub Desktop.
The Email Categorization App organizes Gmail emails with OpenAI and visualizes data using interactive charts for streamlined management.

Here's a README file for your GitHub project:


Email Categorization App

The Email Categorization App automatically fetches and organizes your Gmail emails using OpenAI's GPT-3.5 model. It categorizes emails into concise groups (Security, Work, Personal, Marketing, Spam, and Other) and visualizes the data with interactive charts, making email management effortless.

Features

  • Fetches emails from your Gmail account.
  • Categorizes emails into predefined groups: Security, Work, Personal, Marketing, Spam, and Other.
  • Displays categorized data with interactive Bar, Doughnut, Line, and Radar charts using Chart.js.
  • Responsive UI built with Flask and Bootstrap.
  • Easily explore and filter emails by category.

Installation

1. Clone the repository

git clone https://github.com/yourusername/email-categorization-app.git
cd email-categorization-app

2. Install the dependencies

pip install -r requirements.txt

3. Set up Google API and OpenAI

  • Google API:

    • Create Google API credentials (OAuth 2.0) for Gmail API access.
    • Download the cred.json file and place it in the project folder.
  • OpenAI API:

    • Add your OpenAI API key in the app.py file where indicated.

4. Run the app

python app.py

5. Access the app

  • Open your browser and go to http://localhost:5000.

File Structure

  • app.py: The main Flask application that integrates with OpenAI and Gmail APIs.
  • cred.json: Your Google API credentials for Gmail access.
  • token.json: Stores the user's Gmail tokens (generated automatically after the first authentication).
  • templates/index.html: The front-end HTML with responsive charts and email list.
  • static/: Folder for static files such as custom CSS (optional).

Requirements

  • Python 3.x
  • Flask
  • OpenAI Python Library
  • Google API Client Library
  • Chart.js (loaded via CDN in the index.html)

Example requirements.txt:

Flask==2.0.3
openai==0.27.0
google-auth==2.6.0
google-auth-oauthlib==0.5.0
google-api-python-client==2.33.0

Usage

  • Once running, click "Fetch Emails" on the dashboard to retrieve and categorize your Gmail emails.
  • The charts update automatically to show the distribution of your email categories.
  • Click on any category in the charts to filter and display emails of that type.

License

This project is licensed under the MIT License.


Feel free to customize the sections based on your needs!

import os
import base64
import openai
from flask import Flask, render_template, jsonify
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
# Configure OpenAI API Key
openai.api_key = "sk-"
# If modifying these scopes, delete the file token.json.
SCOPES = ["https://www.googleapis.com/auth/gmail.readonly"]
app = Flask(__name__)
# Authenticate and connect to Gmail API
def authenticate_gmail():
creds = None
if os.path.exists("token.json"):
creds = Credentials.from_authorized_user_file("token.json", SCOPES)
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
"cred.json", SCOPES
)
creds = flow.run_local_server(port=8080)
with open("token.json", "w") as token:
token.write(creds.to_json())
try:
service = build("gmail", "v1", credentials=creds)
return service
except HttpError as error:
print(f"An error occurred: {error}")
return None
# Fetch and categorize emails
def fetch_and_categorize_emails():
service = authenticate_gmail()
if not service:
return []
# Fetch the latest 10 messages
results = service.users().messages().list(userId="me", maxResults=10).execute()
messages = results.get("messages", [])
categorized_emails = []
for message in messages:
msg = service.users().messages().get(userId="me", id=message["id"]).execute()
headers = msg["payload"]["headers"]
subject = next(header["value"] for header in headers if header["name"] == "Subject")
snippet = msg["snippet"]
# Use OpenAI to categorize the email
category = categorize_email(subject, snippet)
categorized_emails.append({
"subject": subject,
"snippet": snippet,
"category": category
})
return categorized_emails
# Function to categorize email content using OpenAI's new ChatCompletion API
def categorize_email(subject, snippet):
prompt = f"""
You are an AI assistant that categorizes emails into the following categories:
- Security: Emails related to security alerts, passwords, or account protection.
- Work: Emails related to work, business, or professional communications.
- Personal: Emails related to personal matters, friends, or family.
- Marketing: Promotional or marketing emails from companies.
- Spam: Unsolicited, irrelevant, or inappropriate emails.
- Other: Any email that doesn't fit into the categories above.
Here is the email:
Subject: {subject}
Content: {snippet}
Please categorize this email.
"""
try:
# Use OpenAI's new ChatCompletion API
response = openai.chat.completions.create(
model="gpt-3.5-turbo", # Use GPT-3.5 or GPT-4 model
messages=[
{"role": "system", "content": "You are a helpful assistant that categorizes emails."},
{"role": "user", "content": prompt}
]
)
# Extract the category from the response
category = response.choices[0].message.content.strip()
return category
except Exception as e:
print(f"Error with OpenAI API: {e}")
return "Unknown"
# Route to fetch categorized emails
@app.route('/emails')
def emails():
categorized_emails = fetch_and_categorize_emails()
# Calculate category counts for visualization
category_counts = {}
for email in categorized_emails:
category = email["category"]
if category in category_counts:
category_counts[category] += 1
else:
category_counts[category] = 1
return jsonify({
"emails": categorized_emails,
"category_counts": category_counts
})
# Route to render the email dashboard
@app.route('/')
def index():
return render_template('index.html')
if __name__ == '__main__':
app.run(debug=True)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Email Categorization Dashboard</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
</head>
<body>
<div class="container mt-5">
<h1 class="text-center">Email Categorization Dashboard</h1>
<button id="fetchEmails" class="btn btn-primary mb-4">Fetch Emails</button>
<!-- Charts Row -->
<div class="row">
<div class="col-md-6">
<canvas id="categoryBarChart"></canvas>
</div>
<div class="col-md-6">
<canvas id="categoryDoughnutChart"></canvas>
</div>
</div>
<div class="row mt-4">
<div class="col-md-6">
<canvas id="categoryLineChart"></canvas>
</div>
<div class="col-md-6">
<canvas id="categoryRadarChart"></canvas>
</div>
</div>
<!-- Email List Section -->
<div id="emailList" class="mt-5">
<h3>Emails for Selected Category</h3>
<div id="selectedEmails"></div>
</div>
</div>
<script>
document.getElementById('fetchEmails').addEventListener('click', function() {
fetch('/emails')
.then(response => response.json())
.then(data => {
const categorizedEmails = data.emails;
const categoryCounts = data.category_counts;
// Prepare category data for charts
const categories = Object.keys(categoryCounts);
const counts = Object.values(categoryCounts);
// Update Bar Chart
const barChartCtx = document.getElementById('categoryBarChart').getContext('2d');
new Chart(barChartCtx, {
type: 'bar',
data: {
labels: categories,
datasets: [{
label: '# of Emails',
data: counts,
backgroundColor: 'rgba(54, 162, 235, 0.2)',
borderColor: 'rgba(54, 162, 235, 1)',
borderWidth: 1
}]
},
options: {
onClick: (evt, item) => displayEmailsForCategory(item, categorizedEmails, categories)
}
});
// Update Doughnut Chart
const doughnutChartCtx = document.getElementById('categoryDoughnutChart').getContext('2d');
new Chart(doughnutChartCtx, {
type: 'doughnut',
data: {
labels: categories,
datasets: [{
label: 'Email Categories',
data: counts,
backgroundColor: ['#FF6384', '#36A2EB', '#FFCE56', '#4BC0C0', '#9966FF', '#FF9F40']
}]
},
options: {
onClick: (evt, item) => displayEmailsForCategory(item, categorizedEmails, categories)
}
});
// Update Line Chart
const lineChartCtx = document.getElementById('categoryLineChart').getContext('2d');
new Chart(lineChartCtx, {
type: 'line',
data: {
labels: categories,
datasets: [{
label: 'Email Trends',
data: counts,
fill: false,
borderColor: 'rgb(75, 192, 192)',
tension: 0.1
}]
}
});
// Update Radar Chart
const radarChartCtx = document.getElementById('categoryRadarChart').getContext('2d');
new Chart(radarChartCtx, {
type: 'radar',
data: {
labels: categories,
datasets: [{
label: 'Email Categories',
data: counts,
backgroundColor: 'rgba(153, 102, 255, 0.2)',
borderColor: 'rgba(153, 102, 255, 1)',
borderWidth: 1
}]
}
});
})
.catch(error => {
console.error('Error fetching emails:', error);
});
});
// Function to display emails for a selected category
function displayEmailsForCategory(item, emails, categories) {
const categoryIndex = item[0].index;
const selectedCategory = categories[categoryIndex];
const selectedEmails = emails.filter(email => email.category === selectedCategory);
const emailListDiv = document.getElementById('selectedEmails');
emailListDiv.innerHTML = '';
selectedEmails.forEach(email => {
const emailCard = `
<div class="card mb-3">
<div class="card-body">
<h5 class="card-title">Subject: ${email.subject}</h5>
<p class="card-text">${email.snippet}</p>
</div>
</div>
`;
emailListDiv.innerHTML += emailCard;
});
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment