This code takes email pdf attachments, download it, merge to one pdf file and send further.
Now it takes all emails which are marked with specific category in that inbox, so it merge all pdf's from all emails to one file.
But I want that it take emails one by one, that after download pdf's from one email it will merge and send them, delete them from folder and just after that it will take second email.
How to make such loop for this code?
import datetime
import os
import win32com.client as win32
from PyPDF2 import PdfFileMerger
from pathlib import Path
path = ('C:\\Users\\Desktop\\Work')
today = datetime.date.today()
outlook = win32.Dispatch("Outlook.Application").GetNamespace("MAPI")
inbox = outlook.GetDefaultFolder(6)
subFolder = inbox.Folders("Test")
messages = subFolder.Items
def save_attachments(subject):
for message in messages:
if message.Categories == "Red Category":
for attachment in message.Attachments:
print(attachment.FileName)
attachment.SaveAsFile(os.path.join(path, str(attachment)))
if __name__ == "__main__":
save_attachments('PB report - next steps')
#Merge PDF's
merger = PdfFileMerger()
path_to_files = r'C:\Users\Desktop\Work/'
for root, dirs, file_names in os.walk(path_to_files):
for file_name in file_names:
merger.append(path_to_files + file_name)
merger.write(r"C:\Users\Desktop\Work\merged.pdf")
merger.close()
#Send PDF with outlook
# construct Outlook application instance
olApp = win32.Dispatch('Outlook.Application')
olNS = olApp.GetNameSpace('MAPI')
# construct the email item object
mailItem = olApp.CreateItem(0)
mailItem.Subject = 'Test'
mailItem.BodyFormat = 1
mailItem.Body = "Pdf merged"
mailItem.To = 'email'
path = (os.path.join('C:\\Users\\Desktop\\Work\\merged.pdf'))
mailItem.Attachments.Add(path)
mailItem.Display()
mailItem.Save()
mailItem.Send()
#Delete PDF's from folder
[f.unlink() for f in Path("C:\\Users\\Desktop\\Work").glob("*") if f.is_file()]
Iterating over all items in the folder is not really a good idea:
for message in messages:
if message.Categories == "Red Category":
Instead, you need to use the Find/FindNext or Restrict methods of the Items class from the Outlook object model. So, in that case you will get all items that correspond to your search criteria and iterate over them only. Read more about these methods in the following articles:
How To: Use Find and FindNext methods to retrieve Outlook mail items from a folder (C#, VB.NET)
How To: Use Restrict method to retrieve Outlook mail items from a folder
Second, there is no need to create a new Outlook Application instance:
# construct Outlook application instance
olApp = win32.Dispatch('Outlook.Application')
olNS = olApp.GetNameSpace('MAPI')
Re-use the existing application instance instead. Moreover, Outlook is a singleton, you can't have two instances running at the same time.
Third, there is no need to display and save the item created before sending:
mailItem.Attachments.Add(path)
mailItem.Send()
Related
I'm trying to use python to get some data that is in an attachment on an outlook email and then use that data in python. I've managed to write the code that will get into the outlook inbox and folder I want and then get the attachments of a specific message, however I'm not sure how to view the content of that attachment. A lot of the other questions and tutorials I've found seem to be more related to saving the attachment in a folder location rather than viewing the attachment in python itself.
For context the data I'm trying to get to is an exported report from adobe analytics, this report is a csv file that is attached to an email as a zip file. The CSV file shows some data for a specific time period and I'm planning on scheduling this report to run weekly so what I want to do is get python to look through all the emails with this report on then stack all this data into one dataframe so that I have all the history plus the latest week's data in one place then export this file out.
Please find the code below that I've written so far. If you need more details or I haven't explained anything very well please let me know. I am fairly new to python especially the win32com library so there might be obvious stuff I'm missing.
#STEP 1---------------------------------------------
#import all methods needed
from pathlib import Path
import win32com.client
import requests
import time
import datetime
import os
import zipfile
from zipfile import ZipFile
import pandas as pd
#STEP 2 --------------------------------------------
#connect to outlook
outlook = win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI")
#STEP 3 --------------------------------------------
#connect to inbox
inbox = outlook.GetDefaultFolder(6)
#STEP 4 --------------------------------------------
#connect to adobe data reports folder within inbox
adobe_data_reports_folder = inbox.Folders['Cust Insights'].Folders['Adobe data reports']
#STEP 5 --------------------------------------------
#get all messages from adobe reports folder
messages_from_adr_folder = adobe_data_reports_folder.Items
#STEP 6 ---------------------------------------------
#get attachement for a specific message (this is just for testing in real world I'll do this for all messages)
for message in messages_from_adr_folder:
if message.SentOn.strftime("%d-%m-%y") == '07-12-22':
attachment = message.Attachments
else:
pass
#STEP 7 ----------------------------------------------
#get the content of the attachment
##????????????????????????????
With the Outlook Object Model, the best you can do is save the attachment as a file (Attachment.SaveAsFile) - keep in mind that MailItem.Attachments property returns the Attachments collection, not a single Attachment object - loop through all attachments in the collection, figure out which one you want (if there is more than one), and save it as file.
To access file attachment data directly without saving as a file, you will need to use Extended MAPI (C++ or Delphi only) or Redemption (any language, I am its author).
Dmitry mentioned below that there isn't the option to view attachment content with an outlook object model.
So I've come up with a solution for this which basically involves using the save method to save the attachment into a folder location on the current working directory and then once that file is save just load that file back up into python as a dataframe. The only thing to note is that I've added an if statement that only saves files that are csvs, obviously this part can be removed if needed.
If you wanted to do this with multiple files and stack all of these into a single dataframe then I just created a blank dataframe at the start (with the correct column names of the file that will be loaded) and concatenated this blank dataframe with the "importeddata" then added this code into the "attachment" for loop so that each time it's appending the data that is saved and loaded from the attachment
#STEP 1---------------------------------------------
#import all methods needed
from pathlib import Path
import win32com.client
import requests
import time
import datetime
import os
import zipfile
from zipfile import ZipFile
import pandas as pd
#STEP 1b ---------------------------------------------
#create a directory where I can save the files
output_dir = Path.cwd() / "outlook_testing"
#STEP 2 --------------------------------------------
#connect to outlook
outlook = win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI")
#STEP 3 --------------------------------------------
#connect to inbox
inbox = outlook.GetDefaultFolder(6)
#STEP 4 --------------------------------------------
#connect to adobe data reports folder within inbox
adobe_data_reports_folder = inbox.Folders['Cust Insights'].Folders['Adobe data
reports']
#STEP 5 --------------------------------------------
#get all messages from adobe reports folder
messages_from_adr_folder = adobe_data_reports_folder.Items
#STEP 6 ---------------------------------------------
#get attachement for a specific message (this is just for testing in real world
#I'll do this for all messages)
for message in messages_from_adr_folder:
body = message.Body
if message.SentOn.strftime("%d-%m-%y") == '07-12-22':
attachments = message.Attachments
for attachment in attachments:
stringofattachment = str(attachment)
#STEP 6b - if the attachment is a csv file then save the attachment to a folder
if stringofattachment.find('.csv') != - 1:
attachment.SaveAsFile(output_dir / str(attachment))
print(output_dir / str(attachment))
#STEP 6C - reload the saved file as a dataframe
importeddata = pd.read_csv(output_dir / str(attachment))
else:
print('NOT CSV')
pass
else:
pass
So I am trying to read in an Excel file from an attachment in Microsoft outlook. The code below works, but only while the email I am trying to read the attachment from is at the top of my inbox. How can I adjust my code so it looks at all emails in my inbox folder looking for the attachment or finding the emailed based on the subject provided. Also I would eventually like to have this work with a shared mailbox, but that is a secondary issue right now.
from win32com.client import Dispatch
import email
import datetime as date
import os
outlook = Dispatch("Outlook.Application").GetNamespace("MAPI")
inbox = outlook.GetDefaultFolder("6")
all_inbox = inbox.Items
val_date = date.date.today()
sub_today = 'subject of email'
att_today = 'Filename'
for msg in all_inbox:
yourstring = msg.Subject.encode('ascii', 'ignore').decode('ascii')
if(yourstring.find('"Filename"') != -1):
break
for att in msg.Attachments:
if att.FileName == att_today:
attachments = msg.Attachments
break
attachment = attachments.Item(1)
fn = os.getcwd() + '\\' + att_today
attachment.SaveASFILE(fn)
df = pd.read_excel(fn)
First of all, to iterate over all items you need to skip items, not break the loop. Use the continue keyword instead of break in the loop.
The Outlook object model provides the Find/FindNext and Restrict methods for getting items that correspond to your conditions. The store provider does the job more efficient than just iterating over all items in the folder. Read more about these methods in the following articles:
How To: Use Find and FindNext methods to retrieve Outlook mail items from a folder (C#, VB.NET)
How To: Use Restrict method to retrieve Outlook mail items from a folder
You can use the following search criteria where the query performs a phrase match query for hello in the message subject (VBA syntax):
filter = "#SQL=" & Chr(34) & "https://schemas.microsoft.com/mapi/proptag/0x0037001E" _
& Chr(34) & " ci_phrasematch " & "'hello'"
To get a shared folder using the NameSpace.GetSharedDefaultFolder method which returns a Folder object that represents the specified default folder for the specified user. This method is used in a delegation scenario, where one user has delegated access to another user for one or more of their default folders (for example, their shared Calendar folder).
I have a folder that has nearly 12k (.msg) files each has a csv attachment.
I managed to get a code to extract the attachment from each .msg file. but due to attachment and subjects are similar the attachment keeps getting over written! I tried to rename with msg.subject but the subject of the msg is similar
import win32com.client
import os
inputFolder = r'directory with my msg' ## Change here the input folder
outputFolder = r'directiry for attachments' ## Change here the attachments output folder
for file in os.listdir(inputFolder):
if file.endswith(".msg"):
outlook = win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI")
filePath = inputFolder + '\\' + file
msg = outlook.OpenSharedItem(filePath)
att = msg.Attachments
for i in att:
i.SaveAsFile(os.path.join(outputFolder, str(msg.subject + ".csv")))
#Saves the file with the attachment name
You need to find an algorithm which allows identifying attachments uniquely - try to combine the attachment file name with email data such as ReceivedTime and etc.
Don't forget to exclude forbidden symbols from the result file name before trying to save the attachment.
I want to forward an Outlook email to a specific folder (not Inbox) using python. I'm using win32com.
When I Forward then Move, my email does appear in the specific folder, but when I look in my Outlook the Datetime and From are not showing.
How do I get the Datetime and From onto the forward/moved email?
import win32com.client
outlook = win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI")
root_folder = outlook.Folders.Item(1)
sub_folder1 = root.Folders['Project1']
next_level = sub_folder1.Folders['ProjectA']
msg = next_level.Items
msgs = msg.GetLast()
dest = sub_folder1.Folders['ProjectB']
NewMsg = msgs.Forward()
NewMsg.Move(dest)
This is because you're making a draft of the original received email.
To simply move the email remove the NewMsg = msgs.Forward() line and replace NewMsg.Move(dest) with msgs.Move(dest).
import win32com.client
outlook = win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI")
root_folder = outlook.Folders.Item(1)
sub_folder1 = root_folder.Folders['Project1']
next_level = sub_folder1.Folders['ProjectA']
msg = next_level.Items
msgs = msg.GetLast()
dest = sub_folder1.Folders['ProjectB']
msgs.Move(dest)
If you wish to make an actual copy and move the new item to the second folder, replace your ending code with
msgcopy = msgs.Copy()
dest = sub_folder1.Folders['ProjectB']
msgcopy.Move(dest)
I am writing a python code to download a specific attachment from unread emails in outlook and mark those emails as read. I have managed to finish 90% of it i.e. I can do an iteration to open unread emails and download the attachments with a specific emails. However, I have two issues.
I am downloading the attachment with the same name, and if there are two attachments with the same name, it just saves the one it extracts from the last iteration. I tried appending a time stamp at the end of the file name but it has the same effect. Any help would be appreciated. This is not mandatory requirement since the mail comes at stipulated intervals and I can write a separate python code to rename it but I want to pack everything in this single email.
I would like to mark the email as read after the attachment is downloaded. I do not know the command for this one. I have attached the code for your reference.
P.S. This is my first real python code. Also this is my first post here. Apologies if this is was already asked elsewhere.
import win32com.client
import os
import time
date_time_stamp = time.strftime("%Y%m%d-%H%M%S")
#set custom working directory
os.chdir('C:\\Users\user_name\Desktop\')
print(os.getcwd())
outlook = win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI")
main_inbox = outlook.GetDefaultFolder(6)
subfolder = main_inbox.Folders.Item("my_child_folder_under_inbox")
subfolderitems = subfolder.Items
message = subfolderitems.GetFirst()
attachment_name = 'my_attachment_name'
#Loop to pick messages that are unread
for message in subfolderitems:
if message.Unread == True:
print("New Mail Found... Downloading Attachment...")
#Loop to check if the attachment name is the same
for attachments in message.Attachments:
if attachments.Filename == attachment_name:
#Saves to the attachment to the working directory
attachments.SaveASFile(os.getcwd() + '\\' + 'my_attachment_name' + date_time_stamp + '.csv')
print (attachments)
time.sleep(2)
break
#Go to next unread messages if any
message = subfolderitems.GetNext()
else:
print ("Checking...")
--
Thanks and Regards,
Sakthi Ganesh K.
I think it may have to do with your 'date_time_stamp', since it tries to download the files in the same second, and the system only keeps the last one. You could try using a UUID for that to ensure it is a unique string:
import uuid
file_uuid = str(uuid.uuid4())
...
attachments.SaveASFile(os.getcwd() + '\\' + 'my_attachment_name' + file_uuid + '.csv')
To mark the message as Read, you could simply do:
message.Unread = False