Forum Archive

How to remove or change a calendar event?

brumm

After finding code to save and find calendar events, I would like to write my own calendar app. But I still don't know how to write ObjectC code, sorry. So could you please tell me how to remove and change calendar events. That is my try to delete a calendar event (last line crashes pythonista). And there is only one event on 2018-12-30...

from  objc_util import *
from ctypes import POINTER
import threading
import calendar, time

store = ObjCClass('EKEventStore').alloc().init()

access_granted = threading.Event()
def completion(_self, granted, _error):
    access_granted.set()
completion_block = ObjCBlock(completion, argtypes=[c_void_p, c_bool, c_void_p])
store.requestAccessToEntityType_completion_(0, completion_block)
access_granted.wait()

startDate = ObjCClass('NSDate').dateWithTimeIntervalSince1970_(time.mktime(time.strptime('2018-12-30 00:00:00', '%Y-%m-%d %H:%M:%S')))
endDate = ObjCClass('NSDate').dateWithTimeIntervalSince1970_(time.mktime(time.strptime('2018-12-30 23:59:59', '%Y-%m-%d %H:%M:%S')))

predicate = store.predicateForEventsWithStartDate_endDate_calendars_(startDate, endDate, None)
event = store.eventsMatchingPredicate_(predicate)

print(event)
LP_c_void_p = POINTER(c_void_p)
err = LP_c_void_p()
#store.removeEvent_span_error_(event, 0, err)
cvp

@brumm to remove, use this

from  objc_util import *
#from ctypes import POINTER
#import threading
import calendar, time

store = ObjCClass('EKEventStore').alloc().init()

# this code is only needed once
#access_granted = threading.Event()
#def completion(_self, granted, _error):
#    access_granted.set()
#completion_block = ObjCBlock(completion, argtypes=[c_void_p, c_bool, c_void_p])
#store.requestAccessToEntityType_completion_(0, completion_block)
#access_granted.wait()

startDate = ObjCClass('NSDate').dateWithTimeIntervalSince1970_(time.mktime(time.strptime('2018-12-30 00:00:00', '%Y-%m-%d %H:%M:%S')))
endDate = ObjCClass('NSDate').dateWithTimeIntervalSince1970_(time.mktime(time.strptime('2018-12-30 23:59:59', '%Y-%m-%d %H:%M:%S')))

predicate = store.predicateForEventsWithStartDate_endDate_calendars_(startDate, endDate, None)

events = store.eventsMatchingPredicate_(predicate) # array of events

for event in events:
    print(event.title())
    if str(event.title()) == 'Test':
        span = 0 # only this instance
        store.removeEvent_span_error_(event,span,None)

You can see that predicate returns an array of events, even if only one.

M calendar has several events at this date, I only remove this one with title = Test

cvp

@brumm to update the event, for instance it's title:

for event in events:
    print(event.title())
    if str(event.title()) == 'Test':
        span = 0 # only this instance
        #store.removeEvent_span_error_(event,span,None)
        event.title = 'ttt'
        store.saveEvent_span_error_(event,span,None) 
brumm

@cvp: Great, you made my day.

AA737

Hello!

Brand new to python, and programming as a whole. I used this template successfully, thank you, but I was wondering how to specify a specific calendar? I assume it would be in the “predicate,” after the start and end time, the “none?”

Thanks!

ccc

how to specify a specific calendar

What exactly does that mean? Can you provides some sample code of what you have working so far with a description of how you want to change it?

The Apple docs for this function call are at https://developer.apple.com/documentation/eventkit/ekeventstore/1507479-predicateforeventswithstartdate?language=objc

cvp

@AA737 said:

I assume it would be in the “predicate,”

Yes, the third parameter is None (all calendars) or an array of EKCalendar objects.

cvp

@AA737 try this

import datetime
from objc_util import *

# EKEventStore = calendar database
store = ObjCClass('EKEventStore').alloc().init()

# Convert string yyyymmdd to NSdate
dateFormat = ObjCClass('NSDateFormatter').alloc().init()
dateFormat.setDateFormat_('yyyyMMdd HH:mm')

for calendar in store.calendars():
    print(calendar.title())
    if str(calendar.title()) == 'your calendar' :
        print('here')
        date1 = dateFormat.dateFromString_('20200101 00:01') 
        date2 = dateFormat.dateFromString_('20201231 23:59')  
        predicate = store.predicateForEventsWithStartDate_endDate_calendars_(date1, date2, [calendar])
        events = store.eventsMatchingPredicate_(predicate)
        for event in events:
            print(event.title())
        break
AA737

@ccc sorry, I didn’t word that very clearly. Right now, I have a parameter to return all events that occur in the next 2 days, from all calendars. I would like to return events from only one calendar. I have reviewed the Apple documentation, but I have to admit I don’t understand it

@cvp thank you, that works well, however, instead of doing an If statement, what would be the format to replace the “None” with a calendar name in the predicate?

