Change order of results


#1

I noticed that when I have a due date in my tasks and filter those tasks, the results are presenting in a descending manner. Is there a way to change that behavior to display my results in an ascending way?


#2

No… search results will always display in outliner order… the way that they are ordered in the underlying outline structure. Searching should just filter, not reorder. (let me know if you see otherwise!)


#3

It does make sense. Thank you.


#4

Later on, when the scripting interface settles down, it will, of course, be possible to write scripts, Keyboard Maestro macros, etc. to do things like copying filtered and sorted versions of working lists to the clipboard.

As TP is a text format, sorting dates would normally depend on using a text-sortable format like yyyy-mm-dd (hh:mm)

For the very simplest @due filters (@due + operator + value), you could already run something like the script below from Keyboard Maestro, FastScripts, or the Script Editor (Yosemite onwards), to capture a @due-filtered copy in the clipboard, with items sorted by @due(yyyy-mm-dd)

(An advantage of something like Keyboard Maestro is that it would make it easier to store and reuse previous filters, and perhaps add something like an (order by [@tag]) clause at the end of filters). I might try to sketch something like that for KM at the weekend)

In the meanwhile, as a rough illustration of one way of doing it (in JavaScript for Applications):

// 0.2 fixes 'path' reading for unsaved TP documents

(function () {
  'use strict';

  // This function evaluated as a code string by TP3 document.evaluate({script: withOptions})
  function queryHarvest(editor, options) {
    return editor.root.evaluateItemPath(options.query).map(function (x) {
      return [x.body.string, x.attributes];
    });
  }

  // () -> maybe s
  function userQuery(sa) {
    try {
      return sa.displayDialog('( defaults to: @due < [d] tomorrow )\n\nFilter:', {
        defaultAnswer: '< tomorrow',
        withTitle: 'TP3: sorted query'
      });
    } catch (e) {
      return null;
    }
  }


  // Parsed query - any gaps filled in with defaults
  // s -> (s, s, s, s)
  function queryParts(s) {
    var op = /(!=|<=|>=|=|<|>|contains|beginswith|endswith|matches)/.exec(s),
      mod = /\[.\]/.exec(s), // e.g. [d]
      strTag = (op ? s.slice(0, op.index).trim() : '@due') || '@due';

    return {
      tag: (strTag[0] === '@') ? strTag : '@' + strTag,
      op: op ? op[0] : '<',
      mod: mod ? mod[0] : '[d]',
      val: mod ? s.slice(mod.index + mod[0].length).trim() : (
        op ? s.slice(op.index + op[0].length).trim() : 'tomorrow'
      )
    };
  }


  // Full set of query parts as string
  // (s, s, s, s) -> s
  function normalQuery(dct) {
    return Object.keys(dct).map(function (x) {
      return dct[x];
    }).join(' ');
  }


  // () -> [s]
  function queryResults() {
    var docs = Application("TaskPaper").documents,
      oDoc = docs.length ? docs[0] : null,
      oFile = oDoc ? oDoc.file() : null,
      strPath = oFile ? oFile.toString() : '';
      
    var r = oDoc && userQuery(sa),
      strQuery = r ? r.textReturned : '',
      dctParts = queryParts(strQuery),
      strField = dctParts.tag.slice(1);

    if (strQuery) {
      sa.activate();

      return strPath + '\n\nFilter: ' + strQuery +
        ' [order by @' + strField + ']\n\n' + oDoc.evaluate({
          script: queryHarvest.toString(),

          withOptions: {
            query: normalQuery(dctParts)
          }
        })

      .sort(function (a, b) {
        var s = 'data-' + strField,
          v = a[1][s],
          v2 = b[1][s];

        return (v > v2) ? 1 : (v < v2) ? -1 : 0;
      })

      .map(function (x) {
        return x[0];
      }).join('\n')
    }

  }

  var a = Application.currentApplication(),
    sa = (a.includeStandardAdditions = true, a),

    strSorted = queryResults();

  if (strSorted) {
    sa.setTheClipboardTo(strSorted);

    sa.displayNotification(strSorted, {
      withTitle: "Sorted list",
      subtitle: 'copied to clipboard',
      soundName: 'glass'
    });

    return strSorted;
  } else return 'Cancelled';

})();

#5

As much as I’d like an ability to to order by nearest due date, the question arises of context. Most of my tasks are connected to projects and therefore need to remain connected. I’m assuming the script would copy to clipboard a reordered list by due date? How does it handle parent and child items connected to the selected item?

I probably wouldn’t want it copied to clipboard, but would like something in TP that would allow me to reorder the view by date.

If there are a lot of dated items in a rather long TP document this would become more of a priority. It would be easy to miss something. Having said that, queries that cover a smaller date range would filter that out.


#6

One approach is this, in which each grouped or sorted task has a project prefix: