If you had 800 events, then this is a job for minds greater than mine. If you want speed that is.
gl,
kel
If you had 800 events, then this is a job for minds greater than mine. If you want speed that is.
gl,
kel
I told you it was going to be interesting.
Think I’ve found a way to speed it up. If I can only remember it.
It was actually pretty quick really. Looking at this code
http://stackoverflow.com/questions/5907368/getting-todays-events-from-ical-with-applescript
set {year:y, month:m, day:d} to current date
set str to (m as string) & " " & (d as string) & " " & (y as string)
set today to date str
set tomorrow to today + 60 * 60 * 24
tell application "iCal"
tell calendar "Lotus Notes"
set curr to every event whose start date is greater than or equal to today ¬
and start date is less than or equal to tomorrow
end tell
end tell
It looked like we could get the events back using a variant of the data criteria, then process them as you did.
I don’t know about sorting by date though.
Hi.
That’s rubbish in that it’s inefficient and only works on machines where the preferences are set for the US date format. This is better:
set today to (current date)
set today's time to 0
set tomorrow to today + days
If you’ve got a lot of events in your calendar, a ‘whose’ filter can take quite a while to execute. (Applications can be very slow when they’re asked to think.) A faster way would be to grab the start dates and summaries for all the events in the calendar, as kel’s done above, and use vanilla AppleScript to identify which dates and summaries are in required range. The following may get you started. It gets date-range input from the user, narrows down the start dates and summaries to those in the range, sorts the results by start date, composes the text, and makes a TextEdit document containing it:
-- Ask the user for the range of dates to be covered.
on getDateRange()
set today to (current date)
set d1 to today's short date string
set d2 to short date string of (today + 6 * days)
set dateRange to text returned of (display dialog "Enter the required date range:" default answer d1 & " - " & d2)
set dateRangeStart to date (text from word 1 to word 3 of dateRange)
set dateRangeEnd to date (text from word -3 to word -1 of dateRange)
set dateRangeEnd's time to days - 1 -- Sets the last date's time to 23:59:59, the last second of the range.
return {dateRangeStart, dateRangeEnd}
end getDateRange
-- Return the start dates and summaries which are in the given date range.
on filterToDateRange(theStartDates, theSummaries, dateRangeStart, dateRangeEnd)
set {eventDatesInRange, eventSummariesInRange} to {{}, {}}
repeat with i from 1 to (count theStartDates)
set thisStartDate to item i of theStartDates
if (not ((thisStartDate comes before dateRangeStart) or (thisStartDate comes after dateRangeEnd))) then
set end of eventDatesInRange to thisStartDate
set end of eventSummariesInRange to item i of theSummaries
end if
end repeat
return {eventDatesInRange, eventSummariesInRange}
end filterToDateRange
-- Sort both the start-date and summary lists by start date.
on sortByDate(eventDatesInRange, eventSummariesInRange)
-- A sort-customisation object for sorting the summary list in parallel with the date list.
script custom
property summaries : eventSummariesInRange
on swap(i, j)
tell item i of my summaries
set item i of my summaries to item j of my summaries
set item j of my summaries to it
end tell
end swap
end script
CustomBubbleSort(eventDatesInRange, 1, -1, {slave:custom})
end sortByDate
-- CustomBubbleSort from "A Dose of Sorts" by Nigel Garvey.
-- The number of items to be sorted here is likely to be small.
on CustomBubbleSort(theList, l, r, customiser)
script o
property comparer : me
property slave : me
property lst : theList
on bsrt(l, r)
set l2 to l + 1
repeat with j from r to l2 by -1
set a to item l of o's lst
repeat with i from l2 to j
set b to item i of o's lst
if (comparer's isGreater(a, b)) then
set item (i - 1) of o's lst to b
set item i of o's lst to a
slave's swap(i - 1, i)
else
set a to b
end if
end repeat
end repeat
end bsrt
-- Default comparison and slave handlers for an ordinary sort.
on isGreater(a, b)
(a > b)
end isGreater
on swap(a, b)
end swap
end script
-- Process the input parameters.
set listLen to (count theList)
if (listLen > 1) then
-- Negative and/or transposed range indices.
if (l < 0) then set l to listLen + l + 1
if (r < 0) then set r to listLen + r + 1
if (l > r) then set {l, r} to {r, l}
-- Supplied or default customisation scripts.
if (customiser's class is record) then set {comparer:o's comparer, slave:o's slave} to (customiser & {comparer:o, slave:o})
-- Do the sort.
o's bsrt(l, r)
end if
return -- nothing
end CustomBubbleSort
-- Compose the text from the items in the start-date and summary lists.
on composeText(eventDatesInRange, eventSummariesInRange)
set txt to ""
set gap to linefeed & linefeed
repeat with i from 1 to (count eventDatesInRange)
set txt to txt & (date string of item i of eventDatesInRange) & (linefeed & item i of eventSummariesInRange & gap)
end repeat
return text 1 thru -3 of txt
end composeText
on main()
tell application "iCal" to set {theStartDates, theSummaries} to {start date, summary} of events of calendar "FL Family Calendar"
set {dateRangeStart, dateRangeEnd} to getDateRange()
set {eventDatesInRange, eventSummariesInRange} to filterToDateRange(theStartDates, theSummaries, dateRangeStart, dateRangeEnd)
sortByDate(eventDatesInRange, eventSummariesInRange)
set txt to composeText(eventDatesInRange, eventSummariesInRange)
tell application "TextEdit"
make new document with properties {text:txt}
activate
end tell
end main
main()
Wow, that looks awfully good. I should be able to run with that for the moment. I’ve got a bit of crunch before a family car trip, but then I’ll have a chance to study and dissect the script, and tweak it if I need to. I’ll document it on a blog post with, of course, full reference to this post and MacScripter.
Thanks NG, the new script works very well. It was easy to adjust the text output to be more compressed.
I’ll enjoy playing with this further, but it solves my original problem as is.
I realize the is a pretty old thread but I came across is because I was wanting to do something very similar in Calendar. The code works great for what I need but I’ve also been trying to modify it to do the same thing for several calendars. I’d still like to get just a single text doc that has date headers and then listing the events for that date (including which calendar it’s on) - something like this (yes I would like the dates in bold if possible):
Monday, January 18, 2016
Calendar1: a thing that’s happening
Calendar2: another thing that’s happening
Wednesday, January 20, 2016
Calendar1: another day, another thing to do
Can anyone guide me on how to make this script work for several calendars?
Thanks.
I wouldn’t describe collating the event data from several calendars by calendar date and then formatting the output text as being particularly similar…
The version below has only been tested in El Capitan. Instead of sorting a single calendar’s event summaries in parallel with the associated start dates, it collates the data from all the calendars into a list of lists ” each sublist containing the start date, summary, and calendar of an event. This list is then sorted by the sublists’ start dates. A new document is then created in TextEdit and text representing the events is output to it as it’s derived from the sorted data.
With more calendars involved, there’ll usually be more events to process, so I’ve used script object referencing in the list collation and replaced the bubble sort with a shell sort. However, most of the running time will be the initial wait for Calendar to return the data to the script, which could take up to a minute if there are a lot of events in the calendars. I think there are faster ways to get the data using ASObjC, but I haven’t got round to learning them or it yet.
-- Tested with Calendar 8.0 and TextEdit 1.11 (new documents defaulting to RTF) in Mac OS 10.11.2
-- Ask the user for the range of dates to be covered.
on getDateRange()
set today to (current date)
set d1 to today's short date string
set d2 to short date string of (today + 6 * days)
set dateRange to text returned of (display dialog "Enter the required date range:" default answer d1 & " - " & d2)
set dateRangeStart to date (text from word 1 to word 3 of dateRange)
set dateRangeEnd to date (text from word -3 to word -1 of dateRange)
set dateRangeEnd's time to days - 1 -- Sets the last date's time to 23:59:59, the last second of the range.
return {dateRangeStart, dateRangeEnd}
end getDateRange
-- Return the start dates, summaries, and calendar names of events in the given date range.
-- {{start date, summary, calendar name}, {start date, summary, calendar name}, . }
on filterToDateRange(theStartDates, theSummaries, theCalendarNames, dateRangeStart, dateRangeEnd)
script o
property sDates : theStartDates
property summaries : theSummaries
property cNames : theCalendarNames
property filteredData : {}
end script
repeat with i from 1 to (count theCalendarNames)
set o's sDates to item i of theStartDates
set o's summaries to item i of theSummaries
set thisCalendarName to item i of theCalendarNames
repeat with j from 1 to (count o's sDates)
set thisStartDate to item j of o's sDates
if (not ((thisStartDate comes before dateRangeStart) or (thisStartDate comes after dateRangeEnd))) then
set thisSummary to item j of o's summaries
set end of o's filteredData to {thisStartDate, thisSummary, thisCalendarName}
end if
end repeat
end repeat
return o's filteredData
end filterToDateRange
-- Sort the filtered data by start date.
on sortByDate(filteredData)
-- A sort-customisation object which compares the first items of two sublists taken from the list being sorted.
script custom
on isGreater(a, b)
return (beginning of a > beginning of b)
end isGreater
end script
CustomShellSort(filteredData, 1, -1, {comparer:custom})
end sortByDate
-- Shell sort. Algorithm: Donald Shell, 1959. AppleScript implementation: Nigel Garvey, 2010.
on CustomShellSort(theList, l, r, customiser)
script o
property comparer : me
property slave : me
property lst : theList
on shsrt(l, r)
set step to (r - l + 1) div 2
repeat while (step > 0)
slave's setStep(step)
repeat with j from (l + step) to r
set v to item j of o's lst
repeat with i from (j - step) to l by -step
tell item i of o's lst
if (comparer's isGreater(it, v)) then
set item (i + step) of o's lst to it
else
set i to i + step
exit repeat
end if
end tell
end repeat
set item i of o's lst to v
slave's rotate(i, j)
end repeat
set step to (step / 2.2) as integer
end repeat
end shsrt
-- Default comparison and slave handlers for an ordinary sort.
on isGreater(a, b)
(a > b)
end isGreater
on rotate(a, b)
end rotate
on setStep(a)
end setStep
end script
-- Process the input parameters.
set listLen to (count theList)
if (listLen > 1) then
-- Negative and/or transposed range indices.
if (l < 0) then set l to listLen + l + 1
if (r < 0) then set r to listLen + r + 1
if (l > r) then set {l, r} to {r, l}
-- Supplied or default customisation scripts.
if (customiser's class is record) then set {comparer:o's comparer, slave:o's slave} to (customiser & {comparer:o, slave:o})
-- Do the sort.
o's shsrt(l, r)
end if
return -- nothing.
end CustomShellSort
-- Create a new TextEdit document with text derived from the gathered data.
on composeText(filteredData)
tell application "TextEdit"
-- Make a new document, with a minimal text so that we can discover the name of its font.
set newDoc to (make new document with properties {text:" "})
set baseFont to font of newDoc's text
-- This ASSUMES that an equivalent bold font exists and that its name is the same as the plain font with " bold" appended.
set boldFont to baseFont & " bold"
-- Dummy text no longer needed.
set newDoc's text to ""
activate
end tell
if (filteredData is {}) then
-- If no events have been discovered in the date range, print that fact.
tell application "TextEdit" to make new paragraph at end of newDoc's text with data "No events found in this period." with properties {font:boldFont}
else
-- Otherwise print the event details.
set currentCalendarDate to "" -- The calendar date currently being processed. (None yet.)
repeat with i from 1 to (count filteredData)
-- Get the data for an event from the list of filtered data.
set {{date string:thisCalendarDate, hours:thisStartTimeH, minutes:thisStartTimeM}, thisSummary, thisCalendar} to item i of filteredData
-- If the calendar date is different from the one we've been processing up till now, output an empty line and the new date string to TextEdit.
if (thisCalendarDate is not currentCalendarDate) then
tell application "TextEdit"
make new paragraph at end of newDoc's text with data linefeed with properties {font:baseFont}
make new paragraph at end of newDoc's text with data (thisCalendarDate & linefeed) with properties {font:boldFont}
end tell
-- Make the new date the one currently being processed.
set currentCalendarDate to thisCalendarDate
end if
-- Create a 24-hour time string from the hours and minutes of the start date.
tell (10000 + thisStartTimeH * 100 + thisStartTimeM) as text to set thisStartTime to text 2 thru 3 & ":" & text 4 thru 5
-- Output the entry for this event to TextEdit.
tell application "TextEdit" to make new paragraph at end of newDoc's text with data ("\"" & thisCalendar & "\" calendar: " & thisStartTime & " " & thisSummary & linefeed) with properties {font:baseFont}
end repeat
end if
end composeText
on main()
say "Getting data from Calendar. It may take a while."
tell application "Calendar" to set {{theStartDates, theSummaries}, theCalendarNames} to {{start date, summary} of events, name} of calendars
set {dateRangeStart, dateRangeEnd} to getDateRange()
set filteredData to filterToDateRange(theStartDates, theSummaries, theCalendarNames, dateRangeStart, dateRangeEnd)
sortByDate(filteredData)
composeText(filteredData)
end main
main()
Fantastic! Thank you, Sir NG! And a more than fair point about my overstatement of the similarity :lol:
This works perfectly - and, as always, I will learn a lot by having a close look at it. Many thanks.
It’s certainly much faster, but it’s also more accurate. The basic problem with using Calendar.app is that the start and end dates for repeating events are the start and end dates set when the event was created. So if you have a repeating event that falls within your time frame, scripts using Calendar.app won’t catch it (unless it’s the first time).
But you don’t actually have to write any ASObjC – all you need is CalendarLib (for 10.9+) or CalendarLib EC (for 10.11 only). So you’d start your script like this:
use script "CalendarLib EC" -- put this at the top of your scripts; use "CalendarLib" for pre-10.11 compatability
use scripting additions
You’d have a handler like this:
on fetchEventsStarting:dateRangeStart ending:dateRangeEnd
set theStore to fetch store
set theCals to fetch calendars {} cal type list {} event store theStore -- all calendars
set theEvents to fetch events starting date dateRangeStart ending date dateRangeEnd searching cals theCals event store theStore
set theFilteredData to {}
repeat with anEvent in theEvents
set theInfo to (event info for event anEvent)
set end of theFilteredData to {event_start_date of theInfo, event_summary of theInfo, calendar_name of theInfo}
end repeat
return theFilteredData
end fetchEventsStarting:ending:
And your main() would be like this:
on main()
set {dateRangeStart, dateRangeEnd} to getDateRange()
set filteredData to my fetchEventsStarting:dateRangeStart ending:dateRangeEnd
composeText(filteredData)
end main
You can then remove filterToDateRange(), sortByDate() and sortByDate().
You can get the CalendarLibs here:
Thanks Shane. That’s great.
Here’s an actual manifestation of your suggestions:
-- Faster version using ASObjC.
-- Requires Shane Stanley's CalendarLib library. (Use the EC version with El Capitan.)
-- <[url=http://www.macosxautomation.com/applescript/apps/Script_Libs.html]www.macosxautomation.com/applescript/apps/Script_Libs.html[/url]>.
-- Doesn't require Calendar.app to be running.
-- Includes the expression dates of repeating events.
-- Tested with CalendarLIb EC and TextEdit 1.11 (new documents defaulting to RTF) in Mac OS 10.11.2.
use script "CalendarLib EC"
use scripting additions
-- Shane's handler to get the event data using his library.
on fetchEventsStarting:dateRangeStart ending:dateRangeEnd
set theStore to fetch store
set theCals to fetch calendars {} cal type list {} event store theStore -- all calendars
set theEvents to fetch events starting date dateRangeStart ending date dateRangeEnd searching cals theCals event store theStore
set theFilteredData to {}
repeat with anEvent in theEvents
set theInfo to (event info for event anEvent)
set end of theFilteredData to {event_start_date of theInfo, event_summary of theInfo, calendar_name of theInfo}
end repeat
return theFilteredData
end fetchEventsStarting:ending:
-- Ask the user for the range of dates to be covered.
on getDateRange()
set today to (current date)
set d1 to today's short date string
set d2 to short date string of (today + 6 * days)
set dateRange to text returned of (display dialog "Enter the required date range:" default answer d1 & " - " & d2)
set dateRangeStart to date (text from word 1 to word 3 of dateRange)
set dateRangeEnd to date (text from word -3 to word -1 of dateRange)
set dateRangeEnd's time to days - 1 -- Sets the last date's time to 23:59:59, the last second of the range.
return {dateRangeStart, dateRangeEnd}
end getDateRange
-- Create a new TextEdit document with text derived from the gathered data.
on composeText(filteredData)
tell application "TextEdit"
-- Make a new document, with a minimal text so that we can discover the name of its font.
set newDoc to (make new document with properties {text:" "})
set baseFont to font of newDoc's text
-- This ASSUMES that an equivalent bold font exists and that its name is the same as the plain font with " bold" appended.
set boldFont to baseFont & " bold"
-- Dummy text no longer needed.
set newDoc's text to ""
activate
end tell
if (filteredData is {}) then
-- If no events have been discovered in the date range, print that fact.
tell application "TextEdit" to make new paragraph at end of newDoc's text with data "No events found in this period." with properties {font:boldFont}
else
-- Otherwise print the event details.
set currentCalendarDate to "" -- The calendar date currently being processed. (None yet.)
repeat with i from 1 to (count filteredData)
-- Get the data for an event from the list of filtered data.
set {{date string:thisCalendarDate, hours:thisStartTimeH, minutes:thisStartTimeM}, thisSummary, thisCalendar} to item i of filteredData
-- If the calendar date is different from the one we've been processing up till now, output an empty line and the new date string to TextEdit.
if (thisCalendarDate is not currentCalendarDate) then
tell application "TextEdit"
make new paragraph at end of newDoc's text with data linefeed with properties {font:baseFont}
make new paragraph at end of newDoc's text with data (thisCalendarDate & linefeed) with properties {font:boldFont}
end tell
-- Make the new date the one currently being processed.
set currentCalendarDate to thisCalendarDate
end if
-- Create a 24-hour time string from the hours and minutes of the start date.
tell (10000 + thisStartTimeH * 100 + thisStartTimeM) as text to set thisStartTime to text 2 thru 3 & ":" & text 4 thru 5
-- Output the entry for this event to TextEdit.
tell application "TextEdit" to make new paragraph at end of newDoc's text with data ("\"" & thisCalendar & "\" calendar: " & thisStartTime & " " & thisSummary & linefeed) with properties {font:baseFont}
end repeat
end if
end composeText
on main()
set {dateRangeStart, dateRangeEnd} to getDateRange()
say "This script will finish before you can say \"Jack ."
set filteredData to my fetchEventsStarting:dateRangeStart ending:dateRangeEnd
composeText(filteredData)
say ". Robinson\"!"
end main
main()
I’ve noticed this morning that Shane’s adaption, as well as returning occurrences of repeating events, returns single events which are in progress during the specified period. I have a January sale noted in one of my calendars which began last Friday (before the default date range if the script’s run today) and ends this coming Sunday (after it). Shane’s handler includes this event in its result and the script goes on to make an entry for it in TextEdit under last Friday’s date. This gives the user the impression of a bug in the script, but it should probably be regarded as a feature not catered for under the script’s original specification. A really posh implementation would need to catch multi-day events and indicate them in some specified way in the text output.
Nigel,
If you mark your sale as all-day (not technically correct, I know), you can filter it out by inserting this after the fetch events line:
set theEvents to filter events event list theEvents without runs all day
Thanks Shane. But that of course excludes all all-day events, which may not be desired. As I said, the script needs to be able to recognise multi-day events now and to have a policy about what to do with them. Ideally too, it shouldn’t display start times with all-day events.
Here’s an attempt to address these points. Unfortunately, I’ve had to reintroduce sorting handlers ” an insertion sort seeming preferable this time:
-- Faster version using ASObjC.
-- Requires Shane Stanley's CalendarLib library. (Use the EC version with El Capitan.)
-- <[url=http://www.macosxautomation.com/applescript/apps/Script_Libs.html]www.macosxautomation.com/applescript/apps/Script_Libs.html[/url]>.
-- Doesn't require Calendar.app to be running.
-- Includes the expression dates of repeating events.
-- Now has a policy for the handling of all-day and multi-day events.
-- Tested with CalendarLIb EC and TextEdit 1.11 (new documents defaulting to RTF) in Mac OS 10.11.2.
use script "CalendarLib EC"
use scripting additions
-- Shane's handler to get the event data using his library, modified for special handling of all-day and multi-day events.
on fetchEventsStarting:dateRangeStart ending:dateRangeEnd
set theStore to (fetch store)
set theCals to (fetch calendars {} cal type list {} event store theStore) -- all calendars
set theEvents to (fetch events starting date dateRangeStart ending date dateRangeEnd searching cals theCals event store theStore)
set theFilteredData to {}
repeat with anEvent in theEvents
-- Besides the start date, summary, and calendar of each event, get its end date and time zone too.
set {event_start_date:thisStartDate, event_end_date:thisEndDate, event_time_zone:thisTimeZone, event_summary:thisSummary, calendar_name:thisCalendarName} to (event info for event anEvent)
-- If a returned event starts before the date range entered by the user, its start and end dates straddle the beginning of the range. Use the range's start date in this case instead of the returned one.
set eventAlreadyStarted to (thisStartDate comes before dateRangeStart)
if (eventAlreadyStarted) then set thisStartDate to dateRangeStart
-- If the event's an all-day one (no time zone), it ends at 00:00:00 the following day. Change this to 23:59:59 on the event day.
if (thisTimeZone is missing value) then set thisEndDate to thisEndDate - 1
-- If the event ends after the specified date range, substitute the last date in the range for the returned end date.
if (thisEndDate comes after dateRangeEnd) then set thisEndDate to dateRangeEnd
-- Store the finalised start date, 'already started' flag, time zone, summary, and calendar name. If a multi-day event, make separate entries for each date occupied in the range.
repeat until (thisStartDate comes after thisEndDate)
set end of theFilteredData to {thisStartDate, eventAlreadyStarted, thisTimeZone, thisSummary, thisCalendarName}
set thisStartDate to thisStartDate + days
set eventAlreadyStarted to true
end repeat
end repeat
-- Since there may afterwards be additional entries for multi-day events, re-sort the entries by start date.
sortByStartDate(theFilteredData)
return theFilteredData
end fetchEventsStarting:ending:
-- Sort the filtered data by start date.
on sortByStartDate(filteredData)
-- Comparison object for a customisable sort. Compares the first items of two passed lists.
script byFirstListItem
on isGreater(a, b)
return (beginning of a > beginning of b)
end isGreater
end script
CustomInsertionSort(filteredData, 1, -1, {comparer:byFirstListItem})
end sortByStartDate
-- Customisable insertion sort. Algorithm: unknown author. AppleScript implementation: Arthur J. Knapp and Nigel Garvey, 2003. Revised by NG, 2010.
on CustomInsertionSort(theList, l, r, customiser)
script o
property comparer : me
property slave : me
property lst : theList
on isrt(l, r)
set u to item l of o's lst
repeat with j from (l + 1) to r
set v to item j of o's lst
if (comparer's isGreater(u, v)) then
set here to l
set item j of o's lst to u
repeat with i from (j - 2) to l by -1
tell item i of o's lst
if (comparer's isGreater(it, v)) then
set item (i + 1) of o's lst to it
else
set here to i + 1
exit repeat
end if
end tell
end repeat
set item here of o's lst to v
slave's rotate(here, j)
else
set u to v
end if
end repeat
end isrt
on isGreater(a, b)
(a > b)
end isGreater
on rotate(a, b)
end rotate
end script
set listLen to (count theList)
if (listLen > 1) then
if (l < 0) then set l to listLen + l + 1
if (r < 0) then set r to listLen + r + 1
if (l > r) then set {l, r} to {r, l}
if (customiser's class is record) then set {comparer:o's comparer, slave:o's slave} to (customiser & {comparer:o, slave:o})
o's isrt(l, r)
end if
return -- nothing.
end CustomInsertionSort
-- Ask the user for the range of dates to be covered.
on getDateRange()
set today to (current date)
set d1 to today's short date string
set d2 to short date string of (today + 6 * days)
set dateRange to text returned of (display dialog "Enter the required date range:" default answer d1 & " - " & d2)
set dateRangeStart to date (text from word 1 to word 3 of dateRange)
set dateRangeEnd to date (text from word -3 to word -1 of dateRange)
set dateRangeEnd's time to days - 1 -- Sets the last date's time to 23:59:59, the last second of the range.
return {dateRangeStart, dateRangeEnd}
end getDateRange
-- Create a new TextEdit document with text derived from the gathered data.
on composeText(filteredData)
tell application "TextEdit"
-- Make a new document, with a minimal text so that we can discover the name of its font.
set newDoc to (make new document with properties {text:" "})
set baseFont to font of newDoc's text
-- This ASSUMES that an equivalent bold font exists and that its name is the same as the plain font with " bold" appended.
set boldFont to baseFont & " bold"
-- Dummy text no longer needed.
set newDoc's text to ""
activate
end tell
if (filteredData is {}) then
-- If no events have been discovered in the date range, print that fact.
tell application "TextEdit" to make new paragraph at end of newDoc's text with data "No events found in this period." with properties {font:boldFont}
else
-- Otherwise print the event details.
set currentCalendarDate to "" -- The calendar date currently being processed. (None yet.)
repeat with i from 1 to (count filteredData)
-- Get the data for an event from the list of filtered data.
set {{date string:thisCalendarDate, hours:thisStartTimeH, minutes:thisStartTimeM}, eventAlreadyStarted, thisTimeZone, thisSummary, thisCalendar} to item i of filteredData
-- If the calendar date is different from the one we've been processing till now, output an empty line and the new date string to TextEdit.
if (thisCalendarDate is not currentCalendarDate) then
tell application "TextEdit"
make new paragraph at end of newDoc's text with data linefeed with properties {font:baseFont}
make new paragraph at end of newDoc's text with data (thisCalendarDate & linefeed) with properties {font:boldFont}
end tell
-- Make the new date the one currently being processed.
set currentCalendarDate to thisCalendarDate
end if
if (eventAlreadyStarted) then -- Continuation of multi-day event.
set thisStartTime to " (already started)"
else if (thisTimeZone is missing value) then -- All-day event.
set thisStartTime to ""
else -- Timed event.
-- Create a 24-hour time string from the hours and minutes of the start date.
tell (10000 + thisStartTimeH * 100 + thisStartTimeM) as text to set thisStartTime to " at " & text 2 thru 3 & ":" & text 4 thru 5
end if
-- Output the entry for this event to TextEdit.
tell application "TextEdit" to make new paragraph at end of newDoc's text with data ("\"" & thisCalendar & "\" calendar: " & thisSummary & thisStartTime & linefeed) with properties {font:baseFont}
end repeat
end if
end composeText
on main()
set {dateRangeStart, dateRangeEnd} to getDateRange()
say "This script will finish before you can say \"Jack ."
set filteredData to my fetchEventsStarting:dateRangeStart ending:dateRangeEnd
composeText(filteredData)
say ". Robinson\"!"
end main
main()
Unfortunately that’s not so. Suppose on Monday morning I add a lunch event, and set it to repeat five times. If I run the fetch events part of the script for, say, Wednesday, it will include the lunch event, which I think is what is wanted. But the start date of the lunch will still be on Monday (mercifully, so will the end date).
That’s not what I see here. If I create a lunch event on any day and set it to repeat five times (ie. on five consecutive days), your library returns data for each repeat occurring on a day covered by the script, individually dated as per the day covered. Only events (single or repeat instances) whose start and end dates straddle the start of the period are returned as having start dates preceding it. (The comment you quoted is misleadingly phrased. I’ll correct it when I post this.)
Ah, I didn’t realise that using fetch events also mean the returned events had their start/end dates changed appropriately – I was looking at that part in Calendar.app, which was still showing the original date.
Don’t miss that sale
I didn’t.
After stumbling across this gem of a thread, I began trying to tweak Nigel’s first iteration of the script as it was serving my needs rather nicely with the exception that it does not print start/end times.
Really though, I’m not sure which is easier to use (and frankly I’m a bit lost on how to implement it). My only needs are that it saves to a text file a list of events in a given date range, with their start datetimes and end datetimes (no need for pretty formatting or entries listed under each day).