以前のコードはよくないところがありました。
Is there a way to wait until method is completed?
↑のやり取りを元に修正しました。
こちらの投稿を見て、script actionを使えばできるかもと思い、挑戦してみました。
0から書くのはきついので、 AIに教えてもらいながら作ってみました。(AIのコードそのままでは動かないので、asanaAPIのリファレンスと睨めっこが必要です。)
なお、私の環境で何度か動作確認をしましたが、完全に動作しない可能性があることをご了承ください。また、script actionはenterprise以上が必要です。
APIを眺めていたら、タスクテンプレートからタスクを作成することもできるので、親タスク、サブタスク間で依存関係がある場合などは、こちらを使えば良さそうです。
気が向いたら、挑戦してみようと思います。
使い方
下の作り方に書いてあるカスタムフィールドで繰り返しの期間を設定します。全て、設定しないとルールは発動しません。
タスクを完了させます。すると、完了したタスクが複製され、指定した繰り返し期間後が期限のタスクが出来上がります。なお、サブタスクまで複製されますが、サブタスクに日付は入りません。
作り方
-
繰り返し期間を指定するカスタムフィールドを作成する
n年後、月、第m週、曜日という、単一選択のカスタムフィールドを作成します。
選択肢は、年は1-9くらい、月は1-12,mは1−5、曜日は月〜日に瀬亭します。 -
タスクの完了をトリガにしたルールを作る
下図のようなルールを作ります。 -
script actionにコードを作成する
スクリプトアクションにしたのコードを貼り付けて保存します。
/**
* What's in scope?
* 1. (string) project_gid, workspace_gid, task_gid (only if triggered on a task)
* 2. (function) log - this behaves like console.log and takes any number of parameters
* 3. (object) *ApiInstance - for each group of APIs, an object containing functions to call the APIs; for example:
* tasksApiInstance.getTask(...)
* goalsApiInstance.addFollowers(...)
* For more info, see https://github.com/Asana/node-asana
*/
function getNextDueDate(nYearsLater, month, occurrence, dayOfWeek){
// change dayOfWeek to number
const dayMap = {
'日': 0,
'月': 1,
'火': 2,
'水': 3,
'木': 4,
'金': 5,
'土': 6
};
dayOfWeek = dayMap[dayOfWeek];
// get the year after n years
const yearAfterNyears = new Date().getFullYear() + nYearsLater;
log("yearAfterNyears:",yearAfterNyears);
// get the first day of month of the year after n years
const firstDayOfMonth = new Date(yearAfterNyears, month - 1, 1);
log("firstDayOfMonth", firstDayOfMonth);
// get a week name of the first day of month of the year after n years
const dayOfWeekOfFirstDay = firstDayOfMonth.getDay();
log("dayOfWeekOfFirstDay", dayOfWeekOfFirstDay);
// get days until first Nth day
const daysUntilFirstNthDay = (dayOfWeek - dayOfWeekOfFirstDay + 7) % 7;
log("daysUntilFirstNthDay", daysUntilFirstNthDay);
// get the first Nth day
const firstNthDay = 1 + daysUntilFirstNthDay;
log("firstNthDay", firstNthDay);
// get the target day
const targetDay = firstNthDay + (occurrence - 1 ) * 7;
log("targetDay", targetDay);
// get date of due date of target
const nextDueDate = new Date(yearAfterNyears, month - 1, targetDay);
log("nextDueDate", nextDueDate);
// check if nextDueDate exists
if(nextDueDate.getMonth() !== month - 1){
return null;
}
return nextDueDate.toISOString().slice(0, 10);
};
function createCustomFieldsDictionary(data) {
const resultDictionary = data.reduce((acc, current) => {
acc[current.name] = current.display_value;
return acc
},{});
return resultDictionary;
}
async function updateTasks(body, taskGids) {
// for...of ループで非同期処理を順番に実行
for (const gid of taskGids) {
log( taskGids);
try {
// await を使って、各API呼び出しが完了するのを待つ
ret = await tasksApiInstance.updateTask(body, gid);
} catch (error) {
log(gid, error);
// エラーハンドリング
}
}
};
async function getSubtasksGids(gid) {
const subTasks = await tasksApiInstance.getSubtasksForTask(gid); // get subtasks from new task
log(JSON.stringify(subTasks, null, 2));
const subTasksGids = subTasks.data.map(item => item.gid); // get gid of subtasks
return subTasksGids
};
const sleep = async (ms) => {
log('Waiting...');
const start = Date.now();
let currentTime = Date.now();
while(currentTime - start < ms){
currentTime = Date.now();
}
};
async function duplicateTaskWithWaitForSubtask(task_gid, body) {
try {
const newTask = await tasksApiInstance.duplicateTask(body, task_gid);
log("duplicated:", JSON.stringify(newTask, null, 2));
for(let i = 0; i < 10; i++){
const jobStatus = await jobsApiInstance.getJob(newTask.data.gid);
log(JSON.stringify(jobStatus, null, 2))
if(jobStatus.data.status === "succeeded"){
return newTask;
};
if(i < 9){
const start = Date.now();
await sleep(700);
const currentTime = Date.now();
log("start:", start, "end:", currentTime);
}
}
} catch (error) {
log('エラー:', error);
throw error;
}
}
async function run() {
// Make an API request to get the triggered task and wait for the response
// Use the task data in your script by referencing task.data.{attribute} ex: task.data.assignee
let body = {};
let ret = [];
//get the task name
const task = await tasksApiInstance.getTask(task_gid, {'opt_fields': "name,due_on,custom_fields"});
log(JSON.stringify(task, null, 2));
// create custum_fileds dictionary
log(JSON.stringify(task.data.custom_fields, null, 2));
const custom_fieldDictionary = createCustomFieldsDictionary(task.data.custom_fields);
log(JSON.stringify(custom_fieldDictionary, null, 2),custom_fieldDictionary["曜日"]);
//duplicat the task
body = {
"data": {
"name": task.data.name,
"include": ["notes","assignee","subtasks","attachments","tags","followers","projects","dependencies","parent"]
}
};
const newTask = await duplicateTaskWithWaitForSubtask(task_gid, body);
body = {
"data": {
"completed": false,
"due_on":
getNextDueDate(
Number(custom_fieldDictionary["n年後"]),
Number(custom_fieldDictionary["月"]),
Number(custom_fieldDictionary["第m週"][1]),
custom_fieldDictionary["曜日"]
)
}
} ;
ret = await updateTasks(body, [newTask.data.new_task.gid]);
// set subtasks to incompleted
const subTasksGids = await getSubtasksGids(newTask.data.new_task.gid);
body = {
"data": {
"completed": false
}
};
ret = await updateTasks(body, subTasksGids)
}
// To function properly, the script must end with returning a Promise via run().
run();

