🙄

Backlog x GAS 俺的メモ

2024/04/02に公開
//バックログのスペースIDを入れます。 以下のhogeのような部分です。
//https://hoge.backlog.jp/
const SPACE_ID = PropertiesService.getScriptProperties().getProperty("SPACE_ID");
//バックログのAPIキーです。 プロフィールアイコン→ 個人設定→ APIから発行する。
//https://hoge.backlog.jp/EditApiSettings.action
const API_KEY = PropertiesService.getScriptProperties().getProperty("API_KEY");
//スプレッドシートのIDを入れます。
const SHEET_ID = "xxxxxxx";
const DEFAULT_SLEEP_TIME = 1;
const ERROR_SLEEP_TIME = 60;
const ISSUE_COUNT_NUM = 100;
let error_count = 0;
const LOG_MODE = 0;
// レート制限
//1行目はタイトルを入れます。これでシートの中身を全部消してもOKです。
const TITLE = [
  "種別",
  "状態",
  "件名",
  "担当者",
  "カテゴリー",
  "期限",
  "課題番号",
  "優先度",
  "内容",
  "URL",
  "更新日(GAS)",
  "失注理由"
];
function getPjList() {
  // const api = "https://" + SPACE_ID + ".backlog.jp/api/v2/projects?apiKey=" + API_KEY;
  // //apiを叩いてレスポンスを変数に入れます。
  // const response = fetchApi(api);
  // //課題一覧を配列にして変数に入れます。
  // const pj_list = JSON.parse(response.getContentText());
  // for (let i = 0; i < Object.keys(pj_list).length; i++) {
  //   if (LOG_MODE > 0) console.log("PJ名:" + pj_list[i].name + "、" + ISSUE_COUNT_NUM + "件目までの取得を開始");
  //   getBackLogData(pj_list[i].id, pj_list[i].name);
  // }
  getBackLogData("xxx", "xx");
}
function getBackLogData(project_id, project_name) {
  //100件ずつしか取得できないのでオフセットを加算して全件取得します。
  let offset = 1;
  //loopがtrueなら以下のwhile文を繰り返します
  let loop = true;
  // sheetが既にあれば追加、なければ追加すべきシートを受け取る
  let sheet = insertOrSelectSheet(project_name);
  // タイトルの分、範囲を取得
  let get_title_range = sheet.getRange(1, 1, 1, TITLE.length);
  // 配列ごと反映する
  get_title_range.setValues([TITLE]);

  while (loop) {
    //sort = statusでステータス順で取得します
    //statusId[] = でステータスが4=完了,3=処理済み,2=処理中,1=未対応で取得できます。
    //countは設定しないと20になります。
    //offsetで初期は1,2周目は101から取得されます
    const api = "https://" + SPACE_ID + ".backlog.jp/api/v2/issues?apiKey=" + API_KEY
      + "&projectId[]=" + project_id + "&sort=status&order=&statusId[]=4&statusId[]=3&statusId[]=2&statusId[]=1&count="
      + ISSUE_COUNT_NUM + "&offset=" + offset;
    console.log(api)

    //apiを叩いてレスポンスを変数に入れます。
    let response = fetchApi(api);
    
    //課題一覧を配列にして変数に入れます。
    while (response === undefined) {
      response = fetchApi(api);
    }
    let issue_list = JSON.parse(response.getContentText());
    let issue_lenth = Object.keys(issue_list).length
    // 0件の時は早期リターン
    if (issue_lenth === 0) break;
    //2行目からは課題情報が入ります。
    let range = 1 + offset;
    let set_value = [];
    // 最大100件ずつデータを配列にする
    for (let i = 0; i < issue_lenth; i++) {
      let issue = issue_list[i];
      // let customFields = JSON.parse(issue.customFields)

      set_value.push ([
        issue.issueType.name,
        issue.status.name,
        issue.summary,
        issue.assignee ? issue.assignee.name : "未設定",
        issue.category ? issue.category.name : "未設定",
        issue.dueDate ? formatDate(new Date(issue.dueDate)) : "未設定",
        issue.issueKey,
        issue.priority.name,
        issue.description,
        "https://" + SPACE_ID + ".backlog.jp/view/" + issue.issueKey,
        new Date(),
        issue.customFields[0].value ? formatDate(new Date(issue.customFields[0].value)) : "未設定",
      ]);
    }

    // 課題数を計算
    let num_rows = set_value.length;
    // 1つ目の課題の項目数を計算
    let num_columns = set_value[0].length;
    let get_columns = sheet.getRange(range, num_columns, num_rows, 1);
    get_columns.setNumberFormat('yyyy/MM/dd H:mm:ss');
    // 課題数と項目数の分、範囲を取得
    let get_range = sheet.getRange(range, 1, num_rows, num_columns);
    // 配列ごと反映する
    get_range.setValues(set_value);
    //取ってきた課題数がISSUE_COUNT_NUMの時は続きがある可能性があるのでISSUE_COUNT_NUM件分ずらしてもう一度ループします
    if (issue_lenth === ISSUE_COUNT_NUM) {
      offset += ISSUE_COUNT_NUM;
      if (LOG_MODE > 1) console.log("PJ名:" + project_name + "、" + (offset + ISSUE_COUNT_NUM - 1) + "件目までの取得を開始");
    } else {
      loop = false;
    }
  }
}
function formatDate(date) {
  let format = 'YYYY/MM/DD';
  format = format.replace(/YYYY/g, date.getFullYear());
  format = format.replace(/MM/g, ('0' + (date.getMonth() + 1)).slice(-2));
  format = format.replace(/DD/g, ('0' + date.getDate()).slice(-2));
  return format;
}
function insertOrSelectSheet(pj_name) {
  let ss = SpreadsheetApp.getActiveSpreadsheet();
  let spreadsheet = SpreadsheetApp.openById(SHEET_ID);
  let sheet = spreadsheet.getSheetByName(pj_name);
  if (sheet === null){
    return ss.insertSheet(pj_name);// 追加したシートを返す
  }
  return sheet;
}
function sleep(sleep_time = DEFAULT_SLEEP_TIME) {
  if (sleep_time !== 0) {
    if (LOG_MODE > 2) console.log(sleep_time + "秒間停止します");
    Utilities.sleep(sleep_time * 1000);
    if (LOG_MODE > 2) console.log("再開します")
  }
}
function fetchApi(url){
  sleep();
  return fetchUrl(url);
}
function fetchUrl(url) {
  try {
    return UrlFetchApp.fetch(url);
  } catch(e) {
    console.log(e);
    if (LOG_MODE > 0) console.log('エラーのため、' + ERROR_SLEEP_TIME + '秒間停止します。')
    if (LOG_MODE > 0) console.log('エラーの数:' + error_count++);
    sleep(ERROR_SLEEP_TIME);
    fetchApi(url);
  }
}

Discussion