Question about scripting for a newbie


#1

I’m new to taskpaper, but not new to using text for todo lists (I just had my own inferior and inadequate system prior). I have a priority system where @priority(effort/result) where effort and result are assigned values 1-5. So I want to tag my tasks with an @priority tag having a continuous valued variable between (0,5]. I’d then like to see my tasks prioritized in that order.

My understanding is that TaskPaper can’t reorder things since searching is just a query. So I’d like to write a script that reorders tasks in each project according to priority. The question I have is: is this a job for writing a javascript and plugging it in as a script for TaskPaper, or would it be easier to just process the list using a python script called by some short cut using Alfred?

FWIW, I’m roughly equally familiar with python and Javascript.

Any help/advice would be greatly appreciated. Thanks.


#2

I’d say this depends on if/what you’re using on iOS. I’ve been writing most of my scripts related to TP in Python these days so that I can use them (with a few modifications) in Editorial on iOS and on the Mac (though not in the TaskPaper app itself – but that’s the beauty of a plain text format).


#3

I would agree that if you want to run the same script on iOS then yes go with Python… with that said if you use the JavaScript API it “should” be much easier to implement your sorting in a robust manner I think. In the TaskPaper JavaScript API you are manipulating a tree of objects, you wouldn’t have to worry about any parsing for example.


#4

Haha! This is exactly the conundrum I was thinking it would be. I do use Editorial on iOS so Python is important, but I wanted to leverage the tree of objects from the JavaScript API.

Well if nothing else you’ve confirmed what I was thinking might be the case.

@derickfay do you have any Python scripts where you do heavy parsing that you’d be willing to share…just so I don’t have to start from scratch?

Thanks everyone.


#5

If you can do the scripting in JavaScript I’ve released TaskPaper’s parser/runtime model as open source. It’s new, not super documented, but I “think” should work for your purpose. Big dependency though.

edit At that URL it’s a NPM style package, but you can build it into a minimized script that should work in most any JavaScript context (i.e. doesn’t have any DOM dependencies I think)


#6

Thanks Jesse. I’ve started trying to figure out the API at
http://guide.taskpaper.com/Item.html
but I’m not expert enough at JavaScript to parse it quite yet.

Does anyone have any suggestions for how to get started writing JavaScript for TaskPaper (I’ve searched the forums extensively for examples and tutorials)? I mean I’m using ScriptEditor, but it would be nice to test this stuff first. Perhaps when I figure it out I should write a tutorial.


#7

There are some here: http://dfay.fastmail.fm/et/

But none have particularly complicated parsing - just some basic regex stuff. If you look in the forums for Editorial there’s a lot more sophisticated stuff, almost all in python:

http://www.editorial-workflows.com/workflows/search?q=tp
http://www.editorial-workflows.com/workflows/search?q=taskpaper


#8

Also - this is a bit outdated but is still an amazingly comprehensive resource:

http://www.macdrifter.com/2014/02/the-taskpaper-rd-notebook.html


#9

If you want to lear how to script with TaskPaper, just check the list of examples. Most of them are easy to understand. If you document your learning process, it might be something that you can share as a tutorial :).

Honestly, give them a look and start from the simple to the complicated.


#10

Yeah, I’ve been looking over the examples for a number of hours now. There seems to be a few pieces missing for me. Just as an FYI, I’ve read through the scripting guide, API, and numerous examples. Something just isn’t clicking here. Maybe you all can answer a few noob questions:

Consider the example code:

function TaskPaperContextScript(editor, options) {
  return editor.selection.selectedItems.map(
    function (item) {
      return item.bodyString
    }
  )
}

Application("TaskPaper").documents[0].evaluate({
  script: TaskPaperContextScript.toString()
})

Here are some questions:

  1. what is “editor”? What type of object and what elements, methods, etc. does it possess?
  2. where did “.map(…” come from?
  3. What is the basic algorithm for trying to modify tags, tasks, etc.? Do I need to read everything in, parse it, modify it, and then write it back? Or is there a simpler way?

Overall, I think I’m just not understanding how to read the API as it’s documented.

As an example of what I want to do, I’d like ultimately to have an @priority() with a computed value. That value could either come from evaluating two other tags, or it could come from a user dialog where the user specifies the numbers to be computed. I’d then like to sort tasks based on the priority.

I sure appreciate any help that can be offered. Thanks.


#11

[quote=“jmb275, post:10, topic:2277”]

  1. what is “editor”? What type of object and what elements, methods, etc. does it possess?[/quote]

I think you missed the all important Scripting API Appendix:

http://guide.taskpaper.com/scripting_api.html

It’s a built in JavaScript (later version) feature:

Definitely simpler way. I think you should get much farther once you look over the API link above. You might start with something like:

editor.outline.root.descendants // array of all user editable items
editor.outline.evaluateItemPath("//@priority") // array of all items that have a priority tag

See Item API for basics of adding and removing items. Getting item attributes… also note that when you get “@priority” attribute you’ll need to use the name data-priority when getting/setting at the API level. All user visible tags names are proceeded with data- as a namespacing mechanism.

That should get you a bit further, but don’t hesitate to ask more questions.


#12

@jessegrosjean thanks for the help.

I think you missed the all important Scripting API Appendix:

I read it, but I’m thinking now I just really don’t understand javascript or something. As an example, what is the difference in the API documentation between the notation used in

.getOutlineEditors() 

and

::outline

one has a dot (which I understand to be a method call), and the other two colons (indicating ???). So I guess I’m not quite sure how to read the API.

Finally, one last question. Do all the items have a “forEach” function? Is this just JavaScript common knowledge I’m unfamiliar with?

Thanks again, sorry to be so ignorant of modern scripting (never done it before, always been C and C++ for me).


#13

Thanks a lot for these links @derickfay!


#14

Do all the items have a “forEach” function? Is this just JavaScript common knowledge I’m unfamiliar with?

.forEach is just JavaScript

Two first ports of call might be

  1. The classic “JavaScript – the Good Parts” http://shop.oreilly.com/product/9780596517748.do
  2. The Mozilla documentation of JavaScript Array methods like .forEach

#15

Thanks @complexpoint


#16

Okay, I have made some progress. I now have a script that inserts @priority tags according to my desires paradigm.

I’ve now started the script to sort them. I’d like them sorted within each project/indentation. For my first attempt, I’m not worrying about a file with multiple projects or complex indentation. Just wanted to take some tasks and sort them. Here’s what I’ve got:

function TaskPaperContextScript(editor, options) {
//debugger;
	var outline = editor.outline;
	// group all the changes together into a single change and make it a single "undo" action
	outline.groupUndoAndChanges(function () {
		// all items having a "priority" tag
		var priority_tasks = outline.evaluateItemPath('//@priority');
		// now sort them
		var sorted_tasks = priority_tasks.sort(function (a, b) {
			var a_priority = a.getAttribute('data-priority', Number);
			var b_priority = b.getAttribute('data-priority', Number);
			return a_priority - b_priority;
		});		
		var projects_array = outline.evaluateItemPath('/@type=project');
		outline.insertItemsBefore(sorted_tasks, projects_array[0].firstChild);
	});
}

Application("TaskPaper").documents[0].evaluate({
  script: TaskPaperContextScript.toString()
});

However, it doesn’t work. It does sort all the tasks by priority, but it doesn’t insert them in the right place. It just sort of puts them at the end of the file.
Here’s my sample file before processing:

Foobar:
- my cool task
- move on to scripting FoldingText  @priority(0.7)
- sort tasks in TaskPaper   @priority(0.5)
- cool task 2
- cool task 3
- the best task

And here it is after running the script

Foobar:
- my cool task
- cool task 2
- cool task 3
- the best task




- sort tasks in TaskPaper   @priority(0.5)
- move on to scripting FoldingText  @priority(0.7)

I think I’m starting to get the hang of the API, but I’m still not sure how to work with the outlines and items in an intelligent way. Any help would be greatly appreciated.


#17

UPDATE:
I’ve made some significant progress. I can find the projects, drill down to find the @priority tasks, and sort them. I can then insert them into the proper order…sort of. I guess sorting tasks within a project is a harder task than it seems. Everything works fine as long as the first task in a project does not have an @priority tag. This makes sense because I can’t insert an item prior to itself. This suggests I need to work directly with the entire set of children tasks at once rather than just pulling out the @priority tasks and sorting them.

Unless someone knows a better way?

Here’s the main snippet of my code:

...
		var projects_array = outline.evaluateItemPath('/@type=project');
		// for each project get the tasks with priority
		projects_array.forEach(function(each) {
			var children = each.children; //outline.evaluateItemPath('/'+each.bodyString+'/@type=task');
			var proj_name = each.bodyString;
			var priority_children = outline.evaluateItemPath('/project='+proj_name+'/@priority');

			var sorted_tasks = priority_children.sort(function (a, b) {
				var a_priority = a.getAttribute('data-priority', Number);
				var b_priority = b.getAttribute('data-priority', Number);
				return a_priority - b_priority;
			});		
//		debugger;			
//			outline.insertItemsBefore(sorted_tasks, each.firstChild);

#18

UPDATE 2.0:
I apologize for all the late night posts as I sort this out. Necessity is the mother of invention, and I had to get this done today. So I have a complete working sorting script that accomplishes my initial goals. I’m going to post my complete solution in another thread since the direction I took this thread was not true to my original post and question.