Using Lists and Tasks

#1

I use Drafts for my daily journaling. As I plan my day I use the Tasks feature and send all my tasks to Todoist. I would also like to be able to send other items to a List in Drafts. Ideally, I would write my Daily Journal, then have an action step that sends tagged lines to a List in Drafts. It is obvious to me how I can send the entire note to a specific list, how can I send a specific line to a list?

Thanks for your help!

#2

You could use a script based action to parse through the current draft contents, and based on the prefix (line tag), append it to another specific draft (e.g. you could specify a relationship between tags and draft UUID), applying any bullet or checklist markers you require.

#3

To break that down a little - first you have to find the draft to you want to add the line to. If it is a specific draft, you can copy the UUID from the (i) detail window, and use it like this to load that draft:

let d = Draft.find("UUID-OF-DRAFT");

Once you have that draft, appending text to the end of it would be adding to the content property, like:

let line = "??"; // not sure how you are determining what is in this line
d.content = d.content + "\n" + line;
d.update(); // to save changes

As for determining what you are appending…it sounds like you want to loop over the lines of a draft and do different things with different lines. That would look like:

let lines = draft.content.split("\n"); // split current draft text into an array of lines.
for (let line of lines) {
  // loop over each line, determine what you want to do with it...
}

Note that if you need a specific line number, like the value you want to add in the list will always be line 3 of the draft, you can use template tags to extract that value, like:

let line3 = draft.processTemplate("[[line|3]]");
1 Like
#4

Wow, thanks guys for the great information. You guys are awesome of taking the time to do this.

I am not as sophisticated as many of you. Let me explain a little further

Lets Say I have a note:

This is meeting text <-Do nothing with this
[ ] This is a task <-Upload into Todoist - I can do this
#Cust1 Discuss the current billing situation <- I would like to move this to a Drafts that has the designation of #Cust1

I don’t have to use a Hashtag, I can use any method to describe the List

Given that, How would I parse out the List item in the action?

Thanks very much!

#5

This is a script that I use for parsing a task from Drafts to send to my task manager - Things. Note that I write tasks starting with a hyphen in the following form: - [ ] This is a task

// Script for searching through the Draft for any Github style check boxes and creates an array of the text following the check box

var draftContent = draft.content;
var regexp = /\-\s\[\s\]\s(.*)/g;
var taskList = '';
while ( result = regexp.exec(draftContent) ) { taskList = taskList + result[1] + '\n';}
draft.setTemplateTag("action", taskList);

I have a second step in the action a callback url with the following (you would have to adapt your next step for Todoist)

things://x-callback-url/add?titles=[[action]]&notes=drafts5%3A%2F%2Fopen%3Fuuid%3D[[uuid]]
#6

If you know Regular Expressions, @kseggleton’s technique would be a good way to scan the draft for lines matching each of your criteria. Especially useful if you need to extract other values from the line, like the “Cust1” designation.

Or, to expand the looping example I had above, you could process the lines in that loop, like:

let lines = draft.content.split("\n"); // split current draft text into an array of lines.

for (let line of lines) {
  if (line.startsWith("[ ]")) {
    //this line is a task - do something to send it to Todoist
  }
  else if (line.startsWith("#")) {
    //this line is one of your # marked lines, so something else with it.
  }
}
#7

Thanks very much for the great ideas. In the @kseggleton parsing example above is there anyway to have to place it in to a “List:” note. If there is an existing note, place it there. If there is not an existing note, create a new “List” Note.

#8

I would really appreciate any help with this. If I am missing something very obvious, please let me know! Thanks in Advance

#9

