Action
todoist project from list
Todoist project from list
This action creates a new project in your todoist account from a template draft.
The template Draft has to be specified by the UUID, copy this from your template list and configure it in the script step of the action.
The action parses this draft and creates a new project where you can configure the name. A prompt will ask you for the name and the action will append a configurable „list-name“.
My current use case is a packing list. The template with all possible things I need to carry to a trip is stored in a Draft.
I divided the packing list with sub-headings for e.g. „Clothing“ with elements for this category.
The action parses each line, sub-headings will become to bold written parent tasks and all tasks below a sub-heading will become child tasks of this sub-heading.
The required format for a draft which can be parsed by this action looks like this:
[any title line] - this line will be ignored, its just your title for the list
[maybe empty line] - any empty line in the source draft will be stripped by a regex replacement
## [subheading 1] - this subheader is the first parent task
- [ ] [item 1 of subheading 1] - this will be the first subtask of the previous subheading
- [ ] [item x of subheading 1] - this will be the xth subtask of the previous subheading
[maybe empty line] - any empty line in the source draft will be stripped by a regex replacement
## [subheading y] - this subheader is the yth parent task
- [ ] [item 1 of subheading y] - this will be the first subtask of the subheading y
You can of course use several subheadings to create as many parent tasks with subtasks as you want.
If you dont want to use parent tasks at all, don’t create sub-headings in your draft.
Depending on the amount of items in your list, this action will take some time to run.
This is mainly justified by the limits of todoists REST API where the script is only allowed to perform 50 requests per minute. Therefore I implemented a sleep function which adapts dynamically to the amount of tasks in your list. I integrated a roughly estimated calculation for the process time and the user will see an info message from the app every 5 tasks.
If you have any issues please reach out to me in the forum @FLohGro
Steps
-
script
// list to todoist // ---------------------------------------------------------------------------------------- // start of user definitions // the uuid of the draft which contains the list. The list is considered to have a special "syntax" syntax is shown after the user definitions const uuidOfSourceDraft = "[uuid of source draft]" // this is the general name of the project in todoist, if you type a name into the prompt, this configured name will be appended after oyur given name (e.g. if you make a trip to "Big Sur" and type this into the prompt, the todoist project will be called "Big Sur - Packing List" const listNameForProject = "Packing List" // todoist REST API limit (https://developer.todoist.com/rest/v1/?python#limits) // needed to calculate a sleep time between the requests to the API, to be able to create every task on the list const todoistApiLimitPerMinute = 50 // end of user definitions // ---------------------------------------------------------------------------------------- // required "syntax" / format of the source draft /* ---------------------------------------------------------------------------------------- [any title line] - this line will be ignored, its just your title for the list [maybe empty line] - any empty line in the source draft will be stripped by a regex replacement ## [subheading 1] - this subheader is the first parent task - [ ] [item 1 of subheading 1] - this will be the first subtask of the previous subheading - [ ] [item x of subheading 1] - this will be the xth subtask of the previous subheading [maybe empty line] - any empty line in the source draft will be stripped by a regex replacement ## [subheading y] - this subheader is the yth parent task - [ ] [item 1 of subheading y] - this will be the first subtask of the subheading y ---------------------------------------------------------------------------------------- */ // you can of course use several subheadings to create as many parent tasks with subtasks as you want. // if you dont want to use a parent task at all, dont create subheadings in your draft // all subheadings will be created as tasks with the subheading as content in bold const listDraft = Draft.find(uuidOfSourceDraft); const listContent = listDraft.content var tasksText = listContent // remove all empty lines in text tasksText = tasksText.replace(/^\s*[\r\n]/gm, "") // remove title of draft tasksText = tasksText.split("\n").slice(1).join("\n") // replace all "## " and write the category in bold - marks a parent task tasksText = tasksText.replace(/##\s([^\n]+)/gm, `**$1**`) // replace all "- [ ] " with "" -> get rid of the taskboxes tasksText = tasksText.replace(/-\s\[[\sx]\]\s/gm, "") var tasks = tasksText.split("\n") let taskAmount = tasks.length - 1 // -1 because last line after split is empty (most times, does not matter in the end since this is just a rough calculation var millisecondsPerTask = 0 var millisecondsPerTaskNoProcessTime = 500 if (taskAmount <= todoistApiLimitPerMinute) { // just an estimation and hopefully todoist is faster than this - just a rough calculation } else { let minutesNeeded = taskAmount / todoistApiLimitPerMinute let millisecondsNeeded = minutesNeeded * 60 * 1000 // just an estimation and hopefully todoist is faster than this - just a rough calculation let processTimePerTaskInMilliseconds = 500 let millisecondsPerTaskNoProcessTime = millisecondsNeeded / taskAmount let millisecondsPerTask = (millisecondsNeeded / taskAmount) - processTimePerTaskInMilliseconds } // ask for name of new project with a prompt // Prompt var p = Prompt.create(); p.title = "Project name"; p.message = "name your trip"; p.addTextField("projectName", "name:", ""); p.addButton("Go"); var con = p.show(); var projectName = "" if (con) { projectName = p.fieldValues["projectName"]; } if (projectName == "") { projectName += listNameForProject } else { projectName += " - " + listNameForProject } // create Todoist object and new project let todoist = Todoist.create() // create project var projectToCreate = { "name": projectName, "color": 41 }; var projectResult = todoist.createProject(projectToCreate) let projectID = projectResult["id"]; var parentTaskID = -1 var createdTasks = 0 for (nTask of tasks) { if (nTask.length != 0) { if (nTask.startsWith("**")) { // its a new parent task // create task and store new parent task id parentTaskID = createTask(nTask, -1) } else { // its a (sub) task if (parentTaskID == -1) { // parentTaskID is not set, create a "normal" task createTask(nTask, -1) } else { // parentTaskID is set, use it as parent for the new task createTask(nTask, parentTaskID) } } createdTasks++; if (createdTasks % 5 == 0) { let remainingTime = ((taskAmount - createdTasks) * millisecondsPerTaskNoProcessTime) / 1000 let infoMessage = createdTasks + " tasks created, " + remainingTime + "s remaining" app.displayInfoMessage(infoMessage) } sleep(millisecondsPerTask) } } function createTask(content, parentTaskID) { if (parentTaskID == -1) { // no parentTaskID given var taskToCreate = { "content": content, "project_id": parseInt(projectID) }; } else { // task witn parent var taskToCreate = { "content": content, "project_id": parseInt(projectID), "parent_id": parentTaskID }; } let task = todoist.createTask(taskToCreate); if (!todoist.lastError) { return task["id"] } else { logMessage = "failed adding task: " + content + " with error message " + todoist.lastError + " :("; context.fail(logMessage); } } function sleep(milliseconds) { const date = Date.now(); let currentDate = null; do { currentDate = Date.now(); } while (currentDate - date < milliseconds); }
Options
-
After Success Default Notification Info Log Level Info