LANG SELRCT

Google Apps Scriptのコードを書く場所  (新規作成: スプレッドシート | スクリプトエディタ

2021年8月15日日曜日

Service CloudのCaseをクローズするアクションを追加したい


ここでは

ケースをクローズする「Close Case」アクションを追加したときの手順を書いて行きます。


状況(status)にClosedを表示する方法はこちら↓



以下の道を通って追加できました。

設定 > オブジェクトマネージャー > ケース > ボタン、リンク、およびアクション > 定義済み設定項目値 > ケースページレイアウト > Case Layout > 


FYI
クローズケースクイックアクションを作成する



順番にやっていきます。


まずは左上の歯車アイコンから「設定」を開きます。


「オブジェクトマネージャ」を検索して選択します。


「ケース」を選択します。


「ボタン、リンク、およびアクション」 を選択します。


「新規アクション」を選択します。


「アクション種別」と「表示ラベル」を入力して保存します。
「名前」は自動で入ります。


「保存」を選択します。


定義済み項目値で「新規」を選択します。


項目名で「状況」を選択、特定値で「Closed」を選択して保存します。


ケースページレイアウトで「Case Layout」を選択します。


「モバイルおよび Lightning のアクション」を選択して、「Close Case」を「Salesforce モバイルおよび Lightning Experience アクション」までドラッグ&ドロップして、保存します。


これでケースのフィードに「Close Case」アクションが追加できました。

追加したいボタンを削除するときは、ドラッグ&ドロップを逆に移動して保存すると、ボタン、リンク、およびアクションで削除できるようになる。


参考

クローズケースクイックアクションを作成する

Lightning Experience のケースページレイアウトへのクイックアクションの追加

クローズケースの状況項目を表示するか非表示にするかを選択

ケースのクローズ

ケースの状況選択リスト値に「クローズ」オプションを割り当てられない

Service CloudでCaseの状況(status)に選択肢を追加したい


ここでは

ケースの状況に「Resolved」を追加してみます。





ケース 状況 選択リスト値で新規に「Resolve」を追加します。

設定 > 
オブジェクトマネージャー > 
ケース > 
項目とリレーション > 
状況 > 
ケース 状況 選択リスト値



以下順番にやっていきます。


オブジェクトマネージャ


ケース

項目とリレーション > 状況

ケース 状況 選択リスト値 > 新規

Resolvedを入力 > 保存


これで状況の選択肢に




補足

Closedと同じ完了フラグをつけるには

Resolvedの編集を選択

完了フラグをチェックして保存


完了フラグのついた状況項目を表示するには

「クローズケースの状況項目を表示」 をチェックする必要があるようです。

その方法は以下の記事に書きました。


Service Cloudでケースの状況にClosedを表示したい


以下のように設定すると表示できました。

設定 > サポート設定 > 編集 > クローズケースの状況項目を表示 をチェック > 保存



デフォルトではCaseの状況で「Closed」が選択肢にありません。

このように表示して選択できるようにしたい。



順番にやっていきます。


右上の歯車から設定を選択します。


サポート設定で「クローズケースの状況項目を表示」にチェックが入っていないので


編集を選択します。


クローズケースの状況項目を表示 をチェックして保存します。


これで「Closed」が選択できるようになりました。


参考

クローズケースの状況項目を表示するか非表示にするかを選択

ケースのクローズ

ケースの状況選択リスト値に「クローズ」オプションを割り当てられない

Salesforce APIでCase IDの一覧を取得したい


SOQLを利用して

SELECT+id+from+Case

というクエリでCase IDの一覧を取得してみます。



ここでは
で書いたtoken.gsもトークンをリフレッシュする際に使うため、別ファイルで保存しておきます。

token.gsと今回書くget.gsは別ファイルで持っておく。


事前準備

PropertiesService の UserProperties に access_token を保存しておく
保存する例



get.gs
function getCaseIds() {
  var query = "SELECT id from Case";
  var url = getUserProperty("instance_url") + "/services/data/v47.0/query?q=" + query;// SOQL クエリを実行する
  var response = UrlFetchApp.fetch(url, optionsGet());
  if(response.getResponseCode() === 401) {
    runRefresh();// アクセストークン切れで401ならトークンを更新する
    response = UrlFetchApp.fetch(url, optionsGet());// 再度リクエストを送る
  }
  Logger.log(response);
}

function optionsGet() {
  return {
    "method" : "GET",
    "headers" : {
      "Authorization": "Bearer " + getUserProperty("access_token")
    },
    "muteHttpExceptions": true
  }
}




getData() を実行すると、ログにCase IDの一覧が出力されます。


関連記事



Salesforce APIで使うアクセストークンを取得してリフレッシュ、リボークもしたい(Google Apps Script)


SalesforceのAPIをGoogle Apps Scriptで利用するときの備忘録です。

アクセストークンを取得→保存して、その後リフレッシュとリボークするコードを書きました。

動作環境:Developers Edition



STEP1: GAS
Webアプリを作成する→作り方


STEP2: Salesforce
接続アプリケーションを作成する→作り方


STEP3: GAS
アクセストークンを取得する
UserPropertiesに保存する
リフレッシュトークンでアクセストークンを更新する
リボークしてトークンを無効にする
→この記事


STEP4: GAS
APIにリクエストを送って結果を取得する→TBD


コードの実行

1. Salesforce上で接続アプリケーションを作成した際に発行される「コンシューマ鍵」「コンシューマの秘密」と、GASをWebアプリとして公開したURLを、グローバルの変数に貼り付ける

2. getMyUrl() を実行してログに出たURLにブラウザでアクセスする

3. UserPropertiesにトークン情報が保存される

4. runRefresh() でアクセストークンが更新される

5. runRevoke() でトークン情報を無効にする



token.gs
/************************************
グローバル
************************************/
var client_id = "コンシューマ鍵";
var client_secret = "コンシューマの秘密"
var redirect_uri = "このGASプロジェクトをWebアプリとして公開したURL";

var token_url = "https://login.salesforce.com/services/oauth2/token";

/************************************
//認可コード取得URL
************************************/
function getMyUrl() {
  var url = "https://login.salesforce.com/services/oauth2/authorize?" +// sandboxの場合はinstance_url 
      "response_type=code" + "&" + 
      "client_id=" + client_id + "&" +
      "redirect_uri=" + redirect_uri + "&" +
      "state=mystate";
  Logger.log(url);
}

/************************************
doGet
************************************/
function doGet(e) {// 認可コード取得URLを開いたときに動く処理
  var response = getAccessToken(e);
  setUserProperties( JSON.parse(response));
  return ContentService.createTextOutput(response);// ブラウザに表示する
}

/************************************
getAccessToken
************************************/
function getAccessToken(e) {// 認可コードを利用してトークン情報を取得して返す
  var code = e["parameter"]["code"];
  var payload = {
    "grant_type": "authorization_code",
    "client_id": client_id,
    "client_secret": client_secret,
    "code": code,
    "redirect_uri": redirect_uri
  }
  
  var options = {
    "method": "post",
    "contentType": "application/x-www-form-urlencoded",
    "payload": payload
  };
  var response = UrlFetchApp.fetch(token_url, options);
  return response;
}

/************************************
runRefresh
************************************/
function runRefresh() {// refresh_tokenを使って更新したトークン情報を返す
  var payload = {
    "grant_type": "refresh_token",
    "refresh_token": getUserProperty("refresh_token"),
    "client_id": client_id,
    "client_secret": client_secret
  }
  var options = {
    "method": "post",
    "contentType": "application/x-www-form-urlencoded",
    "payload": payload
  }
  var response = UrlFetchApp.fetch(token_url, options);
  Logger.log(response);
  setUserProperties(JSON.parse(response));
}

/************************************
revokeToken
************************************/
function revokeToken() {
  var url = "https://login.salesforce.com/services/oauth2/revoke";
  var options = {
    "method": "get",
    "contentType": "application/x-www-form-urlencoded",
    "payload": {
      "token": getUserProperty("access_token") // access_tokenをrevokeする場合
      //token: getProp("refresh_token")// refresh_tokenをrevokeする場合
      }
  }
  var response = UrlFetchApp.fetch(url, options);
  Logger.log(response);
}

/************************************
PropertiesService
************************************/
function setUserProperty(key, value) {// ユーザのプロパティに値をひとつ保存する
  PropertiesService.getUserProperties().setProperty(key, value);
}

function setUserProperties(jobj) {// ユーザのプロパティに値をまとめて保存する
  PropertiesService.getUserProperties().setProperties(jobj);
}

function getUserProperty(key) {// ユーザのプロパティから値をひとつ取得する
  return PropertiesService.getUserProperties().getProperty(key);
}

function getUserProperties() {// ユーザのプロパティから値をまとめて取得する
  return PropertiesService.getUserProperties().getProperties();
}




関連記事



2021年8月10日火曜日

配列内の最頻値を求めたい


統計学の勉強中に最頻値を求めようとしてちょっと手こずりました。

書き方は色々あると思います。
ライブラリを使う手もあると思います。


ここでは
与えられた配列から最頻値を出力するまでのプロセスを思うままに書きました。



コード.gsでやること

values = [1,3,2,3,4,2]

という配列内で最も頻度の多い値を

[2, 3]

の形で取得したくて書いたコードです。



コード.gs
/************************************
これを実行する
************************************/
function getMode(){
  const values =[1,3,2,3,4,2];
  const arrays = returnCountValues(values);
  const countKeyObj = returnCountKeyObj(arrays);
  const countValues = returnObjToArray(countKeyObj);
  const mode = returnMode(countValues);
  Logger.log(countValues);
  Logger.log(mode);
}

/************************************
values = [1,3,2,3,4,2]
を
[[1.0, 1.0], [2.0, 2.0], [3.0, 2.0], [4.0, 1.0]]
にして返す
***********************************/
function returnCountValues(values) {
  let arrays = [];
  let count = 1;
  let currentIndex = 0;
  const sortedValues = values.sort();
  for(let i = 0; i < sortedValues.length; i++) {
    if(sortedValues[i] === sortedValues[i-1]) {
      count++;
      arrays[currentIndex-1] = [sortedValues[i], count];
    } else {
      count = 1;
      arrays.push([sortedValues[i], count])
      currentIndex++;
    }
  }
  return arrays;
}

/************************************
arrays = [[1.0, 1.0], [2.0, 2.0], [3.0, 2.0], [4.0, 1.0]]
を
{1=[1.0, 4.0], 2=[2.0, 3.0]}
にして返す
************************************/
function returnCountKeyObj(arrays) {
  let obj = {};
  for(let i = 0; i < arrays.length; i++) {
    const array = arrays[i];
    if(obj[array[1]]) {
      obj[array[1]].push(array[0])
    } else {
      obj[array[1]] = [array[0]];
    }
  }
  return obj;
}

/************************************
obj = {1=[1.0, 4.0], 2=[2.0, 3.0]}
を
[[1, [1.0, 4.0]], [2, [2.0, 3.0]]]
にして返す
************************************/
function returnObjToArray(obj) {
  return Object.entries(obj);
}

/************************************
values = [[1, [1.0, 4.0]], [2, [2.0, 3.0]]]
から
末尾にある最頻値
[2.0, 3.0]
を返す
************************************/
function returnMode(values) {
  const tail = values[values.length - 1];
  const mode = tail[1];
  return mode
}



実行結果









配列内で重複する値の発生数を取得する(二次元配列で返す)


const values = [1, 2, 3, 2] のそれぞれの数値の件数を取得したい

このように
[[1, 1], [2, 2], [3, 1]]

1は1つ
2は2つ
3は1つ



コード.gs
function getCountValues() {
  const values = [1, 2, 3, 2];
  const countValues = returnCountValues(values);
  Logger.log(countValues);
}

function returnCountValues(values) {
  const sortedValues = values.sort();
  const unique = sortedValues.filter(returnUnique);

  let arrays = [];
  for(let i = 0; i < unique.length; i++) {
    const value = unique[i];
    const count = findDup(value, values);
    arrays.push([value, count]);
  }
  return arrays;
}

function findDup(value, values) {
  let count = 0;
  for(let i = 0; i < values.length; i++) {
    if(values[i] === value) {
      count++;
    }
  }
  return count;
}

function returnUnique(value, index, array){
  var valueIndex = array.indexOf(value);
  var result = valueIndex === index;
  Logger.log([array, "の中で", value, "は", valueIndex, "番目にあって、現在のindexは", index, "なのでuniqueの配列への追加は", result])
  return result;
}
意訳
これを実行する
valuesを決める
returnCountValuesに渡して
結果をログに出す


valuesの要素ごとの件数を返す
values並べ替える→1,2,2,3
それをユニークにする→1,2,3

結果を入れる配列を用意する
uniqueの数だけ繰り返す
uniqueをひとつずつ取り出して
values内の重複を探して
arraysに追加する

完成したarraysを返す


重複を見つける
countの初期値を0にしておく
valuesの数だけ繰り返す
もしvalues内にvalueと同じ値があれば
countに1追加していく


countを返す


ユニークを返す
arrayの中でvalueが何番目にあるか
それが現在のvalueの位置と同じならtrue, 違うならfalse
ログに出してみる
を返してtrueだけのunique配列を作る


実行結果

[[1.0, 1.0], [2.0, 2.0], [3.0, 1.0]]

returnUniqueの中で何をやっているのかログで確認しています。




別の書き方


コード.gs
function getCountValues(){
  const values = [1,2,3,2,2,2,3,3,3];
  const arrays = returnCountValues(values);
  Logger.log(arrays);
}

function returnCountValues(values) {
  let arrays = [];
  let count = 1;
  let currentIndex = 0;
  const sortedValues = values.sort();
  for(let i = 0; i < sortedValues.length; i++) {
    if(sortedValues[i] === sortedValues[i-1]) {
      count++;
      arrays[currentIndex-1] = [sortedValues[i], count];
    } else {
      count = 1;
      arrays.push([sortedValues[i], count])
      currentIndex++;
    }
  }
  return arrays;
}

実行結果


2021年7月25日日曜日

現在日時から四半期の開始日と終了日を知りたい


今の日時を取得して、現在の四半期の開始日と終了日を知りたくて書いたコードです。



コード.gs
function myFunction() {
  const month = new Date().getMonth() + 1;// monthは0始まりなので+1
  const quater = judgeQuater(month);
  Logger.log([month, quater]);
}

/************************************
受け取った月からそのQuaterのStartDateと EndDateとQuaterを返す
************************************/
function judgeQuater(month) {
  switch(month) {
    case 7:
    case 8:
    case 9:
      return ["07-01", "09-30", "Q1"];
    case 10:
    case 11:
    case 12:
      return ["10-01", "12-31", "Q2"];
    case 1:
    case 2:
    case 3:
      return ["01-01", "03-31", "Q3"];
    case 4:
    case 5:
    case 6:
      return ["04-01", "06-30", "Q4"];
  }
}




ログ

2021年6月19日土曜日

Googel Apps Scriptでスプレッドシートの句読点や指定文字のあとに改行を入れて置換したい


コメントの実行プロセスに書いたとおり

選択範囲内の指定文字の後に改行を入れてセルを上書きしたくて書いたコードです。



コード.gs
/************************************
formatText()を実行する

実行プロセス
1. 選択範囲の値を取得する
2. 一行ずつテキストを置換して配列に追加する
3. 選択範囲の値を置換したテキストで上書きする
************************************/
function formatText() {
  Logger.log("start");
  const values = targetRange().getValues();
  let newValues = [];
  for(let i = 0; i < values.length; i++) {
    const text = values[i][0];
    const newText = replaceText(text);
    newValues.push([newText]);
  }
  targetRange().setValues(newValues);
  Logger.log("success!");
}

/************************************
テキストの指定文字を置換して返す
************************************/
function replaceText(text) {
  return text.replace(/、/g, "、\n").replace(/。/g, "。\n").replace(/を/g, "を\n");
}

/************************************
現在選択されている範囲を取得して返す
************************************/
function targetRange() {
  return SpreadsheetApp.getActiveSheet().getSelection().getActiveRange();
}



2021年6月18日金曜日

マウス座標がどれだけ変化したか差分を取得したい









コード.gs
function doGet() {
  return HtmlService.createHtmlOutputFromFile("index");
}
意訳
この機能がやること
指定したHTMLファイルを表示する




index.html
<!DOCTYPE html>
<html>
<body>
  <label>X</label><input type="text" id="movedX" value=0>
  <br>
  <label>Y</label><input type="text" id="movedY" value=0>
  <br>
  <label id="rightLeft"></label>
  <br>
  <label id="upDown"></label>
<script>
window.onmousemove = mouseMoved;
 
function mouseMoved(e) {
  elem("movedX").value = e.movementX;
  elem("movedY").value = e.movementY;

  let rightLeft;
  if(e.movementX > 0) {
    rightLeft = "right";
  } else {
    rightLeft = "left";
  }

  let upDown;
  if(e.movementY > 0) {
    upDown = "down";
  } else {
    upDown = "up";
  }

  elem("rightLeft").textContent = rightLeft;
  elem("upDown").textContent = upDown;
}

function elem(id) {
  return document.getElementById(id);
}
</script>
</body>
</html>



2021年5月23日日曜日

マイドライブを指定してファイルを保存したい(DriveApp.getRootFolder())


以前書いた

CSVデータを作ってGoogleドライブに保存する

では、特定のフォルダを指定して保存しました。

ここでは「マイドライブ」に保存するコードを書きました。


save_as_csv()を実行すると

マイドライブにvar dataで設定したデータが保存されて

そのファイルのURLがログに出力されます。



コード.gs
function save_as_csv() {
  var data = "でーた1, でーた2\nでーた3";
  create_file(data);
}

function create_file(data) {
  var content_type = "text/csv";
  var file_name = "FILE_NAME";
  var blob = Utilities.newBlob("", content_type, file_name);
  var file = blob.setDataFromString(data, "UTF-8");
  var folder = DriveApp.getRootFolder();
  var file = folder.createFile(file);
  var fileUrl = file.getUrl();
  Logger.log(fileUrl);
}




2021年5月5日水曜日

JavaScriptのanimate()でアニメーションを試してみたい


Elementの透明度と位置をアニメーションさせたい。

Element.animate() で実装できました。


デモ




コード.gs
function doGet() {
  return HtmlService.createHtmlOutputFromFile("index");
}
意訳
この機能がやること
指定したHTMLファイルを表示する




index.html
<!DOCTYPE html>
<html>

<body>
<textarea id="ta">hello</textarea>
<script>

animation();
function animation() {
  elem("ta").animate({
    "opacity": [ 0, 1 ], // [ フレーム 1, フレーム 2 ]
    "color": [ "#fff", "#000" ] // [ フレーム 1, フレーム 2 ]
    },
    {
      "duration": 2000,
      "iterations": Infinity
    })

  elem("ta").animate({
    "marginLeft": ["0px", "100px"]
    },
    {
      "duration": 1000,
      "easing": "ease",
      "iterations": Infinity
    })
  
  // 移動後の位置を設定しないと元に戻る
  elem("ta").style.marginLeft = "100px";
}

function elem(id) {
  return document.getElementById(id);
}

</script>
</body>
</html>



参考

Element.animate()

Animation

Keyframe Formats

EffectTiming

2021年5月4日火曜日

Google Apps ScriptでURLの表示を変更したい google.script.history.replace()


google.script.history.replace()

で実現できました。


こういうことを実現したい

HtmlServiceのWebアプリのURL
https://script.google.com/macros/s/ID/exec

ページ遷移を発生させずパラメーターを追加したい
https://script.google.com/macros/s/ID/exec?id=1&value=hello



URLの表示を変更したくて調べてみると、History API を取り扱う が見つかりました。
試してみると、history.pushState() や history.replaceState() では変更できなかった。

さらに調べてみると Class google.script.history (Client-side API) が見つかりました。

google.script.history.push() と google.script.history.replace() で実現できました。
pushとreplaceの違いはリファレンスを参照してください。

今回は replace() を使ったコードを書きました。



コード.gs
function doGet() {
  return HtmlService.createHtmlOutputFromFile("index");
}
意訳
この機能がやること
指定したHTMLファイルを表示する




index.html
<!DOCTYPE html>
<html>
<body>

<script>

replaceParam();
function replaceParam() {
  var params = {
    "id": 1,
    "value": "hello"
  };
  google.script.history.replace("", params, "");
}
</script>

</body>
</html>




参考

Class google.script.history (Client-side API) 

History API を取り扱う


2021年4月30日金曜日

[Spreadsheet] 列ごとに値をユニークにしてオブジェクト化したい(2列目は配列で持つ)


以下のような処理を実現したくて書きました。


スプレッドシートのA、B列に以下のようなデータがある時
prefecturecity
東京新宿
東京新宿
東京渋谷
東京渋谷
東京品川
東京品川
東京品川
東京品川
神奈川横浜
神奈川横浜
神奈川横浜
神奈川横浜
神奈川川崎
神奈川川崎


それぞれの列で重複する値をユニークにして
以下のようなオブジェクトにしたい。

 {東京=[新宿, 渋谷, 品川], 神奈川=[横浜, 川崎]}

A列はオブジェクトのkey。
B列は配列で持つ。



コード.gs
function myFunction() {
  var categoriesObj = createCategoriesObj();
  Logger.log(categoriesObj);
}
/************************************
オブジェクトを作成する
************************************/
function createCategoriesObj() {
  var sheet = SpreadsheetApp.getActiveSheet();
  var lastRow = sheet.getLastRow();
  var categories = sheet.getRange(1, 1, lastRow, 1).getValues();
  var subCategories = sheet.getRange(1, 2, lastRow, 1).getValues();
  
  // ヘッダ行の要素は取り除いておく
  categories.shift();
  subCategories.shift();
  
  // ここからオブジェクトを作っていく
  var obj = {};
  var array = [];
  for(var i = 0; i < categories.length; i++) {
    var category = categories[i][0];
    var categoryPrev = categories[i-1];
    if(obj[category] !== obj[categoryPrev]) {//categoryの値が変わった時に配列を初期化したい
      array = [];// 初期化しないと要素が追加され続ける
    }
    array.push(subCategories[i][0]);
    obj[category] = array.filter(returnUnique);// subCategoriesの値が重複してもここでuniqueにする
  }
  return obj;
}

/************************************
一次元配列の要素の値をユニークにする
************************************/
function returnUnique(value_i, index, array){
  var value_i_index = array.indexOf(value_i);// 配列の何番目に要素があるかを先頭の要素から一つずつ確認して
  var result = value_i_index === index;// 配列の要素のindexと同じなら
  return result;// その値を返して配列に追加する(違うなら追加しない→2つ目以降は存在してもindexが異なるため追加されない)
}



myFunctionを実行すると以下のようなログが出力されます。

2021年4月25日日曜日

テキストの改行を削除したい


改行を含むテキストから改行を削除したくて書いたコードです。

ここでは

text.replace(/\n/g, "")

で改行を削除しています。


デモアプリ
上のテキストエリアにテキストを貼り付けると
改行が削除されたテキストが下に出力されます。






コード.gs
function doGet() {
  return HtmlService.createHtmlOutputFromFile("index");
}
意訳
この機能がやること
指定したHTMLファイルを表示する




index.html
<!DOCTYPE html>
<html>
<body>
  <textarea id="ta" style="width:30vw; height:30vh"></textarea>
  <br>
  <textarea id="result_ta" style="width:30vw; height:30vh"></textarea>

<script>
document.getElementById("ta").onkeyup = taKeyup;

function taKeyup() {
  var text = document.getElementById("ta").value;
  var result = text.replace(/\n/g, "");
  document.getElementById("result_ta").value = result;
}

</script>
</body>
</html>





2021年3月2日火曜日

選択したテキストを配列に入れてテキストエリアに出力したい


今回やりたいこと

アプリケーションの中で
選択したテキストを取得する。

選択したテキストを
テキストエリアに入れる。

選択したテキストを
配列に追加していく。



STEP1
「テキストを」を選択するとテキストエリアに「テキストを」が入る。


STEP2
その後、「追加して」を選択するとテキストエリアに「テキストを,追加して」が入る。



コード.gs
function doGet() {
  return HtmlService.createHtmlOutputFromFile("index");
}
意訳
この機能がやること
指定したHTMLファイルを表示する




index.html
<!DOCTYPE html>
<html>
  <body>
    選択した範囲のテキストを配列に追加してテキストエリアに入れる。
    <textarea id="ta"></textarea>

<script>
document.onmouseup = on_mouseup;

var values = [];

function on_mouseup() {
  var text = window.getSelection();
  values.push(text.toString());
  document.getElementById("ta").value = values;
}

</script>

  </body>
</html>



JIRAのカンバンボードで次世代プロジェクトの課題が表示されないときは、サブフィルターを空にしてみる


(コードを使う話ではありません)

クラシック・次世代、両プロジェクトの課題を一つのカンバンボードに表示したい。

と思ってやってみたら
次世代プロジェクトの課題が表示されないという壁にぶつかりました。


解決方法

カンバン ボード サブフィルターに入っている
fixVersion in unreleasedVersions() OR fixVersion is EMPTY
を削除する。


今回やった手順

STEP1
ボード設定の「基本メニュー」の下の方に
カンバン ボード サブフィルター があります。


STEP2
フィルタの条件部分にマウスポインタを移動すると
右端に鉛筆アイコンが表示されます。
それをクリックして編集します。


STEP3
fixVersion in unreleasedVersions() OR fixVersion is EMPTY
という条件を削除して「更新」をクリックします。


サブフィルタがありません
という状態になります。

これで
クラシック・次世代、両プロジェクトの課題を一つのカンバンボードに表示できました。


補足

サブフィルターの設定だけでなく、「列」のマッピングも確認
  1. ボード設定の「列」を開きます
  2. 右端の「課題を含むステータス」を確認します
  3. 対象のステータスの課題がそこにあると、ボードの列に表示されません
  4. 表示したい列にドラッグ&ドロップして追加すると、ボードに表示されます





「列」のマッピングをしているのに
次世代プロジェクトの課題がボードに表示されない場合は
本投稿のカンバン ボード サブフィルターを空にすると表示されるようになるかも。


参考

Any way to include next-gen issues in a classic board ?
https://community.atlassian.com/t5/Jira-questions/Any-way-to-include-next-gen-issues-in-a-classic-board/qaq-p/1012688

2021年2月28日日曜日

Google Apps Scriptでトリガーを作成したい(1分後に実行して自動で削除する after(1000 * 60))


timeBased().after()を使ってみる。



コード.gs
function setTrigger() {
  const trigger = ScriptApp.newTrigger('myFunction')
    .timeBased()
    .after(1000 * 60)// 1分後にmyFunctinを実行する
    .create();
}

function myFunction() {
  console.log("myFunctionが実行されました。");
  deleteTrigger("myFunction");// トリガーを削除する
}

function deleteTrigger(functionName) {
  const allTriggers = ScriptApp.getProjectTriggers();
  for (let i = 0; i < allTriggers.length; i++) {
    if (allTriggers[i].getHandlerFunction() == functionName) {
      ScriptApp.deleteTrigger(allTriggers[i]);
      console.log("deleteTriggerを実行して、myFunctionのトリガーを削除しました。");
      break;
    }
  }
}




setTriggerを実行すると


myFunctionが登録されます。
このトリガーが実行されるのは
after(1000 * 60)
で設定したミリ秒後です。


そして
myFunctionが実行されると
deleteTrigger("myFunction");
が実行されて
トリガーから削除されます。





2021年2月24日水曜日

VLOOKUP関数の代わりにQUERY関数を使ってみる


Googleスプレッドシートにはクエリ言語を使用して検索できるQUERY関数があります。

本投稿では、普段VLOOKUP関数でやっていることを
QUERY関数でもできそうだったので試してみました。



今回書いた関数
=QUERY(A:B,"select B where A = '"&D2&"' limit 1")



こういうことがしたい

E2に検索結果を返す
  1. D2の値に一致するものを
  2. A列の中から探して
  3. B列の値を返す
  4. 複数結果がある場合は最初に見つかったものだけ返す

今回書いた関数
=QUERY(A:B,"select B where A = '"&D2&"' limit 1")

関数の意訳
QUERY:クエリ言語を使用して、データ全体に対するクエリを実行
A:B:検索範囲
select B:B列の値を返す
where A:A列内で検索する
D2:D2の値に一致するものを探す
limit 1:最初に見つかった結果を返す


複数結果を返したい場合
左側を検索したい場合


参考

VLOOKUP関数では左側を検索できないのでQUERY関数でやってみる


Googleスプレッドシートでは

VLOOKUP関数の範囲指定は
検索キーの右側で
左側にあるデータは参照できない(と思います)

QUERY関数を使うと
左右どちらでも参照できるようになるため
今回その関数を書いてみました。



今回書いた関数
=QUERY(A:B,"select A where B = '"&D2&"' limit 1")



こういうことがしたい

E2に検索結果を返す
  1. D2の値に一致するものを
  2. B列の中から探して
  3. A列の値を返す
  4. 複数結果がある場合は最初に見つかったものだけ返す

今回書いた関数
=QUERY(A:B,"select A where B = '"&D2&"' limit 1")

関数の意訳
QUERY:クエリ言語を使用して、データ全体に対するクエリを実行
A:B:検索範囲
select A:A列の値を返す
where B:B列内で検索する
D2:D2の値に一致するものを探す
limit 1:最初に見つかった結果を返す


複数結果を返したい場合

参考

VLOOKUP関数だけではできない複数結果の取得をQUERY関数でやってみる


Goolgeスプレッドシートでは

一致するキーが複数ある場合、VLOOKUP関数では最初に見つかった結果を返します。

本投稿では一致するすべての結果を返したくて、QUERY関数を使ってみました。



課題
VLOOKUP関数では一致する最初に見つかった結果だけを返す

解決策
QUERY関数で一致するすべての結果を返す



こういうことがしたい

E2に検索結果を返す
  1. D2の値に一致するものを
  2. B列の中から探して
  3. A列の値を返す
  4. 複数結果がある場合は改行を入れる

今回書いた関数
=JOIN(char(10),QUERY(A:B,"select A where B = '"&D2&"'"))


関数の意訳
JOIN:指定した区切り文字を使用して1次元配列の要素を結合
char(10):セルの中で改行する(今回はこれを区切り文字にする)
QUERY:クエリ言語を使用して、データ全体に対するクエリを実行
A:B:検索範囲
select A:A列の値を返す
where B:B列内で検索する
D2:D2の値に一致するものを探す


参考


JOIN

Merge cells with Join formula and CHAR(10)

QUERY syntax using cell reference

2021年2月21日日曜日

Googleドライブのファイルやフォルダのショートカットを作りたい createShortcut(targetId)


createShortcut(targetId)というメソッドができていたので試してみました。


以下のメソッドは廃止されたようです。
 addFile(child) 
 addFolder(child) 
 removeFile(child)
 removeFolder(child) 


Deprecated methods
https://developers.google.com/apps-script/reference/drive/folder#deprecated-methods_1


ファイルのショートカットを作りたい

コード.gs
function createShortCut() {
  const fileId = "このファイルIDのショートカットを作る";
  const targetFolderId = "ショートカットを置くフォルダのID";
  
  DriveApp.getFolderById(targetFolderId).createShortcut(fileId);
}


フォルダのショートカットを作りたい

コード2.gs
function createShortCut() {
  const folderId = "このフォルダIDのショートカットを作る";
  const parentFolderId = "ショートカットを置くフォルダのID";

  DriveApp.getFolderById(parentFolderId).createShortcut(folderId);
}


参考

createShortcut(targetId) 

GoogleドライブのファイルやフォルダをmoveTo(destination)で移動したい


moveTo(destination)というメソッドができていたので試してみました。

以下のメソッドは廃止されたようです。
 addFile(child) 
 addFolder(child) 
 removeFile(child)
 removeFolder(child) 

Deprecated methods
https://developers.google.com/apps-script/reference/drive/folder#deprecated-methods_1


ファイルを移動したい

コード.gs
function moveFile() {
  const fileId = "移動したいファイルID";
  const destinationFolderId = "移動先のフォルダID";
  
  const file = DriveApp.getFileById(fileId);
  const destinationFolder = DriveApp.getFolderById(destinationFolderId);
  
  file.moveTo(destinationFolder);
}


フォルダを移動したい

コード2.gs
function moveFolder() {
  const folderId = "移動したいフォルダのID";
  const destinationFolderId = "移動先のフォルダID";
  
  const folder = DriveApp.getFileById(folderId);
  const destinationFolder = DriveApp.getFolderById(destinationFolderId);
  
  folder.moveTo(destinationFolder);
}


参考

moveTo(destination) 

Deprecated methods

Release Notes
July 27, 2020

2021年2月19日金曜日

Googleドライブの親フォルダ内の子フォルダのファイルを取得してHtmlServiceのselectに入れたい2(テキスト出力する)


先に書いた以下の続きです


Googleドライブのフォルダ構成

親フォルダ
└TEST3
└TEST2
└TEST1
    └サンプル2
    └サンプル1
        └テキスト(ドキュメントのbody)
        └説明(ファイルの説明)


サンプル1の中身


HtmlServiceでこのように表示したい




コード.gs
function doGet(e) {
  return HtmlService.createHtmlOutputFromFile("index");
}

/************************************
getFoldersInFolder
************************************/
function getFoldersInFolder() {
  const folderId = "親フォルダID";
  const parentFolder = DriveApp.getFolderById(folderId);
  const folders = parentFolder.getFolders();
  let objs = [];
  while(folders.hasNext()) {
    const folder = folders.next();
    const obj = {};
    obj["name"] = folder.getName();
    obj["id"] = folder.getId();
    objs.push(obj);
  }
  return objs;
}

/************************************
getFilesInFolder
************************************/
function getFilesInFolder(folderId) {
  const parentFolder = DriveApp.getFolderById(folderId);
  const files = parentFolder.getFiles();
  let objs = [];
  while(files.hasNext()) {
    const file = files.next();
    const obj = {};
    obj["name"] = file.getName();
    obj["id"] = file.getId();
    objs.push(obj);
  }
  return objs;
}

/************************************
getDataInFile
************************************/
function getDataInFile(fileId) {
  const file = DriveApp.getFileById(fileId);
  const obj = {};
  obj["name"] = file.getName();
  obj["id"] = file.getId();
  obj["description"] = file.getDescription();
  obj["text"] = getDocText(fileId)
  return obj;
}

/************************************
getDocText
************************************/
function getDocText(fileId) {
  const doc = DocumentApp.openById(fileId);
  const body = doc.getBody().getText();
  return body;
}




index.html
<!DOCTYPE html>
<html>
<body>
<div id="main">
  <select id="folders_select"></select>
  <br>
  <select id="files_select"></select>
  <br>
  <textarea id="text"></textarea>
  <textarea id="description"></textarea>
</div>
<script>

elem("folders_select").onchange = selectFolderChanged;
elem("files_select").onchange = selectFileChanged;

get_data();

/************************************
get_data()
************************************/
function get_data() {
  google.script.run
  .withFailureHandler(onFailure)
  .withSuccessHandler(onSuccess)
  .getFoldersInFolder();
}

/************************************
onSuccess(result)
************************************/
function onSuccess(folders) {
  const select = elem("folders_select");
  for(let i = 0; i < folders.length; i++) {
    const name = folders[i]["name"];
    const id = folders[i]["id"];
    console.log(name, id)
    createSelectOptions(select, name, id);
  }
}

/************************************
createSelectOptions
************************************/
function createSelectOptions(select, name, id) {
  const option = document.createElement("option");
  option.textContent = name;
  option.setAttribute("id", id);
  select.appendChild(option);
}

/************************************
selectFolderChanged
************************************/
function selectFolderChanged() {
  const select = elem("folders_select");
  const index = select.selectedIndex;
  const id = select[index].id;
  
  google.script.run
  .withFailureHandler(onFailure)
  .withSuccessHandler(onSuccessFolderChanged)
  .getFilesInFolder(id);
}

/************************************
selectFolderChanged
************************************/
function selectFileChanged() {
  const select = elem("files_select");
  const index = select.selectedIndex;
  const id = select[index].id;
  
  google.script.run
  .withFailureHandler(onFailure)
  .withSuccessHandler(onSuccessFileChanged)
  .getDataInFile(id);
}

/************************************
onSuccessFolderChanged
************************************/
function onSuccessFolderChanged(files) {
  const select = elem("files_select");
  for(let i = 0; i < files.length; i++) {
    const name = files[i]["name"];
    const id = files[i]["id"];
    console.log(name, id)
    createSelectOptions(select, name, id);
  }
}

/************************************
onSuccessFileChanged
************************************/
function onSuccessFileChanged(data) {
  console.log(data);
  elem("text").value = data["text"];
  elem("description").value = data["description"];
}

/************************************
onFailure(e)
************************************/
function onFailure(e) {
  alert([e.message, e.stack]);
}

/************************************
elem(id)
************************************/
function elem(id) {
  return document.getElementById(id);
}

</script>
</body>
</html>




最新の投稿

Service CloudのCaseをクローズするアクションを追加したい

ここでは ケースをクローズする「Close Case」アクションを追加したときの手順を書いて行きます。 状況(status)にClosedを表示する方法はこちら↓ Service Cloudでケースの状況にClosedを表示したい 以下の道を通って追加できました。 設定 >...