I've written a short code to download and rename files from a specific folder in my outlook account. The code works great, the only problem is that I typically need to run the code several times to actually download all of the messages. It seems the code is just failing to acknowledge some of the messages, there are no errors when I run through it.
I've tried a few things like walking through each line step by step in the python window, running the code with outlook closed or opened, and trying to print the files after they're successfully saved to see if there are specific messages that are causing the problem.
Here's my code
#! python3
# downloadAttachments.py - Downloads all of the weight tickets from Bucky
# Currently saves to desktop due to instability of I: drive connection
import win32com.client, os, re
#This line opens the outlook application
outlook = win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI")
#Not exactly sure why the inbox is default folder 6 but it works
inbox = outlook.GetDefaultFolder(6)
#box where the messages are to save
TicketSave = inbox.Folders('WDE').Folders('SAVE').Folders('TicketSave')
#box where the messages are moved to
done = inbox.Folders('WDE').Folders('CHES').Folders('Weight Tickets')
ticketMessages = TicketSave.Items
#Key is used to verify the subject line is correct. This script only works if the person sends
# their emails with a consistent subject line (can be altered for other cases)
key = re.compile(r'wde load \d{3}') #requires regulars expressions (i.e. 'import re')
for message in ticketMessages:
#will skip any message that does not match the correct subject line format (non-case sensitive)
check = str(message.Subject).lower()
if key.search(check) == None:
continue
attachments = message.Attachments
tic = attachments.item(1)
ticnum = str(message.Subject).split()[2]
name = str(tic).split()[0] + ' ticket ' + ticnum + '.pdf' #changes the filename
tic.SaveAsFile('C:\\Users\\bhalvorson\\Desktop\\Attachments' + os.sep + str(name))
if message.UnRead == True:
message.UnRead = False
message.Move(done)
print('Ticket pdf: ' + name + ' save successfully')
Alright I found the answer to my own question. I'll post it here in case any other youngster runs into the same problem as me.
The main problem is the "message.Move(done)" second from the bottom.
Apparently the move function alters the current folder thus altering the number of loops that the for loop will go through. So, the way it's written above, the code only ever processes half of the items in the folder.
An easy work around is to switch the main line of the for loop to "for message in list(ticketMessages):" the list is not affected by the Move function and therefore you'll be able to loop through every message.
Hope this helps someone.
Related
EDIT : SOLVED IT!
I added this snippet of code below, in order to trace the position of the main inbox folder
for folder in outlook.Folders:
print(folder)
This highlighted that something had changed within the underlying Outlook structure and Folder[0] was no longer valid. I will now tweak code to make it more robust and dynamically choose folder
END EDIT
I wrote some code to pull emails from Outlook and save the attachments. It worked perfectly up until a few days ago.
I had not touched the code, so I can only assume that something within Outlook has changed. I work in a corporate environment, so there is remote update of software.
Does anybody have any idea what this error means and why its suddenly cropped up ? I am very bleak, as the code worked so well before this hiccup. Alternatively, any better way to retrieve emails and attachments from Outlook, using Python ?
import win32com.client
def main():
pass
def saveAttachments():
outlook = win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI") # Opens Microsoft Outlook
mailbox = outlook.Folders[0] # Based off email address
inbox = mailbox.Folders["Inbox"]
emails = inbox.Items
emails.Sort("[ReceivedTime]", True)
destPath = "\\\\servername\\path\\"
try:
for mail in emails:
if ("Detailed MTM," in mail.subject) and (mail.Attachments.Count > 0):
print(mail.Sender)
print(mail.Subject)
print(mail.Receivedtime)
attachments = mail.Attachments
for file in attachments:
if "MTMDetailed" in str(file):
file.SaveAsFile(destPath + str("MTMDetailed.xls"))
break
except:
file = open(destPath + "error.log", "w")
file.write("Problem")
file.close()
if __name__ == '__main__':
main()
saveAttachments()
File "C:\Tools\Python\lib\site-packages\win32com\client\dynamic.py", line 256, in __getitem__
return self._get_good_object_(self._oleobj_.Invoke(dispid, LCID, invkind, 1, index))
pywintypes.com_error: (-2147352567, 'Exception occurred.', (4096, 'Microsoft Outlook', 'The attempted operation failed. An object could not be found.', None, 0, -2147221233), None)
I added code to iterate through the outlook.Folders to find the one I need, without relying on specific hardcoded position
After extracting some of the email's data, I would like to move the email to a specified folder with python. I've searched and haven't seemed to find what I need.
Has anyone done this before?
Per a comment, I've added my current logic in hopes that it will clarify my problem. I loop through my folder, extract the details. After doing that, I want to move the email to a different folder.
import win32com.client
import getpass
import re
'''
Loops through Lotus Notes folder to view messages
'''
def docGenerator(folderName):
# Get credentials
mailServer = 'server'
mailPath = 'PubDir\inbox.nsf'
# Password
pw = getpass.getpass('Enter password: ')
# Connect
session = win32com.client.Dispatch('Lotus.NotesSession')
# Initializing the session and database
session.Initialize(pw)
db = session.GetDatabase(mailServer, mailPath)
# Get folder
folder = db.GetView(folderName)
if not folder:
raise Exception('Folder "%s" not found' % folderName)
# Get the first document
doc = folder.GetFirstDocument()
# If the document exists,
while doc:
# Yield it
yield doc
# Get the next document
doc = folder.GetNextDocument(doc)
# Loop through emails
for doc in docGenerator('Folder\Here'):
# setting variables
subject = doc.GetItemValue('Subject')[0].strip()
invoice = re.findall(r'\d+',subject)[0]
body = doc.GetItemValue('Body')[0].strip()
# Move email after extracting above data
# ???
As you will move the document before getting the next one, I'd recommend to replace your loop with
doc = folder.GetFirstDocument()
while doc:
docN = folder.GetNextDocument(doc)
yield doc
doc = docN
And then to move the message to the proper folder you need
doc.PutInFolder(r"Destination\Folder")
doc.RemoveFromFolder(r"Origin\Folder")
Of course, take care of escaping your backslashes or using raw strings literals to pass correctly the view name.
doc.PutInFolder creates the folder if it doesn't exist. In that case the user needs to have permissions to create public folders, otherwise the created folder will be private. (If the folder already exists, of course, this is not a problem.)
My Script downloads a zipfile, exctracts the relevant parts replaced files and folders etc. It used to work flawlessly, for some reason its has now decided to stop working and only partly downloads the zipfile, and of course, as the zipfile is incomplete I get an error, saying the downloaded file is not a zipfile. my script is as follows.
def downloadupdate():
xbmcgui.Dialog().ok(
"[B][COLOR white]Daily Updater[/B][/COLOR]",
"Favourites and some software will now update",
"Elements of your system will be cleaned",
"Daily Update will take at most 2 minutes")
#ONLY HAVE THE SUPER FAVOURITES FOLDER IN THE ZIPFILE!!
url = 'http://x.com/x/x/Super Favourites.zip'
destination = xbmc.translatePath('special://home/userdata/addon_data/iupdatefix/Super Favourites.zip')
favzip = urllib.urlopen(url)
xbmc.executebuiltin("Notification(Downloading new updates, PLEASE WAIT,()")
with open(xbmc.translatePath('special://home/userdata/addon_data/iupdatefix/Super Favourites.zip'), "wb") as zipFile:
zipFile.write(favzip.read())
xbmc.executebuiltin("Notification(Download Complete, Please wait,()")
time.sleep(5)
xbmc.executebuiltin("Notification(Updating Click and Play channels, Please wait,()")
updatezip = xbmc.translatePath('special://home/userdata/addon_data/iupdatefix/Super Favourites.zip')
extractupdate = xbmc.translatePath('special://home/userdata/addon_data/plugin.program.super.favourites/')
oldfav = xbmc.translatePath('special://home/userdata/addon_data/plugin.program.super.favourites/Super Favourites')
yeszip = os.path.exists(updatezip)
time.sleep(5)
if yeszip:
xbmc.executebuiltin("Notification(Removing previous, Please wait,()")
shutil.rmtree(oldfav, ignore_errors=False)
xbmc.executebuiltin("Notification(Updating, now,()")
gh = open(updatezip, 'rb')
zp = zipfile.ZipFile(gh)
zp.extractall(extractupdate)
gh.close()
time.sleep(3)
xbmc.executebuiltin("Notification(updated, Now Checking sd,()")
# put this command at the end of whole script --->>>os.remove(updatezip)
else:
xbmc.executebuiltin("Notification(Update file Corrupt, Please try again,()")
The problem has been solved, It was not the code. It was the hosting service, I had to reset the File permissions on the server and after that all works perfectly again.
We have an AJA Ki Pro recorder at another location and I a need to created an automated system that pulls the recorded files over to my editing studio. So far I have successfully been able to pull recordings using a python script run via an Applescript through Automator. I than can trigger the application from iCal. Basically my script involves setting the "MediaState" parameter on my recorder to "Data" (value=1) so I can pull files, comparing the files on the recorder to my local files (it only downloads what I dont already have locally), and then setting the "MediaState" property back to "Rec" (value=0) so the recorder is ready to go again.
Here are the 2 problems I have been unable to resolve so far. Bear with me, I have about 2 days worth of experience with Python :) It seems that I have somehow created a loop where it constantly says "Looking for new clips" and "No new clips found". Ideally I would like to have the script terminate if no new clips are found. I would also like to set this up so that when it finishes a download through cURL, it automatically sets my "MediaState" back to value=0 and ends the script. Here is my code so far:
# This script polls the unit downloads any new clips it hasn't already downloaded to the current directory
# Arguments: hostname or IP address of Ki Pro unit
import urllib, sys, string, os, posix, time
def is_download_allowed(address):
f = urllib.urlopen("http://"+address+"/config?action=get¶mid=eParamID_MediaState")
response = f.read()
if (response.find('"value":"1"') > -1):
return True
f = urllib.urlopen("http://"+address+"/config?action=set¶mid=eParamID_MediaState&value=1")
def download_clip(clip):
url = "http://" + address + "/media/" + clip
print url
posix.system("curl --output " + clip + " " + url);
def download_clips(response):
values = response.split(":")
i = 0
for word in values:
i += 1
if(word.find('clipname') > -1):
clip = values[i].split(',')[0].translate(string.maketrans("",""), '[]{} \,\"\" ')
if not os.path.exists(clip):
print "Downloading clip: " + clip
download_clip(clip)
else:
f = urllib.urlopen("http://"+address+"/config?action=set¶mid=eParamID_MediaState&value=0")
print "No new clips found"
address = sys.argv[1]
while 1:
if (is_download_allowed(address)):
print "Looking for new clips"
f = urllib.urlopen("http://"+address+"/clips")
response = f.read()
download_clips(response)
If the download_clips function is looping through the clip names, why you need that infinite while 1 loop? I think it is not necessary. Just remove it and dedent the block.
In my application I have a method for users to convert a report to a PDF document. This works perfectly - once. If the user clicks the button again, the conversion hangs.
This is my code:
def print_report(self):
web = QtWebKit.QWebView()
filename = "reporttemplate.html"
file = open(filename,'r')
html = file.read()
file.close()
web.setHtml(html)
#web.show()
printer = QtGui.QPrinter()
printer.setPageSize(QtGui.QPrinter.Letter)
printer.setOutputFormat(QtGui.QPrinter.PdfFormat)
# ---- BROKEN ----
# This next line is where it hangs on the second call to this function.
# The first time it works, and generates the PDF as expected.
# ---- BROKEN ON THE NEXT LINE! ----
printer.setOutputFileName(r'C:\path\to\report\directory\file.pdf')
def convertIt():
web.print_(printer)
print "Pdf generated"
web.close()
QtCore.QObject.connect(web, QtCore.SIGNAL("loadFinished(bool)"), convertIt)
My thought is that the printer still has the file open. If that's the case, how can I close the file?
It works if I relaunch the application and the file already exists. For that reason, I don't believe it's hanging because the file already exists.
Testing your code I noticed that for me it only works when I put web.setHtml(html) at the end (last statement) in the print_report method. Doing that I was able to generate file.pdf as many times as I wanted to.