Skip to content

Instantly share code, notes, and snippets.

@brbcoding
Created December 16, 2020 18:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save brbcoding/f32a69566ed0e3db7cc99167f9c1f8a5 to your computer and use it in GitHub Desktop.
Save brbcoding/f32a69566ed0e3db7cc99167f9c1f8a5 to your computer and use it in GitHub Desktop.
Taskmaster.js
let config = input.config({
title: 'Taskmaster v0.2',
description: "Create and link tasks to projects based on a project template and task template",
items: [
input.config.table('projectTemplatesTable', {
label: 'What table do you store your project templates in?'
}),
input.config.table('taskTemplatesTable', {
label: 'What table dox you store your task templates in?'
}),
input.config.table('projectsTable', {
label: 'What table do you store your projects in?'
}),
input.config.table('tasksTable', {
label: 'What table do you store your tasks in?'
}),
]
})
let projectTemplatesTable = config.projectTemplatesTable
let taskTemplatesTable = config.taskTemplatesTable
let projectsTable = config.projectsTable
let tasksTable = config.tasksTable
// TODO: add settings for any hardcoded fields
let project = await input.recordAsync("Select a project", projectsTable)
const projectTasks = project.getCellValue("Tasks")
// TODO: if the project already has tasks
// prompt the user to make sure they want to continue
let projectTemplate = project.getCellValue('Request Type')
if(!projectTemplate) {
projectTemplate = await input.recordAsync("Select a template to use when creating tasks for this project", projectTemplatesTable)
await projectsTable.updateRecordAsync(project.id, { "Request Type": [{ id: projectTemplate.id }]})
const projects = await projectsTable.selectRecordsAsync()
project = projects.getRecord(project.id)
}
let projectApproval = project.getCellValue('Approval')
const deadlineRequested = project.getCellValueAsString('Deadline or Open')
const deadline = project.getCellValueAsString("Deadline")
// if there is a deadline requested, and the project timeline hasn't been approved
if(!projectApproval && deadlineRequested === "Deadline") {
const acceptDeadline = await input.buttonsAsync(`The requester has a deadline for this project: ${deadline}. Do you want to accept?`, ['Yes', 'No'])
const approvalStatus = acceptDeadline === "Yes" ? "Approved: Accept Deadline" : "Approved: Timeline Pending"
// if no approval status is selected, force the user to select one.
await projectsTable.updateRecordAsync(project.id, { "Approval": { "name": approvalStatus }})
const projects = await projectsTable.selectRecordsAsync()
project = projects.getRecord(project.id)
}
// check if the project has a deadline, and if it's been accepted
output.markdown(`Creating tasks...`)
// 2. duplicate all records, replacing linked project with selected project
const templateTableRecords = await taskTemplatesTable.selectRecordsAsync()
// Request Type == Project Template
// TODO: make this less stringy
const templateRecords = (templateTableRecords.records || []).filter(record => record.getCellValueAsString('Request Type') === project.getCellValueAsString('Request Type'))
const tasksFieldsNames = [...tasksTable.fields.map(field => field.name), "Use Project Driver", "Start"]
const newRecords = templateRecords.map(record => {
const recordFields = taskTemplatesTable.fields.reduce((field, fieldCurr) => {
let fieldVal = null
if(fieldCurr.isComputed || fieldCurr.name === "Request Type" || (tasksFieldsNames.indexOf(fieldCurr.name) === -1)) {
return { ...field }
} else if(fieldCurr.name === "Use Project Driver") {
let useProjectDriver = record.getCellValue("Use Project Driver")
let projectDriver = project.getCellValue("Driver")
if(useProjectDriver === true && projectDriver && projectDriver.length > 0) {
fieldVal = [{ id: projectDriver[0].id }]
return { ...field, "Task Driver": fieldVal }
} else {
return { ...field }
}
} else if (fieldCurr.name === "Start") {
fieldVal = project.getCellValue("Start")
} else if(fieldCurr.type === "singleSelect") {
fieldVal = { name: record.getCellValueAsString(fieldCurr.name) }
} else if(fieldCurr.type === "multipleRecordLinks") {
} else if (fieldCurr.type === "singleCollaborator") {
let collaborator = record.getCellValue(fieldCurr.name)
if(collaborator) {
fieldVal = [{ id: collaborator.id }]
}
} else {
fieldVal = record.getCellValue(fieldCurr.name)
}
return { ...field, [fieldCurr.name]: fieldVal }
}, { "Project": [{id : project.id }] })
return {
fields: recordFields
}
})
const insertedRecords = await tasksTable.createRecordsAsync(newRecords)
// required post-insert updates
// - dependencies
// - start / end times?
let tasks = await tasksTable.selectRecordsAsync()
let newTasks = insertedRecords.map(recordId => tasks.getRecord(recordId))
// start date of the second task should be the end date of the first task
// const firstTask = await tasks.getRecord(insertedRecords[0])
// const firstTaskStart = firstTask.getCellValueAsString("Start")
// let start = new Date(firstTaskStart)
// TODO: setup start/end times based on the deadline
// we could probably backtrack by using `reverse`
// check if the project has a requested deadline
// and if we have accepted the deadline
// of Approval = Accepted: Deadline
// setup dependencies
let taskDependencies = []
templateRecords.forEach(record => {
const dependencies = record.getCellValue("Dependency")
// lookup indexes from taskDependencies
taskDependencies.push({
id: record.id,
dependentIndices: !dependencies ? null : dependencies.map(dependency => templateRecords.findIndex(d => d.id === dependency.id))
})
})
let postInsertUpdates = newTasks.map((record, idx) => {
const dependentIds = taskDependencies[idx].dependentIndices
if(dependentIds && dependentIds.length) {
// TODO: check to make sure it has a dependency
// const previousRecordDuration = tasks.getRecord(previousRecordId).getCellValue("Duration (Days)")
// start = addDays(start, previousRecordDuration)
// check if the template that created this has a dependency
const update = {
id: record.id,
fields: {
// "Start": start,
"Dependency": dependentIds.map(id => {
return { id: insertedRecords[id] }
})
}
}
return update
}
})
// TODO: combine this and the dependency setup
// let projectApproval = project.getCellValueAsString('Approval')
// const deadlineRequested = project.getCellValueAsString('Deadline or Open')
// const deadline = project.getCellValueAsString("Deadline")
// Approved: Accept Deadline
let dateUpdates = []
if(project.getCellValueAsString('Approval') === "Approved: Accept Deadline") {
let end = project.getCellValue('Deadline')
let start = tasks.getRecord(insertedRecords[insertedRecords.length - 1]).getCellValue("Duration (Days)")
dateUpdates = insertedRecords.reverse().map((record, idx) => {
end = idx === 0 ? end : start
const recordDuration = tasks.getRecord(record).getCellValue("Duration (Days)")
start = subtractDays(new Date(end), recordDuration)
const update = {
id: record,
fields: {
"Start": start,
"End": end,
}
}
return update
})
}
output.markdown("🔗 Updating Dependencies...")
while(postInsertUpdates.length) {
const update = postInsertUpdates.shift()
if(update) await tasksTable.updateRecordAsync(update.id, update.fields)
}
output.markdown("📅 Scheduling Tasks...")
while(dateUpdates.length) {
const update = dateUpdates.shift()
if(update) await tasksTable.updateRecordAsync(update.id, update.fields)
}
// get the duration of the final task
// add it to start
// use it as the project end date
// tasks = await tasksTable.selectRecordsAsync()
// const lastRecordDuration = tasks.getRecord(insertedRecords[insertedRecords.length - 1]).getCellValue("Duration (Days)")
// start = addDays(start, lastRecordDuration)
// output.markdown(start.toString())
// await projectsTable.updateRecordAsync(project.id, { "End": start })
output.markdown("Created tasks!")
function addDays(start, days) {
var date = new Date(start)
date.setDate(date.getDate() + days)
return date
}
function subtractDays(start, days) {
var date = new Date(start)
date.setDate(date.getDate() - days)
return date
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment