2021年5月30日日曜日

RESAS APIを呼び出してデータを取得する

2024年10月3日追記

RESAS APIは2025年3月24日で提供が終了します。詳しくは以下のリンクを参照してください。

https://opendata.resas-portal.go.jp/docs/api/v1/index.html

追記終了


日本政府が公開しているデータを手入力する手間を省くために RESAS APIを呼び出すことにしました。RESASには、こちらからアクセスします。ヘルプにある紹介を抜粋します。

地域経済分析システム(RESAS:リーサス)は、地方創生の様々な取り組みを情報面から支援するために、経済産業省と内閣官房(まち・ひと・しごと創生本部事務局)が提供しています。
自治体職員の方や、地域の活性化に関心を持つ様々な分野の方によって、効果的な施策の立案・実行・検証のためなどに広く利用されています。

以下、Oracle APEXのアプリケーションから、RESAS APIを使って都道府県一覧を取り出し、データベースに保存するまでの作業ログです。

最初にRESAS APIの利用者登録を行ないます。RESAS APIのトップ・ページを開きます。


RESAS-API利用者登録のフォームが開くので、必要事項を入力してIDを取得します。


取得したIDでログインをすると、マイページにアクセスできるようになります。


マイページからAPIキーの取得/確認ができます。


Oracle APEXのRESTデータ・ソースを使って、RESAS APIを呼び出すことにします。受け皿となるアプリケーションを作成します。アプリケーションの作成を開始し、名前をRESAS情報とします。他の設定はせず、そのままアプリケーションの作成をクリックします。


アプリケーションが作成されたら、RESTデータ・ソースを作成します。共有コンポーネントのRESTデータ・ソースを開きます。


RESTデータ・ソースの一覧より、作成をクリックします。


RESTデータ・ソースの作成として最初からを選んで、次へ進みます。


今回はRESAS APIを呼び出して都道府県一覧を取得します。参照したAPIの仕様はこちらです。

RESTデータ・ソース・タイプに簡易HTTPを選択します。名前はRESAS - 都道府県一覧としました。URLエンドポイントにhttps://opendata.resas-portal.go.jp/api/v1/prefecturesを入力し、次へ進みます。


リモート・サーバー、ベースURLおよびサービスURLパスは変更不要です。確認だけを行い、次へ進みます。


認証が必要です。をONにします。APIキーの設定方法はRESAS API詳細仕様に記載されている通り、HTTPのリクエスト・ヘッダーとしてX-API-KEYを追加し、値としてAPIキーを指定します。

資格証明は- 新しい資格証明の入力 -を選択します。認証タイプはHTTPヘッダー、名前はX-API-KEYです。値に取得済みのAPIキーを入力します。検出をクリックします。


REST APIが発行され、取得されたデータが表示されます。データ・プロファイルも確認しておきます。


PREFCODEがNUMBER型、PREFNAMEがVARCHAR2(4000)として認識されていることが確認できます。RESTデータ・ソースの作成を実行します。


RESTデータ・ソースが作成されました。


作成されたRESTデータ・ソースの内容を確認するため、レポートのページを作成します。ページ作成ウィザードを開始し、レポートをクリックします。


対話モード・レポートをクリックします。


ページ名は都道府県一覧とします。次へ進みます。


ナビゲーションのプリファレンスとして、新規ナビゲーション・メニュー・エントリの作成を選択し、次へ進みます。


データ・ソースとしてRESTデータ・ソースを選択し、RESTデータ・ソースに先程作成したRESAS - 都道府県一覧を指定します。作成をクリックします。


ページが作成されたら、それを実行して結果を確認します。


47都道府県の情報がリストされます。


RESAS APIを呼び出すことで、都道府県一覧を取得できることが確認できました。

今まで作成した設定を再利用しやすくするために、設定内容を調整します。

最初に共有コンポーネントのWeb資格証明を開きます。


作成済みのWeb資格証明が一覧されます。作成されたWeb資格証明はワークスペースで共有されるため、実際にはワークスペース・ユーティリティのWeb資格証明が開いています。また、このアプリケーションだけではなく、ワークスペース内で作成済みのWeb資格証明が一覧されます。作成したWeb資格証明を開きます。


作成されている資格証明は都道府県一覧だけではなく、RESAS API全般で有効であるため、名前をRESASの資格証明に変更します。また、静的識別子はRESAS_API_KEYとします。この資格証明はURLに対して有効に設定されているhttps://opendata.resas-portal.go.jp/api/v1/以下へのアクセスに使うことができます。変更の適用をクリックします。


Web資格証明の参照は内部IDに基づいているため、名前や静的識別子の変更の影響はありません。PL/SQLコード内でWeb資格証明の静的識別子を参照している場合(APEX_WEB_SERVICE.MAKE_REST_REQESTなどの呼び出しの引数として)は、参照しているコードの変更が必要になります。

今回は都道府県のコードと名前をAPI経由で取得していますが、これは変更が発生しない情報です。ですので、ローカルの表にコピーし、そちらを参照することによりREST API呼び出しを行わない様にします。

共有コンポーネントのRESTデータ・ソースを開き、作成したRESTデータ・ソースの同期化を設定します。同期化の管理を実行します。


同期先を新規表とし、表名としてRESAS_PREFECTURESを指定します。保存をクリックします。


ここで指定した同期化表を新たに作成します。表の作成をクリックします。

表はRESTデータ・ソースとして検出されたデータ・プロファイルをもとに定義されます。SQLの表示をクリックすると、作成される表のDDLを確認できます。主キー制約やNOT NULL制約の追加など、あらかじめ設定した表を先に作成して、同期化表として指定することもできます。RESTデータ・ソースの同期に影響のない範囲であれば、ここで新規に作成された表の定義を後から変更することもできます。

create table "RESAS_PREFECTURES"(
"PREFCODE" NUMBER
,"PREFNAME" VARCHAR2(4000)
,"APEX$SYNC_STEP_STATIC_ID" VARCHAR2(255)
,"APEX$ROW_SYNC_TIMESTAMP" TIMESTAMP WITH TIME ZONE
)
/


表が作成されたら、保存して実行をクリックします。都道府県一覧が変わることはないため、定期的な同期処理は不要です。保存と実行で行われる一回だけの処理でデータのコピーは完了します。


同期化された内容を確認するため、先程作成した都道府県一覧のレポートのページを開きます。対話モード・レポートのソースの同期化表の使用をONに変更します。


ページを実行し、レポートの内容に変化が無いことを確認します。



RESAS APIを呼び出してデータを取得する方法の紹介は以上になります。

今回作成したアプリケーションのエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/resas-api.sql

Oracle APEXのアプリケーション作成の参考になれば幸いです。

ちなみに登録したWeb資格証明の資格証明シークレットはエクスポートの対象からは除外されます。ですので、アプリケーションをインポートした後に自分自身で取得したRESAS APIのAPIキーを登録する必要があります。

完

2021年5月28日金曜日

改行で区切ったJSON(Newline Delimited JSON)のデータをロードする

 政府CIOポータルのワクチン接種ダッシュボードのページより、都道府県別接種回数詳細のデータがオープンデータとして、NDJSON - 改行区切りJSON形式でダウンロードできるようになっています。

最新のOracle APEX - 21.1でもNDJSONのロードはサポートしていないため、ちょっとコードを書いてロードしてみました。

5月28日現在ですが、都道府県別接種回数詳細のデータをこちらからダウンロードすることができました。このデータを取り込むので、あらかじめダウンロードしておきます。ダウンロードしたファイル名はprefecture.ndjsonでした。

以下、作業ログになります。

データを取り込む表の定義と、取り込んだデータを扱うアプリケーションの作成を行います。

クイックSQLを開き、以下のモデルより表COVID19_DATA_FILES、COVID19_VACCINATION_RESULTSを作成します。

# prefix: covid19
# semantics: default
data_files
store_date date
content file

vaccination_results
count_date date
prefecture num
gender vc20
age vc20
medical_worker vc20
status num
count num


SQLの生成、SQLスクリプトの保存、レビューおよび実行を続けて行い、SQLをレビューしたのち即時実行まで実施します。



表が作成されたら、アプリケーションの作成を実行します。確認画面が開くので、そこでもアプリケーションの作成をクリックします。


アプリケーションの名前をワクチン接種状況とし、アプリケーションの作成をクリックします。


アプリケーションを実行し、ダウンロードしてあるファイルprefecture.ndjsonをデータベースに保存します。メニューからData Filesのページを開き、作成をクリックします。


Contentにダウンロードしたprefecture.ndjsonを指定し、作成をクリックします。


行が一行追加されていることがわかります。レポートとして調整できていませんが、対応は後回しとします。

NDJSONには対応していませんが、Oracle APEXではJSON形式のデータのロードには対応しています。ですのでNDJSONに含まれる1行のJSONを対象にして、Oracle APEX 21.1の新機能を使ったデータ・ロード定義を作ります。

prefecture.ndjsonに含まれる1行をtest.jsonとして、ファイルに切り出します。例えば、以下のようにheadコマンドを使うことができます。

% head -1 prefecture.ndjson > test.json


任意の一行なので、ファイルの内容をコピペして作成することもできます。

{"date":"2021-04-12","prefecture":"01","gender":"F","age":"65-","medical_worker":false,"status":1,"count":84}


サンプルとして使用するファイルが作成されたら、共有コンポーネントのデータ・ロード定義を開きます。


データ・ロード定義の一覧画面より、作成をクリックします。


データ・ロードの作成は最初から行います。


データ・ロード定義の名前としてVACCINATIONを指定します。ターゲット・タイプは表、表名にCOVID19_VACCINATION_RESULTSを指定します。次へ進みます。


ソース・タイプとしてファイルのアップロードを選択し、ファイルの選択をクリックしてtest.jsonを指定します。次へ進みます。


DATE_(Date)のマップ先が決まっていないので、COUNT_DATE (Date) を選びます。データ・ロードの作成をクリックします。ページの作成は行いません。


データ・ロード定義が作成されました。開いて内容を編集します。


静的IDはVACCINATIONです。これはデータ・ロードを行うプロシージャの呼び出し時に指定します。次にロード・メソッドが追加になっていることを確認します。主キーの設定をしていないため、デフォルトは追加です。エラー処理のエラー時としてエラーをコレクションに記録を選択し、エラー・コレクション名にLOAD_ERRORを指定します。変更の適用をクリックします。


これでデータをロードする準備が整いました。ページ番号4のVaccication Resultsのページを開きます。


データのロードを実行するボタンを追加します。リージョン・ボタンの上でコンテキスト・メニューを表示させ、ボタンの作成を実行します。識別の名前をB_LOADとし、ラベルはデータのロードとします。ボタンの位置として対話モード・レポートの検索バーの右を指定します。


ボタンB_LOADをクリックしたときに実行されるプロセスを作成します。PL/SQLコードは以下を使います。

declare
C_NL constant raw(1) := utl_raw.cast_to_raw(chr(10));
l_blob blob;
l_tmp_blob blob;
l_current integer;
l_start integer := 1;
l_line varchar2(32767);
l_res covid19_vaccination_results%rowtype;
l_vres apex_data_loading.t_data_load_result;
l_total integer := 0;
begin
-- もっとも最後にロードしたデータを選択します。
select content into l_blob from covid19_data_files
where id = (select max(id) from covid19_data_files);
-- 一旦メモリにロードします。
dbms_lob.createtemporary(l_tmp_blob, true);
dbms_lob.copy(
dest_lob => l_tmp_blob,
src_lob => l_blob,
amount => dbms_lob.lobmaxsize
);
-- データは入れ替えます。
delete from covid19_vaccination_results;
-- JSONのデータを1行ごとに取り出し、APEX_DATA_LOADING.LOAD_DATAを呼び出します。
while true
loop
-- 改行位置を見つける。
l_current := dbms_lob.instr(l_tmp_blob, C_NL, l_start);
-- 一行を取り出す。
l_line := utl_raw.cast_to_varchar2(
dbms_lob.substr(l_tmp_blob, (l_current - l_start), l_start)
);
-- 改行が見つからなければ終了。
-- ファイルの最終行でも改行がある - いきなりEOFにはならないのが前提。
exit when (l_current = 0);
l_vres := apex_data_loading.load_data
(
p_static_id => 'VACCINATION',
p_data_to_load => l_line
);
l_total := l_total + l_vres.processed_rows;
-- 次の行の処理へ移る。
l_start := l_current + 1;
end loop;
apex_debug.info('Total Number of COVID19_VACCINATIION_RESULTS: ' || l_total);
dbms_lob.freetemporary(l_tmp_blob);
end;

プロセスの作成を実行し、識別の名前をデータのロードとします。タイプはコードを実行、ソースのPL/SQLコードとして、上記のコードを記述します。サーバー側の条件のボタン押下時にB_LOADを指定します。


ページを実行し、データのロードをクリックします。


残念ですが、apex.oracle.comでは、リソース・マネージャの制限でエラーが発生するため、最後までデータのロードができません。手元のデータベースにOracle APEX 21.1を導入した環境では成功しています。(Temporary LOBを使用することで、こちらのエラーは回避できました。)


毎回データ・ロード定義を参照するAPEX_DATA_LOADING.LOAD_DATAを呼び出すのは、さすがに時間がかかります。以下のように、直接JSONをパースするコードを記載すると処理速度は向上します。ただし、データのフォーマット変更にはコード変更で対応する必要があります。

declare
C_NL constant raw(1) := utl_raw.cast_to_raw(chr(10));
l_blob blob;
l_tmp_blob blob;
l_current integer;
l_start integer := 1;
l_line varchar2(32767);
l_vres covid19_vaccination_results%rowtype;
l_json json_object_t;
l_total integer := 0;
begin
-- もっとも最後にロードしたデータを選択します。
select content into l_blob from covid19_data_files
where id = (select max(id) from covid19_data_files);
-- 一旦メモリにロードします。
dbms_lob.createtemporary(l_tmp_blob, true);
dbms_lob.copy(
dest_lob => l_tmp_blob,
src_lob => l_blob,
amount => dbms_lob.lobmaxsize
);
-- データは入れ替えます。
delete from covid19_vaccination_results;
-- JSONのデータを1行ごとに取り出し、JSONを処理します。
while true
loop
-- 改行位置を見つける。
l_current := dbms_lob.instr(l_tmp_blob, C_NL, l_start);
-- 1行のJSON形式のデータを取り出す。
l_line := utl_raw.cast_to_varchar2(
dbms_lob.substr(l_tmp_blob, (l_current - l_start), l_start)
);
-- 改行が見つからなければ終了。
-- ファイルの最終行でも改行がある - いきなりEOFにはならないのが前提。
exit when (l_current = 0);
-- ロードする - 直接JSONを使う。
l_json := json_object_t.parse(l_line);
l_vres.count_date := to_date(l_json.get_String('date'),'YYYY-MM-DD');
l_vres.prefecture := l_json.get_Number('prefecture');
l_vres.gender := l_json.get_String('gender');
l_vres.age := l_json.get_String('age');
l_vres.medical_worker := l_json.get_String('medical_worker');
l_vres.status := l_json.get_Number('status');
l_vres.count := l_json.get_Number('count');
insert into covid19_vaccination_results values l_vres;
l_total := l_total + 1;
-- 次の行の処理へ移る。
l_start := l_current + 1;
end loop;
apex_debug.info('Total Number of COVID19_VACCINATIION_RESULTS: ' || l_total);
dbms_lob.freetemporary(l_tmp_blob);
end;

データの取得を一度にできるように、Data Filesのページにボタンを追加します。ページ番号2のData Filesのページを開きます。リージョン・ボタンの上でコンテキスト・メニューを開き、ボタンの作成を実行します。識別のボタン名をB_GETとし、ラベルをデータの取得とします。ボタン位置は対話モード・レポートの検索バーの右を指定します。


データの取得には以下のコードを使用します。

declare
l_blob blob;
l_uncompress blob;
begin
l_blob := apex_web_service.make_rest_request_b
(
p_url => :VACCINATION_RESULT_URL,
p_http_method => 'GET'
);
l_uncompress := utl_compress.lz_uncompress(l_blob);
insert into covid19_data_files(store_date, content) values(sysdate, l_uncompress);
end;

プロセスの作成を行います。識別の名前をデータの取得、タイプをコードの実行とします。ソースのPL/SQLコードとして上記を記述し、サーバー側の条件のボタンの押下時としてB_GETを指定します。


コード内でデータを取得するURLをVACCINATION_RESULT_URLとしているので、これをアプリケーション定義の置換文字列として設定します。


Data Filesのページを実行し、データの取得を実行します。


ネットワークのアクセスに関するエラー(ORA-29273: HTTPリクエストに失敗しました)が発生するときは、ACLの追加を行ないます。ACLの追加を行うコードの例は以下になります。

begin

  dbms_network_acl_admin.create_acl(acl => 'vrs.xml',

                                    description => 'Vaccine Dashboard',

                                    principal => 'APEX_210100',

                                    is_grant  => true,

                                    privilege => 'connect');

  dbms_network_acl_admin.assign_acl(acl => 'vrs.xml',

                                    host => 'vrs-data.cio.go.jp');

end;

/

commit;


データの取得が成功すると表示が一行増え、Store Dateにデータを取得した日付も表示されます。


Vaccination Resultsのページを開き、データのロードを実行すると、直近で取得したデータ(Data Filesのページでデータの取得を行なったデータ)で入れ替わります。


以上で今回目的としていた、NDJSON形式のデータ・ロードを行うアプリケーションは完成です。データの取得とロードはほぼPL/SQLでコーディングしているため、Oracle APEXの自動化を使って実行させるように変更するのも容易でしょう。

データさえ表に取り込めば、色々なレポーティングを実装することができます。

今回作成したアプリケーションのエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/load-ndjson.sql

Oracle APEXのアプリケーション作成の参考になれば幸いです。

完