LoginSignup
1
0

More than 1 year has passed since last update.

【一般】東京都オープンデータソースAPIを活用してみる(データ書込み編)

Last updated at Posted at 2022-10-12

これまで基礎準備編データ取得編と記事にしてきましたが、今回はAPIエンドポイントから取得し、JSONファイルとして保存されたデータをデータベースに書込む処理の自動化を行います。
前回と同じくSharperlightスケジューラのタスクとして作成します。

データ書込みタスク

Sharperlightアプリケーションメニューからスケジューラを起動します。
image.png
スケジューラが起動したら、新規ボタンで新しいタスクを追加します。
コードグループ説明等を記入します。
image.png
適用ボタンで一度保存しましょう。
アクションタブに移動します。新規アイコンでアクション一覧を表示しJavaScriptアクションを追加します。
JavaScriptアクションが追加されるとコード編集領域が下部に表示されるので、以下のコードを貼り付けます。

var workfolder = lib_task.ParamGet("WorkFolder");
var maxPages = lib_task.ParamGet("MaxFiles");
var chunkLimit = 100;
    
//=======================================================================================================
// 前処理
//=======================================================================================================
// データモデルのテーブル情報を取得
var wb = JSON.parse(lib_app.writeback.MetaDataJsonGet("TOKYOAPI", "Covid19Patient"));
if (wb.errorMessage.length > 0) {
    lib_task.LogMessage("Error: " + wb.errorMessage);
    pages = maxPages;
}

// データモデルのテーブル情報からフィールドのコードと実SQLフィールド名を抜き出す
var datamodelFieldInfo = [];
for (var wbcol in wb.columns) {
    let info = {
        code: wb.columns[wbcol].fieldCode.slice(1),
        sqlName: wb.columns[wbcol].sqlAttr.replace("[", "").replace("]", "")
    }
    datamodelFieldInfo.push(info);
}



//=======================================================================================================
// JSONファイルをひとつひとつ処理する
//=======================================================================================================
var pages = 0;
var batch = {
    items: []
}
batch.items.push(wb);

while (pages < maxPages){
    if (lib_task.state.abort ) { break; }
    
    pages++;
    

    // ファイルからデータを読む
    var filePath = workfolder + "stagingResult_" + pages.toString() + ".json";
    //_taskData.log.debug(_taskData,"===>: " + filePath);
    if(!lib_sys.io.file.Exists(filePath)){
        lib_task.LogMessage("FINISH, No more data");
        break;
    }
    try {
        var file = lib_sys.io.file.ReadAllText(filePath);
    } catch (err) {
        lib_task.LogMessage("Error: " + err);
        break;
    }

    // ファイルが空っぽの場合は、無視する
    if (file.length == 0) continue;
    
    // ファイルの中身を得る
    fileContent = JSON.parse(file);
    lib_task.LogMessage("Recs=" + fileContent[0].length);
    if (fileContent[0].length == 0){ 
        lib_task.LogMessage("Warning: No contents");
        continue; 
    }
    
    
    //=====================================================================================================
    // データをデータベースに書き込んでいく
    //=====================================================================================================
    var dataFields;
    var cells = [];
    var chk = 0;
    var found = false;
    wb.rows = [];
    
    // レコードをひとつひとつ処理していく
    for (var r=0; r<fileContent[0].length; r++) {
        if (lib_task.state.abort) { break; }
        
        // レコードの取り出し
        var record = fileContent[0][r];
    
        chk++;
        cells = [];
        // レコードからフィールド名を取得
        dataFields = Object.keys(record);
        
        // 書き込み用オブジェクトを作成する (Sharperlight特有)
        for (var i in datamodelFieldInfo){
            found = false;
            if (datamodelFieldInfo[i].code == "Row_ID") continue;
            
            for (var f in dataFields) {
                if (dataFields[f].indexOf(datamodelFieldInfo[i].sqlName) > -1){ // データモデルで定義しているSQLフィールド名とレコードのフィールド名が一致
                    console.log(datamodelFieldInfo[i].sqlName + "/" + dataFields[f]);
                    cells.push({
                        "code": datamodelFieldInfo[i].code, // データモデルで定義しているフィールドコード
                        "value": _convert_ISODate(record[dataFields[f]]) // レコードの実データ
                    });
                    found = true;
                    break;
                }

            }

            // データモデルで定義しているフィールドがレコード内に存在しなかった場合
            if (!found){
                if (datamodelFieldInfo[i].code.indexOf("Date") > -1){
                    cells.push({
                        "code": datamodelFieldInfo[i].code,
                        "value": _convert_ISODate("1900-01-01")
                    });
                }else{
                    cells.push({
                        "code": datamodelFieldInfo[i].code,
                        "value": _convert_ISODate("-")
                    });
                }
            }
        }

        console.log(cells.length + " cols processed");
        wb.rows.push({
            "cells": cells
        });
        console.log(wb.rows.length + " rows processed");

        // 変数chunkLimitで指定した複数のレコードをまとめて書き込む
        if (chk == chunkLimit) {
            wb.mode = 'Update';
            wb.breakOnFirstRowError = true;
            var wbStatus;
            try {
                wbStatus = JSON.parse(lib_app.writeback.ExecuteBatch(batch));
            } catch (err) {
                lib_task.LogMessage("Warning: " + err.message + "/ " + JSON.stringify(wb.rows));
                wb.rows = [];
                chk = 0;
                continue;
            }

            if (wbStatus.errorMessage.length > 0) {
                var message = "ERROR ON: " + '\n';
                for (var c = 0; c < wbStatus.status.length; c++) {
                    var si = wbStatus.status[c];
                    message += (si.message + '\n' + JSON.stringify(wb.rows) + '\n');
                }
                console.log(message);
            }
            wb.rows = [];
            chk = 0;
        }
    }

    // 書き込みが終わっていないデータを書き込む
    if (wb.rows.length > 0) {
        wb.mode = 'Update';
        wb.breakOnFirstRowError = true;
        var wbStatus;
        try {
            wbStatus = JSON.parse(lib_app.writeback.ExecuteBatch(batch));
        } catch (err) {
            lib_task.LogMessage("Warning: " + err.message + "/ " + JSON.stringify(wb.rows));
            wb.rows = [];
            chk = 0;
            continue;
        }

        if (wbStatus.errorMessage.length > 0) {
            var message = "ERROR ON: " + '\n';
            for (var c = 0; c < wbStatus.status.length; c++) {
                var si = wbStatus.status[c];
                message += (si.message + '\n' + JSON.stringify(wb.rows) + '\n');
            }
            console.log(message);
        }
        wb.rows = [];
        chk = 0;
    }
}

