Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
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