How to adapt script to include project label in list of actions sorted by due date?


#1

Dear all,
I use a script that sorts items with tags by due date (thanks to the generous help of user complexpoint). The contents of the script can be found below.

My question is as follows. I have some lines in an FT-file like this (note that the preview of this forum doesn’t show the “”##" before each project line):

project name 1.todo

  • next action A @duedate(2016-02-11)

project name 2.todo

  • next action B @duedate(2016-02-12)

Applying the script creates a file containing this:
"### Due
2016-02-11 next action A
2016-02-12 next action B"

I would like to get something like this:
"### Due
2016-02-11 project name 1: next action A
2016-02-12 project name 2: next action B"

Can this be done? If yes, does anyone have a suggestion how to adapt the script (see below)?

Thank you very much,
Paul

CURRENTLY USED SCRIPT TO SORT ITEMS BY DUE DATE:

define(function(require, exports, module) {
‘use strict’;

    var Extensions = require('ft/core/extensions'),
            DateUtils = require('ft/util/date');

    Extensions.add('com.foldingtext.editor.commands', {
            name: 'date',
            description: 'Insert the current date',
            performCommand: function (editor) {
                    editor.replaceSelection(new Date().format('mediumDate'));
            }
    });

    Extensions.add('com.foldingtext.editor.commands', {
            name: 'time',
            description: 'Insert the current time',
            performCommand: function (editor) {
                    editor.replaceSelection(new Date().format('shortTime'));
            }
    });

});


#2

Is this all of the script? The above looks like the contents of a spec.js file in a plugin. If so, is there a main.js file in the same plugin folder?


#3

Thank you for your response.

I am not sure, I may have been mistaken about the script (it was a year ago that I managed to get this working, but I have forgotten the details). My apologies for the confusion.

I had found the script that I menioned in a file main.js – but I am not sure about it now. In any case, to answer your question, I think that all relevant information can be found here: http://support.foldingtext.com/t/sorting-items-with-tags-by-due-date/665. This contains my original question and the solutions I received from the forum.

Does this answer your question? Again, my apologies (I have no knowledge of programming and running scripts).

Thank you,
Paul


#4

I looked up in more detail what I did last year to make the script work. Through the script editor I located the script in the Library/Scripts folder. This is the script (via copy/paste from script opened in text editor):

JsOsaDAS1.001.00bplist00—Vscript_úfunction run() {
var blnDebug = 0;

// create new doc from sorted list ? 
var blnMakeDoc = 1; //(0 if not)


function fnFT(editor, opt) {
        
    //     EVALUATE THE NODE PATH //@due TO HARVEST MATCHES
    var oTree = editor.tree(),
        lstNodes = oTree.evaluateNodePath('//@due'),
        lstDue=[], strList;
    
    // BUILD A LIST OF DATES FOLLOWED BY TEXT
    lstNodes.forEach(function (oNode) {
        lstDue.push(oNode.tag('due') + '\t' + oNode.text());
    });
    
    // SORT AND CONVERT TO STRING
    strList = lstDue.sort().join('\n');
    
    
    // PLACE IN CLIPBOARD
    Pasteboard.writeString(
        strList
    );
    
    return strList;
    
}

var appFT = Application("FoldingText"),
    docsFT=appFT.documents(),
    oDoc = docsFT.length ? docsFT[0] : null,
    fnProcess, varResult, docResult;

if (!oDoc) return null;

fnProcess = (blnDebug ? oDoc.debug : oDoc.evaluate);

varResult = fnProcess({
    script: fnFT.toString(),
    withOptions: {}
});

// If blnMakeDoc (at the top of this script) = 1
// then create a new document to contain the result

if (blnMakeDoc && varResult) {
    docResult = appFT.Document().make();
    docResult.textContents = "### Due\n" + varResult;
}
return varResult;

}
≤jscr˙fifi≠

Not sure what the symbols on the last line indicate, but it works…

Paul


#5

Okay. Try this. Find this code block:

// BUILD A LIST OF DATES FOLLOWED BY TEXT
lstNodes.forEach(function (oNode) {
    lstDue.push(oNode.tag('due') + '\t' + oNode.text());
});

And swap it for:

	// BUILD A LIST OF DATES FOLLOWED BY TEXT
	lstNodes.forEach(function (oNode) {
		var projectHeader = oNode.evaluateNodePath('ancestor::@type heading')[1];
		if (projectHeader) {
      	lstDue.push(oNode.tag('due') + '\t' + projectHeader.text() + '\t' + oNode.text());
    		} else {
		var projectHeader = oNode.evaluateNodePath('ancestor::@type heading')[0];
		lstDue.push(oNode.tag('due') + '\t' + projectHeader.text() + '\t' + oNode.text());
		}
	});

A couple of provisos:

1: I’m still learning my way around Javascript, so please do back up your original file and be confident about reversing any changes that you make in response to my suggestions.
2: This is probably not the most elegant solution. It should handle two levels of depth for project headers—

e.g.
# project heading:

  • next action A @duedate(2016-02-11)

and:
## sub-project

  • 2016-02-12 next action B

Any lower level headings (projects) below that will simply draw upon the title of the second level heading of the branch it belongs to. (That sentence sounds a lot more complicated than it should.) I’m sure there’s an easier way to recognise a node’s immediate ancestor/heading, but I haven’t figured that out yet. Hopefully someone else with more experience will be inspired to improve on my attempt… *smiles


Creating printable TaskPaper 3 reports
#6

Looks good. For the immediately enclosing project, you should, I think, be able to take the last member of the match array (index -1);

Perhaps something like:

function run() {
    var blnDebug = 0;

    // create new doc from sorted list ? 
    var blnMakeDoc = 1; //(0 if not)


    function fnFT(editor, opt) {

        // EVALUATE THE NODE PATH //@due TO HARVEST MATCHES,
        // BUILD A LIST OF DATES FOLLOWED BY TEXT,
        // SORT AND CONVERT TO STRING
        var strList = editor.tree().evaluateNodePath('//@due')
            .map(function (oNode) {

                var lstProj = oNode.evaluateNodePath('ancestor::@type heading[-1]'),
                    strProj = (lstProj.length ? lstProj[0].text() : '?');

                return oNode.tag('due') + '\t' + strProj + '::' + oNode.text();

            }).sort().join('\n');


        // PLACE IN CLIPBOARD
        Pasteboard.writeString(
            strList
        );

        return strList;

    }

    var appFT = Application("FoldingText"),
        docsFT = appFT.documents(),
        oDoc = docsFT.length ? docsFT[0] : null,
        fnProcess, varResult, docResult;

    if (!oDoc) return null;

    fnProcess = (blnDebug ? oDoc.debug : oDoc.evaluate);

    varResult = fnProcess({
        script: fnFT.toString(),
        withOptions: {}
    });

    // If blnMakeDoc (at the top of this script) = 1
    // then create a new document to contain the result

    if (blnMakeDoc && varResult) {
        docResult = appFT.Document().make();
        docResult.textContents = "### Due\n" + varResult;
    }
    return varResult;
}

#7

@JSLR and @complexpoint,
Thank you very much for your responses. I used the last script, and it works exactly like what I had in mind. I’m impressed!! Thank you so much for your help, I appreciate this very much.
Best wishes,
Paul