Skip to content

Commit 928efee

Browse files
Enhancement: Added Support for Attachments and HTML Emails to Automate Email Script (#310)
* Added a feature and READme.md guide * feat: Add CSV delimiter selection and cell styling to CSV to Excel script * Explaination od Modifications
1 parent c1b5394 commit 928efee

File tree

4 files changed

+267
-38
lines changed

4 files changed

+267
-38
lines changed

Automate Emails Daily/README.md

+151
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
# Guidebook: Email Automation Script with Advanced Features
2+
3+
# Overview
4+
5+
This Python script automates the process of sending emails to multiple recipients with enhanced features such as support for attachments and HTML-formatted emails. The script uses Python's built-in libraries (smtplib, ssl, and email) to send emails securely through Gmail's SMTP server. It also allows the user to customize the email's content, subject, recipients, and more via environment variables or in-script configuration.
6+
7+
# Features Added
8+
9+
1. Support for Attachments: You can now attach files to the email, making it more versatile for different use cases like sending reports, documents, or images.
10+
2. HTML Email Support: You can send emails in HTML format, giving you more flexibility with rich text formatting, embedded links, and other styling options.
11+
12+
# Prerequisites
13+
14+
1. Python 3.x
15+
2. A Gmail account (with less secure apps access enabled or an app-specific password if using 2FA)
16+
3. Required libraries: smtplib, ssl, os, email (all built-in Python libraries)
17+
18+
# Environment Setup
19+
To ensure security, it's recommended to store sensitive information like email credentials in environment variables. For this guide, we will store the Gmail password as an environment variable:
20+
21+
export EMAIL_PASSWORD='your_gmail_password'
22+
23+
# Code Breakdown
24+
1. Import Required Modules
25+
26+
import smtplib
27+
import ssl
28+
import os
29+
from email.message import EmailMessage
30+
from email.utils import formataddr
31+
-------------------------------------------------------------------------------------
32+
smtplib: Used to create the connection to the Gmail SMTP server.
33+
ssl: Provides a layer of security for the email communication.
34+
os: Used to access environment variables (like the email password).
35+
email.message: Allows crafting email messages, including text, HTML, and attachments.
36+
37+
**send_email Function**
38+
39+
This is the main function that sends the email.
40+
41+
Function Parameters:
42+
sender_email (str): The email address sending the email.
43+
sender_name (str): The sender's name that will appear in the email.
44+
password (str): The sender's email password, pulled from environment variables.
45+
receiver_emails (list): A list of email addresses to send the email to.
46+
email_body (str): The body of the email, which can be in plain text or HTML.
47+
email_subject (str): The subject line of the email. Default is "No subject."
48+
49+
**Example Function**
50+
51+
send_email(
52+
sender_email="[email protected]",
53+
sender_name="Your Name",
54+
password=os.environ.get("EMAIL_PASSWORD"),
55+
receiver_emails=["[email protected]", "[email protected]"],
56+
email_body="Hello, this is a test email!",
57+
email_subject="Test Email"
58+
)
59+
60+
-------------------------------------------------------------------------------------
61+
62+
**Setting Up Email Headers**
63+
The email headers include the subject, sender, recipient, and format:
64+
65+
msg["Subject"] = email_subject
66+
msg["From"] = formataddr((f"{sender_name}", f"{sender_email}"))
67+
msg["BCC"] = sender_email
68+
msg.set_content(email_body) # This can also be an HTML body
69+
70+
-------------------------------------------------------------------------------------
71+
72+
**SMTP Server Connection**
73+
Here we establish a connection to Gmail's SMTP server and use TLS (Transport Layer Security) to ensure a secure connection.
74+
75+
smtp_port = 587
76+
smtp_server = "smtp.gmail.com"
77+
ssl_context = ssl.create_default_context()
78+
79+
-------------------------------------------------------------------------------------
80+
81+
**Login and Sending the Email**
82+
After logging in, the script loops through each recipient in the receiver_emails list and sends the email.
83+
84+
my_server = smtplib.SMTP(smtp_server, smtp_port)
85+
my_server.starttls(context=ssl_context)
86+
my_server.login(sender_email, password)
87+
88+
-------------------------------------------------------------------------------------
89+
90+
**Adding Attachments**
91+
If you want to send attachments, use the following modification:
92+
93+
if attachments:
94+
for file in attachments:
95+
with open(file, "rb") as f:
96+
file_data = f.read()
97+
file_name = os.path.basename(file)
98+
msg.add_attachment(file_data, maintype='application', subtype='octet-stream', filename=file_name)
99+
100+
-------------------------------------------------------------------------------------
101+
102+
**Sending HTML Emails**
103+
To send HTML emails, modify the email body to contain HTML:
104+
105+
msg.add_alternative("""\
106+
<html>
107+
<body>
108+
<p>Hello, <br>
109+
This is an <b>HTML email</b>!</p>
110+
</body>
111+
</html>
112+
""", subtype='html')
113+
114+
-------------------------------------------------------------------------------------
115+
116+
**Error Handling**
117+
The script includes basic error handling to notify you if the connection or email-sending process fails:
118+
119+
except Exception as e:
120+
print(f"ERROR: {e}")
121+
122+
--------------------------------------------------------------------------------------
123+
124+
**Full Example with Attachment and HTML Support**
125+
126+
send_email(
127+
sender_email="[email protected]",
128+
sender_name="Your Name",
129+
password=os.environ.get("EMAIL_PASSWORD"),
130+
receiver_emails=["[email protected]", "[email protected]"],
131+
email_body="<h1>This is a Test Email with HTML</h1>",
132+
email_subject="Test Email with HTML and Attachment",
133+
attachments=["path/to/attachment1", "path/to/attachment2"]
134+
)
135+
136+
137+
--------------------------------------------------------------------------------------
138+
139+
# How to run the script
140+
141+
Ensure the required environment variable (EMAIL_PASSWORD) is set.
142+
Customize the sender email, receiver emails, email body, and subject in the script.
143+
Run the script from the command line:
144+
145+
python email_automation.py or main.py
146+
147+
You can also schedule the script to run daily using cron jobs or Task Scheduler (Windows).
148+
149+
150+
# Thank You for reading this tutorial. I hope you found it helpful. If you have any questions or need further
151+

Automate Emails Daily/main.py

+40-10
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,46 @@
33
import os
44
from email.message import EmailMessage
55
from email.utils import formataddr
6+
from mimetypes import guess_type
67

78
def send_email(sender_email: str,
89
sender_name: str,
910
password:str,
10-
receiver_emails: str ,
11+
receiver_emails: list,
1112
email_body: str,
12-
email_subject: str="No subject",)-> None:
13+
email_subject: str = "No subject",
14+
is_html: bool = False,
15+
attachments: list = None) -> None:
1316

1417
msg = EmailMessage()
1518
msg["Subject"] = email_subject
1619
msg["From"] = formataddr((f"{sender_name}", f"{sender_email}"))
17-
msg["BCC"] = sender_email
18-
msg.set_content(email_body)
20+
msg["BCC"] = sender_email # Can add CC or BCC here if needed
1921

22+
# Support both plain text and HTML emails
23+
if is_html:
24+
msg.add_alternative(email_body, subtype='html')
25+
else:
26+
msg.set_content(email_body)
27+
28+
# Add attachments if provided
29+
if attachments:
30+
for file_path in attachments:
31+
try:
32+
with open(file_path, 'rb') as file:
33+
file_data = file.read()
34+
file_name = os.path.basename(file_path)
35+
mime_type, _ = guess_type(file_path)
36+
if mime_type:
37+
mime_main, mime_subtype = mime_type.split('/')
38+
else:
39+
mime_main, mime_subtype = 'application', 'octet-stream'
40+
41+
msg.add_attachment(file_data, maintype=mime_main, subtype=mime_subtype, filename=file_name)
42+
print(f"Attached file: {file_name}")
43+
except Exception as e:
44+
print(f"Failed to attach {file_path}: {e}")
45+
2046
smtp_port = 587
2147
smtp_server = "smtp.gmail.com"
2248

@@ -48,14 +74,18 @@ def send_email(sender_email: str,
4874
finally:
4975
my_server.quit()
5076

51-
# change these variables to suite your requirements
77+
# Example usage
5278
sender_email = "[email protected]"
5379
sender_name = "your name"
5480
password = os.environ.get("EMAIL_PASSWORD")
5581

56-
email_subject = "good morning"
57-
email_body = "good morning, hope you have a wonderful day"
58-
59-
82+
email_subject = "Good morning"
83+
email_body = """
84+
<h1>Good Morning!</h1>
85+
<p>Hope you have a <strong>wonderful day</strong>.</p>
86+
"""
87+
receiver_emails = ["[email protected]", "[email protected]"]
88+
attachments = ["path/to/attachment1.pdf", "path/to/attachment2.jpg"]
6089

61-
send_email(sender_email, sender_name, password, receiver_emails, email_body,email_subject)
90+
# Sending the email as HTML with attachments
91+
send_email(sender_email, sender_name, password, receiver_emails, email_body, email_subject, is_html=True, attachments=attachments)

CSV to Excel/README.md

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Explanation of Modifications
2+
3+
**Support for Multiple CSV Files:**
4+
5+
Modification: We now accept multiple CSV files, split by commas, in the csv_files list.
6+
7+
Sheet Name: For each CSV file, a new sheet is created, named after the CSV file (without the extension).
8+
9+
Loop: We loop over each CSV file in csv_files and process it individually.
10+
11+
**Automatic CSV Header Detection and Formatting:**
12+
13+
Modification: The first row of each CSV file is detected as the header.
14+
15+
Formatting: The header row is formatted with bold text (Font(bold=True)) and a yellow background (PatternFill).
16+
17+
Flag: A header_detected flag ensures that formatting is only applied to the first row
18+
19+
**Handling Empty or Invalid Files:**
20+
21+
Error handling remains in place for file not found and general exceptions.
22+
23+
#Thank You

CSV to Excel/csv_excel.py

+53-28
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,62 @@
11
import openpyxl
22
import os
3+
from openpyxl.styles import Font, PatternFill
34

45
# Get the CSV and Excel file names from the user
5-
csv_name = input("Name of the input CSV file with extension: ")
6-
sep = input("Separator of the CSV file: ")
7-
excel_name = input("Name of the output excel file with extension: ")
8-
sheet_name = input("Name of the output excel sheet: ")
6+
csv_files = input("Enter the CSV files separated by commas (e.g., file1.csv, file2.csv): ").split(',')
7+
sep = input("Separator of the CSV files (default is ','): ") or ',' # Default to comma separator if not provided
8+
excel_name = input("Name of the output Excel file with extension: ")
99

10-
# Load the CSV file
10+
# Load or create Excel workbook
1111
if os.path.exists(excel_name):
1212
workbook = openpyxl.load_workbook(excel_name)
13-
sheet = workbook[sheet_name] if sheet_name in workbook.sheetnames else workbook.create_sheet(sheet_name)
1413
else:
1514
workbook = openpyxl.Workbook()
16-
sheet = workbook.active
17-
sheet.title = sheet_name
18-
19-
# Write the CSV data to the Excel sheet
20-
try:
21-
with open(csv_name, "r", encoding="utf-8") as file:
22-
excel_row = 1
23-
for line in file:
24-
data = line.strip().split(sep)
25-
excel_column = 1
26-
for value in data:
27-
sheet.cell(row=excel_row, column=excel_column, value=value)
28-
excel_column += 1
29-
excel_row += 1
30-
31-
# Save the Excel file
32-
workbook.save(excel_name)
33-
34-
except FileNotFoundError:
35-
print("Error: The CSV file was not found.")
36-
except Exception as e:
37-
print(f"An error occurred: {e}")
15+
16+
# Loop over multiple CSV files to write them into different sheets
17+
for csv_name in csv_files:
18+
csv_name = csv_name.strip() # Trim any whitespace
19+
sheet_name = os.path.splitext(os.path.basename(csv_name))[0] # Sheet name based on the CSV filename
20+
21+
# Create a new sheet for each CSV file
22+
if sheet_name in workbook.sheetnames:
23+
sheet = workbook[sheet_name]
24+
else:
25+
sheet = workbook.create_sheet(sheet_name)
26+
27+
# Write CSV data to the Excel sheet
28+
try:
29+
with open(csv_name, "r", encoding="utf-8") as file:
30+
excel_row = 1
31+
header_detected = False # Flag to check if header formatting should be applied
32+
33+
for line in file:
34+
data = line.strip().split(sep)
35+
excel_column = 1
36+
37+
# Apply header formatting for the first row (headers)
38+
if not header_detected:
39+
for value in data:
40+
cell = sheet.cell(row=excel_row, column=excel_column, value=value)
41+
# Apply bold font and background color for the header row
42+
cell.font = Font(bold=True)
43+
cell.fill = PatternFill(start_color="FFFF00", end_color="FFFF00", fill_type="solid")
44+
excel_column += 1
45+
header_detected = True # Mark the first row as header
46+
else:
47+
for value in data:
48+
sheet.cell(row=excel_row, column=excel_column, value=value)
49+
excel_column += 1
50+
51+
excel_row += 1
52+
53+
except FileNotFoundError:
54+
print(f"Error: The CSV file '{csv_name}' was not found.")
55+
except Exception as e:
56+
print(f"An error occurred while processing {csv_name}: {e}")
57+
58+
# Save the Excel file with all sheets
59+
workbook.save(excel_name)
60+
61+
print(f"All CSV files have been processed and saved to {excel_name}.")
62+

0 commit comments

Comments
 (0)