Try   HackMD

HAPI FHIR測試環境建立

學習FHIR SDK的第一個挑戰往往是如何建立一個合適的測試環境,這份工作,同時包含伺服器架設與測試資料產生。本文說明如何應用Docker快速建立HAPI FHIR Server與使用Synthea產生測試資料。

環境假設

系統已經安裝Docker Desktop

Docker Compose

由於容器相關技術已經非常成熟,特別是對於測試環境的建立,Docker是最佳選擇。最單純的做法是使用docker-compose.yml,設定成功之後即可重複使用。測試環境將使用HAPI FHIR JPA Server,資料庫則採用Postgres。

基本的docker-compose.yml範例如下:

version: '3.7'

services:

  db:
    container_name: db
    image: postgres
    restart: always
    environment:
      POSTGRES_PASSWORD: hapipoc
      POSTGRES_USER: hapipoc
      POSTGRES_DB: hapi
    # not needed for networking between containers but here for troubleshooting
    ports:
      - "5433:5432"

  fhir:
    container_name: fhir
    image: hapiproject/hapi:latest
    ports:
      - "8080:8080"
    environment:
      HAPI_FHIR_USERNAME : admin
      HAPI_FHIR_PASSWORD : admin
      profiles.active: r4
      spring.datasource.url: 'jdbc:postgresql://bmaindb:5432/hapi'
      spring.datasource.username: hapipoc
      spring.datasource.password: hapipoc
      spring.datasource.driverClassName: org.postgresql.Driver
      spring.jpa.properties.hibernate.dialect: ca.uhn.fhir.jpa.model.dialect.HapiFhirPostgres94Dialect

由於是測試環境,密碼就直接寫在docker-compose.yml中,正式環境需要採用更安全的作法。YAML檔案內容很直接清楚,就不多做說明,唯一例外是hibernate.dialect,如果不是使用Postgres,相關設定就必須修改。

在"命另提示字元"下與Docker-compose.yml相同的目錄底下執行

docker compose up

就可以啟動測試環境,包含HAPI FHIR與Postgres

HAPI FHIR特殊設定

由於HAPI FHIR有些特殊功能(動態匯入IG、IPS、CR等)並不適用於每一個應用場景,因此會透過環境變數將相關功能enable或disable

  • hapi.fhir.ig_runtime_upload_enabled : 可於執行階段動態匯入IG
  • hapi.fhir.ips_enabled : 啟用IPS(International Patient Summary)相關功能,主要是$summary operation
  • hapi.fhir.cr_enabled : 啟用CR(Clinical Reasoning)相關功能
  • hapi.fhir.bulk_export_enabled/hapi.fhir.bulk_import_enabled : 啟用Bulk Data import/export功能
hapi.fhir.ig_runtime_upload_enabled: true 
hapi.fhir.ips_enabled: true
hapi.fhir.cr_enabled:true

hapi.fhir.bulk_export_enabled: true
hapi.fhir.bulk_import_enabled: true

FHIR另一個特殊應用就是和已經存在的IG結合,提供資料驗證功能。根據IG是否於registry.fhir.org註冊,相關的設定如下:

  • 已註冊(以TW Core為例)
hapi.fhir.implementationguides.twcore.name: tw.gov.mohw.twcore
hapi.fhir.implementationguides.twcore.version: 0.1.1
hapi.fhir.implementationguides.twcore.reloadExisting: false
hapi.fhir.implementationguides.twcore.installMode: STORE_AND_INSTALL

設定匯入TW Core IG很明顯開機時間會拉長,完成後可以從管理畫面觀察到TW Core IG相關的Resource都已匯入。

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

  • 未註冊(以IPS為例)
hapi.fhir.implementationguides.ips_1_1_0.packageUrl: https://build.fhir.org/ig/HL7/fhir-ips/package.tgz
hapi.fhir.implementationguides.ips_1_1_0.name: hl7.fhir.uv.ips
hapi.fhir.implementationguides.ips_1_1_0.version: 1.1.0

Synthea - 合成資料產生器

建立FHIR Server之後,另一個問題就是測試資料。由於醫療資料具有高度的機敏性,並不適合以實際資料作為練習,所以使用合成資料(Synthetic Data)有其必要性。Synthea為一個開源軟體之醫療資料產生器,官網資料如下:

https://synthetichealth.github.io/synthea/#home

  • 下載Synthea
    ​​git clone https://github.com/synthetichealth/synthea.git
    ​​cd synthea
    ​​./gradlew build check test
    
  • 產生合成資料(Synthetic Data),產生10筆病患資料
    ​​run_synthea -p 10
    

合成資料匯入

Synthea產生的資料,預設目錄是/output/fhir。該目錄底下會有一個醫院訊檔案(以hospitalInformation開頭)、一個醫事人員檔案(以practitionerInformation)以及多個Patient檔案(以上述指令為例,雖然預計產生十筆病患資料,但由於模擬過程中,病患可能死亡,因此病患資料可能大於10筆)。

可以用以下HAPI FHIR Java SDK程式碼將資料一起匯入剛建立的HAPI FHIR Server。

// Load synthetic data from /output/fhir directory
String path = "../output/fhir";
try {
    Files.walk(Paths.get(path)).forEach(filePath -> {
        if (Files.isRegularFile(filePath)) {
           
            // Only load resources from the /output/fhir directory with file name start with "practitionerinformation"
            if (filePath.toString().contains("practitionerInformation") || filePath.toString().contains("hospitalInformation")) {
                System.out.println("Loading " + filePath);
                // Read the file as a string
                String resourceString = "";
                try {
                    resourceString = new String(Files.readAllBytes(filePath));
                } catch (IOException e) {
                    e.printStackTrace();
                }
                // Parse the string to a FHIR resource
                Bundle bundle = ctx.newJsonParser().parseResource(Bundle.class, resourceString);
                // Use the client to store a new resource instance
                IBaseBundle outcome = client.transaction().withBundle(bundle).execute(); 
            }
        }
        {
            //System.out.println("Skipping " + filePath);
        }
    });
} catch (IOException e) {
    e.printStackTrace();
}

由於所有資料都是FHIR Bundle格式,也可以使用REST API POST指令將資料一筆一筆匯入。

Swagger整合

HPAI FHIR已經和Swagger整合,輸入http://localhost:8080/fhir/swagger-ui/#就可以進入相關畫面:

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

以Parient為例,點選Patient之後,就可以看到以下畫面。若不想查詢Resource的Search Parameter,使用Swagger UI也是一個替代方案。

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

也可以直接在此頁面測試API

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

延伸閱讀

FHIR REST API介紹
International Patient Summary (IPS)
HAPI FHIR介紹