マッピングセッティングプロファイル仕様
1.概要
マッピングセッティングプロファイルは、FHIRリソースに関連するデータ変換・生成のための言語です。
変換元のデータはCSVのほか、FHIRリソース、各種メディアなどを想定しています。
変換先のデータはFHIRリソースのほか、CSVを含むテキスト、異なるバージョンのFHIRリソース
などを想定しています。
制約:
- 現バージョンではCSVファイルからm文書n情報を生成するために必要な機能のみを実装しています。
以下は2つのCSVファイルからPatientリソースを生成するプログラム例です。
* $PatientIdentification = files(PatientIdentification.csv) // 患者情報ファイル
* $PatientAddress = files(PatientAddress*.csv, HIS_PATIENT_ID) // 患者住所ファイル
<<context Identification, $PatientIdentification // 患者レコード毎ループ
* $Patient = createResource(Patient,${HIS_PATIENT_ID}) // リソースを作成
* $Patient.birthDate = YYYYMMDD2date(${BIRTHDATE}) // 誕生日を設定
* $Patient.gender = HIS_SEX2gender(${SEX}) // 性別を設定
<<context Address, $PatientAddress.get(${HIS_PATIENT_ID}) // CSVファイルをネスト
* $Patient.address[+].postalCode = ${POSTAL_CD} // 郵便番号
* $Patient.address[=].period.start = YYYYMMDD2date(${PA_START_DATE}) //開始日
* $Patient.address[=].period.end = YYYYMMDD2date(${PA_END_DATE}) //終了日
>>
>>
変換プログラムは上から順に実行します。
<<, >>で囲まれた部分はループです。
マッピングセッティングプロファイル処理系はCSV変換を簡潔に記述するための機能を内蔵しています。
- PatientIdentification.csvという名前の患者情報ファイルのレコードごとに外側のループを繰り返します。
${<カラム名>}という形式でCSVレコードが含む文字列を参照できます。- CSVファイルを特定のカラムの内容でグループ分けできます。
「files(PatientAddress*.csv, HIS_PATIENT_ID)」はHIS_PATIENT_IDの値でグループ分けをしています。「$PatientAddress.get(${HIS_PATIENT_ID})」でグループをとりだし、その値でつぎのループを回しています。
マッピングセッティングプロファイルが処理するデータの型には、つぎのものがあります。
- 文字列型
- 数値型(整数またはBigDecimalで表現される)
- boolean型(真偽値。定数は「
$$$_true」と「$$$_false」) - null型(表記は「」)
- List型(添え字が0~の整数)
- Map型(添え字が文字列)
- Tree(樹状のデータ構造)
- レコードセット型(CSVファイルから得たレコード群)
- リソース型(FHIRリソース。HL7が提供するJavaライブラリによる実装)
$で始まる英数字の文字列は変数名です。
「files(PatientIdentification.csv)」などは関数呼び出しです。マッピングセッティングプロファイル処理系が提供するシステム関数に加え、マッピングプロファイル(.frumap) で関数を定義できます。
上記のプログラムで「PatientIdentification.csv」や「Patient」はRaw文字列と呼ぶ文字列定数です。 Raw文字列はつぎの書式を持ち、代入文の右辺と関数の引数に置くことができます。
- 英字で始まる英数字の文字列
つぎの場所にはRaw文字列が置けます。逆にここへ式を置く場合にはかっこ「(」「 )」で囲む必要があることに注意してください。
- 代入文の右辺
- 関数呼び出しの引数
- execのパラメータや関数引数の初期化での「=」の右辺
サンプル(3文書6情報の一部):Patient基本情報を生成するマッピングプロファイル
サンプル(Patient)
以下は、患者基本情報からPatientリソースへ変換して永続化するサンプルです。
マッピングセッティングプロファイルの構文(例:files(...)で入力読込、<<context>>のループ、FHIRリソースへの代入、commit()で保存)を理解する際にお役立てください。詳細は本仕様の各章をご参照ください。
実行手順(ZIP・API)はCSV変換手順 > クイックスタート(Patient)を参照してください。
// -*- coding: utf-8 -*-
// 初期化
//readConversionTables()
* $PatientResult = files(_INPUT/patient_sample.csv)
<<context Result, $PatientResult
<if ${IS_CHILD_RESOURCE} = ('TRUE')
* $PatientRoot = createChildResource(Patient)
><else
* $PatientRoot = createResource(Patient, ${PATIENT_ID})
>
print -----, columns()
// 準拠プロファイル
* $PatientRoot.meta.profile[+] = 'http://jpfhir.jp/fhir/eCS/StructureDefinition/JP_Patient_eCS'
<if ${IS_CHILD_RESOURCE} != ('TRUE') or ${HAVE_LAST_UPDATED} = ('TRUE')
* $PatientRoot.meta.lastUpdated = getNow()
>
// 被保険者個人識別子
* $PatientRoot.identifier[+].use = usual
* $PatientRoot.identifier[=].system = 'http://jpfhir.jp/fhir/clins/Idsystem/JP_Insurance_memberID'
* $PatientRoot.identifier[=].value = ${INSURANCE_ID}
// 自施設の患者番号
<if ${PATIENT_ID} != '' and ${INSTITUTION_NUMBER} != ''
* $PatientRoot.identifier[+].use = usual
* $PatientRoot.identifier[=].system = ('urn:oid:1.2.392.100495.20.3.51.1' + ${INSTITUTION_NUMBER})
* $PatientRoot.identifier[=].value = ${PATIENT_ID}
>
// 姓名
* $PatientRoot.name.text = (${FAMILY_NAME} + ' ' + ${GIVEN_NAME})
* $PatientRoot.name.family = ${FAMILY_NAME}
* $PatientRoot.name.given[+] = ${GIVEN_NAME}
* $PatientRoot.name.extension[+].url = 'http://hl7.org/fhir/StructureDefinition/iso21090-EN-representation'
* $PatientRoot.name.extension[=].valueCode = IDE
// カナ姓名
<if ${FAMILY_NAME_KANA} != '' and ${GIVEN_NAME_KANA} != ''
* $PatientRoot.name[+].text = (${FAMILY_NAME_KANA} + ' ' + ${GIVEN_NAME_KANA})
* $PatientRoot.name.family = ${FAMILY_NAME_KANA}
* $PatientRoot.name.given[+] = ${GIVEN_NAME_KANA}
* $PatientRoot.name.extension[+].url = 'http://hl7.org/fhir/StructureDefinition/iso21090-EN-representation'
* $PatientRoot.name.extension[=].valueCode = SYL
>
// 性別
* $PatientRoot.gender = ${GENDER}
// 生年月日
* $PatientRoot.birthDate = toDate(${BIRTH_DATE})
// 住所
* $PatientRoot.address.text = ${ADDRESS_TEXT}
* $PatientRoot.address.state = ${ADDRESS_STATE}
* $PatientRoot.address.city = ${ADDRESS_CITY}
<if ${ADDRESS_POSTALCODE} != ''
* $PatientRoot.address.postalCode = ${ADDRESS_POSTALCODE}
>
<if ${ADDRESS_LINE} != ''
* $PatientRoot.address.line[+] = ${ADDRESS_LINE}
>
$$$patients[${PATIENT_ID}] = $PatientRoot
>>
<<function public getPatient($patientId)
return $$$patients[$patientId]
>>
// ------------------ 以下変換関数
// 初期化
<<function readConversionTables()
// なし
>>
// 変換定義ファイルの読み込み
<<function makeMap($filename)
print $filename
$input = files(_CONVERTER/map/$($filename).csv)
<<context csv, $input
* $$$conv[$filename][${param1}].system = ${system}
* $$$conv[$filename][${param1}].code = ${code}
* $$$conv[$filename][${param1}].display = ${display}
>>
>>
// 変換
<<function conv($fileName,$param1)
$ret = $$$conv[$fileName][$param1]
// 処理が必要な場合はここで定義
return $ret
>>
本サンプルのCSV配置・ZIP化・API実行は、CSV変換手順のルールに従ってください。(input/とdefinition/の2階層、ZIPファイルをBase64エンコードした文字列で送るなど )
2.ファイル
マッピングプロファイル(.frumap)と入力データはマッピングパックというデータとしてマッピングセッティングプロファイル処理系に渡されます。
マッピングパックはファイルシステムのフォルダ構造を抽象化したもので、「/」で区切られたパスと対応するファイル本体の並びを保持します。
マッピングパックが保持するファイルはテキストファイルとバイナリファイルの2種類です。 これらはマッピングパックが生成されるときに拡張子で区別します。
テキストファイルはUTF-8エンコーディングされ、LF(\n)で改行します。
マッピングプロファイル(.frumap) とCSVファイルはテキストファイルでなければなりません。
マッピングセッティングプロファイル処理系が扱うCSVファイルはつぎの形式です。
- カンマ「,」でレコードを区切る。
- カンマなどのエスケープはExcel準拠とする(Apache commons CSVを使用)
- 先頭行でカラム名を定義する。
制約:
- ファイルパスには一般のOSのファイル名として使える文字が使えます。しかしマッピングセッティングプロファイル処理系では正規化などの処理を行っていないため、見た目が同じでも異なるコードを使ったファイル名を同一のものとして扱えません。したがって、ファイルパスとファイル名には英数・アンダースコア・ハイフンのみを使うことを推奨します。
実行時にマッピングセッティングプロファイル処理系へつぎの2つのマッピングパックが渡されます。
- _CONVERTER:変換プログラムと変換データ
- _INPUT:入力データ(csvファイルなど)
実行プログラム(起動プログラムやexecで指定するプログラム)は_CONVERTERマッピングパックから読み出します。
関数「files」と「listFilenames」では引数にファイルパスのglob表現を指定できます。
いずれもデフォルトでは_INPUTマッピングパックを対象として処理します。
用語変換などに使うCSVファイルを入力データではなく変換定義の方に含める場合にはパスの先頭に「_CONVERTER/」を付加することで変換定義のファイルを扱うことができます。 入力ファイルを明示的に指定する場合には「_INPUT/」を付加します。 「_CONVERTER/」と「_INPUT/」をglob表現のワイルドカードとして含めることはできません。
記述例
$csv = files('csvs/patient.csv')
特定のファイルを読み込む。
ワイルドカード「*」「?」を含まないので、指定したファイルが存在しなければエラー停止する。
$csv = files('**/*.csv')
$csv = files('_INPUT/**/*.csv')
入力データのマッピングパックからすべてのCSVファイルをパスの辞書順で読み込む。
該当するファイルが1個も存在しない場合にはエラーではなく長さゼロのデータを読み込む。
$csv = files('_CONVERTER/**/*.csv')
変換データのマッピングパ ックからすべてのCSVファイルをパスの辞書順で読み込む。
該当するファイルが1個も存在しない場合にはエラーではなく長さゼロのデータを読み込む。
3.プログラム
関連するプログラムは1個のマッピングパックとしてマッピングセッティングプロファイル処理系に渡されます。
プログラムはどのディレクトリへ置いても拡張子をはずした名前で呼び出せます。 「Group1/Observation.frumap」は「Observation」という名前で呼び出します。
プログラムはメインプログラムとユーザ定義関数で構成します。
先頭行から、最終行あるいは最初のユーザ定義関数が現れるまでがメインプログラムです。 ユーザ定義関数を定義したあとにメインプログラムを書くことはできません。
たとえば、つぎの3種類のプログラムで1群の機能を構成するような使いかたができます。
- 全体 制御
リソースを生成する順番や、Compositionの構成を記述します。 - リソース生成
リソース種ごとに生成方法を記述します。 ただし、プログラミング上の都合で1個のリソース種に複数のプログラムが対応することや、 同一のCSVデータセットから同時に複数のリソースを生成することもあります。 - 変換関数
リソース生成などに必要な関数を定義します。
4.変数
変数には通常変数とカラム変数の2種類があります。
4.1.通常変数
一般的なプログラミング言語の変数と同様に、あるスコープの中で値を保持し参照できる変数です。
通常変数の書式はつぎの通りです。
<スコープ種別><変数名>
<スコープ種別> つぎのいずれか
$ : ローカル変数
$$ : 関数ローカル変数
$$$: グローバル変数
<変数名> :先頭が英文字で英・数・アンダースコアから構成する文字列
例:
$a
$$j_12
$$$A
先頭がアンダースコアの変数名はマッピングセッティングプロファイル処理系の予約名です。 現時点では利用可能ですが、将来的に使用不可となる場合があります。
以下にスコープごとの変数の特性・使い方について説明します。
4.1.1.ローカル変数
メインプログラム・関数、ループなどで区切られたプログラム内で有効な変数です。
- 定義時に1回だけ書き込み可能です。
- ループ内で定義したローカル変数は、ループの繰り返しごとに消去します。
- 書き込みされていないローカル変数を読み出すことはできません(エラー終了します)。
- 他のメインプログラム・関数からローカル変数を読むことはできません。
- 同じ名前のローカル変数と関数ローカル変数は定義できません。
- メインプログラム・関数内で同じ名前のローカル変数は複数定義できません。 ただしループ内でシステムが定 義する$_lineなどの変数は、多重ループにおいて複数定義されることがあります(後述)
例:
「$x」はループの先頭で毎回定義され、ループの繰り返し時点で毎回消去されます。
<<for $x: range(1, 5)
print $x
>>
関数の引数はローカル変数としなければなりません。
<<function example($arg)
print $arg
>>
4.1.2.関数ローカル変数
メインプログラム・関数内で有効な変数です。
- 何回でも書き込み可能です。
- 書き込みされていない関数ローカル変数を読み出すことはできません(エラー終了します)。
- 関数ローカル変数は、メインプログラム・関数を終了したときに消去します。
- 他のメインプログラム・関数から関数ローカル変数を読み書きできません。
- 同じ名前のローカル変数と関数ローカル変数は定義できません。
例:
$$count = 0
<<while $$count < 5
* $$count = ($$count + 1)
print $$count
>>
4.1.3.グローバル変数
すべてのメインプログラム・関数から読み書きできる変数です。
- 何回でも書き込むことが可能です。
- 書き込みされていないグローバル変数を読み出すと「」(null)値が得られます。
例:
$$$xが書き込み済であればその値をプリントします。
まだ書き込みがなされていない場合にはエラーにはならず「{}」null値をプリントします。
print $$$x
4.2.カラム変数
CSVファイルのカラム値を格納することからカラム変数と呼びます。 メインプログラム・関数の呼び出しを越えて読み出すことが可能です。 詳細な操作をするプログラムが大局的なプログラムと引数渡しを介さずに情報を共有するための 機能です。 カラム値のほかシェルスクリプトの環境変数のような使い方ができます。
書式はつぎの通りです。
${<カラム名>}
<カラム名>;任意の文字列。ただし連続する空白(\u0020)は1文字とし、先頭と後尾の空白は削除する。
例:
${HIS_PATIENT_ID}
プログラマがカラム変数を定義する方法はつぎの通りです。
<<contextループによるCSVファイル- exec文のパラメータ
exec文によるメインプログラム読み出し、関数呼び出し、多重ループにおいて、呼び出された側や内側のループから、呼び出し元や外側のループで定義されたカラム変数はすべて読み出せます。
同じカラム名でカラム変数を定義しなおすことは可能です(「コンテキスト」の項参照)。
制約: