IM-BPM for Accel Platform IM-BPM チュートリアルガイド 第18版 2021-04-01

7.3.1. IM-BloomMakerを利用してプロセス定義のグラフを作成する

このチュートリアルでは、IM-LogicDesignerとIM-BloomMakerを利用して、プロセス定義のグラフを表示するページを作成します。
IM-LogicDesignerを利用してIM-BPMのテーブルから、プロセス定義の実行状態のデータを取得します。
取得したデータをIM-BloomMakerのグラフエレメントで表示することにより、プロセス定義の情報をグラフで表します。

注意

本サンプルは2020 Winter(Azalea)以降の環境でのみ動作します。

注意

本サンプルのSQL定義は PostgreSQLでのみ動作するサンプルです。
../../../../_images/bloommaker_addon_process_definition_graph_0001.png
図 : 「完成イメージ」

7.3.1.1. 本サンプルの構成資材

コラム

IM-BloomMaker定義情報のインポートについて

インポート手順は「IM-BloomMaker for Accel Platform ユーザ操作ガイド」-「定義ファイルをインポートする」を参照してください。

コラム

IM-LogicDesigner定義情報のインポートについて

インポート手順は「IM-LogicDesigner ユーザ操作ガイド」-「インポート/エクスポート」を参照してください。

コラム

IM-Authz(認可) ポリシー - XML形式定義情報のインポートについて

パブリックストレージへのファイルの配置は「システム管理者操作ガイド」-「ファイル操作」を参照してください。
また、ジョブネット「認可(ポリシー)インポート」の実行時に実行パラメータfile を追加し、値に上記の認可ファイルのパブリックストレージ上のパスを指定してください。
または、パブリックストレージへの配置の際にファイル名をauthz-policy.xmlへリネームしてパブリックストレージのルート直下に配置し、ジョブを実行してください。

コラム

IM-BPMのテーブル定義情報について

IM-BPMのテーブル定義情報は「intra-mart Accel Series ドキュメントライブラリ IM-BPM」より下記のテーブル定義書をダウンロードして参照してください。

7.3.1.2. 本サンプルの使用方法

  1. サンプル資材をインポートします。
    IM-Authz(認可) ポリシー - XML形式定義情報をインポートする前にIM-BloomMaker定義情報とIM-LogicDesigner定義情報をインポートしてください。
  2. {ベースURL}/bpm/tutorial/process/definition/graph/{プロセス定義ID} へアクセスし、サンプル画面を表示します。

  3. 指定したプロセス定義の直近1年の完了プロセス数の推移や、平均処理時間の推移がグラフで表示されます。

7.3.1.3. IM-LogicDesigner定義情報(im_logicdesigner-data-bloommaker_addon_process_definition_graph.zip)の詳細

