Created
June 26, 2016 19:41
-
-
Save mscuthbert/28b03786c753052588e3609bba192cfc to your computer and use it in GitHub Desktop.
Javascript for Automation (JXA) task to take Cultured Code Things Today list tagged with times and put it in calendar
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* ThingsToCal -- Copyright (c) 2016 Michael Scott Cuthbert | |
* Released under a BSD License | |
* | |
*/ | |
ObjC.import("stdlib"); | |
class ThingsOrganizer { | |
constructor() { | |
this.app = Application.currentApplication(); | |
this.app.includeStandardAdditions = true; | |
this.things = Application('Things'); | |
this.cal = Application('Calendar'); | |
this.todayEvents = undefined; | |
this.initError = false; | |
this.thingsCalIndex = undefined; | |
} | |
postConstructorInit() { | |
this.cal.activate(); | |
this.thingsCalendar = this.cal.calendars['Things']; | |
try { | |
this.thingsCalendar.name(); | |
} catch (e) { | |
this.app.displayAlert("Program cannot run without a Calendar called Things"); | |
this.initError = true; | |
return; | |
} | |
var thingsCalId = this.thingsCalendar.id(); | |
for (var i = 0; i < this.cal.calendars.length; i++) { | |
if (this.cal.calendars[i].id() == thingsCalId) { | |
this.thingsCalIndex = i; | |
break; | |
} | |
} | |
//console.log(this.thingsCalendar.name()); | |
this.focusList = this.getList('FocusToday'); | |
} | |
getList(listName) { | |
for (var i = 0; i < this.things.lists.length; i++) { | |
var thisList = this.things.lists[i]; | |
var thisListId = thisList.id(); | |
if (thisListId == listName) { | |
return thisList; | |
} | |
} | |
return null; | |
} | |
todayToDos() { | |
var allToDos = []; | |
var focusList = this.focusList; | |
//console.log(focusList.toDos.length); | |
for (var i = 0; i < focusList.toDos.length; i++) { | |
var thisTodo = focusList.toDos[i]; | |
if (thisTodo.status() != "open") { | |
continue; | |
} | |
allToDos.push(thisTodo); | |
} | |
return allToDos; | |
} | |
setCalendarFromToday() { | |
var todayToDos = this.todayToDos(); | |
var blockedTimes = this.blockedTimes(); | |
var currentDayOfWeek = new Date().getDay(); | |
// start | |
var startDate = new Date(); | |
startDate.setSeconds(0); | |
startDate.setMilliseconds(0); | |
startDate.setMinutes( Math.round(startDate.getMinutes() / 5) * 5 ); | |
todoLoop: | |
for (var thisTodo of todayToDos) { | |
var timeRequired = this.timeFromTagList(thisTodo.tags); | |
var isBlocked = true; | |
while (isBlocked) { | |
var endDate = new Date(startDate.getTime()); | |
endDate.setMinutes(endDate.getMinutes() + timeRequired); | |
var isBlocked = false; | |
for (var [blockStart, blockEnd] of blockedTimes) { | |
if ( ((blockStart >= startDate) && (blockStart < endDate)) || | |
((blockEnd > startDate) && (blockEnd <= endDate)) || | |
((blockStart <= startDate) && (blockEnd >= endDate)) | |
) { | |
isBlocked = true; | |
} | |
} | |
if (isBlocked) { | |
startDate.setMinutes(startDate.getMinutes() + 15); | |
} | |
if (startDate.getDay() != currentDayOfWeek) { | |
break todoLoop; | |
} | |
} | |
this.addCalEvent(thisTodo.name(), startDate, endDate); | |
var startDate = endDate; | |
} | |
this.cal.reloadCalendars(); | |
} | |
timeFromTagList(tags) { | |
var totTime = 0; | |
for (var i = 0; i < tags.length; i++) { | |
var thisTag = tags[i]; | |
var tagParent = thisTag.parentTag(); | |
if (tagParent !== null && tagParent.name().toLowerCase() == 'time') { | |
totTime += this.timeFromTagName(thisTag.name()); | |
} | |
} | |
// console.log(totTime); | |
if (totTime == 0) { | |
return 60; // default length | |
} else { | |
return totTime | |
} | |
} | |
timeFromTagName(tagName) { | |
if (tagName.match(/mins?/i)) { | |
var tagStrip = tagName.replace(/mins?/i, ''); | |
return parseInt(tagStrip); | |
} | |
if (tagName.match(/ho?u?rs?/i)) { | |
var tagStrip = tagName.replace(/hrs?/i, ''); | |
return 60 * parseInt(tagStrip); | |
} | |
} | |
addCalEvent(todoSummary, startDate, endDate) { | |
//var oldEvent = this.thingsCalendar.events[20]; | |
//console.log(oldEvent.summary()); | |
var newEvent = this.cal.Event( | |
{ | |
description: todoSummary, | |
summary: todoSummary, | |
startDate: startDate, | |
endDate: endDate | |
} | |
); | |
//console.log(newEvent); | |
this.thingsCalendar.events.push(newEvent); | |
} | |
/** | |
* blockedTimes() is an array of two-element arrays (startTime, endTime) | |
* for each one an event must be before both start and end or after both start and end | |
* to be free. | |
*/ | |
blockedTimes() { | |
var allEvents = this.getTodayEvents(); | |
var blockList = []; | |
for (var [ev, calNum] of allEvents) { | |
if (calNum === this.thingsCalIndex) { | |
continue; // do not schedule around deleted events... | |
} | |
var stend = [ev.startDate(), ev.endDate()]; | |
blockList.push(stend); | |
} | |
return blockList; | |
} | |
// Clear existing things calendar events if their startDate is past now. | |
clearTodayThingsEvents() { | |
if (this.thingsCalIndex === undefined) { | |
return; // safety check | |
} | |
var nowDate = new Date(); | |
var allEvents = this.getTodayEvents(); | |
for (var [ev, calNum] of allEvents) { | |
if (calNum !== this.thingsCalIndex) { | |
continue; | |
} | |
if (ev.startDate() > nowDate) { | |
console.log("Deleting... " + ev.summary()); | |
this.cal.delete(ev); | |
} | |
} | |
} | |
getTodayEvents() { | |
if (this.todayEvents !== undefined) { | |
return this.todayEvents; | |
} | |
var nowDate = new Date(); | |
var allEvents = this.eventsByDay(nowDate); | |
this.todayEvents = allEvents; | |
return allEvents; | |
} | |
eventsByDay(dateObj) { | |
var startOfDay = new Date(dateObj.getTime()); | |
startOfDay.setHours(0); | |
startOfDay.setMinutes(0); | |
startOfDay.setSeconds(0); | |
startOfDay.setMilliseconds(0); | |
var endOfDay = new Date(dateObj.getTime()); | |
endOfDay.setHours(23); | |
endOfDay.setMinutes(59); | |
endOfDay.setSeconds(59); | |
endOfDay.setMilliseconds(999); | |
// console.log(startOfDay); | |
// console.log(endOfDay); | |
// console.log(new Date()); | |
var todaySearch = { | |
_and: [ | |
{ startDate: { _greaterThan: startOfDay }}, | |
{ startDate: { _lessThan: endOfDay }} | |
] | |
}; | |
var foundEvents = this.cal.calendars.events.whose(todaySearch); | |
var convertedEvents = foundEvents(); | |
var allEventSelectors = [] | |
convertedEvents.forEach( function(cal, calIndex) { | |
for (var ev of cal) { | |
allEventSelectors.push([ev, calIndex]); | |
// console.log(ev.summary); | |
} | |
}); | |
// console.log(foundEvents.startDate()); | |
// console.log(foundEvents.endDate()); | |
// console.log(new Date()); | |
return allEventSelectors; | |
} | |
} | |
function run(argv) { | |
// console.log(JSON.stringify(argv)); | |
var thingsOrganizer = new ThingsOrganizer(); | |
thingsOrganizer.app.displayNotification("Running script -- this may take a minute or more", | |
{ | |
withTitle: "ThingsToCal" | |
}); | |
thingsOrganizer.postConstructorInit(); | |
if (this.initError) { | |
return; | |
} | |
thingsOrganizer.clearTodayThingsEvents(); | |
thingsOrganizer.setCalendarFromToday(); | |
thingsOrganizer.app.displayNotification("Things to Calendar conversion complete", | |
{ | |
withTitle: "ThingsToCal" | |
}); | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment