I am trying to get all appointments from client outlook with python and win32com. client, I just want to get all appointments from 2019 so that I could restrict the appointment Items, but when I restrict them I don't get the recurring appointments.
I already tried to enable recurring items with appointments.IncludeRecurrences = "True", but that didn't help.
import win32com.client
import time
import datetime
import os
f=open("mem.txt", "w")
counter=0
outlook= win32com.client.Dispatch("Outlook.Application")
namespace=outlook.GetNamespace("MAPI")
recipient = namespace.createRecipient("Some Calender")
resolved = recipient.Resolve()
sharedCalendar = namespace.GetSharedDefaultFolder(recipient, 9)
appointments = sharedCalendar.Items
# Restrict items
begin = datetime.date(2019, 1, 1)
end = datetime.date(2019, 12, 30)
restriction = "[Start] >= '" + begin.strftime("%m/%d/%Y") + "' AND [End] <= '" +end.strftime("%m/%d/%Y") + "'"
restrictedItems = appointments.Restrict(restriction)
appointments.IncludeRecurrences = "True"
# Iterate through restricted AppointmentItems
for appointmentItem in restrictedItems:
month= appointmentItem.Start
month=str(month)[5:-18] #just trim the month out of the date
if month=='08': #need appointments from specific
#mystuff
#the code works but I want the recurring appointments too
print(counter)
f.close()
Did you try setting IncludeRecurrences to True before Restricting your items ?
Basically switching those two lines :
restrictedItems = appointments.Restrict(restriction)
appointments.IncludeRecurrences = "True"
Firstly, to retrieve all Outlook appointment items from the folder that meets the predefined condition, you need to sort the items in ascending order and set the IncludeRecurrences to true. You will not catch recurrent appointments if you don’t do this before using the Restrict method!
folderItems = folder.Items;
folderItems.IncludeRecurrences = true;
folderItems.Sort("[Start]");
resultItems = folderItems.Restrict(restrictCriteria);
You may find the following articles helpful:
How To: Use Restrict method in Outlook to get calendar items
How To: Retrieve Outlook calendar items using Find and FindNext methods
Related
I found a way to query all global address in outlook with python,
import win32com.client
import csv
from datetime import datetime
# Outlook
outApp = win32com.client.gencache.EnsureDispatch("Outlook.Application")
outGAL = outApp.Session.GetGlobalAddressList()
entries = outGAL.AddressEntries
# Create a dateID
date_id = (datetime.today()).strftime('%Y%m%d')
# Create empty list to store results
data_set = list()
# Iterate through Outlook address entries
for entry in entries:
if entry.Type == "EX":
user = entry.GetExchangeUser()
if user is not None:
if len(user.FirstName) > 0 and len(user.LastName) > 0:
row = list()
row.append(date_id)
row.append(user.Name)
row.append(user.FirstName)
row.append(user.LastName)
row.append(user.JobTitle)
row.append(user.City)
row.append(user.PrimarySmtpAddress)
try:
row.append(entry.PropertyAccessor.GetProperty("http://schemas.microsoft.com/mapi/proptag/0x3a26001e"))
except:
row.append('None')
# Store the user details in data_set
data_set.append(row)
# Print out the result to a csv with headers
with open(date_id + 'outlookGALresults.csv', 'w', newline='', encoding='utf-8') as csv_file:
headers = ['DateID', 'DisplayName', 'FirstName', 'LastName', 'JobTitle', 'City', 'PrimarySmtp', 'Country']
wr = csv.writer(csv_file, delimiter=',')
wr.writerow(headers)
for line in data_set:
wr.writerow(line)
But it querys user one by one and it's very slow. I only need to query user from IT department out of 100,000 users. How can I write the filter to avoid querying all users?
On the Extended MAPI (C++ or Delphi) level, you can access the entries as IMAPITable MAPI interface and retrieve multiple entries in a single call (IMAPITable::QueryRows). You should still keep in mind that you are limited to 32KB on each request, so you'd need to retrieve the data in batches, which is still better than one entry at a time.
IMAPITable is also exposed as AddressEntries.RawTable property in OOM, but since it is Extended MAPI, it can be accessed in C++ or Delphi only.
If using Redemption (any language, I am its author) is an option, it exposes its own MAPITable interface, and in particular ExecSQL method, which allows to retrieve data in a single call.
In VBA:
set Session = CreateObject("Redemption.RDOSession")
Session.MAPIOBJECT = Application.Session.MAPIOBJECT
set GAL = Session.AddressBook.GAL
set Table = GAL.AddressEntries.MAPITable
set Recordset = Table.ExecSQL("SELECT Name, FirstName, LastName, JobTitle, EntryID, " & _
"""http://schemas.microsoft.com/mapi/proptag/0x3A27001f"" As BusinessAddressCity, " & _
"""http://schemas.microsoft.com/mapi/proptag/0x3a26001f"" As BusinessAddressCountry, " & _
"""http://schemas.microsoft.com/mapi/proptag/0x39FE001F"" As PrimarySmtpAddress, " & _
"from list ")
while not Recordset.EOF
Debug.Print(Recordset.Fields("Name").Value & " - " & Recordset.Fields("PrimarySmtpAddress").Value)
Recordset.MoveNext
wend
You mention that you prefer to query the data rather than retrieve all entries - that should be the preferred solution; you should never retrieve all data unless you are creating some kind of synchronization app and you need everything on initial sync. Unfortunately, address book interfaces in MAPI (and hence in Outlook Object Model) are not very flexible when it comes to searching, especially compared to the search capabilities for the messages in folders.
If you want a single matching GAL user, use Application.Session.CreateRecipient / Recipient.Resolve and hope the alias resolves and there are no ambiguous entries. the search will be performed in all address book containers on the search path.
If you want multiple entries, you can filter on a few properties explicitly exposed by GAL for searching (Alias, City, Company, Department, First Name, Last Name, Office, Title). This is what you see in the address book window in Outlook if you click "Advanced Find". Redemption exposes it through the RDOAddressListSearch object:
set Session = CreateObject("Redemption.RDOSession")
Session.MAPIOBJECT = Application.Session.MAPIOBJECT
set AddrList = Session.Addressbook.GAL
set Search = AddrList.Search
Search.FirstName = "John"
Search.City = "Phoenix"
set AddressEntries = Search.GetResults
for each AddressEntry in AddressEntries
Debug.Print AddressEntry.Name
next
#Dmitry Streblechenko 's method solved my problem.
The final code that can work is:
Set Session = CreateObject("Redemption.RDOSession")
Session.Logon
Set GAL = Session.AddressBook.GAL
Set AddrList = Session.AddressBook.GAL
Set Search = AddrList.Search
Search.DisplayName = "renlei"
Set AddressEntries = Search.GetResults
For Each AddressEntry In AddressEntries
Debug.Print AddressEntry.Name
Next
Every morning I get spot data on FX volumes via an email, I'd like to build a process to search two pieces of data within the body of the email and save them as a new variable which I can then refer to later.
I've got the process to search my emails, order them according to date and check whether the entered data exists within the emails, but because the data is contained within a format between two commas, I am unsure how to take that data out and assign it to a new variable.
Format for example is this:
BWP/USD,0
CHF/AMD T,0
This is what I've achieved thus far:
import win32com.client
import os
import time
import re
# change the ticker to the one you're looking for
FX_volume1 = "BWP/USD"
FX_volume2 = "CHF/AMD"
outlook = win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI")
inbox = outlook.GetDefaultFolder(6)
messages = inbox.Items
messages.Sort("[ReceivedTime]", True)
# find spot data
for message in messages:
if message.subject.startswith("FX SPOT FIGURES"):
if FX_volume1 and FX_volume2 in message.body:
data = message.body
print(data)
else:
print('No data for', FX_volume1, 'or', FX_volume2, 'was found')
break
Any idea how to take this forward?
Thanks for any assistance/pointers
import win32com.client
import os
import time
import re
# change the ticker to the one you're looking for
FX_volume1 = "BWP/USD"
FX_volume2 = "CHF/AMD"
outlook = win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI")
inbox = outlook.GetDefaultFolder(6)
messages = inbox.Items
messages.Sort("[ReceivedTime]", True)
# find spot data
for message in messages:
if message.subject.startswith("FX SPOT FIGURES"):
case1 = re.match(FX_volume1 + ",(\d*)", message.body)
case2 = re.match(FX_volume2 + ",(\d*)", message.body)
case (1 and 2) will be match objects if a match is found, else they will be None. To retrieve your values just do val = case1.group(1). Hence:
EDIT:
if case1 not None:
FX_vol1_val = case1.group(1)
if case2 not None:
FX_vol2_val = case1.group(1)
For more info on match objects:
https://docs.python.org/3/library/re.html#match-objects
If you are expecting floats, see the following link:
Regular expression for floating point numbers
EDIT 2:
Hi, so as you couldn't get it working I gave it a quick try and it worked for me with the following example. Just to add to regex notation, anything that you put in brackets (), if the pattern matches, the contents between the brackets will be stored.
import re
my_text = "BWP/USD,1"
FX_pattern = "BWP/USD," # added the comma here for my convinience
my_match = re.match(FX_pattern, "(\d*)")
print("Group 0:", my_match.group(0))
print("Group 1:", my_match.group(1))
Printout:
Group 0: BWP/USD,1
Group 1: 1
I am trying to use the restrict method to search for emails where the subject equals a certain string, however, this string changes slightly everyday since the sender adds the current date. I am trying to use a variable which would contain the current date and insert it into the restrict method.
I keep getting an invalid syntax error message specifically with regards to the Restrict line.
from datetime import datetime, timedelta
received_dt = datetime.now()
received_dt = received_dt.strftime('%Y-%m-%d')
subject_search_string = "Daily Statement" + recevied_dt
msg = msg.Restrict("[Subject] = " subject_search_string)
The subject value must be single-quoted:
msg = msg.Restrict("[Subject] = '" subject_search_string "'")
I can get all appointments from my main calendar, like this:
def getCalendarEntries():
Outlook = win32com.client.Dispatch("Outlook.Application")
ns = Outlook.GetNamespace("MAPI")
appointments = ns.GetDefaultFolder(9).Items
appointments.Sort("[Start]")
appointments.IncludeRecurrences = "True"
begin = datetime.date.today()
end = begin + datetime.timedelta(days = 100);
restriction = "[Start] >= '" + begin.strftime("%m/%d/%Y") + "' AND [End] <= '" +end.strftime("%m/%d/%Y") + "'"
restrictedItems = appointments.Restrict(restriction)
events={'Start':[],'End':[],'Organizer':[],'Subject':[],'Duration':[]}
for a in restrictedItems:
events['Start'].append(a.Start)
events['End'].append(a.End)
events['Organizer'].append(a.Organizer)
events['Subject'].append(a.Subject)
events['Duration'].append(a.Duration)
return events
And I can save events into my main calendar, like this:
def addevent(start, subject, duration):
Outlook = win32com.client.Dispatch("Outlook.Application")
appointment = Outlook.CreateItem(1) # 1=outlook appointment item
appointment.Start = start
appointment.Subject = subject
appointment.Duration = duration
appointment.Save()
My issue is that I don't know how to connect to another calendar folder. I don't want the "DefaultFolder" but a specific one. I would be really greatful if someone could help me.
Found the answer myself. One has to replace
appointments = ns.GetDefaultFolder(9).Items
with
recipient = ns.createRecipient("foo#outlook.com")
resolved = recipient.Resolve()
appointments = ns.GetSharedDefaultFolder(recipient, 9).Items
another way of read Items in another calender with subfolders i.e. \abc#outlook.com\Calendar\Work_Exchange
could be:
privateol = ns.Folders('abc#outlook.com')
privatecal = privateol.Folders('Calendar')
privatesubfolder = privatecal.Folders('Work_Exchange')
What i don't know is, how to write in this specific Subfolder, so if anyone wants to provide...
lg
Edit:
to write to the specific subfolder, one has to replace appointment.Save() with appointment.Move(privatesubfolder)
maybe it's not the best solution, but it works
Had exact the same issue but got brilliant help: use the Items.Add() Method and it perfectly works in whatever folder you are in. See here: Create Outlook appointment in subfolder/subcalendar with python
I try to make an application to read events from Outlook shared calendar. I use Python 3.8.0 on win10. Here is my function to do this.
def getSharedCalendarEntries(TS_name, days=1000): #TS_name is name of shared calendar
MailboxToAccess = 'owner#gmail.com'
Outlook = win32com.client.Dispatch("Outlook.Application")
namespace = Outlook.GetNamespace("MAPI")
recipient = namespace.createRecipient(MailboxToAccess)
resolved = recipient.Resolve()
sharedCalendar = namespace.GetSharedDefaultFolder(recipient, 9).Folders(TS_name).Items
sharedCalendar.Sort("[Start]")
sharedCalendar.IncludeRecurrences = 'True'
today = datetime.datetime(2019,1,1)
begin = today.date().strftime('%d/%m/%Y')
tomorrow = datetime.timedelta(days=days)+today
end = tomorrow.date().strftime('%d/%m/%Y')
sharedCalendar = sharedCalendar.Restrict("[Start] >= '" +begin+ "' AND [END] <= '" +end+ "'")
events = {'Start':[],'End':[],'Subject':[],'Duration':[]}
mEv = []
for app in sharedCalendar: #petla po rezerwacjach
adate = datetime.datetime(app.Start.year, app.Start.month, app.Start.day).date()
events['Start'].append(adate)
aend = datetime.datetime(app.End.year, app.End.month, app.End.day).date()
events['End'].append(aend)
events['Duration'].append(int(app.Duration/1440))
events['Subject'].append(app.Subject)
mEvent = Event(adate, aend, int(app.Duration/1440), app.Subject)
mEv.append(mEvent)
return mEv
Everything was working and I was able to read events, but suddenly something happened (I didn't change anything in code) and I have such error:
File "C:\Users\user_catalog\Desktop\outread.py", line 60, in
getSharedCalendarEntries
sharedCalendar = namespace.GetSharedDefaultFolder(recipient, 9).Folders(TS_name).Items
File
"C:\Users\user_catalog\AppData\Local\Programs\Python\Python38\lib\site-packages\win32com\client\dynamic.py",
line 197, in call
return self._get_good_object_(self.oleobj.Invoke(*allArgs),self.olerepr.defaultDispatchName,None)
pywintypes.com_error: (-2147352567, 'Exception occurred.', (4096,
'Microsoft Outlook', ' An attempt to perform the operation failed.
Could not find the object.', None, 0, -2147221233), None)
I had read-only access to shared calendars. Owner of shared calendars said that she logged-out of network, and time of logged out was the same time my application stopped working.
Have any of you had such problem or have some tips for me?
Thank you in advance!
Pio
Would you mind trying the code below? It will get you a dataframe with subject, occurence of the meeting, start time and end time.
import win32com.client, datetime
import pandas as pd
from datetime import time, timedelta
#connect to outlook
outlook = win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI")
appointments = outlook.GetDefaultFolder(9).Items #Calendário
#use these 2 lines to short the list
appointments.Sort('[Start]')
#Recurrent events only show the first start date, use the following to get the REAL occurrence of the event.
appointments.IncludeRecurrences = 'True'
#Beginning of the week(monday) to end of week(friday)
today = datetime.date.today()
start = today - timedelta(days=today.weekday())
end = start + timedelta(days=5)
#String of restriction time that will be used to filter dates on outlook
restriction = "[Start] >= '" + start.strftime("%d/%m/%Y") + "' AND [End] < '" +end.strftime("%d/%m/%Y") + "'"
print(restriction)
restrictedItems = appointments.Restrict(restriction)
#create empty data frame with columns to be fetched
i = 0
calendario = pd.DataFrame(columns=['Subject', 'When', 'Start Time','End Time'])
#Loop on items to fill the dataframe
for appointmentItem in restrictedItems:
calendario.loc[i] = [appointmentItem.Subject,appointmentItem.Start.strftime("%m/%d/%Y"),appointmentItem.Start.strftime("%H:%M"),appointmentItem.End.strftime("%H:%M")]
i = i + 1
#display dataframe
calendario