2022年8月31日水曜日

都道府県別年齢3区部分別人口を円グラフで表示する

 総務省統計局より都道府県、年齢3区分別人口の統計データを入手し、カード・リージョンのそれぞれのカードに、都道府県ごとの年齢3区分別の円グラフを表示するAPEXアプリケーションを作成してみます。


グラフの元となるデータとして、総務省統計局日本の統計第2章 人口・世帯にある、2-6 都道府県、年齢区分別人口(エクセル:15KB)を使用します。Excelファイルはn220200600.xlsxというファイル名でダウンロードされました。

SQLワークショップユーティリティクイックSQLを使って、Excelファイルの内容をインポートする表を作成します。以下のモデルを使用します。
# prefix: ccb
populations
    prefecture_name vc50 /nn
    total     num
    age00to14 num
    age15to64 num
    age65     num
    age75    num
SQLの生成SQLスクリプトを保存レビューおよび実行を順番にクリックし、表CCB_POPULATIONSを作成します。アプリケーションの作成は行いません。


Excelファイルの内容を表CCB_POPULATIONSにインポートします。

SQLワークショップユーティリティデータ・ワークショップを開きます。

データのロードを実行します。


ダウンロードしたファイルn220200600.xlsxを選択します。


ロード先既存の表を選択し、として先ほど作成したCCB_POPULATIONSを選びます。設定列見出し最初の行にヘッダーが含まれるチェックを外します。Excelの列と表CCB_POPULATIONSの列をマッピングするため、構成を開きます。


ソース列のCOL001には列をマップしません。それ以外はソース列とマップ先を以下のように指定します。

COL002 = PREFECTURE_NAME
COL003 = TOTAL
COL004 = AGE00TO14
COL005 = AGE15TO64
COL006 = AGE65
COL007 = AGE75

以上を設定し、変更の保存をクリックします。


データのロードを実行します。


表にロードできなかった行が一覧されます。これらの行は表CCB_POPULATIONS_ERR$に保存されています。エラーで取り込めなかった行に、列PREFECTURE_NAMEに値がある行は含まれていないことより、必要なデータはすべてロードできています。

取消をクリックし、データのロードを終了します。


アプリケーション作成ウィザードを起動します。

アプリケーションの名前都道府県別年齢3区分人口とします。予め作成されているホーム・ページ削除し、カード・リージョンのページを追加します。


カードのページは、以下の設定とします。

ページ名を都道府県別年齢3区分人口表またはビューとしてCCB_POPULATIONSを選択します。表示形式はグリッドタイトル列PREFECTURE_NAME本体列TOTALを指定します。

カードのページを追加したのち、アプリケーションの作成を実行します。


アプリケーションが作成されます。

都道府県別年齢3区分人口として作成されたページに含まれるカード・リージョンに、円グラフを表示させます


今回の実装は、ギャラリサンプル・アプリケーションSample Cardsに含まれるページ11Gauge Meter Chart Cardsを参考にしています。


都道府県別年齢3区分人口のページを開き、ページ・プロパティにSample Cardsと同等の設定を加えます。

JavaScriptファイルURLとして、以下を設定します。

[require jet]

JavaScriptページ・ロード時に実行として、以下を設定します。

require(["ojs/ojchart"], function() {});

CSSファイルURLとして、以下を設定します。

#JET_CSS_DIRECTORY#alta/oj-alta-notag-min.css

上記はSample Cardsを確認して初めて分かる設定ではないかと思います。


この時点でアプリケーションを実行すると、ページの表示は以下のようになります。


カードの表示を改善します。

Bodyにあるカード・リージョン都道府県別年齢3区分人口を選択し、プロパティ・エディタの属性タブを開きます。

本体の拡張フォーマットをONにし、HTML式として以下を記述します。

<b>総数:</b> &TOTAL.

メディアの拡張フォーマットをONにし、HTML式として以下を記述します。オプションの詳細はOracle JETのoj-chartのAPI Docを確認します。



以上で円グラフが表示されるようになりました。

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

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

2022年8月30日火曜日

APEXアプリケーションの変更点を調べる

 APEXアプリケーションの変更(コンポーネントの作成、変更、削除)は、管理メニューのアクティビティのモニターを開いた画面にある、開発者アクティビティアプリケーション変更(詳細)から確認することができます。

デフォルトではいくつかの列が非表示になっています。すべて表示させると以下のレポートになります。一覧される履歴を制限するために、期間アプリケーションによる絞り込みを行うと良いでしょう。日付の降順で一覧すると見やすくなると思います。

このレポートに列として、APEX表名SCNおよびコンポーネント・キーが含まれています。

Autonomous Databaseの場合、APEXがインストールされているスキーマが保護されているため、APEX表に直接アクセスすることはできません。すべて標準ビューを介してのアクセスになるため、これから説明する作業はできません。

誰が何をいつ変更したか、といったことはAutonomous Databaseでも、上記のレポートより確認できます。いつ、についてはSCNより正確な時刻を割り出すことも可能です。

select scn_to_timestamp(<SCN>) at time zone 'Asia/Tokyo' from dual;

オンプレミス環境の場合、APEX表に直接問い合わせを発行できるため、実施された変更を確認できます。

アクション作成および削除であれば、コンポーネント(の種類)とコンポーネント名から概ね実行された作業は分かります。そのため、詳細まで調べる必要性はあまりないかと思います。

以下は、アクション変更のときの確認手順です。

例としてアプリケーションID110ページ番号3のページにあるファセットP3_MGRラベルを、マネージャーから上司に変更し保存します。


アプリケーションの変更のレポートを確認すると、変更履歴が見つかります。変更されたAPEX表名として、WWV_FLOWS_STEPSとWWV_FLOW_STEP_ITEMSがあります。


コマンドライン・ツールでデータベースに接続します。

APEXがインストールされているスキーマをカレント・スキーマに変更します。APEX 22.1の場合はAPEX_220100がAPEXがインストールされているスキーマになります。

SQL> alter session set current_schema = apex_220100;


Session altered.


SQL> 


現在のデータと変更前のデータを、別の表に保存します。APEX表名SCNコンポーネント・キーの値を使い、以下のCREATE TABLE文を実行します。

CREATE TABLE <変更後の表> AS SELECT * FROM <APEX表名> where ID = <コンポーネント・キー>;
CREATE TABLE <変更前の表> AS SELECT * FROM <APEX表名> as of scn <列SCNの値> where ID = <コンポーネント・キー>;

APEXのワークスペース・スキーマとして、APEXDEVが作成済みであるとします。

上記のDDLを実行して、表を作成します。変更後のアイテムのデータを表STEP_ITEMS_AC変更前STEP_ITEMS_BC変更後のページのデータを表STEPS_AC変更前STEPS_BCに保存しています。

SQL> create table apexdev.step_items_ac as select * from wwv_flow_step_items where id = 4025742564048869;


Table created.


SQL> create table apexdev.step_items_bc as select * from wwv_flow_step_items as of scn 3874676 where id = 4025742564048869;


Table created.


SQL> create table apexdev.steps_ac as select * from wwv_flow_steps where flow_id = 110 and id = 3;


Table created.


SQL> create table apexdev.steps_bc as select * from wwv_flow_steps as of scn 3874678 where flow_id = 110 and id = 3;


Table created.


SQL> 


APEX表は大抵列ID が主キーで、コンポーネント・キーで検索すると1行だけが返されます。ただし、表WWV_FLOW_STEPS(これはページのメタデータ)は例外で、アプリケーションIDであるFLOW_IDとページIDであるIDの複合主キーなので、FLOW_IDとIDを検索条件にします。

変更前の情報の検索には、フラッシュバック問い合わせ(AS OF SCN)を使っています。そのため、初期化パラメータのundo_retentionの期間内に検索を実行する必要があります。

APEXのアプリケーションを作って、変更後と変更前の表の違いを確認します。

アプリケーションのページにクラシック・レポートのリージョンを2つ作成します。ひとつはソース表名変更後の表STEP_ITEMS_ACを指定します。もうひとつはソース表名変更前の表STEP_ITEMS_BCを指定します。リージョンの配置を横並びにするため、変更前のリージョンのレイアウト新規行の開始OFFにします。


クラシック・レポートの属性を開き、外観テンプレートとしてValue Attribute Pairs - Columnを選択します。列と値を縦方向に一覧表示します。


以上の設定を行い、アプリケーションを実行します。


Promptマネージャーから上司に変更されていることが確認できます。


変更後の列Last Updated ByとLast Updated Onより、変更した人と時刻を確認できます。


変更履歴には表WWV_FLOW_STEPSへの変更がレポートされています。しかし、表WWV_FLOW_STEP_ITEMSと同様の手順で変更内容を確認すると、メタデータには変更は見つかりませんでした。変更したのはファセットのラベルだけなので、これは想定通りです。列Last Updated ByとLast Updated Onのみが変更されています。


APEXアプリケーションの変更点を調べる方法の紹介は以上になります。

ちなみにアプリケーションの変更履歴はAPEX表WWV_FLOW_BUILDER_AUDIT_TRAILに保存されています。この表にはビューやシノニムは登録されていないため、ユーザーSYSやSYSTEMのみがアクセスできます。Autonomous Databaseの場合は管理者ユーザーのADMINであってもアクセスできません。必ずアクティビティのモニターを開いて確認する必要があります。

WWVで始まるAPEX表を直接問い合わせることは、サポート対象外です。そのため取得した情報の扱いは、参考程度にとどめておくべきです。APEXアプリケーションのメタデータを参照する場合は、APEX_で始まる標準ビューを使用します。

Grid.jsを使ってみる

 オープン・ソースのJavaScriptプラグインであるGrid.jsをOracle APEXで使ってみます。Grid.jsの紹介には"It works with most JavaScript frameworks, including React, Angular, Vue and VanillaJS."とあります。Oracle  APEXはJavaScriptフレームワークとは謳っていませんが、Grid.jsを組み込むことは可能です。

以前にOracle APEXでHighchartsを使う方法CKEditor5のInline Editorを使う方法を紹介しています。それと似た作業になります。

サンプル・データセットEMP/DEPTに含まれる表EMPを、Grid.jsにて表示します。


Grid.jsが受け付ける形式でデータを返すRESTサービスを作成します。

Grid.js側のコードは、ExamplesのCustom HTTP clientの例を参考にして記述します。


この例のコメントとして、RESTサービスが返すべきデータのフォーマットが記載されています。


正直なところ、上記のコードからはフォーマットがピンと来なかったので、Examplesのコードに含まれているURLにアクセスして、出力されるレスポンスを確認しました。


表EMPのデータを上記のフォーマットで返すSELECT文は以下になります。
select 
  json_object(
    'data' value coalesce(json_arrayagg(line),'[]') format json
    , 'total' value count(*)
  ) 
from (
  select json_object(empno, ename, job, sal, comm, hiredate) as line
  from emp
);
SQLワークショップSQLコマンドより上記のSQLを実行することにより、出力されるJSONオブジェクトを確認できます。


上記のSELECT文の結果を返す、RESTfulサービスのGETハンドラを作成します。

SQLワークショップRESTfulサービスを開きます。

モジュールgridjsを作成します。モジュール・パス/gridjs/とします。続いてURIテンプレートemp/をモジュールgridjsに作成します。

テンプレートemp/にGETハンドラを作成します。ソースとして以下を記述します。



完全なURLは、Grid.jsを初期化するコードにサーバー側のURLとして指定するため、コピーをして後で参照できるようにしておきます。

RESTfulサービスを、APEXセッションからのみ呼び出しができるように保護します。

ORDSの権限としてgridjsを作成します。ロールとしてRESTful Servicesを選択します。保護されたモジュールとしてgridjsを選択します。選択するロールはRESTful Servicesでなくてもかまいません(専用のロールを新規に作る方が望ましいでしょう)。ここで選択したロールを、APEXのユーザーに割り当てます。


ユーザーとグループの管理を開き、RESTfulサービスへアクセスするユーザーを編集します。グループ割当てとしてRESTful Servicesを割り当てます。


以上でデータのソースとなるRESTfulサービスの準備ができました。

APEXアプリケーションを作成し、Grid.jsを組み込みます。

アプリケーション作成ウィザードを起動し、空のアプリケーションを作成します。

アプリケーションの名前Grid.jsとします。


アプリケーションが作成されます。

ページ・デザイナにてホーム・ページを開きます。


Grid.jsのドキュメントのInstallにに含まれるjsdelivrのセクションを参照し、以下の設定を行います。

ページ・プロパティJavaScriptファイルURLとして以下を設定します。

https://cdn.jsdelivr.net/npm/gridjs/dist/gridjs.umd.js

CSSファイルURLとして以下を設定します。

https://cdn.jsdelivr.net/npm/gridjs/dist/theme/mermaid.min.css


Bodyにグリッドを表示するリージョンを作成します。

識別タイトルGridタイプとして静的コンテンツを選択します。外観テンプレートとして装飾の少ないBlank with Attributes (No Grid)を選択します。詳細静的IDとしてgridを設定します。


作成したリージョンGridを対象として、Grid.jsを初期化します。