I tried a few things, but I really can’t figure out the format.
Thanks for the help

 store = ObjCClass('EKEventStore').alloc().init()
 now = datetime.now()

start_date = now.strftime('%Y-%m-%d %H:%M:%S')

start_date = datetime.now() + timedelta(hours=0)
tomorrow = datetime.now() + timedelta(days=2)

start_date = start_date.strftime('%Y-%m-%d %H:%M:%S')
tomorrow_formatted = tomorrow.strftime('%Y-%m-%d %H:%M:%S')

startDate = ObjCClass('NSDate').dateWithTimeIntervalSince1970_(time.mktime(time.strptime(start_date, '%Y-%m-%d %H:%M:%S')))

endDate =   ObjCClass('NSDate').dateWithTimeIntervalSince1970_(time.mktime(time.strptime(tomorrow_formatted, '%Y-%m-%d %H:%M:%S')))

predicate = store.predicateForEventsWithStartDate_endDate_calendars_(startDate, endDate, None)

events = store.eventsMatchingPredicate_(predicate)’ ‘ ‘
cvp

@AA737 said:

what would be the format to replace the “None” with a calendar name in the predicate?

The third parameter of the predicate is not a calendar name but an array of calendars EKCalendar objects.

cvp

@AA737 try this

calendars_array = [calendar for calendar in store.calendars() if str(calendar.title()) == 'your calendar']
predicate = store.predicateForEventsWithStartDate_endDate_calendars_(date1, date2, calendars_array)
events = store.eventsMatchingPredicate_(predicate) 
AA737

@cvp that worked great, thank you! I have a lot to learn...

cvp

@AA737 Believe me, you don't begin with the easiest project 😀

AA737

@cvp Have you noticed you can only run your calendar script about 8 times in a row, and then it stops working and Pythonista has to be reset? Any idea why, or a fix for it?

cvp

@AA737 said:

run your calendar script about 8 times in a row, and then it stops working

You're right and I did never see that. I'll check. Perhaps an Apple limitation

cvp

@AA737 After testing, I've remarked that after 10 runs (always), there is no crash at all but the returned calendars list of EKEventStore is empty and sincerely, I didn't find anything similar in Google nor any explanation.

AA737

@cvp said:

@AA737 After testing, I've remarked that after 10 runs (always), there is no crash at all but the returned calendars list of EKEventStore is empty and sincerely, I didn't find anything similar in Google nor any explanation.

Interesting, thank you for looking into it, and all your help!

JonB

@cvp maybe try an autoreleasepool context handler around the code of interest, and then autorelease() and delete the python objects for objc objects you don't need anymore, like the predicate, and maybe the store, calendar arrays, etc. it sounds like ekeventstore gets pissed if you are not releasing its objects.

cvp

@AA737 Thanks to @JonB , this works

import datetime
import objc_util

with objc_util.autoreleasepool():

    # EKEventStore = calendar database
    store = objc_util.ObjCClass('EKEventStore').alloc().init()

    # Convert string yyyymmdd to NSdate
    dateFormat = objc_util.ObjCClass('NSDateFormatter').alloc().init()
    dateFormat.setDateFormat_('yyyyMMdd HH:mm')

    date1 = dateFormat.dateFromString_('20200101 00:01') 
    date2 = dateFormat.dateFromString_('20201231 23:59')  

    calendars_array = [calendar for calendar in store.calendars() if str(calendar.title()) == 'Sorties']
    predicate = store.predicateForEventsWithStartDate_endDate_calendars_(date1, date2, calendars_array)
    events = store.eventsMatchingPredicate_(predicate)
    for event in events:
        print(event.title())

    store.autorelease()

del events, predicate, store, calendars_array 
ccc

To convert from Python datetimes...

from datetime import datetime
print(f'{datetime.now():%Y%m%d %H:%M}')
cvp

@ccc I know, but here we need Objectivec NSDateFormatter objects

ccc

Yes. My point was about converting from Python datetimes to Objective C datetimes.

AA737

@cvp said:

@AA737 Thanks to @JonB , this works
```
import datetime
import objc_util

with objc_util.autoreleasepool():

# EKEventStore = calendar database
store = objc_util.ObjCClass('EKEventStore').alloc().init()

# Convert string yyyymmdd to NSdate
dateFormat = objc_util.ObjCClass('NSDateFormatter').alloc().init()
dateFormat.setDateFormat_('yyyyMMdd HH:mm')

date1 = dateFormat.dateFromString_('20200101 00:01')
date2 = dateFormat.dateFromString_('20201231 23:59')

calendars_array = [calendar for calendar in store.calendars() if str(calendar.title()) == 'Sorties']
predicate = store.predicateForEventsWithStartDate_endDate_calendars_(date1, date2, calendars_array)
events = store.eventsMatchingPredicate_(predicate)
for event in events:
print(event.title())

store.autorelease()

del events, predicate, store, calendars_array
```

Fantastic, thanks @cvp and @JonB !