Here’s a very rough draft of a core script which simply toggles tracking in Tyme on and off for the task selected in TaskPaper 3
(If it doesn’t find an existing match for the selected task (and/or its parent project) in Tyme, a new project and/or task is created in Tyme using the TaskPaper 3 bodyStrings for each)
// Draft 0.0.5
// Changes 0.0.5 updated for API change: .bodyDisplayString -> .bodyContentString
// Changes: 0.0.4 updated for TaskPaper 3.0 build 167 (editor.selection.startitem)
// Changes: 0.0.3
// - Text of name used for tasks and projects in Tyme now excludes any TP tags
// - Simple TaskPaper tags (no parentheses) on task or their ancestors up to
// (but not including) the enclosing project are pass through as Tyme 'taskTags'
// - The **values** of @account( ...) or @ref( ... ) task on the project line or its path
// are passed through as Tyme 'projectTags'
// (To use other tag names for the two levels of project tags, change the values of the
// strAccountTag and strRefTag arguments in the last line of the script.
// 1. Install http://tyme-app.com/mac
// 2. In TaskPaper 3, select a task in some project,
// and run this (El Capitan JavaScript for Automation) draft script to test
// the toggling of time-tracking for the selected task.
// If the selected task is being tracked, tracking stops.
// If the selected task is NOT being tracked:
// a) tracking of any other task ends, and
// b) tracking of this task begins.
// (Each toggle event adds/completes a new timeSession record for the task in Tyme.app)
// ADJUST THE VALUES OF THESE TWO ARGUMENTS IN THE LAST LINE OF THE SCRIPT
(function (strAccountTag, strRefTag) {
'use strict';
strAccountTag = strAccountTag || 'account';
strRefTag = strRefTag || 'ref';
// ACCOUNT TAG
// e.g. if something like @ref(BBC) @ref(FT) @ref(NYT) is on any ancestor
// of the timed task in TP, the value of the @ref tag (eg. 'BBC' 'FT' etc)
// will be passed through to Tyme as a tag which can be used to aggregate billing and reporting
// FUNCTION EVALUATED IN THE TASKPAPER (rather than JSA) CONTEXT:
// () -> MayBe {projectName:String, taskName:String, projectTags:[String], taskTags:[String]}
function fnTPSeln(editor, options) {
// value of any instanceof this tag on this node or its path ?
function ancestralTagValue(node, strTag) {
var lstHits = node.evaluateItemPath(
'(ancestor-or-self::@' + strTag + ')[-1]'
);
return lstHits.length > 0 ? (
[lstHits[0].attributes['data-' + strTag]]
) : [];
}
// all map results flattened down to a single list
// [a] -> (a -> b) -> [b]
function concatMap(xs, f) {
return [].concat.apply([], xs.map(f));
}
// all simple no-parentheses tags on this task and/or path up to
// (but excluding) the project of a task
// TP3Node -> [String]
function simpleTaskTags(nodeTask) {
return concatMap(
nodeTask.evaluateItemPath(
'ancestor-or-self::* except ancestor::@type=project/ancestor-or-self::*'
),
function (item) {
var dctAttribs = item.attributes;
return Object.keys(dctAttribs).filter(function (k) {
return dctAttribs[k] === '';
}).map(function (k) {
return k.slice(5);
});
}
);
}
// Up to two project tags e.g. for Account and Refce
// TP3Node -> String -> String -> [String]
function projectTags(prj, strAccountTag, strRefTag) {
return (strAccountTag ? ancestralTagValue(prj, strAccountTag) : []).concat(
strRefTag ? ancestralTagValue(prj, strRefTag) : []
);
}
// MAIN (for TaskPaper context, rather than JavaScript for Automation)
// () -> MayBe {projectName:String, taskName:String, projectTags:[String], taskTags:[String]}
var rng = editor.selection,
sln = rng ? rng.startItem : null,
// project containing selection ?
prjs = sln ? sln.evaluateItemPath(
'(ancestor::@type=project)[-1]'
) : [],
prj = prjs.length > 0 ? prjs[0] : undefined;
// project and task strings ?
return sln && prj ? {
project: prj.bodyContentString,
task: sln.bodyContentString,
projectTags: projectTags(prj, options.accountTag, options.refTag),
taskTags: simpleTaskTags(sln)
} : undefined;
}
// FUNCTIONS FOR OSASCRIPT JS CONTEXT
// (AUTOMATING TYME.APP with TASKPAPER DATA)
// Found or created Project, Task, or ProjectTag or TaskTag
// Application -> Object -> String -> String -> Object
function foundOrCreatedByName(app, oParent, strClass, strName) {
var elements = oParent[
strClass.charAt(0).toLowerCase() + strClass.slice(1) + 's'
],
iElem = elements.name().indexOf(strName);
// Found or, if not found, newly created
return iElem !== -1 ? elements.at(iElem) : (function () {
var elem = app[strClass]({
name: strName
});
elements.push(elem);
return elem;
})();
}
// Found or created (and tagged) Project or Task
// Application -> Object -> String -> String -> [String] -> Object
function taggedFoundOrCreatedByName(app, oParent, strClass, strName, lstTags) {
var oElem = foundOrCreatedByName(app, oParent, strClass, strName),
strTagClass = strClass + 'tag';
lstTags.forEach(function (strTag) {
// Find or create a project or task tag
foundOrCreatedByName(
app, oElem,
strTagClass,
strTag
);
});
return oElem;
}
// MAIN (for osascript JS context)
// From TaskPaper: {projectName:String, taskName:String, projectTags:[String], taskTags:[String]}
var ds = Application('com.hogbaysoftware.TaskPaper3').documents,
dctProjTask = ds.length ? ds[0].evaluate({
script: fnTPSeln.toString(),
withOptions: {
accountTag: strAccountTag,
refTag: strRefTag
}
}) : undefined;
// Assuming that a task in a project outline was selected ...
if (dctProjTask && dctProjTask.project && dctProjTask.task) {
var tyme = Application('Tyme'),
oTask = taggedFoundOrCreatedByName(
tyme,
taggedFoundOrCreatedByName(
tyme, tyme,
'Project', dctProjTask.project,
dctProjTask.projectTags
),
'Task', dctProjTask.task,
dctProjTask.taskTags
);
tyme.activate();
// TOGGLE TRACKING OF THE TASK IN TYME.APP
var oTracked = tyme.trackedtask,
strID = oTracked.id();
if (strID) {
tyme.delete(oTracked);
if (strID !== oTask.id()) tyme.trackedtask = oTask;
} else tyme.trackedtask = oTask;
} else {
var a = Application.currentApplication(),
sa = (a.includeStandardAdditions = true, a);
sa.activate;
sa.displayDialog(
[
'http://www.taskpaper.com/',
'http://tyme-app.com/mac/',
'',
'Select a TaskPaper task within a project outline ...'
].join('\n'), {
withTitle: 'Session timing from TaskPaper 3 with Tyme',
buttons: ['OK'],
defaultButton: 'OK',
givingUpAfter: 20
});
}
return dctProjTask;
})('account', 'ref'); // enter alterative Referencing tag. e.g. 'bill' for @bill(BBC)