ページ・プロパティJavaScriptファンクションおよびグローバル変数の宣言として、以下を記述します。RESTfulサービスの認証に使用します。

let apexSession = apex.env.APP_ID + ',' + apex.env.APP_SESSION;

ページ・ロード時に実行として、以下を記述します。



コード中で使用されている置換文字列G_DATA_SOURCE_URLを、アプリケーション定義置換に設定します。置換値はRESTfulサービスの完全なURLです。


グリッドをリフレッシュするボタンを作成します。

識別ボタン名B_REFRESHラベルリフレッシュとします。動作アクションとして動的アクションで定義を選択します。詳細カスタム属性としてdata-action="#action$force-render"を設定します。


Grid.jsのグリッドを初期化するリージョン、正確にはdiv要素に子要素が含まれていると初期化に失敗します。そのため、ここで作成するボタンB_REFRESHをリージョンGridに含めることはできません。エラーが発生しないようにするために、リージョンGridの静的ID空白にし、ソースHTMLコードとして

<div id="grid"></div>

を記述することもできます。


以上でAPEXアプリケーションへのGrid.jsの組み込みは完了です。

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

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

追記

RESTfulサービスの代わりにAjaxコールバックを使った実装例です。

AjaxコールバックとしてプロセスGET_EMPを作成します。ソースPL/SQLコードは、RESTfulサービスのGETハンドラのコードと同じです。


作成したAjaxコールバックを呼び出すように、ページ・プロパティJavaScriptページ・ロード時に実行を以下に変更します。


2022年8月29日月曜日

誤って削除したアプリケーションを回復する

 誤ってアプリケーションを削除してしまっても、削除したアプリケーションと同じIDのアプリケーションを作成した後に、過去のアプリケーションをエクスポート/インポートすると、削除したアプリケーションを回復することができます。

例えば、アプリケーションIDが102のアプリケーションパラメータ付きレポートを誤って削除してしまった状況を考えます。

アプリケーションが削除されると、アプリケーションを開くことができないためエクスポートの機能も呼び出せません。

ワークスペース・ユーティリティのエクスポートを開きます。

エクスポートする対象としてアプリケーションを選択します。

削除されたアプリケーションは選択リストに含まれないため、エクスポートできません。

そこで、アプリケーション作成ウィザードを起動し、アプリケーションID102(回復するアプリケーションのアプリケーションIDと同じ)のアプリケーションを作成します。アプリケーションの名前はなんでも構いませんが、回復するアプリケーションも含めて、他のアプリケーションとは異なる名前(英数字が望ましい)にします。今回の例ではdummyです。

アプリケーションが作成されたら、エクスポートを実行します。

現在から○○分前の指定として、削除されたアプリケーションが確実に存在していた時刻となる分を設定します。以下の例では20分としています。

エクスポートを実行します。

ファイルがエクスポートされます。続けて、エクスポートされたファイルをインポートします。


インポートを実行し、エクスポートされたファイルを選択します。


予めエクスポートされたファイルを確認しアプリケーション名に間違いがなければ、次のアプリケーションとしてインストールに、エクスポート・ファイルからアプリケーションID 102を再利用を選びます。確信が持てない場合は、新規アプリケーションIDを自動割当てを選んで、一旦別アプリケーションとしてインポートします。


アプリケーションを置き換える場合は、アプリケーションの置換を行う確認が求められます。表示されているメッセージに、置き換えられるアプリケーションの名前置き換えるアプリケーションの名前が表示されるので(置き換えるアプリケーションの名前はUnicodeそのままなのでわかりにくいですが)、それぞれ間違いが無いことを確認します。


インポートが完了すると、削除してしまったアプリケーションが回復していることが確認できます。


削除したデータが回復できるのはデータベースの初期化パラメータundo_retentionに依存します。設定値を確認し、その期間内にエクスポート作業を実施する必要があります。

残念なことにundo_retentionが有効な期間内にエクスポートができなかった場合は、直近の変更までを回復する方法はありません。その場合は、自動的にバックアップされているアプリケーションより、最近のアプリケーションを回復します。

ワークスペース・ユーティリティバックアップの管理を開きます。


リストア対象のアプリケーションを開きます。


アクションからリストアを実行します。


次のアプリケーションとしてインストールRestore Application ID 102を選択ます。

アプリケーションのリストアを実行します。


直近ではありませんが、これで最近のバックアップより削除されたアプリケーションを回復することができます。