Skip to content

Instantly share code, notes, and snippets.

@mscuthbert
Created June 26, 2016 19:41
  • Star 6 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
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
/**
* 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