7.3.1.3.1. ロジックフロー(【チュートリアル】bloommaker_addon_process_definition_graph-get_graph_data_process_instances

画面に表示されるグラフのうち、下図の赤枠の部分で表示されているグラフの表示に必要なデータを、データベースからIM-LogicDesignerを使用して取得する方法を説明します。
../../../../_images/bloommaker_addon_process_definition_graph_0006.png
図 : 完成イメージ
プロセス定義の完了したインスタンスの数と平均処理時間を1ヶ月ごとに集計し、IM-BloomMakerのグラフエレメントへ渡すことのできる形式へ整形します。
../../../../_images/bloommaker_addon_process_definition_graph_0004.png
図 : 「ロジックフロー(【チュートリアル】bloommaker_addon_process_definition_graph-get_graph_data_process_instances)」
このロジックフローの入出力設定は以下のように設定されています。
入力として検索開始日時とプロセス定義IDを受け取ります。
出力はIM-LogicDesignerの完了プロセス数と平均処理時間のデータを、IM-BloomMakerのグラフエレメントのプロパティである seriesxAxisCategories へ渡せる状態に整形したデータです。
  • 入力
    キー名 説明
    searchAfter <date> 検索開始日時
    processDefinitionId <string> プロセス定義ID
  • 出力
    キー名 説明
    avgProcessTime <object[]> IM-BloomMakerのグラフエレメント「折れ線グラフ」のプロパティ series に渡すためのデータ
    avgProcessTime.name <string> データの名前(このサンプルでは、固定文字列で 処理時間
    avgProcessTime.data <object[]> データ
    avgProcessTime.data.y <long> 経過時間のミリ秒
    avgProcessTime.data.yForTooltip <string> グラフのツールチップに表示する文字列
    endActivityInfoList <object[]> IM-BloomMakerのグラフエレメント「棒グラフ」のプロパティ series に渡すためのデータ
    endActivityInfoList.name <string> データの名前(このサンプルでは、固定文字列で 完了プロセス数
    endActivityInfoList.data <double[]> データ
    periodKeyList <string[]> IM-BloomMakerのグラフエレメント「棒グラフ」のプロパティ xAxisCategories に渡すためのデータ
../../../../_images/bloommaker_addon_process_definition_graph_0005.png
図 : 「入出力設定」

コラム

IM-BloomMakerのグラフエレメントについて

IM-BloomMakerのグラフエレメントについての詳細は、「intra-mart Developer Site」-「IM-BloomMaker グラフエレメントの使い方(基本編)」を参照してください。
  1. 検索終了日時を計算する。
    入力では検索の開始日時しか受け取っていないため、検索の完了日時を計算する必要があります。
    このサンプルでは、JavaScript定義を用いて、検索開始日時の11ヶ月後の月末を検索終了日時としています。
    このJavaScript定義は 【チュートリアル】bloommaker_addon_process_definition_graph-get_graph_data_process_instances_tasks のロジックフローで利用しているものと同一です
  2. SQL定義でテーブルからデータを取得する。
    ACT_HI_ACTINST テーブルから、取得してきたデータの DURATION_ の値を 月ごとに ACT_ID_ でまとめた平均値を算出します。
    SQL定義の中のクエリは以下のように設定されています。
    SELECT
        PROCINST.END_ACT_ID_ AS end_act_id,
        SUM(PROCINST.DURATION_) AS end_act_id_sum,
        COUNT(PROCINST.END_ACT_ID_) AS end_act_id_count,
        to_char((PROCINST.END_TIME_ + (interval '0 seconds')), 'yyyy-MM') AS period_key
    FROM
        (SELECT
        PROC_DEF_ID_,
        CASE WHEN END_ACT_ID_ IS NULL THEN '*terminateEndEvent'
                ELSE END_ACT_ID_
                END AS END_ACT_ID_,
        DURATION_,
        END_TIME_
        FROM
        ACT_HI_PROCINST) PROCINST
    WHERE
        PROCINST.END_TIME_ IS NOT NULL
        AND
        PROCINST.END_TIME_ >= /*startDate*/'2020-01-01'
        AND
        PROCINST.END_TIME_ <= /*endDate*/'2020-08-01'
        AND
        PROCINST.PROC_DEF_ID_ = /*processDefinitionId*/''
    GROUP BY to_char((PROCINST.END_TIME_ + (interval '0 seconds')), 'yyyy-MM'), PROCINST.END_ACT_ID_
    
    このSQL定義を実行すると、指定したプロセス定義の期間ごとのインスタンスの完了数と合計処理時間を、終了時のアクティビティIDごとに取得できます。
    平均処理時間が存在しない期間のデータはこの中には含まれません。

    コラム

    このSQL文をカスタマイズすることで、独自の集計結果を取得できます。
    このSQL定義の出力に合わせて後述のJavaScript定義を修正する必要があります。
  3. JavaScript定義を使用して、出力する形へ整形する。
    SQL定義で取得したデータをIM-BloomMakerのグラフコンポーネントへ渡せる形に整形します。
    最終的にグラフに表示させるためのデータの形は以下です。ツールチップを使用しない場合は data は経過時間の実数値の配列とします。
    今回は完了プロセス数と平均処理時間の二種類のグラフ用のデータを作成します。
    [{
        name: "データ名"
        data: [                         // そのデータの変化の推移
            {
                y: 100000               // 経過時間の実数値
                yForTooltip: "1分40秒"  // グラフのツールチップに表示するための経過時間をみやすい形に整形した文字列
            },
            {
                y: 10
                yForTooltip: "1秒未満"
            },
            {
                y: 50000
                yForTooltip: "50秒"
            }
        ]
    }]
    
    一例として、本サンプルでは以下のような処理で実現しています。
    平均処理時間については、そのままグラフに表示してしまうとわかりにくくなってしまいます。
    そのため、グラフのツールチップに表示するための、読みやすい文字列に変換した値も合わせて生成しています。
    /**
    * mkPeriodKey
    *
    * 与えられた日付を"YYYY-MM"の形の文字列に変換する。
    *
    * @param input {date} - 変換元の日付データ
    * @return {string} "YYYY-MM"形式の文字列
    */
    function mkPeriodKey(date) {
        let monthVal = ('0' + (date.getMonth() + 1)).slice(-2);
        return date.getFullYear() + '-' + monthVal;
    }
    
    /**
    * calcData
    *
    * 与えられた期間で終了したプロセスインスタンスの合計と、平均処理時間を算出する。
    *
    * @param summaryEntity {Object[]} - SQL定義で取得したデータ
    * @param summaryEntity.period_key {string} - YYYY-MM 形式の文字列
    * @param summaryEntity.end_act_id {string} - アクティビティID
    * @param summaryEntity.end_act_id_sum {long} - 合計処理時間
    * @param summaryEntity.end_act_id_count {long} - 終了したインスタンス数
    * @param periodKey {string} - "YYYY-MM"形式の文字列
    * @return {Number[]} [その期間で終了したインスタンス数の合計, 平均処理時間]
    */
    function calcData(summaryEntity, periodKey) {
        let filterdData = summaryEntity.filter(function (v) {
            return v.period_key === periodKey;
        });
    
        let processingTimeSum = filterdData.reduce(function(acc, current) {return acc + current.end_act_id_sum}, 0);
        let processInstanceCount = filterdData.reduce(function(acc, current) {return acc + current.end_act_id_count}, 0);
    
        let endActivitiEvent = filterdData.map(function(v) {
          return {
            id: v.end_act_id,
            data: v.end_act_id_count
          };
        });
    
        if (processInstanceCount === 0) {
            return [endActivitiEvent, 0];
        }
        return [endActivitiEvent, processingTimeSum/processInstanceCount];
    }
    
    /**
    * timeFormat
    *
    * 経過時間のミリ秒を人が理解しやすい形に変換する。
    *
    * @param milSec {long} - 変換対象のミリ秒
    * @param locale {string} - ロケールの文字列
    * @return {string} 変換後の文字列
    */
    function timeFormat(milSec, locale) {
      let i18n = {
        LESS_THAN_SECOND: {
          en: "Less than 1s",
          ja: "1秒未満",
          zh_CN: "1秒不足"
        },
        DAYS: {
          en: "Day(s)",
          ja: "日",
          zh_CN: "日"
        },
        HOURS: {
          en: "h",
          ja: "時間",
          zh_CN: "时间"
        },
        MINUTES: {
          en: "min",
          ja: "分",
          zh_CN: "分"
        },
        SECONDS: {
          en: "s",
          ja: "秒",
          zh_CN: "秒"
        }
      };
      let TO_SEC = 1000;
      let TO_MIN = TO_SEC * 60;
      let TO_HOUR = TO_MIN * 60;
      let TO_DAY = TO_HOUR * 24;
    
      if (milSec < TO_SEC) {
        return i18n.LESS_THAN_SECOND[locale];
      }
    
      let str = '';
      let _milSec = milSec;
      if (_milSec >= TO_DAY) {
        let val = Math.floor(_milSec/TO_DAY);
        str += val + i18n.DAYS[locale];
        _milSec -= val*TO_DAY;
      }
      if (_milSec >= TO_HOUR) {
        let val = Math.floor(_milSec/TO_HOUR);
        str += val + i18n.HOURS[locale];
        _milSec -= val*TO_HOUR;
      }
      if (_milSec >= TO_MIN) {
        let val = Math.floor(_milSec/TO_MIN);
        str += val + i18n.MINUTES[locale];
        _milSec -= val*TO_MIN;
      }
      if (_milSec >= TO_SEC) {
        let val = Math.floor(_milSec/TO_SEC);
        str += val + i18n.SECONDS[locale];
        _milSec -= val*TO_SEC;
      }
      return str;
    }
    
    /**
    * timeList2graphDataList
    *
    * 経過時間のミリ秒のリストをBloomMakerのグラフ表示用の形式に変換します。
    *
    * @param timeList {long[]} - 変換対象のミリ秒のリスト
    * @param locale {string} - ロケールの文字列
    * @return {Object} BloomMakerのグラフ表示用の形式({y: 実数値, yForTooltip: ツールチップ用の値})
    */
    function timeList2graphDataList(timeList, locale) {
      return timeList.map(function (v) {
        return {
          y: v,
          yForTooltip: timeFormat(v, locale)
        };
      });
    }
    
    /**
    * run.
    *
    * @param input {Object} - task input data.
    * @param input.summaryEntity {Object[]} - SQL定義で取得したデータ
    * @param input.summaryEntity.period_key {string} - YYYY-MM 形式の文字列
    * @param input.summaryEntity.end_act_id {string} - アクティビティID
    * @param input.summaryEntity.end_act_id_sum {long} - 合計処理時間
    * @param input.summaryEntity.end_act_id_count {long} - 終了したインスタンス数
    * @param input.startDate {date} - 集計開始期間
    * @param input.processDefinitionId {string} プロセス定義ID
    * @return {Object} task result.
    */
    function run(input) {
        let startDateFullYear = input.startDate.getFullYear();
        let startDateMonth = input.startDate.getMonth();
    
        let i18nTerminateEndEventLabel = {
          en: "Forced Termination",
          ja: "強制停止",
          zh_CN: "强制停止"
        };
    
        let id2nameDict = {'*terminateEndEvent': i18nTerminateEndEventLabel[input.locale]};
        let bpmRepositoryService = new bpm.RepositoryService();
        let bpmModelResource = bpmRepositoryService.getBpmnModel(input.processDefinitionId);
        bpmModelResource.data.processes.forEach(function (processes) {
          processes.flowElements.forEach(function(elements) {
            id2nameDict[elements.id] = elements.name;
          });
        });
    
        // 範囲となる月日の文字列のリストを作成
        let periodKeyList = [];
        for (let i=0; i<12; i++) {
            let targetMonth = i + startDateMonth;
            let targetDate = new Date(startDateFullYear, targetMonth);
            periodKeyList.push(mkPeriodKey(targetDate));
        }
    
        // 今回出てくるアクティビティIDのリストを作成
        let endActIdList = input.summaryEntity.map(function (v) {
            return v.end_act_id;
        }).filter(function (v, i, array) {
            return array.indexOf(v) === i;
        });
    
        // 期間ごとの完了したインスタンスの合計数と平均処理時間を算出
        let processInstanceCntList = [];
        let averageProcessingTimeList = [];
        periodKeyList.forEach(function (periodKey) {
            let data = calcData(input.summaryEntity, periodKey, endActIdList);
            let processInstanceCnt = data[0];
            let averageProcessingTime = data[1];
            processInstanceCntList.push(processInstanceCnt);
            averageProcessingTimeList.push(averageProcessingTime);
        });
    
        let i18nAvgProcessTimeLabel = {
            en: "Processing time",
            ja: "処理時間",
            zh_CN: "处理时间"
        };
    
        let avgProcessTime = [{
            name: i18nAvgProcessTimeLabel[input.locale],
            data: timeList2graphDataList(averageProcessingTimeList, input.locale)
        }];
    
        let duplicateNameList = endActIdList.map(function(actId) {
          return id2nameDict[actId];
        }).filter(function(v, i ,arr) {
          return arr.indexOf(v) !== i;
        });
    
        let endActivityInfoList = endActIdList.map(function(actId) {
          return {
            name: duplicateNameList.indexOf(id2nameDict[actId]) < 0 ? id2nameDict[actId] : id2nameDict[actId] + '(' + actId + ')',
            data: processInstanceCntList.map(function(priodData) {
              let res = priodData.filter(function(v) {
                return v.id === actId;
              });
              return res.length === 0 ? 0 : res[0].data;
            })
          };
        });
    
        return {
            avgProcessTime: avgProcessTime,
            endActivityInfoList: endActivityInfoList,
            periodKeyList: periodKeyList
        };
    }
    

    コラム

    独自の集計結果を表示させるためには、前述のSQL定義の出力に合わせてJavaScript定義の入力と整形の処理を修正する必要があります。
  4. JavaScript定義の結果を出力に繋ぎます。
    • これで、入力として検索開始日時とプロセス定義IDを受け取り、IM-BloomMakerのグラフエレメント用のデータを出力するロジックフローが完成しました。

7.3.1.3.2. ロジックフロー(【チュートリアル】bloommaker_addon_process_definition_graph-get_graph_data_process_instances_tasks

画面に表示されるグラフのうち、下図の赤枠の部分で表示されているグラフの表示に必要なデータを、データベースからIM-LogicDesignerを使用して取得する方法を説明します。
../../../../_images/bloommaker_addon_process_definition_graph_0007.png
図 : 完成イメージ
プロセス定義の完了したタスクの情報を1ヶ月ごとに集計し、IM-BloomMakerのグラフエレメントへ渡すことのできる形式へ整形します。
../../../../_images/bloommaker_addon_process_definition_graph_0002.png
図 : 「ロジックフロー(【チュートリアル】bloommaker_addon_process_definition_graph-get_graph_data_process_instances_tasks)」
このロジックフローの入出力設定は以下のように設定されています。
入力として検索開始日時とプロセス定義IDを受け取ります。
出力はIM-BloomMakerのグラフエレメント「棒グラフ」のプロパティである seriesxAxisCategories へ渡せる状態に整形されたデータです。
  • 入力
    キー名 説明
    searchAfter <date> 検索開始日時
    processDefinitionId <string> プロセス定義ID
  • 出力
    キー名 説明
    summaryData <object[]> IM-BloomMakerのグラフエレメント「棒グラフ」のプロパティ series に渡すためのデータ
    summaryData.name <string> データの名前(このサンプルでは、タスク名)
    summaryData.data <object[]> データ
    summaryData.data.y <long> 経過時間のミリ秒
    summaryData.data.yForTooltip <string> グラフのツールチップに表示する文字列
    periodKeyList <string[]> IM-BloomMakerのグラフエレメント「棒グラフ」のプロパティ xAxisCategories に渡すためのデータ
../../../../_images/bloommaker_addon_process_definition_graph_0003.png
図 : 「入出力設定」

コラム

IM-BloomMakerのグラフエレメントについて

IM-BloomMakerのグラフエレメントについての詳細は、「intra-mart Developer Site」-「IM-BloomMaker グラフエレメントの使い方(基本編)」を参照してください。
  1. 検索終了日時を計算する。
    入力では検索の開始日時しか受け取っていないため、検索の完了日時を計算する必要があります。
    このサンプルでは、JavaScript定義を用いて、検索開始日時の11ヶ月後の月末を検索終了日時としています。
  2. SQL定義でテーブルからデータを取得する。
    ACT_HI_ACTINST テーブルから、取得してきたデータの DURATION_ の値を 月ごとに ACT_ID_ でまとめた平均値を算出します。
    SQL定義の中のクエリは以下のように設定されています。
    SELECT
        to_char((ACTINST.END_TIME_ + (interval '0 seconds')), 'yyyy-MM') AS period_key,
        ACT_ID_ AS act_id,
        ACT_NAME_ AS act_name,
        AVG(DURATION_) AS processing_time
    FROM
        ACT_HI_ACTINST ACTINST
    WHERE
        ACTINST.END_TIME_ IS NOT NULL
        AND
        ACTINST.END_TIME_ >= /*startDate*/'2020-01-01'
        AND
        ACTINST.END_TIME_ <= /*endDate*/'2020-06-01'
        AND
        ACTINST.PROC_DEF_ID_ = /*processDefinitionId*/''
        AND
        ACTINST.ACT_TYPE_ IN ('userTask', 'receiveTask', 'callActivity', 'imDraftWorkflowServiceTask', 'imApplyWorkflowServiceTask', 'imDraftFormaServiceTask', 'imApplyFormaServiceTask', 'imDraftBisServiceTask', 'imApplyBisServiceTask')
    GROUP BY to_char((ACTINST.END_TIME_ + (interval '0 seconds')), 'yyyy-MM'), ACT_ID_, ACT_NAME_
    ORDER BY period_key
    
    このSQL定義を実行すると、アクティビティIDの特定期間の平均処理時間が取得できます。
    平均処理時間が存在しないアクティビティIDの特定期間のデータはこの中には含まれません。

    コラム

    このSQL文をカスタマイズすることで、独自の集計結果を取得できます。
    このSQL定義の出力に合わせて後述のJavaScript定義を修正する必要があります。
  3. JavaScript定義を使用して、出力する形へ整形する。
    SQL定義で取得したデータをIM-BloomMakerのグラフコンポーネントへ渡せる形に整形します。
    最終的にグラフに表示させるためのデータの形は以下です。
    表示させたいタスクの数だけこのデータを作成し、配列にする必要があります。
    [{
        name: "データ名"
        data: [                         // そのデータの変化の推移
            {
                y: 100000               // 経過時間の実数値
                yForTooltip: "1分40秒"  // グラフのツールチップに表示するための経過時間をみやすい形に整形した文字列
            },
            {
                y: 10
                yForTooltip: "1秒未満"
            },
            {
                y: 50000
                yForTooltip: "50秒"
            }
        ]
    }]
    
    一例として、本サンプルでは以下のような処理で実現しています。
    /**
    * mkPeriodKey
    *
    * 与えられた日付を"YYYY-MM"の形の文字列に変換する。
    *
    * @param input {date} - 変換元の日付データ
    * @return {string} "YYYY-MM"形式の文字列
    */
    function mkPeriodKey(date) {
        let monthVal = ('0' + (date.getMonth() + 1)).slice(-2);
        return date.getFullYear() + '-' + monthVal;
    }
    
    /**
    * getProcessingTime
    *
    * summaryEntity から指定されたタスク名・日付に該当する処理時間を返す。
    * 存在しない場合は0を返す。
    *
    * @param summaryEntity {Object[]} - SQL定義で取得したデータ
    * @param summaryEntity.period_key {string} - YYYY-MM 形式の文字列
    * @param summaryEntity.act_id {string} - アクティビティID
    * @param summaryEntity.processing_time {long} - タスクの処理時間
    * @param summaryEntity.act_name {string} - タスク名
    * @param actName {string} - 抽出対象のタスク名
    * @param periodKey {string} - 抽出対象の日付文字列
    * @return {number} 平均処理時間
    */
    function getProcessingTime(summaryEntity, actName, periodKey) {
        let filterdData = summaryEntity.filter(function (v) {
            return v.act_name === actName && v.period_key === periodKey;
        });
        if (filterdData.length === 0) {
            return 0;
        }
        return filterdData[0].processing_time;
    }
    
    /**
    * timeFormat
    *
    * 経過時間のミリ秒を人が理解しやすい形に変換する。
    *
    * @param milSec {long} - 変換対象のミリ秒
    * @param locale {string} - ロケールの文字列
    * @return {string} 変換後の文字列
    */
    function timeFormat(milSec, locale) {
      let i18n = {
        LESS_THAN_SECOND: {
          en: "Less than 1s",
          ja: "1秒未満",
          zh_CN: "1秒不足"
        },
        DAYS: {
          en: "Day(s)",
          ja: "日",
          zh_CN: "日"
        },
        HOURS: {
          en: "h",
          ja: "時間",
          zh_CN: "时间"
        },
        MINUTES: {
          en: "min",
          ja: "分",
          zh_CN: "分"
        },
        SECONDS: {
          en: "s",
          ja: "秒",
          zh_CN: "秒"
        }
      };
      let TO_SEC = 1000;
      let TO_MIN = TO_SEC * 60;
      let TO_HOUR = TO_MIN * 60;
      let TO_DAY = TO_HOUR * 24;
    
      if (milSec === null || milSec < TO_SEC) {
        return i18n.LESS_THAN_SECOND[locale];
      }
    
      let str = '';
      let _milSec = milSec;
      if (_milSec >= TO_DAY) {
        let val = Math.floor(_milSec/TO_DAY);
        str += val + i18n.DAYS[locale];
        _milSec -= val*TO_DAY;
      }
      if (_milSec >= TO_HOUR) {
        let val = Math.floor(_milSec/TO_HOUR);
        str += val + i18n.HOURS[locale];
        _milSec -= val*TO_HOUR;
      }
      if (_milSec >= TO_MIN) {
        let val = Math.floor(_milSec/TO_MIN);
        str += val + i18n.MINUTES[locale];
        _milSec -= val*TO_MIN;
      }
      if (_milSec >= TO_SEC) {
        let val = Math.floor(_milSec/TO_SEC);
        str += val + i18n.SECONDS[locale];
        _milSec -= val*TO_SEC;
      }
      return str;
    }
    
    /**
    * timeList2graphDataList
    *
    * 経過時間のミリ秒のリストをBloomMakerのグラフ表示用の形式に変換します。
    *
    * @param timeList {long[]} - 変換対象のミリ秒のリスト
    * @param locale {string} - ロケールの文字列
    * @return {Object} BloomMakerのグラフ表示用の形式({y: 実数値, yForTooltip: ツールチップ用の値})
    */
    function timeList2graphDataList(timeList, locale) {
      return timeList.map(function (v) {
        return {
          y: v,
          yForTooltip: timeFormat(v, locale)
        };
      });
    }
    
    /**
    * run.
    *
    * @param input {Object} - task input data.
    * @param input.summaryEntity {Object[]} - SQL定義で取得したデータ
    * @param input.summaryEntity.period_key {string} - YYYY-MM 形式の文字列
    * @param input.summaryEntity.act_id {string} - アクティビティID
    * @param input.summaryEntity.processing_time {long} - タスクの処理時間
    * @param input.summaryEntity.act_name {string} - タスク名
    * @param input.startDate {date} - 集計開始期間
    * @return {Object} task result.
    */
    function run(input) {
        let startDateFullYear = input.startDate.getFullYear();
        let startDateMonth = input.startDate.getMonth();
    
        // 範囲となる月日の文字列のリストを作成
        let periodKeyList = [];
        for (let i=0; i<12; i++) {
            let targetMonth = i + startDateMonth;
            let targetDate = new Date(startDateFullYear, targetMonth);
            periodKeyList.push(mkPeriodKey(targetDate));
        }
    
        // 今回出てくるアクティビティの情報のリストを作成
        let actInfoList = input.summaryEntity.map(function (v) {
            return {
              id: v.act_id,
              name: v.act_name
            };
        }).filter(function (v, i, array) {
            return array.map(function(info) {
              return info.id;
            }).indexOf(v.id) === i;
        });
    
        // 別IDでアクティビティ名が重複しているものを探す
        let duplicateNameList = actInfoList.map(function(info) {
          return info.name;
        }).filter(function(v, i ,arr) {
          return arr.indexOf(v) !== i;
        });
    
        // ActivityNameそれぞれに対してグラフ用のデータを作成する。
        let summaryEntity = actInfoList.map(function (actInfo) {
            let timeList = periodKeyList.map(function (periodKey) {
              return getProcessingTime(input.summaryEntity, actInfo.name, periodKey);
            });
            return {
              name: duplicateNameList.indexOf(actInfo.name) < 0 ? actInfo.name: actInfo.name + '(' + actInfo.id + ')',
              data: timeList2graphDataList(timeList, input.locale)
            };
        });
    
        return {
            summaryData: summaryEntity,
            periodKeyList: periodKeyList
        };
    }
    

    コラム

    独自の集計結果を表示させるためには、前述のSQL定義の出力に合わせてJavaScript定義の入力と整形の処理を修正する必要があります。
  4. JavaScript定義の結果を出力に繋ぎます。
    • これで、入力として検索開始日時とプロセス定義IDを受け取り、IM-BloomMakerのグラフエレメント用のデータを出力するロジックフローが完成しました。

7.3.1.3.3. ルーティング定義

  1. ルーティング定義の作成。
    • 作成したロジックフローをREST APIとして呼び出せるようにルーティング定義を作成します。
      • 本サンプルではそれぞれのロジックフローに対し、以下のルーティングを設定しています。
        • 【チュートリアル】bloommaker_addon_process_definition_graph-get_graph_data_process_instances_tasks: api/bpm/tutorial/process-summary-of-completed-activities-group-by-month
        • 【チュートリアル】bloommaker_addon_process_definition_graph-get_graph_data_process_instances: api/bpm/tutorial/process-summary-of-completed-instances-group-by-month
    • 作成した後に任意の認可を設定します。
      • 本サンプルでは、「IM-BPM管理者」に対して「実行」権限を付与しています。
    • 以上でIM-BloomMakerからロジックフローを呼び出す準備が完了しました。

    コラム

    ルーティングの認可設定の詳細は「IM-LogicDesigner ユーザ操作ガイド」-「フロールーティングの認可設定」を参照してください。

7.3.1.4. IM-BloomMaker定義情報(im_bloommaker-data-bloommaker_addon_process_definition_graph.zip)の詳細

7.3.1.4.1. コンテンツ定義

IM-LogicDesignerで作成したREST APIを利用して、プロセス定義の実行状態のグラフを表示する画面を作成します。
画面を作成する上でのポイントは以下の点です
  • 対象のプロセス定義IDを画面のURLから判断する。
  • アクションでIM-LogicDesignerで作成したAPIを使用してグラフ表示用のデータを取得する。
  • Y軸が平均処理時間を表す場合は、y軸の単位を適切な値に変換して表示する。
  • 取得してきたデータをグラフエレメントへマッピングする。
  • ページを開いたタイミングで、データを取得し描画する。
  1. 対象のプロセス定義IDを画面のURLから受け取る。
    画面のURLに入力されている値をIM-BloomMakerのコンテンツ定義で使用する場合はルーティング定義のURLの設定と入力値の設定が必要です。
    この入力値はリクエストパラメータとしてREST APIでのデータ取得で使用するため、リクエストパラメータ用の変数に代入をしてください。

    コラム

    入力値の設定に関する詳細は「IM-BloomMaker for Accel Platform ユーザ操作ガイド」 - 「入力の設定方法」を参照してください。
  2. アクションでIM-LogicDesignerで作成したAPIを使用してグラフ表示用のデータを取得する。
    アクションエディタにて、アクションアイテム URL「」にリクエストを送信する を使用してデータを取得します。

    コラム

    アクションの設定に関する詳細は「IM-BloomMaker for Accel Platform ユーザ操作ガイド」 - 「アクションを設定する」を参照してください。
  3. Y軸が平均処理時間を表す場合は、y軸の単位を適切な値に変換して表示する。
    取得してきたデータのままだと、平均処理時間の単位がミリ秒となっているので、必要に応じて分や日などに変換をする必要があります。
    アクションアイテム URL「」にリクエストを送信する の下に カスタムスクリプトを実行する を配置します。
    カスタムスクリプトで平均処理時間の単位を変更処理を記述することにより、グラフに表示した際のY軸の値がわかりやすくなります。
  4. 取得してきたデータをグラフエレメントへマッピングする。
    • 「グラフ」に分類されるエレメントの一覧から、「折れ線グラフ」または「棒グラフ」を設置します。

    • IM-LogicDesignerで作成したロジックフローの出力には以下の2種類の情報が含まれていますので、それぞれマッピングします。
      • グラフエレメントのプロパティ series にマッピングするためのグラフのメインとなるデータ
      • グラフエレメントのプロパティ xAxisCategories にマッピングするためのX軸のラベルに表示する文字列のデータ
    • ツールチップを表示させる場合は、グラフエレメントのプロパティ tooltipPointFormat に以下の値を設定します。
      • <span style="color:{point.color}">●</span> {series.name}: <b>{point.yForTooltip}</b><br/>
  5. ページを開いたタイミングで、データを取得し描画する。
    • 作成したアクションを アクション「」を実行する を利用してひとまとめにしたアクションを作成します。
    • 「コンテナ」を選択した状態で、プロパティの「ページ読み込み時」の項目で作成したアクションを指定します。
  6. 集計開始日時を指定させる。
    プロセス定義詳細画面にあるように集計の開始日時を選択可能とするには、以下の方法で実現できます。
    • プルダウンに表示させる値のラベルと、値の配列をそれぞれ変数に作成する。
    • 「フォーム部品」に分類されるエレメントの一覧から、「プルダウン」を設置する
    • プロパティ labelsvalues にそれぞれの配列を指定し、 value にリクエストパラメータの searchAfter の変数を指定する。
    • 選択が変更された時点でデータを取得するようにするため、プルダウンエレメントの入力値変更時イベントにデータ取得処理のアクションを指定する。

7.3.1.4.2. ルーティング定義

  1. コンテンツ定義で作成した画面にアクセスをするために、ルーティング定義を作成します。
    ルーティング定義を作成する際のURLは、コンテンツ定義の入力値に渡すプロセス定義を含めた動的URLとする必要があります。

    コラム

    ルーティングの登録に関する詳細は「IM-BloomMaker for Accel Platform ユーザ操作ガイド」 - 「ルーティングを新規登録する」を参照してください。
  2. ルーティング定義に認可を設定する。
    作成したルーティングにアクセスするためには、適切な認可の設定が必要です。
    本サンプルでは、「IM-BPM管理者」に対して「参照」権限を付与しています。

    コラム

    ルーティングの認可設定の詳細は「IM-BloomMaker for Accel Platform チュートリアルガイド」-「ルーティングの認可を設定する」を参照してください。