作業内容は
- 患者基本情報項目の洗い出し・デザイン
- 患者基本情報データの取得
- 患者基本情報表示画面の作成
- 検査結果表示の修正・作成
である。
4人で話し合い、作業分担を決めた後、各自スケジュール表(WBS)を作成した。
そして、完成目標が10月20日(土)のオープンキャンパス展示となったため、それまでの完成を目指す。
- .controller('PopulationChartController', ['$scope', function($scope) {
- $scope.formatData = function() {
- if (!$scope.data) {
- return [];
- }
- //alert(JSON.stringify($scope.data,null,2));
- var total = [],
- males = [],
- females =[];
- $scope.data.forEach(function(item){
- var strDate = item.sampleDate.replace(/ /g, ' 02:');
- var sampleDate = Date.parse(strDate.replace(/-/g, '/'));
- var value = parseFloat(item.value);
- var normalValue = item.normalValue.split(/-/);
- var lower = parseFloat(normalValue[0]);
- var upper = parseFloat(normalValue[1]);
- total.push({x: sampleDate, y: upper});
- males.push({x: sampleDate, y: value});
- females.push({x: sampleDate, y: lower});
- });
- return [
- {
- key: '上限',
- values: total
- },
- {
- key: '下限',
- values: females
- },
- {
- key: '検査結果',
- values: males
- }
- ];
- };
- .controller('PopulationChartController', ['$scope', function($scope) {
- $scope.formatData = function() {
- if (!$scope.data) {
- return [];
- }
- //alert(JSON.stringify($scope.data,null,2));
- var total = [],
- males = [],
- females =[];
- $scope.data.forEach(function(item){
- var strDate = item.sampleDate.replace(/ /g, ' 02:');
- var sampleDate = Date.parse(strDate.replace(/-/g, '/'));
- var value = parseFloat(item.value);
- var normalValue = item.normalValue.split(/-/);
- var lower = parseFloat(normalValue[0]);
- var upper = parseFloat(normalValue[1]);
- total.push({x: sampleDate, y: upper});
- males.push({x: sampleDate, y: value});
- females.push({x: sampleDate, y: lower});
- });
- return [
- {
- key: '上限',
- values: total,
- color: '#0000CC',
- area:true
- },
- {
- key: '下限',
- values: females,
- color: '#FF0000',
- area:true
- },
- {
- key: '検査結果',
- values: males,
- color: '#00FF00',
- strokeWidth:4
- }
- ];
- };
図1 グラフに色を付けて表示した結果 |
図2 線の太さとエリアの色付けをして表示した結果 |
上記のコーディングはタブバーの定義である。
- <ons-tabbar>
- <ons-tab label="Tab 1" icon="ion-***"active></ons-tab>
- <ons-tab label="Tab 2" icon="ion-***"></ons-tab>
- </ons-tabbar>
まず、上記htmlの「ons-navigator」の直下に追加していく。また、ナビゲータがデフォルト表示する「page」をこれから定義する「tab.html」へ変更しておく
- <ons-navigator var="navi" page="tab.html"></ons-navigator>
まず、1行目の「ons-template」でこのタブバーを「tab.html」として定義し、,2行目で「ons-page」の定義を行う。
- <ons-template id="tab.html">
- <ons-page>
- <ons-tabbar>
- <ons-tab page="main.html" label="メイン" icon="ion-home"active>
- </ons-tab>
- <ons-tab page="demographics.html" label="基本情報" icon="ion-person">
- </ons-tab>
- <ons-tab page="settings.html" label="設定" icon="ion-settings">
- </ons-tab>
- </ons-tabbar>
- </ons-page>
- </ons-template>
図3 タブバーの追加を行った結果 |
![]() |
図4MONACAアプリの新規ファイル作成画面 |
上記のプログラムを記入した後、スマホで確認をすると、
- <ons-page>
- <ons-toolbar>
- <div class="center">基本情報</div>
- </ons-toolbar>
- </ons-page>
図5 基本情報タブをタップした結果 |
これは、前回コーディングしたプログラムである。10行目が、検査結果を連想記憶に設定しているところであるが、これを以下のように書き換える。
- Countries.get()
- .then(
- function(countries) {
- var list = countries.list[0];
- that.list = [];
- list.items.forEach(function(element){
- that.list.push(
- {
- itemName:element.itemName,
- value:element.value,
- unit:element.unit
- }
- );
- });
- }
- );
Math.roundは、javascriptで四捨五入を行うときに使う関数であるが,この関数は小数点以下1桁目を四捨五入して整数を返す関数である。小数点以下3桁目を四捨五入するために,まず検査値を100倍にし、それをMath.round関数を使って四捨五入する。その結果を100で割ると、小数点以下3桁目を四捨五入した結果が得られる。
- value:Math.round(element.value *100) /100
図1 検査結果を小数点以下第2位まで表示した結果 |
まず先ほども修正した上記javascriptのプログラムを以下のように書き換える。
- Countries.get()
- .then(
- function(countries) {
- var list = countries.list[0];
- that.list = [];
- list.items.forEach(function(element){
- that.list.push(
- {
- itemName:element.itemName,
- value:Math.round(element.value *100) /100
- unit:element.unit
- }
- );
- });
- }
- );
まず、条件判定を行うにあたって、基準値から上限・下限の取りだしを行う必要があるのだが、これは以前のブログに書いたプログラムを流用し、7~9行目のように記入する。尚、7行目に関しては、今回検査データはelementに格納されているため、element.normalValueへ変更した。次に、10行目に背景を表すクラス属性を格納する変数を「bgColor」として定義する。そして、11行目~15行目にif文を使って条件判断を記述する。内容は「検査値(element.value)が下限値(lower)以上かつ、上限値(upper)以下であれば正常(normal),そうでなければ異常(abnormal)」といったものである。最後に、that.list.pushで追加する連想配列の21行目にbgColor:bgColorと記載することで、クラス属性を表すbgColorの設定を行った。
- Countries.get()
- .then(
- function(countries) {
- var list = countries.list[0];
- that.list = [];
- list.items.forEach(function(element){
- var normalValue = element.normalValue.split(/-/);
- var lower = parseFloat(normalValue[0]);
- var upper = parseFloat(normalValue[1]);
- var bgColor;
- if(element.value >= lower && element.value <= upper){
- bgColor ="normal";
- }else{
- bgColor ="abnormal";
- }
- that.list.push(
- {
- itemName:element.itemName,
- value:Math.round(element.value *100) /100 ,
- unit:element.unit,
- bgColor:bgColor
- }
- );
- });
- }
- );
そしてhtmlの2行目にある「ons-list-item」タグに「class="{{country.bgColor}}" 」を記述することで、javascriptで設定したクラス属性値("normal"または"abnormal")を設定することができる。
- <ons-list>
- <ons-list-item class="{{country.bgColor}}"
- ng-click="countries.showCountry(country.itemName)" modifier="chevron"
- ng-repeat="country in countries.list | filter:query">
- <ons-col width="50%">{{ country.itemName }}</ons-col>
- <ons-col>{{country.value}}</ons-col>
- <ons-col>{{country.unit}}</ons-col>
- </ons-list-item>
- </ons-list>
図2検査結果一覧画面で異常値に色を付けた結果 |
上記のコードは以前のブログで記述したcountries.list配列に検査名を格納する処理であるが、このコード6行目以降を下記の様に変更する。
- Countries.get()
- .then(
- function(countries) {
- var list = countries.list[0];
- that.list = [];
- list.items.forEach(function(element){
- that.list.push(element.itemName);
- });
- }
- );
以前のブログで調べた検査―データ中にある検査名、検査値、単位それぞれに対応する値をpushメソッドを利用してthat.list配列の中に格納する。javascriptでは、この様なディクショナリ形式(連想配列)で関連する一塊のデータをまとめて記述できる。
- list.items.forEach(function(element){
- that.list.push(
- {
- itemName:element.itemName,
- value:element.value,
- unit:element.unit
- }
- );
4行目の{{country}}はcounties.listから取り出した要素データを表示するものだが、これを下記の様に書き換える。
- <ons-list>
- <ons-list-item ng-click="countries.showCountry(country)" modifier="chevron"
- ng-repeat="country in countries.list | filter:query">
- {{ country }}
- </ons-list-item>
この様にcountry.検査名(itemName),検査値(value),単位(unit)と記述することで、対応したデータを表示することが出来る。
- {{ country.itemName }}
- {{ country.value}}
- {{ country.unit}}
図1 検査項目一覧画面 |
<ons-col>はonsen-UI上で列を表示するタグである。
- <ons-col>{{ country.itemName }}</ons-col>
- <ons-col>{{country.value}}</ons-col>
- <ons-col>{{country.unit}}</ons-col>
図2 ons-colタグで整形した検査項目一覧画面 |
1行目にwidthを記述することで幅の調節を行う事が出来る。
- <ons-col width="60%">{{ country.itemName }}</ons-col>
- <ons-col>{{country.value}}</ons-col>
- <ons-col>{{country.unit}}</ons-col>
図3 width属性で幅の調整を行った検査項目一覧画面 |
- <ons-list-item class="normal" ng-click="countries.showCountry(country.itemName)...
まず、htmlの「ons-list-item」タグへ「normal」「abnormal」というclassの値を設定する。
- <ons-list-item class="abnormal" ng-click="countries.showCountry(country.itemName)...
上記の記述はそれぞれ「abnormal」であれば背景を赤、「normal」であれば背景を白にするといった定義である。
- .abnormal {
- background-color: #FF0000;
- }
- .normal{
- background-color: #FFFFFF;
- }
さらに健康志向の個人の増加に伴い,個人が常に携帯しているスマートフォンなどを利用して日々の運動量,心拍数,食事量,睡眠状態などの多種多様な項目を測定して管理できるアプリケーションが提供され始めている。さらに
現段階では,これらのアプリケーションはスタンドアローンで動作しており,アプリケーション同士や特定健診システムや保健指導システムとの連携は取れていない。 しかし将来的には,アプリケーションが連動して日々健康指導を密に行うようなことが望まれる。このようなデータも含めた個人の生涯にわたる健康情報を管理し解析することで,個人に最適な快適で効果が高く効率的な健康指導が可能になると期待される。と続く。
上記のコードはNVD3を用いたグラフ描画の方法なのだが、これを以下のコードに書き換える
- nv.addGraph(function() {
- chart = nv.models.stackedAreaChart()
- .useInteractiveGuideline(true)
- .showControls(false)
- .margin({left: 50, right: 30});
2行目の「chart = nv.models.lineChart()」という記述を用いることで折れ線グラフの描画を行う事が出来る。しかし、この状態でアプリケーションを実行すると、エラーが表示され、グラフの描画を行う事が出来ない。3~5行目の.から始まる記述はメソッドを示しているのだが、いずれかのメソッドがlineChartのメソッドではないものが記述されている為、エラーが表示されてしまう。ここで、一つ一つのメソッドに対してコメントアウトを行い、どのメソッドがlineChartのものではないのか検証したところ、4行目の「.showControls(false)」といったメソッド(グループモードと積み上げモードを切り替える)がlineChartのメソッドではなかったので、このメソッドをコメントアウトしたところ、折れ線グラフでの描画を行う事が出来た。
- nv.addGraph(function() {
- chart = nv.models.lineChart()
- .useInteractiveGuideline(true)
- .showControls(false)
- .margin({left: 50, right: 30});
この「chart.xAxis」というものは、グラフのx軸を示しているものである。2行目の「.tickFormat」というものは目盛の形式を示しており、(,r)のrは数字という意味であり、カンマは数字に3桁ごとにカンマを付与するといったものである。
- chart.xAxis
- .tickFormat(d3.format(',r'));
上記のxAxisのtickFormatメソッドは、変数dを通じて渡されてきた座標目盛りの値を、無名関数を使って任意の形式に編集している。ここでは基準日時からの経過秒が格納されている数値変数dをnew Date(d)によってJavascriptの日付オブジェクトに変換し、それをd3.time.format関数を使って書式'%Y-%m-%d'により「YYYY-MM-DD」の形式に編集している。
- chart.xAxis
- .tickFormat(
- function(d) {
- return d3.time.format('%Y-%m-%d')(new Date(d));
- }
- );
図1 x軸の目盛りを日付にしたグラフ画面 |
これは1,000,000や、1,000等の値の大きい数をそれぞれ、MやKの単位をつけて短くするものであるが、今回扱うデータはあまり大きい値ではないので、この部分をコメントアウトする。
- chart.yAxis
- .tickFormat(function(d) {
- if ((d / 1000000) >= 1) {
- d = Math.round(d / 100000) / 10 + 'M';
- }
- else if ((d / 1000) >= 1) {
- d = Math.round(d / 100) / 10 + 'K';
- }
- return d;
- });
9行目の「d + scope.data[0].unit;」という記述でグラフのy軸の数値に検査単位を付与することが出来る。ここで、scope.dataは、PopulationChartディレクティブのビューであるHTML中のpopulation-chartタグに付与されたdata属性を表している(下記)。
- chart.yAxis
- .tickFormat(function(d) {
- /* if ((d / 1000000) >= 1) {
- d = Math.round(d / 100000) / 10 + 'M';
- }
- else if ((d / 1000) >= 1) {
- d = Math.round(d / 100) / 10 + 'K';
- }*/
- return d + scope.data[0].unit;
- });
このcountry.populationにはPopulationサービスで作成した検査項目の時系列データが配列として格納されており、その直近の検査日の結果scope.data[0]の検査単位であるscope.data[0].unitを使用してy軸の目盛りに単位をつけている。
- <population-chart ng-if="country.showChart" data="country.population"></population-chart>
ここで、「lower」は基準値の下限、「upper」は基準値の上限である。
- .yDomain([2 * lower-upper, 2 * upper-lower])
直近の検査項目の情報はscope.data[0]に入っているので、そこから基準値scope.data[0].normalValueを取り出し、それを区切り文字「-」で分割し、先頭要素を下限値、2番目の要素を上限値として実数に変換する。これにより、下記の図2の様にグラフのy軸の最大値と最小値の設定を行う事ができ、正常値範囲がわかりやすくなった。
- var normalValue = scope.data[0].normalValue.split(/-/);
- var lower = parseFloat(normalValue[0]);
- var upper = parseFloat(normalValue[1]);
図2 基準値範囲を見やすくしたグラフ画面 |
まず、Javascriptの上記「poplationchartController」の12行目からのプログラムを以下のものに書き換える。
- .controller('PopulationChartController', ['$scope', function($scope) {
- $scope.formatData = function() {
- if (!$scope.data) {
- return [];
- }
- alert(JSON.stringify($scope.data,null,2));
- var total = [],
- males = [],
- females =[];
- for (var i = 0; i < $scope.data.length; i++) {
- var data = $scope.data[i];
- total.push({x: data.age, y: data.total});
- males.push({x: data.age, y: data.males});
- females.push({x: data.age, y: data.females});
- }
OpenDolphinから取得した検査結果は以前のブログに書いたように次のようなフォーマットをしている。
- $scope.data.forEach(function(item){
- var strDate = item.sampleDate.replace(/ /g, ' 02:');
- var sampleDate =Date.parse(strDate.replace(/-/g, '/'));
- var value =parseFloat(item.value);
- var normalValue = item.normalValue.split(/-/);
- var lower = parseFloat(normalValue[0]);
- var upper = parseFloat(normalValue[1]);
- total.push({x: sampleDate, y: upper});
- males.push({x: sampleDate, y: value});
- females.push({x: sampleDate, y: lower});
- });
- return [
- {
- key: '上限',
- values: total
- },
- {
- key: '下限',
- values: females
- },
- {
- key: '検査結果',
- values: males
- }
- ];
- };
{ "list": [ { "progressState": null, "items": [ { "laboCode": null, "lipemia": null, "hemolysis": null, "dialysis": null, "reportStatus": "E", "groupCode": "01", "groupName": "生化学的検査", "parentCode": "0001", "medisCode": "3J010000002327101", "itemName": "総ビリルビン", "abnormalFlg": null, "normalValue": "0.2-1.2", "specimenCode": "023", "specimenName": "血清", "commentCode1": null, "comment1": null, "commentCode2": null, "comment2": null, "sortKey": null, "itemCode": "0001", "sampleDate": "2018-05-06 00:00", "patientId": "1.3.6.1.4.1.9414.70.1:00001", "unit": "mg/dL", "value": "0.4", "id": 1120 }, ・ ・ ・ (中略) ・ ・ ・ { "laboCode": null, "lipemia": null, "hemolysis": null, "dialysis": null, "reportStatus": "E", "groupCode": "07", "groupName": "一般検査", "parentCode": "0493", "medisCode": "1B040130201506211", "itemName": "便中ヒトヘモ定性2日目", "abnormalFlg": null, "normalValue": "陰性", "specimenCode": "015", "specimenName": "便", "commentCode1": null, "comment1": null, "commentCode2": null, "comment2": null, "sortKey": null, "itemCode": "0493", "sampleDate": "2018-05-04 00:00", "patientId": "1.3.6.1.4.1.9414.70.1:00001", "unit": null, "value": "インセイ", "id": 1084 } ], "sampleDate": "2018-05-04 00:00", "laboCenterCode": "LSCC", "moduleKey": "LSCC2018050442011", "reportFormat": null, "patientName": "山下 浩介", "patientSex": "M", "numOfItems": null, "patientId": "1.3.6.1.4.1.9414.70.1:00001", "id": 1051 } ] }この中で、検査日時はsampleDateで、「2018-06-06 00:00」という形式になっている。Javascriptで日時として認識されるデータは「2018/06/06 00:00:00」という形式なので、これを正規表現を使って適切な書式に書き換える。
var strDate = item.sampleDate.replace(/ /g, ' 02:');まず、「strDate」という変数を定義し、検査日時item.sampleDate中のスペースを「 02:」に置き換える。これにより、元の時刻「00:00」を「02:00:00」という時分秒の形式に置き換えることができる。なお、「02:00:00」は午前2時0分0秒を意味するが、この時間には意味がない。「01:00:00」でも何でもよかった。
var sampleDate =Date.parse(strDate.replace(/-/g, '/'));次に、strDate.replace(/-/g, '/')で「strDate」の中の「-」を「/」へ置き換えて、日付「2018-06-06」を「2018/06/06」という形式に変換し、「Date.parse」という命令により文字列の日時をJavascriptの日時オブジェクトに変換する(Javascriptの日付オブジェクトは基準日時からの経過秒で表現されている)。これらの置き換え作業によって、検査日時を変数「sampleDate」に設定できた。
var value =parseFloat(item.value);これは、検査値「item.value」を「paresFloat」命令で文字列から実数値に変換し、変数「value」に代入しているところである。
var normalValue = item.normalValue.split(/-/);これは「item.normalValue」に入っている検査の基準値を上限と下限に分離して、それぞれ変数「upper」と「lower」に格納する処理である。item.normalValueの中に格納されている値は「0-10」という書式になっているので、「-」を区切り文字として、「-」の前後で数字を分ける。例えば、「0.2-1.2」という値であれば、「0.2」と「1.2」の二つの値に分け、それぞれを下限値、上限値とする。
var lower = parseFloat(normalValue[0]);
var upper = parseFloat(normalValue[1]);
total.push({x: sampleDate, y: upper});
males.push({x: sampleDate, y: value});
females.push({x: sampleDate, y: lower});
図1 検査結果グラフ |
前回 レーダーチャートの表示を行うことが出来たので、今回は実際の値を代入したグラフの描画を試みる。 .controller('RaderChartController', ['$scope', 'Countries', funct...