function _convert_ISODate(xValue) {
    if (!/^(-?(?:[1-9][0-9]*)?[0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(.[0-9]+)+([0-9]+)?$/g.test(xValue)) {
        if (!/^(-?(?:[1-9][0-9]*)?[0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])?$/g.test(xValue)) {
            return xValue;
        } else {
            return (xValue.replace('0000-', '1900-'));
        }
    } else {
        var xDate = new Date(xValue);
        return (xDate.toLocaleString());
    }
}

コード中に登場するこのような関数は、Sharperlightエンジンが提供する関数群です。
lib_task.ParamGet()、lib_app.writeback.ExecuteBatch()、lib_sys.io.file.ReadAllText()など

このコードは、前のタスクで指定された作業フォルダに保存されたJSONファイルを読み込んで、Sharperlightデータモデルの定義を利用し、接続しているデータベースの対象テーブルに、指定したレコード数を一括書き込みするコードです。この処理は作業フォルダ内の全JSONファイルに対して繰り返し行われます。

コードのテストは、接続テストボタンで行うことができます。またタスクをOKボタンで閉じた後、タスク一覧の右クリックメニューから実行を選択する事でも行えます。
image.png
image.png

タスクのパラメータとして作業フォルダ名と最大処理ファイル数を渡すようにしました。
image.png
以上で書込みタスクの作成は完了です。

データ取得タスクとデータ書込みタスクの連携

前回作成したデータ取得タスクが実行された直後、データ書込みタスクを実行したいです。なので親タスクを作ってその二つのタスクを子タスクとして順番に呼ぶようにします。
新規ボタンで新しいタスクを追加します。
コードグループ説明等を記入し、適用ボタンで保存します。
image.png
アクションタブに移動し、新規アイコンでタスク一覧を表示し、指定タスクを実行アクションを追加します。
実行するタスクを選択します。最初はデータ取得タスクで、次がデータ書込みタスクです。
image.png
適用ボタンで保存します。
トリガータブに移動し、実行時間を設定します。
新規アイコンで新しいトリガーを追加します。タイプ属性にサービスタイマー-開始/毎週/毎月を選びます。サービスタイマー属性では、開始を選び、開始時刻朝6時に設定します。
image.png
OKボタンで保存して閉じます。これでSharperlightサービスが起動していれば毎朝6時にこのタスクは実行されます。

タスクが正常に実行されると、データベーステーブルはこのようになります。
image.png

これでAPIエンドポイントからデータを取得して、データベースに書込むことができました。あとはSharperlightのレポーティング機能を利用してお好みのレポートを作成することができます。

では、失礼します。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0