So here is my solution for “lists”: [disclaimer: I got some inspiration from other users in this great community but I cant remember who exactly. I recycled some code / actions which where shared from them, so the credit simply goes to the comunity :slight_smile: ]
I use a tag “ref_list” for all my lists in drafts. every list has a title like “# Reading” with the listtag “ref_list”. This tag can be changed in the actions, but it wont work without a specific tag for your lists.

  1. When I want to add something to a list I use my action Add to list.
    This action will create a prompt with the titles of all my available lists (e.g. Reading, Movies, Music,…), this will work dynamically so if I create a new list with the same tag, the next time I use this action, the title will appear, too. The last Button in the Prompt is “CREATE NEW LIST” so everytime I need to setup a new list, this can simply be done by this button and the action will prompt to set the title of the new list and afterwards add the content to this new list. (the title will be markdown formatted e.g. “# my new list” and the elements will be added as simple list item “- [ ] my item”.

  2. When I need to see / review one of my lists, I use the action Show List which will create a prompt (like the action add to list) and create a button with the titles of my list drafts (based on the tag “ref_list”). After selection a list, the specific draft will be loaded into the editor. If there is now draft with the tag “ref_list” then no button will appear in the prompt.

  3. I played around with sorting the lists, with the action Sort Simple List. This action will move every done item in a simple list ("- [x]") to the bottom of the draft into a section with the heading “## done” every other item will stay at the top, and they wont be reordered. Mainly I used this for e.g. a list of links I wanted to checkout and dont delete directly…

  4. When I want to cleanup a list (remove the done items) i use the action clean done items in simple list which will remove every line which includes a done item (and also remove the heading “## done”)

I hope this helps you, if you have questions or problems, just ask :slight_smile:

Add to an existing document in DEVONThink
#10

Thanks very much! I can see the light at the end of the tunnel. As you can see, I am a scripting rookie so I apologize for the basic questions.

Now I need to figure out how to put both parts of the equation together. I would like to take the script above from @agiletortoise , the one that goes through a reviews every line for a “list entry,” and then use it to run script. Ideally, I would use an identifier to start a tag description so it drops it into the list construct you described. Going back to my example, if I type #Cust1 , that would put it in your Cust1 list.

Thanks again!

#11

No problem, I’m not good at JS, too but learning by doing helps…
A few thoughts:

to create todoist tasks in your todoist inbox its very straight forwart to use the Todoist Integration from drafts. You setup a credential for todoist (first call of the action will ask for your todoist credentials, afterwards you wont need to type them again into drafts until you remove the credentials in settings) and you can use the quickAdd API to push tasks into your inbox.
To Start your Script, I’d do the following:

/* create a todoist object */
var todoist = Todoist.create();
/* create an array to collect all the tasks while the script goes through your log draft */
var taskArray = [];
/* create an array to collect all the list items while the script goes through your log draft */
var listArray = [];

then the script from @agiletortoise can be used to loop through the lines and you simply add the elements to the desired array:

let lines = draft.content.split("\n"); // split current draft text into an array of lines.

for (let line of lines) {
  if (line.startsWith("[ ]")) {
    taskArray.push(line);
  }
  else if (line.startsWith("#")) {
    listArray.push(line);
  }
}

now, the simple part is to add all the tasks to your todoist inbox with the quickAdd API:
it will loop through the taskArray and push every element to your todoist inbox. You could also push them the right projects with the above referenced descriptions in drafts reference but this will be more complicated to do!

for(let task of taskArray){
 todoist.quickAdd(task);
}

The part of your log entries marked with “#” does depend on your setup. If you e.g. have a special tag to all these drafts you can filter for them with a workspace object - I’ll go with this solution for now and explain what I think would needs to be done.
First the tag for your lists should be defined at the beginning of your script (maybe you want to change it later or share your results. so e.g. put:

const listTag = "ref_list";

… to the beginning of the script.
Then, after pushing the tasks to todoist, your script should create a temporary workspace with all your list drafts:

var result;
{
/* create the temporary workspace */
let ws = Workspace.create();
/* set the tagfilter of the workspace to your list tag */
ws.tagFilter = listTag;
/* save all resulting draft objects into "res" */
result = ws.query("all");
}

Maybe the next step can be accomplished a lot faster, too but I dont know -. You would need to collect the uuids and titles of the draft objects contained in the variable result. I’d use a map for this:

var myLists = new Map();
for(res of result){
  myLists.set(res.title,res.uuid);
}

The map myLists now contains all the titles and corresponding uudis of your available lists.
Next step would be to seperate the List name (#Cust1) from the rest of your line and then check if this list already exists.

  1. If the list already exits, append the rest of the line to the list.
  2. If the list does not exist, create a new one and add the rest of the line to the new list.
for(let item of listArray){
/* split item by whitespaces - first element is the "list name" */
 let curList = item.split(" ")[0];
 let itemContent = item.replace(curList + " ","");
 if(myLists.has(curList)){
 /* the list is already existing, append the content of item to this list */
  let curDraft = Draft.find(myLists.get(curList));
  curDraft.content = curDraft.content + "\n - " + itemContent;
 } else {
/* the list is not existing, create it, add it to myLists and append the content to the new list */
 let newDraft = Draft.create();
 newDraft.content = curList + "\n\n - " + itemContent;
 newDraft.update();
 myLists.set(newDraft.title,newDraft.uuid);
}
}

I hope I didn’t forget an important thing - I did not test all the code here, so please check it out and then report if anything does not work.

edit: If you dont want the “#” in the title, you can of course replace / remove this, too in the title and just use the “Cust1”

#12

I can’t help you enough for this fantastic advice. The time you spent is very much appreciated. Thank you!!!

As I go to the run the script.

I am getting an error, when I run this line: if(myLists.has(curList){

The error message is:

Script Error: Syntax Error:
Unexpected token ‘{‘. Expected to end an ‘if’ condition.
Column undefined

Thank you!

#13

Hi, no problem :slight_smile:
The line you copied is missing a bracket „)“ to close the of condition:
if(myLists.has(curList)){

Sorry for that;)

#14

Making progress, the script now runs!

If I put a

#Cust1 Testing List entries

It always creates a new note with the header #Cust1, but it does not show in the list view. If I use and existing list name, it doesn’t add it to the existing group of lists, it creates a new Drafts.

Thanks again for all your time and help!

#15

Great!
I looked again to my explanations and I think you need to call:

....
let curDraft = Draft.find(myLists.get(curList));
curDraft.content = curDraft.content + "\n - " + itemContent;
curDraft.update()

that the draft will be saved properly.

Could you share your action script here or into the action directory (you can choose “unlisted”) there. And an example draft.
Then I can test it by myself and see whats going wrong.

I think the “#” sign is a problem because of markdown syntax handling. draft.title returns “cust1” for a draft with “#cust1” in the first line.
You can try to change the sign to something else (like “+”), which may fix this issue.
What’s also possible, that you get rid of the check with the hashtag in your code and instead replace it befor checking it against the map. Be aware that you need to add the hashtag to the title if you create a new one. If you add a space after the “#” you will see better proper markdown formating :slight_smile:


for(let item of listArray){
/* split item by whitespaces - first element is the "list name" */
 let curList = item.split(" ")[0];
 let itemContent = item.replace(curList + " ","");
 curList.replace("#","");
 if(myLists.has(curList)){
 /* the list is already existing, append the content of item to this list */
  let curDraft = Draft.find(myLists.get(curList));
  curDraft.content = curDraft.content + "\n - " + itemContent;
  curDraft.update();
 } else {
/* the list is not existing, create it, add it to myLists and append the content to the new list */
 let newDraft = Draft.create();
 newDraft.content = "# " + curList + "\n\n - " + itemContent;
 newDraft.update();
 myLists.set(newDraft.title,newDraft.uuid);
}
}
#16

I am so appreciative of all the time you are spending on this script. Thank you.

Here is the current script:

/* create a todoist object /
var todoist = Todoist.create();
/
create an array to collect all the tasks while the script goes through your log draft /
var taskArray = [];
/
create an array to collect all the list items while the script goes through your log draft */
var listArray = [];

let lines = draft.content.split("\n"); // split current draft text into an array of lines.

for (let line of lines) {
if (line.startsWith("[ ]")) {
taskArray.push(line);
}
else if (line.startsWith("#")) {
listArray.push(line);
}
}

for(let task of taskArray){
todoist.quickAdd(task);
}

const listTag = “ref_list”;

var result;
{
/* create the temporary workspace /
let ws = Workspace.create();
/
set the tagfilter of the workspace to your list tag /
ws.tagFilter = listTag;
/
save all resulting draft objects into “res” */
result = ws.query(“all”);
}

var myLists = new Map();
for(res of result){
myLists.set(res.title,res.uuid);
}

for(let item of listArray){
/* split item by whitespaces - first element is the “list name” /
let curList = item.split(" “)[0];
let itemContent = item.replace(curList + " “,””);
if(myLists.has(curList)){
/
the list is already existing, append the content of item to this list /
let curDraft = Draft.find(myLists.get(curList));
curDraft.content = curDraft.content + "\n - " + itemContent;
} else {
/
the list is not existing, create it, add it to myLists and append the content to the new list */
let newDraft = Draft.create();
newDraft.content = curList + "\n\n - " + itemContent;
newDraft.update();
myLists.set(newDraft.title,newDraft.uuid);
}
}

#17

Got it, HERE you go!

I tried it with an example draft like this:

this is an example draft
[ ] first task
[ ] second task
+Cust1 List item
+Cust1 Another list item
+Cust2 Another list
+Cust2 second list item in the other list
[ ] third task

and it worked.
As I said, the “#” sign is a problem in draft.title - thats why i changed the identifier to a “+”.
If you download and install the linked action, you can set the identifier and your list tag at the beginning of the script.

The issue with recreation of drafts when calling the action again was because I forgot to add the tag.

Just to document this here, two - here is the script (by the way if you insert a script between “```” it will be formatted for better readability):

// list & task usage script

const listTag = "test_list";
const listSign = "+";
const taskSign = "[ ]";

/* create a todoist object */
var todoist = Todoist.create();
/* create an array to collect all the tasks while the script goes through your log draft */
var taskArray = [];
/* create an array to collect all the list items while the script goes through your log draft */
var listArray = [];

let lines = draft.content.split("\n"); // split current draft text into an array of lines.

for (let line of lines) {
  if (line.startsWith(taskSign)) {
    line = line.replace(taskSign + " ", "");
    taskArray.push(line);
  } else if (line.startsWith(listSign)) {
    listArray.push(line);
  }
}

for (let task of taskArray) {

  todoist.quickAdd(task);
}

var result; {
  /* create the temporary workspace */
  let ws = Workspace.create();
  /* set the tagfilter of the workspace to your list tag */
  ws.tagFilter = listTag;
  /* save all resulting draft objects into “res” */
  result = ws.query("all");
}

var myLists = new Map();
for (res of result) {
  myLists.set(res.title, res.uuid);
}

for (let item of listArray) {
  /* split item by whitespaces - first element is the “list name” */
  let curList = item.split(" ")[0];
  let itemContent = item.replace(curList + " ", "");
  if (myLists.has(curList)) {
    /* the list is already existing, append the content of item to this list */
    
    let curDraft = Draft.find(myLists.get(curList));
    curDraft.content = curDraft.content + "\n - " + itemContent;
    curDraft.update();
  } else {
    /* the list is not existing, create it, add it to myLists and append the content to the new list */
    let newDraft = Draft.create();
    newDraft.content = curList + "\n\n - " + itemContent;
    newDraft.addTag(listTag);
    newDraft.update();
    myLists.set(newDraft.title, newDraft.uuid);
  }
}
#18

Getting there! Thanks so much.

Just two different things

When I use the + identifier for lists, it creates a seperate draft and will collect all the items. However, it creates a document with the header of “+ Cust1” it doesn’t appear as part of the list dialog

The tasks are not going to Todoist. Not sure where they are going.

Thanks again!

#19

Hi,

yes the + sign is just a workaround because of the handling with “#”.
If you want to have the # as sign thats possible, too. We just need to modify the script that way.
You can try to figure this out by yourself if you want:

  • the “#” sign will not be part of the map, but of the string when you compare them with the list.
  • you’ll need to replace it in the comparison and add it back again, when you need to create a new note.

At the moment its necessary that the first whitespace in a list item is after the lists name so “+Cust1 item” not “+ Cust1 item”

did you use “[]” or “[ ]” as identifier for the task?

its always good if you quickly want to debugg something to just create an alert message at that point in the script like:

alert("publishing task to todoist");

maybe you just did not use the actual sign for your tasks ("[ ]").

#20

Hi @CharlestonSC,

I updated the Action in the Directory and changed the signs again. I Hope this will work for you know, please Report any issues :slight_smile:

Here is the ACTION.

And my test draft was:

This is a test draft
+ One task
* Cust1 A list item
* Cust1 Another list item
+ Second task
* Cust1 again an item
* Cust2 an item for another list
* Cust2 another item for the Otter list
+ one more task

Please notice the spaces between the identifier and the Description. Its hardcoded to have one whitespace After the sign and for list items, one After the lists Name.
Ist will now create a list with the Header „# Cust1“ which should appear in you Show list action if you change the listTag at the beginning of the script according to your Setup.