Skip to content

Instantly share code, notes, and snippets.

@doug4j
Last active February 3, 2019 10:50
Show Gist options
  • Save doug4j/6e98e6b2435bf2d45ae061100addca2b to your computer and use it in GitHub Desktop.
Save doug4j/6e98e6b2435bf2d45ae061100addca2b to your computer and use it in GitHub Desktop.
// jshint esversion:6
(function() {
var start = new Date().getTime();
var app = Application('OmniFocus'); //This assumes that OmniFocus is installed on MacOS
app.includeStandardAdditions = true;
var doc = app.defaultDocument;
var content = doc.documentWindows.at(0).content;
var selectedTree = content.selectedTrees();
var selectedLen = selectedTree.length;
var totalTasks = 0;
var elapsedMsg = "";
var estimatedDuration = "";
var tasks = [];
for (var i=0;i < selectedLen; i++) {
try{
var task = selectedTree[i];
if (isLeafTask(task)) {
tasks.push(task);
totalTasks = totalTasks + 1;
}
} catch(e) {
if (e.message === "User canceled.") {
return; //get out of the loop (end the program)
} else {
app.displayDialog("[Exception] Content Selected index [" + i + "] is not a task: " + e);
}
}
}
if (tasks.length > 1) {
elapsedMsg = "Elapsed time thusfar: " + elapsedTimeMsg(start);
estimatedDuration = estimateElapsedTimeMsg(tasks.length);
app.displayDialog("It is estimated that verification will take " + estimatedDuration + ". Press OK to continue or cancel to stop. " + elapsedMsg);
}
var validationErrors = [];
tasks.forEach(task => {
var expected = verifyTaskStructure(task);
if (expected.isError) {
validationErrors.push(expected);
}
});
function isLeafTask(item) {
try {
//app.displayDialog(item.value().name())
if(item.value().inInbox()) {
return true;
}
if (item.value().containingProject().id() === item.value().id()) {
//This is a project, tag, or some other top-level container
return false;
}
//app.displayDialog("could be a leaf task:" + item.value().name())
//app.displayDialog("numberOfTasks:" + item.value().numberOfTasks())
//item.flattenTasks().length === 0
return item.value().numberOfTasks() == 0;
} catch (e) {
return false;
}
}
function newValidatorMandaotryEstimatedMinutes() {
var answer = {
isError: true,
validatorType: "MandatoryEstimatedMinutes",
toStringDetails: function() {
return "\n - validatorType [" + this.validatorType + "]";
},
};
return answer;
}
function newValidatorMandatoryOneTagOnly(rootTag) {
var answer = {
usageCount: 0,
isError: true,
usedTags: [],
rootTag: rootTag,
validatorType: "MandatoryOneTagOnly",
toStringDetails: function() {
return "\n - validatorType [" + this.validatorType +
"]\n usageCount [" + this.usageCount +
"]\n rootTag [" + this.rootTag + "]";
},
};
return answer;
}
function newValidatorAtLeastOneTag(rootTag) {
var answer = {
usageCount: 0,
isError: true,
usedTags: [],
rootTag: rootTag,
validatorType: "AtLeastOneTag",
toStringDetails: function() {
return "\n - validatorType [" + this.validatorType +
"]\n usageCount [" + this.usageCount +
"]\n rootTag [" + this.rootTag + "]";
},
};
return answer;
}
function isNotTaskSystemInternal(item) {
var tags = item.value().tags();
for(var i=0;i < tags.length;i++){
var tag = tags[i];
if (tag.name() === "Sys Internal") {
return false;
}
}
return true;
}
function getWhen(item) {
var tags = item.value().tags();
for(var i=0;i < tags.length;i++){
var tag = tags[i];
var rootTag = getRootTag(tag);
if (rootTag.name() === "Whens") {
return tag;
}
}
return null;
}
function verifyTaskMandaotryEstimatedMinutes(task, compositValidator) {
var when = getWhen(task);
if (deepDetailWhenEligible(when)) {
var estimatedMinutes = task.value().estimatedMinutes();
if (estimatedMinutes === null) {
compositValidator.isError = true;
compositValidator.errorValidators.push(newValidatorMandaotryEstimatedMinutes());
} else if (estimatedMinutes <= 0) {
compositValidator.isError = true;
compositValidator.errorValidators.push(newValidatorMandaotryEstimatedMinutes());
}
}
}
function deepDetailWhenEligible(when) {
if (when === null) {
return false;
}
if ( (when.name() === "Now") || (when.name() === "Next") || (when.name() === "Weekend") ||
(when.name() === "Daily Start") || (when.name() === "Daily End") || (when.name() === "Weekly") ) {
return true;
}
return false;
}
function verifyTaskMandatoryOneTagOnlyItems(task, compositValidator) {
var tags = task.value().tags();
var expectedMandatoryOneTagOnlyItems = {
"Whens": newValidatorMandatoryOneTagOnly("Whens"),
"Organizations": newValidatorMandatoryOneTagOnly("Organizations"),
};
var when = getWhen(task);
if (deepDetailWhenEligible(when)) {
expectedMandatoryOneTagOnlyItems.Energies = newValidatorMandatoryOneTagOnly("Energies");
expectedMandatoryOneTagOnlyItems.Sources = newValidatorMandatoryOneTagOnly("Sources");
expectedMandatoryOneTagOnlyItems["Stale Dates (End of)"] = newValidatorAtLeastOneTag("Stale Dates (End of)");
}
for(var i=0;i < tags.length;i++){
var tag = tags[i];
var rootTag = getRootTag(tag);
for(let key of Object.keys(expectedMandatoryOneTagOnlyItems)) {
if (rootTag.name() == key) {
var thisTagStatistics = expectedMandatoryOneTagOnlyItems[key];
thisTagStatistics.usageCount = thisTagStatistics.usageCount + 1;
if (thisTagStatistics.usageCount > 1) {
thisTagStatistics.isError = true;
}
if (thisTagStatistics.usageCount == 1) {
thisTagStatistics.isError = false;
}
}
}
}
for(let key of Object.keys(expectedMandatoryOneTagOnlyItems)) {
var expectedMandatoryOneTagOnly = expectedMandatoryOneTagOnlyItems[key];
if(expectedMandatoryOneTagOnly.isError) {
compositValidator.isError = true;
compositValidator.errorValidators.push(expectedMandatoryOneTagOnly);
}
}
}
function verifyTaskAtLeastOneTagItems(task, compositValidator) {
var tags = task.value().tags();
var expectedAtLeastOneTagItems = {};
var when = getWhen(task);
if (deepDetailWhenEligible(when)) {
expectedAtLeastOneTagItems.Sources = newValidatorMandatoryOneTagOnly("Sources");
expectedAtLeastOneTagItems.Deliverables = newValidatorAtLeastOneTag("Deliverables");
expectedAtLeastOneTagItems["Related Parties"] = newValidatorAtLeastOneTag("Related Parties");
}
tags.forEach(tag => {
var rootTag = getRootTag(tag);
for(let key of Object.keys(expectedAtLeastOneTagItems)) {
if (rootTag.name() == key) {
var thisTagStatistics = expectedAtLeastOneTagItems[key];
thisTagStatistics.usageCount = thisTagStatistics.usageCount + 1;
if (thisTagStatistics.usageCount >= 1) {
thisTagStatistics.isError = false;
}
}
}
});
for (let key of Object.keys(expectedAtLeastOneTagItems)) {
let expectedOneTagOnly = expectedAtLeastOneTagItems[key];
if (expectedOneTagOnly.isError) {
compositValidator.isError = true;
compositValidator.errorValidators.push(expectedOneTagOnly);
}
}
}
function verifyTaskStructure(task) {
var compositValidator = {
isError: false,
errorValidators: [],
name: task.name(),
id: task.id(),
objectToValidate: "Task",
};
if (isNotTaskSystemInternal(task)) {
verifyTaskMandatoryOneTagOnlyItems(task, compositValidator);
verifyTaskAtLeastOneTagItems(task, compositValidator);
verifyTaskMandaotryEstimatedMinutes(task, compositValidator);
}
return compositValidator;
}
function getRootTag(tag){
var parentTag = tag.container();
if (parentTag.name().trim() === "OmniFocus") {
return tag;
}
return getRootTag(parentTag);
}
function estimateElapsedTimeMsg(count) {
var elapsedSecs = count * 1.6;
var elapsedMsg = "";
if (elapsedSecs > 60) {
elapsedMsg = Math.floor(elapsedSecs / 60) + " min(s) " + (elapsedSecs % 60).toFixed(2) + " sec(s)";
} else {
elapsedMsg = elapsedSecs.toFixed(2) + " sec(s)";
}
return elapsedMsg;
}
function elapsedTimeMsg(startTime) {
var elapsedSecs = ((new Date().getTime() - start) / 1000);
var elapsedMsg = "";
if (elapsedSecs > 60) {
elapsedMsg = Math.floor(elapsedSecs / 60) + " min(s) " + (elapsedSecs % 60).toFixed(2) + " sec(s)";
} else {
elapsedMsg = elapsedSecs.toFixed(2) + " sec(s)";
}
return elapsedMsg;
}
elapsedMsg = "Total Elapsed time: " + elapsedTimeMsg(start);
var result = "";
if (validationErrors.length == 0) {
result = "OK";
app.displayDialog("Overall Validation as [" + result + "] for " +
totalTasks + " task(s) of " + (selectedLen - totalTasks) +
" non-leaf item(s).\n" + "Estimated duration: " + estimatedDuration + "\n" + elapsedMsg);
} else {
result = "Error";
var maxDisplayError = 3;
var currentErrorCount = 0;
var errSumary = "";
for (let validationError of validationErrors) {
currentErrorCount = currentErrorCount + 1;
if (currentErrorCount > maxDisplayError) {
break;
}
if (errSumary != "") {
errSumary = errSumary + "\n";
}
errSumary = errSumary + " #" + currentErrorCount + " Validation Error [" + validationError.name + "] of objectToValidate [" + validationError.objectToValidate + "]";
for (let validator of validationError.errorValidators) {
errSumary = errSumary + validator.toStringDetails();
}
}
if (validationErrors.length <= maxDisplayError) {
app.displayDialog("Overall Validation as [" + result + "] " + validationErrors.length + " all errors for " + totalTasks+ " task(s) of " + (selectedLen - totalTasks) +" non-leaf item(s)\n" + errSumary);
} else {
app.displayDialog("Overall Validation as [" + result + "] showing " + maxDisplayError + " of " + validationErrors.length + " errors for " + totalTasks + " task(s) of " + (selectedLen - totalTasks) + " non-leaf item(s)\n" + errSumary);
}
}
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment