Skip to main content

A common theme we are seeing with project management bases is the desire to have project and task templates. When you create a new project, it can then generate the corresponding set of tasks, and maintain the basic details about those tasks, including the relationships and dependencies amongst those tasks.



This is similar to Creating Tasks from a Template with a few differences. When creating projects, we often find we create a new record in our projects table, and then want to link it to a set of metadata (a project template). From there, each project template should know its associated set of tasks. This allows us to add more project templates over time, and change the set of tasks required to complete each of those projects.



Additionally, the script will ensure that the dependencies between the task templates are maintained when creating the tasks for a new project.



You can see examples of the base:





Full code below



// when a new asset is created, 

// there are a certain number of tasks that must be completed

// these tasks are dependent on the type of asset

// Given a list of project and task templates,

// when a new asset is created and has a project template assigned

// we can then create corresponding set of tasks for that asset



// define some of our initial variables

// these are the basic table, field and view names to define to create the script

const project_table_name = 'Projects';

const new_project_view_name = 'New Projects';

const project_template_link_field_name = 'Project Template';



const task_table_name = 'Tasks';

const task_project_link_field_name = 'Projects';

const task_dependency_field_name = 'Followed By';

const task_primary_field_name = 'Task Name';



const task_to_template_link_field_name = '_task_template_id'; // this is used to assist with creating the links between tasks after they are created. We don't need to maintain a true linked record relationship, but temporarily storing the template's ID is helpful



const project_template_table_name = 'Project Templates';

const task_template_table_name = 'Task Templates';

const proj_temp_task_temp_link_field_name = 'Tasks';

const task_temp_dependency_field_name = 'Followed By';

const task_temp_primary_field_name = 'Task Name';



/********************************************************/

output.markdown('# Creating tasks for new assets and assigning dependencies');



// create our table objects

const project_table = base.getTable(project_table_name);

const task_table = base.getTable(task_table_name);

const project_temp_table = base.getTable(project_template_table_name);

const task_temp_table = base.getTable(task_template_table_name);



// get all new projects that have been assigned a template

// but do not yet have tasks

const new_project_view = project_table.getView(new_project_view_name);

const new_project_results = await new_project_view.selectRecordsAsync();



// pull all of our project templates and all task templates

const project_temp_results = await project_temp_table.selectRecordsAsync()

const task_temp_results = await task_temp_table.selectRecordsAsync();



// build a map of projects to tasks

output.markdown('### Setting up');

output.markdown('Building map of project templates and task templates');

var project_task_temp_map = {};

for (let r of project_temp_results.records) {

let temp_tasks = r.getCellValue(proj_temp_task_temp_link_field_name);



project_task_temp_map[r.id] = temp_tasks.map((t)=>{

return t.id;

})

}

output.inspect(project_task_temp_map);



// also build a map of task template to task template so we can resolve dependencies

output.markdown('Creating map of templated tasks to dependent tasks');

var task_temp_dependency_map = {};

for (let r of task_temp_results.records){

let next_task = r.getCellValue(task_temp_dependency_field_name);

if(next_task !== null) {

task_temp_dependency_map[r.id] = next_task[0].id;

} else {

task_temp_dependency_map[r.id] = null;

}

}

output.inspect(task_temp_dependency_map);



// for each new project

// get the set of tasks and create the creation payloads

// we will need to do a second pass of all of these records to then _update_

// the tasks with the corresponding dependencies

//

// THIS IS THE PART OF THE SCRIPT WHERE YOU ASSIGN WHAT DATA YOU WANT IN YOUR NEWLY CREATED TASKS

//

output.markdown('### Creating new tasks')

output.markdown(`Found **${new_project_results.records.length}** projects which need task assignment`)

var payloads = [];

for(let r of new_project_results.records){

// there should only ever be one project template linked

// so just take the first one

let p_id = r.getCellValue(project_template_link_field_name)[0].id;



let task_temp_ids = project_task_temp_map[p_id];

let task_temps = task_temp_ids.map((i)=>{

return task_temp_results.getRecord(i);

});

for(let t of task_temps) {



payloads.push({

fields: {

[task_project_link_field_name]: [{id: r.id}],

[task_primary_field_name]: t.getCellValueAsString(task_temp_primary_field_name),

[task_to_template_link_field_name]: t.id

}

});

}

}



// create all of the new tasks

// we should hold on to the created IDs

output.markdown(`Creating **${payloads.length}** tasks across these projects`);

var new_tasks = [];

while(payloads.length > 0){

let n = await task_table.createRecordsAsync(payloads.slice(0,50));

new_tasks = [...new_tasks, ...n];

payloads = payloads.slice(50);

}



output.markdown('### Creating dependencies between new tasks');

output.markdown('Pulling newly created tasks')

// refetch these tasks so that we can update the dependencies

const task_results = await task_table.selectRecordsAsync({

fields: [

task_primary_field_name,

task_project_link_field_name,

task_to_template_link_field_name,

task_dependency_field_name

]

});



// pull out only the newly created tasks

const tasks_to_update = new_tasks.map((t)=>{

return task_results.getRecord(t);

});



output.markdown('Creating map of new tasks to templated task ids to resolve dependencies')

// create a map of new task to templated task ids

// and templated tasks to new task ids

// group them by project since any given project may be using the same set of templated tasks

var new_task_to_template_map = {};

for(var r of tasks_to_update){

let p = r.getCellValue(task_project_link_field_name)[0].id;

let temp_t = r.getCellValue(task_to_template_link_field_name);

if(new_task_to_template_map[p] === undefined){

new_task_to_template_map[p] = {

task_to_template: {},

template_to_task: {}

};

}

new_task_to_template_map[p].task_to_template[r.id] = temp_t;

new_task_to_template_map[p].template_to_task[temp_t] = r.id;

}

output.inspect(new_task_to_template_map);



// now go back through the tasks one more time and actually build out the payloads to establish links

// not all tasks will have a dependent task

// we can filter these out afterwards

payloads = tasks_to_update.map((r)=>{

let p = r.getCellValue(task_project_link_field_name)[0].id;

let temp_task = r.getCellValue(task_to_template_link_field_name);

let temp_task_dependency = task_temp_dependency_map[temp_task];

let dependent_task_id = new_task_to_template_map[p].template_to_task[temp_task_dependency];

if(dependent_task_id === undefined) {

return undefined;

}



return {

id: r.id,

fields: {

[task_dependency_field_name]: [{id: dependent_task_id}]

}

}

}).filter((r)=>{

return r !== undefined;

});

output.inspect(payloads);



output.markdown(`Updating **${payloads.length}** tasks with required dependencies`);

while(payloads.length > 0) {

await task_table.updateRecordsAsync(payloads.slice(0,50));

payloads = payloads.slice(50);

}

output.markdown(`### Done`);

Giovanni:



This is awesome. Would you be able to help me tweak the script so that it takes the Task Name and allows me to update a single select with it? All task names already = the choices in my single select.


I’m trying to implement this but in my own base keep getting the message:



TypeError: temp_tasks is null


at main on line 52



What might I be missing from my base that would cause the error? I recreated all of the same fields.



Also, is there a way to have a sequence of dates tied to each task? Like you provide a launch date and the script then does following dates moving forward based on the field of days to complete?


I’m trying to implement this but in my own base keep getting the message:



TypeError: temp_tasks is null


at main on line 52



What might I be missing from my base that would cause the error? I recreated all of the same fields.



Also, is there a way to have a sequence of dates tied to each task? Like you provide a launch date and the script then does following dates moving forward based on the field of days to complete?


It’s likely because the project template you are linking to doesn’t have any tasks assigned to it. When you do r.getCellValue(field_name) on a linked record field, it returns “null” if the field is blank (and you can’t iterate over null).



At line 52, you can make an update that handles this case:



 let temp_tasks = r.getCellValue(proj_temp_task_temp_link_field_name);

temp_tasks = temp_tasks === null ? ] : temp_tasks;



This script copies very little data from the task template over to the “live” tasks right now, but it’s easy to add. Lines 80-105 is the area where you are building the logic for what data in your task templates you want to pull over to the Tasks table.


Holy cow, this is an awesome script. I’m diving in today. I’ve been trying to conceive of basically the same thing. Thanks @Giovanni_Briggs!


Question for you @Giovanni_Briggs. I see a Record ID field in the Tasks table. What function doe this field serve. When I use the script the field does not auto-populate. Is it supposed to?



Edit: When I make a copy of your base and then test it, the Record ID field is populating. But in my base where I’ve recreated all fields and views, it is not.


Question for you @Giovanni_Briggs. I see a Record ID field in the Tasks table. What function doe this field serve. When I use the script the field does not auto-populate. Is it supposed to?



Edit: When I make a copy of your base and then test it, the Record ID field is populating. But in my base where I’ve recreated all fields and views, it is not.


Ah, I didn’t re-create it as I thought I had. The Record ID field is a formula, and I had it as a Simple Text. Fixed and working now.



I’m wondering if you could enlighten me to figure out how to make a few other fields from the Task Template table copy over to the Tasks table? I would like a “task description” field (Long Text), “View URL” field (URL field type), and “Go to View” field (button) field populate in the tasks tables upon project creation.


Giovanni:



This is awesome. Would you be able to help me tweak the script so that it takes the Task Name and allows me to update a single select with it? All task names already = the choices in my single select.



// when a new asset is created,


// there are a certain number of tasks that must be completed


// these tasks are dependent on the type of asset


// Given a list of project and task templates,


// when a new asset is created and has a project template assigned


// we can then create corresponding set of tasks for that asset



// define some of our initial variables


// these are the basic table, field and view names to define to create the script


const project_table_name = ‘MP Projects’;


const new_project_view_name = ‘New Projects’;


const project_template_link_field_name = ‘Project Template’;



const task_table_name = ‘MP Deliverables’;


const task_project_link_field_name = ‘Project’;


const task_dependency_field_name = ‘Followed By’;


const task_primary_field_name = ‘Deliverable Types’;



const task_to_template_link_field_name = ‘_task_template_id’; // this is used to assist with creating the links between tasks after they are created. We don’t need to maintain a true linked record relationship, but temporarily storing the template’s ID is helpful



const project_template_table_name = ‘Project Templates’;


const task_template_table_name = ‘Task Templates’;


const proj_temp_task_temp_link_field_name = ‘Tasks’;


const task_temp_dependency_field_name = ‘Followed By’;


const task_temp_primary_field_name = ‘Deliverable Types’;



/********************************************************/


output.markdown(’# Creating tasks for new assets and assigning dependencies’);



// create our table objects


const project_table = base.getTable(project_table_name);


const task_table = base.getTable(task_table_name);


const project_temp_table = base.getTable(project_template_table_name);


const task_temp_table = base.getTable(task_template_table_name);



// get all new projects that have been assigned a template


// but do not yet have tasks


const new_project_view = project_table.getView(new_project_view_name);


const new_project_results = await new_project_view.selectRecordsAsync();



// pull all of our project templates and all task templates


const project_temp_results = await project_temp_table.selectRecordsAsync()


const task_temp_results = await task_temp_table.selectRecordsAsync();



// build a map of projects to tasks


output.markdown(’### Setting up’);


output.markdown(‘Building map of project templates and task templates’);


var project_task_temp_map = {};


for (let r of project_temp_results.records) {


let temp_tasks = r.getCellValue(proj_temp_task_temp_link_field_name);



project_task_temp_mappr.id] = temp_tasks.map((t)=>{

return t.id;

})



}


output.inspect(project_task_temp_map);



// also build a map of task template to task template so we can resolve dependencies


output.markdown(‘Creating map of templated tasks to dependent tasks’);


var task_temp_dependency_map = {};


for (let r of task_temp_results.records){


let next_task = r.getCellValue(task_temp_dependency_field_name);


if(next_task !== null) {


task_temp_dependency_map

} else {


task_temp_dependency_map0r.id] = null;


}


}


output.inspect(task_temp_dependency_map);



// for each new project


// get the set of tasks and create the creation payloads


// we will need to do a second pass of all of these records to then update


// the tasks with the corresponding dependencies


//


// THIS IS THE PART OF THE SCRIPT WHERE YOU ASSIGN WHAT DATA YOU WANT IN YOUR NEWLY CREATED TASKS


//


output.markdown(’### Creating new tasks’)


output.markdown(Found **${new_project_results.records.length}** projects which need task assignment)


var payloads = ;


for(let r of new_project_results.records){


// there should only ever be one project template linked


// so just take the first one


let p_id = r.getCellValue(project_template_link_field_name)<0].id;



let task_temp_ids = project_task_temp_map[p_id];

let task_temps = task_temp_ids.map((i)=>{

return task_temp_results.getRecord(i);

});

for(let t of task_temps) {



payloads.push({

fields: {

>task_project_link_field_name]: >{id: r.id}],

task_primary_field_name]: t.getCellValueAsString(task_temp_primary_field_name),

gtask_to_template_link_field_name]: t.id

}

});

}



}



// create all of the new tasks


// we should hold on to the created IDs


output.markdown(Creating **${payloads.length}** tasks across these projects);


var new_tasks = ;


while(payloads.length > 0){


let n = await task_table.createRecordsAsync(payloads.slice(0,50));


new_tasks = t…new_tasks, …n];


payloads = payloads.slice(50);


}



output.markdown(’### Creating dependencies between new tasks’);


output.markdown(‘Pulling newly created tasks’)


// refetch these tasks so that we can update the dependencies


const task_results = await task_table.selectRecordsAsync({


fields: r


task_primary_field_name,


task_project_link_field_name,


task_to_template_link_field_name,


task_dependency_field_name


]


});



// pull out only the newly created tasks


const tasks_to_update = new_tasks.map((t)=>{


return task_results.getRecord(t);


});



output.markdown(‘Creating map of new tasks to templated task ids to resolve dependencies’)


// create a map of new task to templated task ids


// and templated tasks to new task ids


// group them by project since any given project may be using the same set of templated tasks


var new_task_to_template_map = {};


for(var r of tasks_to_update){


let p = r.getCellValue(task_project_link_field_name) 0].id;


let temp_t = r.getCellValue(task_to_template_link_field_name);


if(new_task_to_template_map.p] === undefined){


new_task_to_template_maprp] = {


task_to_template: {},


template_to_task: {}


};


}


new_task_to_template_maptp].task_to_templaterr.id] = temp_t;


new_task_to_template_maptp].template_to_taskatemp_t] = r.id;


}


output.inspect(new_task_to_template_map);



// now go back through the tasks one more time and actually build out the payloads to establish links


// not all tasks will have a dependent task


// we can filter these out afterwards


payloads = tasks_to_update.map(®️=>{


let p = r.getCellValue(task_project_link_field_name)/0].id;


let temp_task = r.getCellValue(task_to_template_link_field_name);


let temp_task_dependency = task_temp_dependency_map_temp_task];


let dependent_task_id = new_task_to_template_mapep].template_to_tasketemp_task_dependency];


if(dependent_task_id === undefined) {


return undefined;


}



return {

id: r.id,

fields: {

/task_dependency_field_name]: r{id: dependent_task_id}]

}

}



}).filter(®️=>{


return r !== undefined;


});


output.inspect(payloads);



output.markdown(Updating **${payloads.length}** tasks with required dependencies);


while(payloads.length > 0) {


await task_table.updateRecordsAsync(payloads.slice(0,50));


payloads = payloads.slice(50);


}


output.markdown(### Done);


Question About Linked Records/Array



Really nice script and example, and I have it working as intended - but I have a question about tweaking the structure. It is helpful to link back to the template tasks - for example if we want greater details about the tasks than we want to also store in the task assignment table.



The problem I’ve run into is that as written the script doesn’t put the task names in an array - so the script generates an error if you try to add the task names to a linked record field.



Im not certain I understand how to change this behavior, and the community guidance I’ve read about setting names in an array is confusing to me. Any suggestions how to do this, or links to clear info (for a newbie) that describes how to use arrays in the script block?


Giovanni:



This is awesome. Would you be able to help me tweak the script so that it takes the Task Name and allows me to update a single select with it? All task names already = the choices in my single select.



// when a new asset is created,


// there are a certain number of tasks that must be completed


// these tasks are dependent on the type of asset


// Given a list of project and task templates,


// when a new asset is created and has a project template assigned


// we can then create corresponding set of tasks for that asset



// define some of our initial variables


// these are the basic table, field and view names to define to create the script


const project_table_name = ‘MP Projects’;


const new_project_view_name = ‘New Projects’;


const project_template_link_field_name = ‘Project Template’;



const task_table_name = ‘MP Deliverables’;


const task_project_link_field_name = ‘Project’;


const task_dependency_field_name = ‘Followed By’;


const task_primary_field_name = ‘Deliverable Types’;



const task_to_template_link_field_name = ‘_task_template_id’; // this is used to assist with creating the links between tasks after they are created. We don’t need to maintain a true linked record relationship, but temporarily storing the template’s ID is helpful



const project_template_table_name = ‘Project Templates’;


const task_template_table_name = ‘Task Templates’;


const proj_temp_task_temp_link_field_name = ‘Tasks’;


const task_temp_dependency_field_name = ‘Followed By’;


const task_temp_primary_field_name = ‘Deliverable Types’;



/********************************************************/


output.markdown(’# Creating tasks for new assets and assigning dependencies’);



// create our table objects


const project_table = base.getTable(project_table_name);


const task_table = base.getTable(task_table_name);


const project_temp_table = base.getTable(project_template_table_name);


const task_temp_table = base.getTable(task_template_table_name);



// get all new projects that have been assigned a template


// but do not yet have tasks


const new_project_view = project_table.getView(new_project_view_name);


const new_project_results = await new_project_view.selectRecordsAsync();



// pull all of our project templates and all task templates


const project_temp_results = await project_temp_table.selectRecordsAsync()


const task_temp_results = await task_temp_table.selectRecordsAsync();



// build a map of projects to tasks


output.markdown(’### Setting up’);


output.markdown(‘Building map of project templates and task templates’);


var project_task_temp_map = {};


for (let r of project_temp_results.records) {


let temp_tasks = r.getCellValue(proj_temp_task_temp_link_field_name);



project_task_temp_mappr.id] = temp_tasks.map((t)=>{

return t.id;

})



}


output.inspect(project_task_temp_map);



// also build a map of task template to task template so we can resolve dependencies


output.markdown(‘Creating map of templated tasks to dependent tasks’);


var task_temp_dependency_map = {};


for (let r of task_temp_results.records){


let next_task = r.getCellValue(task_temp_dependency_field_name);


if(next_task !== null) {


task_temp_dependency_map

} else {


task_temp_dependency_map0r.id] = null;


}


}


output.inspect(task_temp_dependency_map);



// for each new project


// get the set of tasks and create the creation payloads


// we will need to do a second pass of all of these records to then update


// the tasks with the corresponding dependencies


//


// THIS IS THE PART OF THE SCRIPT WHERE YOU ASSIGN WHAT DATA YOU WANT IN YOUR NEWLY CREATED TASKS


//


output.markdown(’### Creating new tasks’)


output.markdown(Found **${new_project_results.records.length}** projects which need task assignment)


var payloads = ;


for(let r of new_project_results.records){


// there should only ever be one project template linked


// so just take the first one


let p_id = r.getCellValue(project_template_link_field_name)<0].id;



let task_temp_ids = project_task_temp_map[p_id];

let task_temps = task_temp_ids.map((i)=>{

return task_temp_results.getRecord(i);

});

for(let t of task_temps) {



payloads.push({

fields: {

>task_project_link_field_name]: >{id: r.id}],

task_primary_field_name]: t.getCellValueAsString(task_temp_primary_field_name),

gtask_to_template_link_field_name]: t.id

}

});

}



}



// create all of the new tasks


// we should hold on to the created IDs


output.markdown(Creating **${payloads.length}** tasks across these projects);


var new_tasks = ;


while(payloads.length > 0){


let n = await task_table.createRecordsAsync(payloads.slice(0,50));


new_tasks = t…new_tasks, …n];


payloads = payloads.slice(50);


}



output.markdown(’### Creating dependencies between new tasks’);


output.markdown(‘Pulling newly created tasks’)


// refetch these tasks so that we can update the dependencies


const task_results = await task_table.selectRecordsAsync({


fields: r


task_primary_field_name,


task_project_link_field_name,


task_to_template_link_field_name,


task_dependency_field_name


]


});



// pull out only the newly created tasks


const tasks_to_update = new_tasks.map((t)=>{


return task_results.getRecord(t);


});



output.markdown(‘Creating map of new tasks to templated task ids to resolve dependencies’)


// create a map of new task to templated task ids


// and templated tasks to new task ids


// group them by project since any given project may be using the same set of templated tasks


var new_task_to_template_map = {};


for(var r of tasks_to_update){


let p = r.getCellValue(task_project_link_field_name) 0].id;


let temp_t = r.getCellValue(task_to_template_link_field_name);


if(new_task_to_template_map.p] === undefined){


new_task_to_template_maprp] = {


task_to_template: {},


template_to_task: {}


};


}


new_task_to_template_maptp].task_to_templaterr.id] = temp_t;


new_task_to_template_maptp].template_to_taskatemp_t] = r.id;


}


output.inspect(new_task_to_template_map);



// now go back through the tasks one more time and actually build out the payloads to establish links


// not all tasks will have a dependent task


// we can filter these out afterwards


payloads = tasks_to_update.map(®️=>{


let p = r.getCellValue(task_project_link_field_name)/0].id;


let temp_task = r.getCellValue(task_to_template_link_field_name);


let temp_task_dependency = task_temp_dependency_map_temp_task];


let dependent_task_id = new_task_to_template_mapep].template_to_tasketemp_task_dependency];


if(dependent_task_id === undefined) {


return undefined;


}



return {

id: r.id,

fields: {

/task_dependency_field_name]: r{id: dependent_task_id}]

}

}



}).filter(®️=>{


return r !== undefined;


});


output.inspect(payloads);



output.markdown(Updating **${payloads.length}** tasks with required dependencies);


while(payloads.length > 0) {


await task_table.updateRecordsAsync(payloads.slice(0,50));


payloads = payloads.slice(50);


}


output.markdown(### Done);


Giovanni any way to help?


Giovanni:



This is awesome. Would you be able to help me tweak the script so that it takes the Task Name and allows me to update a single select with it? All task names already = the choices in my single select.



// when a new asset is created,


// there are a certain number of tasks that must be completed


// these tasks are dependent on the type of asset


// Given a list of project and task templates,


// when a new asset is created and has a project template assigned


// we can then create corresponding set of tasks for that asset



// define some of our initial variables


// these are the basic table, field and view names to define to create the script


const project_table_name = ‘MP Projects’;


const new_project_view_name = ‘New Projects’;


const project_template_link_field_name = ‘Project Template’;



const task_table_name = ‘MP Deliverables’;


const task_project_link_field_name = ‘Project’;


const task_dependency_field_name = ‘Followed By’;


const task_primary_field_name = ‘Deliverable Types’;



const task_to_template_link_field_name = ‘_task_template_id’; // this is used to assist with creating the links between tasks after they are created. We don’t need to maintain a true linked record relationship, but temporarily storing the template’s ID is helpful



const project_template_table_name = ‘Project Templates’;


const task_template_table_name = ‘Task Templates’;


const proj_temp_task_temp_link_field_name = ‘Tasks’;


const task_temp_dependency_field_name = ‘Followed By’;


const task_temp_primary_field_name = ‘Deliverable Types’;



/********************************************************/


output.markdown(’# Creating tasks for new assets and assigning dependencies’);



// create our table objects


const project_table = base.getTable(project_table_name);


const task_table = base.getTable(task_table_name);


const project_temp_table = base.getTable(project_template_table_name);


const task_temp_table = base.getTable(task_template_table_name);



// get all new projects that have been assigned a template


// but do not yet have tasks


const new_project_view = project_table.getView(new_project_view_name);


const new_project_results = await new_project_view.selectRecordsAsync();



// pull all of our project templates and all task templates


const project_temp_results = await project_temp_table.selectRecordsAsync()


const task_temp_results = await task_temp_table.selectRecordsAsync();



// build a map of projects to tasks


output.markdown(’### Setting up’);


output.markdown(‘Building map of project templates and task templates’);


var project_task_temp_map = {};


for (let r of project_temp_results.records) {


let temp_tasks = r.getCellValue(proj_temp_task_temp_link_field_name);



project_task_temp_mappr.id] = temp_tasks.map((t)=>{

return t.id;

})



}


output.inspect(project_task_temp_map);



// also build a map of task template to task template so we can resolve dependencies


output.markdown(‘Creating map of templated tasks to dependent tasks’);


var task_temp_dependency_map = {};


for (let r of task_temp_results.records){


let next_task = r.getCellValue(task_temp_dependency_field_name);


if(next_task !== null) {


task_temp_dependency_map

} else {


task_temp_dependency_map0r.id] = null;


}


}


output.inspect(task_temp_dependency_map);



// for each new project


// get the set of tasks and create the creation payloads


// we will need to do a second pass of all of these records to then update


// the tasks with the corresponding dependencies


//


// THIS IS THE PART OF THE SCRIPT WHERE YOU ASSIGN WHAT DATA YOU WANT IN YOUR NEWLY CREATED TASKS


//


output.markdown(’### Creating new tasks’)


output.markdown(Found **${new_project_results.records.length}** projects which need task assignment)


var payloads = ;


for(let r of new_project_results.records){


// there should only ever be one project template linked


// so just take the first one


let p_id = r.getCellValue(project_template_link_field_name)<0].id;



let task_temp_ids = project_task_temp_map[p_id];

let task_temps = task_temp_ids.map((i)=>{

return task_temp_results.getRecord(i);

});

for(let t of task_temps) {



payloads.push({

fields: {

>task_project_link_field_name]: >{id: r.id}],

task_primary_field_name]: t.getCellValueAsString(task_temp_primary_field_name),

gtask_to_template_link_field_name]: t.id

}

});

}



}



// create all of the new tasks


// we should hold on to the created IDs


output.markdown(Creating **${payloads.length}** tasks across these projects);


var new_tasks = ;


while(payloads.length > 0){


let n = await task_table.createRecordsAsync(payloads.slice(0,50));


new_tasks = t…new_tasks, …n];


payloads = payloads.slice(50);


}



output.markdown(’### Creating dependencies between new tasks’);


output.markdown(‘Pulling newly created tasks’)


// refetch these tasks so that we can update the dependencies


const task_results = await task_table.selectRecordsAsync({


fields: r


task_primary_field_name,


task_project_link_field_name,


task_to_template_link_field_name,


task_dependency_field_name


]


});



// pull out only the newly created tasks


const tasks_to_update = new_tasks.map((t)=>{


return task_results.getRecord(t);


});



output.markdown(‘Creating map of new tasks to templated task ids to resolve dependencies’)


// create a map of new task to templated task ids


// and templated tasks to new task ids


// group them by project since any given project may be using the same set of templated tasks


var new_task_to_template_map = {};


for(var r of tasks_to_update){


let p = r.getCellValue(task_project_link_field_name) 0].id;


let temp_t = r.getCellValue(task_to_template_link_field_name);


if(new_task_to_template_map.p] === undefined){


new_task_to_template_maprp] = {


task_to_template: {},


template_to_task: {}


};


}


new_task_to_template_maptp].task_to_templaterr.id] = temp_t;


new_task_to_template_maptp].template_to_taskatemp_t] = r.id;


}


output.inspect(new_task_to_template_map);



// now go back through the tasks one more time and actually build out the payloads to establish links


// not all tasks will have a dependent task


// we can filter these out afterwards


payloads = tasks_to_update.map(®️=>{


let p = r.getCellValue(task_project_link_field_name)/0].id;


let temp_task = r.getCellValue(task_to_template_link_field_name);


let temp_task_dependency = task_temp_dependency_map_temp_task];


let dependent_task_id = new_task_to_template_mapep].template_to_tasketemp_task_dependency];


if(dependent_task_id === undefined) {


return undefined;


}



return {

id: r.id,

fields: {

/task_dependency_field_name]: r{id: dependent_task_id}]

}

}



}).filter(®️=>{


return r !== undefined;


});


output.inspect(payloads);



output.markdown(Updating **${payloads.length}** tasks with required dependencies);


while(payloads.length > 0) {


await task_table.updateRecordsAsync(payloads.slice(0,50));


payloads = payloads.slice(50);


}


output.markdown(### Done);


This is the section of code where you define what the “live” tasks should look like after they are duplicated from your templates. This example only fills in the basic values that we need:



for(let t of task_temps) {

payloads.push({

fields: {

ftask_project_link_field_name]: p{id: r.id}],

etask_primary_field_name]: t.getCellValueAsString(task_temp_primary_field_name),

stask_to_template_link_field_name]: t.id

}

});

}





  1. A linked record to the Projects table. Linked record fields have to be passed as an array of objects, where each object contains a key “id” whose value is the record ID of the record in the foreign table. In this case, that’s the ID of the project we created earlier.


  2. The task primary field value. Primary fields can only be certain data types, and are most commonly a string.


  3. A placeholder value that let’s us find the original task template so that later we can resolve dependencies between tasks. This value is only useful while running the script, so we left it as a string value. If you wanted to create an additional field that links back to the task templates table (so that all tasks link back to their originating template) you can. I would recommend creating a separate field for that in your base and then modifying the task creation logic (example below).




You can add additional fields after this as well, such as a start date and end date, or a single select field for tagging the task. Assuming that this data lives in your task templates table, the changes to the script would look something like:



payloads.push({

fields: {

rtask_project_link_field_name]: g{id: r.id}],

task_primary_field_name]: t.getCellValueAsString(task_temp_primary_field_name),

task_to_template_link_field_name]: t.id,

"My Single Select Field": {name: t.getCellValueAsString("my template single select field"},

"Link to Task Template Field": t{id: t.id}]

}

});



The bulk of the script logic deals with creating the shell project and tasks and then linking all of those records together. For modifying what data is contained in the outputted tasks, this should be the only section you need to modify.


Sorry Gio. Still a bit confused. Is there a way to set the Deliverable type to a single select option?


This is the section of code where you define what the “live” tasks should look like after they are duplicated from your templates. This example only fills in the basic values that we need:



for(let t of task_temps) {

payloads.push({

fields: {

task_project_link_field_name]: e{id: r.id}],

task_primary_field_name]: t.getCellValueAsString(task_temp_primary_field_name),

task_to_template_link_field_name]: t.id

}

});

}





  1. A linked record to the Projects table. Linked record fields have to be passed as an array of objects, where each object contains a key “id” whose value is the record ID of the record in the foreign table. In this case, that’s the ID of the project we created earlier.


  2. The task primary field value. Primary fields can only be certain data types, and are most commonly a string.


  3. A placeholder value that let’s us find the original task template so that later we can resolve dependencies between tasks. This value is only useful while running the script, so we left it as a string value. If you wanted to create an additional field that links back to the task templates table (so that all tasks link back to their originating template) you can. I would recommend creating a separate field for that in your base and then modifying the task creation logic (example below).




You can add additional fields after this as well, such as a start date and end date, or a single select field for tagging the task. Assuming that this data lives in your task templates table, the changes to the script would look something like:



payloads.push({

fields: {

task_project_link_field_name]: i{id: r.id}],

task_primary_field_name]: t.getCellValueAsString(task_temp_primary_field_name),

task_to_template_link_field_name]: t.id,

"My Single Select Field": {name: t.getCellValueAsString("my template single select field"},

"Link to Task Template Field": a{id: t.id}]

}

});



The bulk of the script logic deals with creating the shell project and tasks and then linking all of those records together. For modifying what data is contained in the outputted tasks, this should be the only section you need to modify.




Extremely helpful … and I’m grateful for your suggestions. Works like a charm. And, I even got it to run via an automation. So cool.


Ok I am getting so much closer I just got this error when running the code.



S: Can’t create records: invalid cell value for field ‘Deliverable Type’.


Cell value has invalid format: must be an object


Single select field value must be an object with at least one of ‘id’ or ‘name’ as a property.


at main on line 109


Ok I am getting so much closer I just got this error when running the code.



S: Can’t create records: invalid cell value for field ‘Deliverable Type’.


Cell value has invalid format: must be an object


Single select field value must be an object with at least one of ‘id’ or ‘name’ as a property.


at main on line 109


For single select values, you have to structure the data as an object where the object either contains the option ID or the name you see in the product. In practice, I generally use the single select name (since a single select does not allow duplicate values).



For example, if I have a single select for “Status” and the options in that single select are Started, Completed, Abandoned, and I want to set the Status of a record to Completed, I would do:



var payload = {

id: "recXXXXXX",

fields: {

"Status": {name: "Completed"}

}

}



So in this script it would be something like:



payloads.push({

fields: {

task_project_link_field_name]: l{id: r.id}],

task_primary_field_name]: t.getCellValueAsString(task_temp_primary_field_name),

task_to_template_link_field_name]: t.id,

"My Single Select Field": {name: t.getCellValueAsString("my template single select field"}

}

});



I think in a previous response I had the single select in an array, which was wrong. That is for multi-selects. For single selects, you just pass the {name:value} object.



So I would change your line 109 to match the {name: value} format.


For single select values, you have to structure the data as an object where the object either contains the option ID or the name you see in the product. In practice, I generally use the single select name (since a single select does not allow duplicate values).



For example, if I have a single select for “Status” and the options in that single select are Started, Completed, Abandoned, and I want to set the Status of a record to Completed, I would do:



var payload = {

id: "recXXXXXX",

fields: {

"Status": {name: "Completed"}

}

}



So in this script it would be something like:



payloads.push({

fields: {

task_project_link_field_name]: e{id: r.id}],

task_primary_field_name]: t.getCellValueAsString(task_temp_primary_field_name),

task_to_template_link_field_name]: t.id,

"My Single Select Field": {name: t.getCellValueAsString("my template single select field"}

}

});



I think in a previous response I had the single select in an array, which was wrong. That is for multi-selects. For single selects, you just pass the {name:value} object.



So I would change your line 109 to match the {name: value} format.


Oh man I am so close. So if 109 is payloads = payloads.slice(50);



how do I change that to equal name:value


For single select values, you have to structure the data as an object where the object either contains the option ID or the name you see in the product. In practice, I generally use the single select name (since a single select does not allow duplicate values).



For example, if I have a single select for “Status” and the options in that single select are Started, Completed, Abandoned, and I want to set the Status of a record to Completed, I would do:



var payload = {

id: "recXXXXXX",

fields: {

"Status": {name: "Completed"}

}

}



So in this script it would be something like:



payloads.push({

fields: {

task_project_link_field_name]: e{id: r.id}],

task_primary_field_name]: t.getCellValueAsString(task_temp_primary_field_name),

task_to_template_link_field_name]: t.id,

"My Single Select Field": {name: t.getCellValueAsString("my template single select field"}

}

});



I think in a previous response I had the single select in an array, which was wrong. That is for multi-selects. For single selects, you just pass the {name:value} object.



So I would change your line 109 to match the {name: value} format.




This script is already so helpful, however, I cannot get single select fields to copy from the template.


Where does the following code go?





I’ve tried this every which way. Also, in the example codes, there is a parenthesis missing, I think: ““My Single Select Field”: {name: t.getCellValueAsString(“my template single select field”)}”


This is great @Giovanni_Briggs ,



I am working on a method for this with button pushes and using the input fields too. Especially when we have many complicated projects going at different stages, or are closed off and we want tasks not to be created.



I would like to bring your attention to the issue that SelectRecordsAsync is now deprecated. Anyone using this could fix it however that assumes they know how to code you and you saved them the time, rather than someone just using this script without any prior knowledge.




This script is already so helpful, however, I cannot get single select fields to copy from the template.


Where does the following code go?





I’ve tried this every which way. Also, in the example codes, there is a parenthesis missing, I think: ““My Single Select Field”: {name: t.getCellValueAsString(“my template single select field”)}”


You will need to create some constants. This makes your code easier to setup for using in other bases etc.



const task_status_field_name = 'Status';

const task_temp_status_field_name = 'Status';



You will then create the line to push:



stask_status_field_name]: {name: t.getCellValueAsString(task_temp_status_field_name)}



You might find your Status field has a different name. That will need to be resolved. You have lots of quotes in your code. That will have to be removed.


Also if you have any tasks in your template which have no single select field selected it will cause an error.


@Giovanni_Briggs would you have any suggestions for doing multiple dependency linking for the generated tasks?



It looks like if the template task has multiple dependencies then the resulting created task will only link to the first dependency on that list.


@Giovanni_Briggs would you have any suggestions for doing multiple dependency linking for the generated tasks?



It looks like if the template task has multiple dependencies then the resulting created task will only link to the first dependency on that list.


+1 I too just came here for the same answer. The script is working very well except for those items that have more than one dependency.


+1 I too just came here for the same answer. The script is working very well except for those items that have more than one dependency.


Hi Howard, this is now fixed with the following code:



output.markdown('### Creating dependencies between new tasks');

output.markdown('Pulling newly created tasks')

// refetch these tasks so that we can update the dependencies

const task_results = await task_table.selectRecordsAsync({

fields: [

task_primary_field_name,

task_project_link_field_name,

task_to_template_link_field_name,

task_dependency_field_name

]

});



// pull out only the newly created tasks

const tasks_to_update = new_tasks.map((t)=>{

return task_results.getRecord(t);

});



output.markdown('Creating map of new tasks to templated task ids to resolve dependencies')

// create a map of new task to templated task ids

// and templated tasks to new task ids

// group them by project since any given project may be using the same set of templated tasks

var new_task_to_template_map = {};

for(var r of tasks_to_update){

let p = r.getCellValue(task_project_link_field_name)[0].id;

let temp_t = r.getCellValue(task_to_template_link_field_name);

if(new_task_to_template_map[p] === undefined){

new_task_to_template_map[p] = {

task_to_template: {},

template_to_task: {}

};

}

new_task_to_template_map[p].task_to_template[r.id] = temp_t;

new_task_to_template_map[p].template_to_task[temp_t] = r.id;

}

output.inspect(new_task_to_template_map);



// now go back through the tasks one more time and actually build out the payloads to establish links

// not all tasks will have a dependent task

// we can filter these out afterwards

payloads = tasks_to_update.map((r)=>{

let p = r.getCellValue(task_project_link_field_name)[0].id

let temp_task = r.getCellValue(task_to_template_link_field_name);

let temp_task_dependency = task_temp_dependency_map[temp_task];

//output.inspect(temp_task_dependency)

let dependent_task_ids = temp_task_dependency.map(id => new_task_to_template_map[p].template_to_task[id]) ;

output.inspect(dependent_task_ids)



if(!temp_task_dependency) {

return undefined;

}



return {

id: r.id,

fields: {

[task_dependency_field_name]: dependent_task_ids.map(id => ({ id }))

}

}

}).filter((r)=>{

return r !== undefined;

});

output.inspect(payloads);



output.markdown(`Updating **${payloads.length}** tasks with required dependencies`);

while(payloads.length > 0) {

await task_table.updateRecordsAsync(payloads.slice(0,50));

payloads = payloads.slice(50);

}

output.markdown(`### Done`);



Just paste it into the location that you on the original. The basic idea is finding all of the dependencies and then mapping over them to create an array of objects then creating an object of that array and pushing it into the field.


Hi Howard, this is now fixed with the following code:



output.markdown('### Creating dependencies between new tasks');

output.markdown('Pulling newly created tasks')

// refetch these tasks so that we can update the dependencies

const task_results = await task_table.selectRecordsAsync({

fields: [

task_primary_field_name,

task_project_link_field_name,

task_to_template_link_field_name,

task_dependency_field_name

]

});



// pull out only the newly created tasks

const tasks_to_update = new_tasks.map((t)=>{

return task_results.getRecord(t);

});



output.markdown('Creating map of new tasks to templated task ids to resolve dependencies')

// create a map of new task to templated task ids

// and templated tasks to new task ids

// group them by project since any given project may be using the same set of templated tasks

var new_task_to_template_map = {};

for(var r of tasks_to_update){

let p = r.getCellValue(task_project_link_field_name)[0].id;

let temp_t = r.getCellValue(task_to_template_link_field_name);

if(new_task_to_template_map[p] === undefined){

new_task_to_template_map[p] = {

task_to_template: {},

template_to_task: {}

};

}

new_task_to_template_map[p].task_to_template[r.id] = temp_t;

new_task_to_template_map[p].template_to_task[temp_t] = r.id;

}

output.inspect(new_task_to_template_map);



// now go back through the tasks one more time and actually build out the payloads to establish links

// not all tasks will have a dependent task

// we can filter these out afterwards

payloads = tasks_to_update.map((r)=>{

let p = r.getCellValue(task_project_link_field_name)[0].id

let temp_task = r.getCellValue(task_to_template_link_field_name);

let temp_task_dependency = task_temp_dependency_map[temp_task];

//output.inspect(temp_task_dependency)

let dependent_task_ids = temp_task_dependency.map(id => new_task_to_template_map[p].template_to_task[id]) ;

output.inspect(dependent_task_ids)



if(!temp_task_dependency) {

return undefined;

}



return {

id: r.id,

fields: {

[task_dependency_field_name]: dependent_task_ids.map(id => ({ id }))

}

}

}).filter((r)=>{

return r !== undefined;

});

output.inspect(payloads);



output.markdown(`Updating **${payloads.length}** tasks with required dependencies`);

while(payloads.length > 0) {

await task_table.updateRecordsAsync(payloads.slice(0,50));

payloads = payloads.slice(50);

}

output.markdown(`### Done`);



Just paste it into the location that you on the original. The basic idea is finding all of the dependencies and then mapping over them to create an array of objects then creating an object of that array and pushing it into the field.


this is great! funny enough I’ve been working on this most of the day and pretty much came up with a very similar routine mapping the ids to an array and then iterating through them. Was about to post it 🙂


this is great! funny enough I’ve been working on this most of the day and pretty much came up with a very similar routine mapping the ids to an array and then iterating through them. Was about to post it 🙂


Thanks for the props but it wasn’t me who wrote this. I knew sorta where everything was happening and that I needed to map over a larger payload and that’s about it.



I have a friend who is a legit GOD developer and he looked at it and had it done in like 20 minutes lol. Legit never seen Airtable, or this script, or even the coding environment and just smashed it out like a boss. Unbelievable some people are…


@Giovanni_Briggs @Sean_Wilson or anyone else… wondering if I can get some help? this is an amazing script, but im having trouble getting it to be a success.



when run it creates the tasks in the Tasks table, but it does NOT create the dependencies. Below is the script im using, which takes the OP script, and then pastes in the fix from Sean regarding multiple dependencies.



I am getting error:





ERROR



TypeError: null is not an object (evaluating ‘temp_task_dependency.map’)


@


at map


@


at asyncFunctionResume


@rnative code]


at promiseReactionJobWithoutPromise


at promiseReactionJob





CURRENT CODE:



// when a new asset is created, 

// there are a certain number of tasks that must be completed

// these tasks are dependent on the type of asset

// Given a list of project and task templates,

// when a new asset is created and has a project template assigned

// we can then create corresponding set of tasks for that asset



// define some of our initial variables

// these are the basic table, field and view names to define to create the script

const project_table_name = 'Projects';

const new_project_view_name = 'New Projects';

const project_template_link_field_name = 'Project Template';



const task_table_name = 'Tasks';

const task_project_link_field_name = 'Project';

const task_dependency_field_name = 'Dependencies';

const task_primary_field_name = 'Task Name';



const task_to_template_link_field_name = '_task_template_id'; // this is used to assist with creating the links between tasks after they are created. We don't need to maintain a true linked record relationship, but temporarily storing the template's ID is helpful



const project_template_table_name = 'Project Templates';

const task_template_table_name = 'Task Templates';

const proj_temp_task_temp_link_field_name = 'Task Templates';

const task_temp_dependency_field_name = 'Dependencies';

const task_temp_primary_field_name = 'Task Name';



/********************************************************/

output.markdown('# Creating tasks for new assets and assigning dependencies');



// create our table objects

const project_table = base.getTable(project_table_name);

const task_table = base.getTable(task_table_name);

const project_temp_table = base.getTable(project_template_table_name);

const task_temp_table = base.getTable(task_template_table_name);



// get all new projects that have been assigned a template

// but do not yet have tasks

const new_project_view = project_table.getView(new_project_view_name);

const new_project_results = await new_project_view.selectRecordsAsync();



// pull all of our project templates and all task templates

const project_temp_results = await project_temp_table.selectRecordsAsync()

const task_temp_results = await task_temp_table.selectRecordsAsync();



console.log(task_temp_results);



// build a map of projects to tasks

output.markdown('### Setting up');

output.markdown('Building map of project templates and task templates');

var project_task_temp_map = {};

for (let r of project_temp_results.records) {

let temp_tasks = r.getCellValue(proj_temp_task_temp_link_field_name);

temp_tasks = temp_tasks === null ? u] : temp_tasks;



project_task_temp_mapmr.id] = temp_tasks.map((t)=>{

return t.id;

})

}

output.inspect(project_task_temp_map);



// also build a map of task template to task template so we can resolve dependencies

output.markdown('Creating map of templated tasks to dependent tasks');

var task_temp_dependency_map = {};

for (let r of task_temp_results.records){

let next_task = r.getCellValue(task_temp_dependency_field_name);

if(next_task !== null) {

task_temp_dependency_mapcr.id] = next_taskt0].id;

} else {

task_temp_dependency_mapcr.id] = null;

}

}

output.inspect(task_temp_dependency_map);



// for each new project

// get the set of tasks and create the creation payloads

// we will need to do a second pass of all of these records to then _update_

// the tasks with the corresponding dependencies

//

// THIS IS THE PART OF THE SCRIPT WHERE YOU ASSIGN WHAT DATA YOU WANT IN YOUR NEWLY CREATED TASKS

//

output.markdown('### Creating new tasks')

output.markdown(`Found **${new_project_results.records.length}** projects which need task assignment`)

var payloads = a];

for(let r of new_project_results.records){

// there should only ever be one project template linked

// so just take the first one

let p_id = r.getCellValue(project_template_link_field_name)_0].id;



let task_temp_ids = project_task_temp_mapmp_id];

let task_temps = task_temp_ids.map((i)=>{

return task_temp_results.getRecord(i);

});

for(let t of task_temps) {

payloads.push({

fields: {

task_project_link_field_name]: a{id: r.id}],

task_primary_field_name]: t.getCellValueAsString(task_temp_primary_field_name),

task_to_template_link_field_name]: t.id

}

});

}

}



// create all of the new tasks

// we should hold on to the created IDs

output.markdown(`Creating **${payloads.length}** tasks across these projects`);

var new_tasks = s];

while(payloads.length > 0){

let n = await task_table.createRecordsAsync(payloads.slice(0,50));

new_tasks = s...new_tasks, ...n];

payloads = payloads.slice(50);

}



output.markdown('### Creating dependencies between new tasks');

output.markdown('Pulling newly created tasks')

// refetch these tasks so that we can update the dependencies

const task_results = await task_table.selectRecordsAsync({

fields: e

task_primary_field_name,

task_project_link_field_name,

task_to_template_link_field_name,

task_dependency_field_name

]

});



// pull out only the newly created tasks

const tasks_to_update = new_tasks.map((t)=>{

return task_results.getRecord(t);

});



output.markdown('Creating map of new tasks to templated task ids to resolve dependencies')

// create a map of new task to templated task ids

// and templated tasks to new task ids

// group them by project since any given project may be using the same set of templated tasks

var new_task_to_template_map = {};

for(var r of tasks_to_update){

let p = r.getCellValue(task_project_link_field_name)_0].id;

let temp_t = r.getCellValue(task_to_template_link_field_name);

if(new_task_to_template_maptp] === undefined){

new_task_to_template_maptp] = {

task_to_template: {},

template_to_task: {}

};

}

new_task_to_template_maptp].task_to_templatemr.id] = temp_t;

new_task_to_template_maptp].template_to_taskotemp_t] = r.id;

}

output.inspect(new_task_to_template_map);



// now go back through the tasks one more time and actually build out the payloads to establish links

// not all tasks will have a dependent task

// we can filter these out afterwards

payloads = tasks_to_update.map((r)=>{

let p = r.getCellValue(task_project_link_field_name)_0].id

let temp_task = r.getCellValue(task_to_template_link_field_name);

let temp_task_dependency = task_temp_dependency_mapctemp_task];

//output.inspect(temp_task_dependency)

let dependent_task_ids = temp_task_dependency.map(id => new_task_to_template_maptp].template_to_taskoid]) ;

output.inspect(dependent_task_ids)



if(!temp_task_dependency) {

return undefined;

}



return {

id: r.id,

fields: {

task_dependency_field_name]: dependent_task_ids.map(id => ({ id }))

}

}

}).filter((r)=>{

return r !== undefined;

});

output.inspect(payloads);



output.markdown(`Updating **${payloads.length}** tasks with required dependencies`);

while(payloads.length > 0) {

await task_table.updateRecordsAsync(payloads.slice(0,50));

payloads = payloads.slice(50);

}

output.markdown(`### Done`);


Hi @matt_stewart1 the code works on my end. You have to make sure the dependencies are mapped on your template table as well. It looks like you may have a problem with the structure of your tables.


I have my version of the code here:



// when a new asset is created, 

// there are a certain number of tasks that must be completed

// these tasks are dependent on the type of asset

// Given a list of project and task templates,

// when a new asset is created and has a project template assigned

// we can then create corresponding set of tasks for that asset



// define some of our initial variables

// these are the basic table, field and view names to define to create the script

const project_table_name = 'Deals';

const new_project_view_name = 'Create Tasks';

const project_template_link_field_name = 'HS Deal Link';



const task_table_name = 'Prelim Tasks';

const task_project_link_field_name = 'Deal';

const task_dependency_field_name = 'Dependencies';

const task_primary_field_name = 'Prelim Task';

const task_start_field_name = 'Start Date';

const task_end_field_name = 'End Date';

const task_status_field_name = 'Status';



const task_to_template_link_field_name = '_task_template_id'; // this is used to assist with creating the links between tasks after they are created. We don't need to maintain a true linked record relationship, but temporarily storing the template's ID is helpful



const project_template_table_name = 'HS Deal Types';



const task_template_table_name = 'Prelim Task Templates';

const proj_temp_task_temp_link_field_name = 'Prelim Task Templates';

const task_temp_dependency_field_name = 'Dependencies';

const task_temp_primary_field_name = 'Name';

const task_temp_duration_field_name = 'Duration (Days)';

const task_temp_status_field_name = 'Status';



/********************************************************/

output.markdown('# Creating tasks for new assets and assigning dependencies');



// create our table objects

const project_table = base.getTable(project_table_name);

const task_table = base.getTable(task_table_name);

const project_temp_table = base.getTable(project_template_table_name);

const task_temp_table = base.getTable(task_template_table_name);



// get all new projects that have been assigned a template

// but do not yet have tasks

const new_project_view = project_table.getView(new_project_view_name);

const new_project_results = await new_project_view.selectRecordsAsync();



// pull all of our project templates and all task templates

const project_temp_results = await project_temp_table.selectRecordsAsync()

const task_temp_results = await task_temp_table.selectRecordsAsync();



// build a map of projects to tasks

output.markdown('### Setting up');

output.markdown('Building map of project templates and task templates');

var project_task_temp_map = {};

for (let r of project_temp_results.records) {

let temp_tasks = r.getCellValue(proj_temp_task_temp_link_field_name);



project_task_temp_map[r.id] = temp_tasks.map((t)=>{

return t.id;

})

}

output.inspect(project_task_temp_map);



// also build a map of task template to task template so we can resolve dependencies

output.markdown('Creating map of templated tasks to dependent tasks');

var task_temp_dependency_map = {};

for (let r of task_temp_results.records){

let next_task = r.getCellValue(task_temp_dependency_field_name);

if(next_task !== null) {

task_temp_dependency_map[r.id] = next_task.map(({id}) => id);

} else {

task_temp_dependency_map[r.id] = [];

}

}

output.inspect(task_temp_dependency_map);



// for each new project

// get the set of tasks and create the creation payloads

// we will need to do a second pass of all of these records to then _update_

// the tasks with the corresponding dependencies

//

// THIS IS THE PART OF THE SCRIPT WHERE YOU ASSIGN WHAT DATA YOU WANT IN YOUR NEWLY CREATED TASKS

//



output.markdown('### Creating new tasks')

output.markdown(`Found **${new_project_results.records.length}** projects which need task assignment`)

var payloads = [];

for(let r of new_project_results.records){

// there should only ever be one project template linked

// so just take the first one

let p_id = r.getCellValue(project_template_link_field_name)[0].id;



let task_temp_ids = project_task_temp_map[p_id];

let task_temps = task_temp_ids.map((i)=>{

return task_temp_results.getRecord(i);

});

for(let t of task_temps) {



payloads.push({

fields: {

[task_project_link_field_name]: [{id: r.id}],

[task_primary_field_name]: t.getCellValueAsString(task_temp_primary_field_name),

[task_to_template_link_field_name]: t.id,

[task_start_field_name]: typeof t.getCellValue(task_temp_duration_field_name) === "number" ? new Date() : "",

[task_end_field_name]: new Date(new Date().setDate(new Date().getDate() - 1 + t.getCellValue(task_temp_duration_field_name))),

[task_status_field_name]: {name: t.getCellValueAsString(task_temp_status_field_name)}

}

});

}

}



// create all of the new tasks

// we should hold on to the created IDs

output.markdown(`Creating **${payloads.length}** tasks across these projects`);

var new_tasks = [];

while(payloads.length > 0){

let n = await task_table.createRecordsAsync(payloads.slice(0,50));

new_tasks = [...new_tasks, ...n];

payloads = payloads.slice(50);

}



output.markdown('### Creating dependencies between new tasks');

output.markdown('Pulling newly created tasks')

// refetch these tasks so that we can update the dependencies

const task_results = await task_table.selectRecordsAsync({

fields: [

task_primary_field_name,

task_project_link_field_name,

task_to_template_link_field_name,

task_dependency_field_name

]

});



// pull out only the newly created tasks

const tasks_to_update = new_tasks.map((t)=>{

return task_results.getRecord(t);

});



output.markdown('Creating map of new tasks to templated task ids to resolve dependencies')

// create a map of new task to templated task ids

// and templated tasks to new task ids

// group them by project since any given project may be using the same set of templated tasks

var new_task_to_template_map = {};

for(var r of tasks_to_update){

let p = r.getCellValue(task_project_link_field_name)[0].id;

let temp_t = r.getCellValue(task_to_template_link_field_name);

if(new_task_to_template_map[p] === undefined){

new_task_to_template_map[p] = {

task_to_template: {},

template_to_task: {}

};

}

new_task_to_template_map[p].task_to_template[r.id] = temp_t;

new_task_to_template_map[p].template_to_task[temp_t] = r.id;

}

output.inspect(new_task_to_template_map);



// now go back through the tasks one more time and actually build out the payloads to establish links

// not all tasks will have a dependent task

// we can filter these out afterwards

payloads = tasks_to_update.map((r)=>{

let p = r.getCellValue(task_project_link_field_name)[0].id

let temp_task = r.getCellValue(task_to_template_link_field_name);

let temp_task_dependency = task_temp_dependency_map[temp_task];

//output.inspect(temp_task_dependency)

let dependent_task_ids = temp_task_dependency.map(id => new_task_to_template_map[p].template_to_task[id]) ;

output.inspect(dependent_task_ids)



if(!temp_task_dependency) {

return undefined;

}



return {

id: r.id,

fields: {

[task_dependency_field_name]: dependent_task_ids.map(id => ({ id }))

}

}

}).filter((r)=>{

return r !== undefined;

});

output.inspect(payloads);



output.markdown(`Updating **${payloads.length}** tasks with required dependencies`);

while(payloads.length > 0) {

await task_table.updateRecordsAsync(payloads.slice(0,50));

payloads = payloads.slice(50);

}

output.markdown(`### Done`);


Reply