Encontrar

記事
· 2024年7月23日 6m read

FHIR 实践

FHIR是标准,是规范,使用FHIR使大家可以使用同一种语言、语义进行交流,名称、API都是统一的,只要符合FHIR标准,任何系统都可交互。对业务开发者来说,大部分接口交互的定义交给FHIR来处理,效率大大提高。

学习FHIR首先是一个再学习的过程,这个过程对有业务开发基础的人员来说要有个入门,这个入门就是FHIR的各个结构定义的含义,以及与业务系统数据以及接口的对照关系。

FHIR需要用到的几个工具:

 

InterSystems IRIS对于 FHIR相关内容的开发,带来了极大的便利性,里面自带一套定义好的FHIR服务器,也就是包含了FHIR定义的资源及其操作方法。

我这里选用的是最新版本的InterSystems IRIS 2024,做了传统业务系统中科室、医院、病人信息、就诊信息的FHIR转换,主要过程如下:

1. 安装InterSystems IRIS

2. 配置FHIR 服务器

3. 在HIS数据库(IRIS2022)上写一个Restful API 接口,做为对外的统一出口

// w ##class(src.rest.inif).HttpOut({})
ClassMethod HttpOut(Para As %Library.DynamicObject, Type = "1") As %Library.DynamicObject
{
  new (Para,Type)
  s valueType=$classname(Para)
  if (valueType="%Library.DynamicArray"){s Param=[]}
  if (valueType="%Library.DynamicObject"){s Param={}}
  s iter=Para.%GetIterator()
  while iter.%GetNext(.key , .srcKey ) {
    d Param.%Set(key,srcKey)
  }
  s retObj={}
  if ($classname(Param)="%Library.DynamicObject") {
    return:(Param.%Size()=0) {}
  }else {
    s Param=##class(%Library.DynamicObject).%FromJSON(Para)
  }
  set httpReq=##class(%Net.HttpRequest).%New()
  set httpReq.HTTPVersion="1.1"
  set httpReq.Timeout=5
  set httpReq.SocketTimeout=10
  if (Param.Port="443") {s httpReq.Https=1
    s httpReq.SSLConfiguration="ISC.FeatureTracker.SSL.Config"
  } else {s httpReq.Port=Param.Port}
  ;b ;http1                                                                                                                                                                                           
  s httpReq.Server=Param.Server
  
  //表头
  if ((Param.%IsDefined("Header"))&&(Param.Header.%Size()>0)){
      set iter = Param.Header.%GetIterator()
      while iter.%GetNext(.key , .value ) {
        do httpReq.SetHeader(key,value)
     }
  }
  //参数
  if ((Param.%IsDefined("Param"))&&(Param.Param.%Size()>0)){
      set iter = Param.Param.%GetIterator()
      while iter.%GetNext(.key , .value ) {
        do httpReq.SetParam(key,value)
     }
  }
  if ($classname(Param.EntityBody)="%Library.DynamicObject") {
    do httpReq.EntityBody.Write((Param.EntityBody).%ToJSON())   
  }

  ;b ;http2
  if '(Param.%IsDefined("type")) {s Param.type="Post"}
  if (Param.type="Get") {
    set sc=httpReq.Get(Param.Url)
    ;b ;httpget
  }
  if (Param.type="Post") {
    set sc=httpReq.Post(Param.Url,0)
  }
  if (Param.type="Put") {
    ;w "put"_Param.Url,!
    set sc=httpReq.Put(Param.Url)
  }
  if (Param.type="Patch") {
    
    set sc=httpReq.Patch(Param.Url)
  }
  if (Param.type="Delete") {
    set sc=httpReq.Delete(Param.Url)
  }
  
   
  ;B ;httpsc
  if sc=1 { 
    if Type="1" {
      set retObj=httpReq.HttpResponse.Data.Read()
      if $classname(retObj)="%Library.DynamicObject" {
        set retObj=##class(%DynamicObject).%FromJSON(retObj)
      }else{
        return retObj
      }
    }
    if Type="2" {
      ;b ;aa2
      set retObj=httpReq.HttpResponse.Data.Read()
    }
    if Type="3" {set retObj=httpReq.HttpResponse
      ;B ;3
    }
  }else {q "0:网络不通"}
  ;b ;comhttp
  ;set retObj=$zconvert(retObj,"I","UTF8")
  s httpReq=""
  b:retObj="Service Unavailable"
  q retObj
}

4. 做一个对象与FHIR转换程序。(目的是以后将HIS里面的数据转换为FHIR

5. 整理FHIRJSON对象:通过Forge或是网站https://hl7.org/FHIR/R4B/resourcelist.html,把FHIR 定义的资源中的各个数据元素内容转换为对象的定义。在整理的过程中我们总结了下面四点要注意的地方:

  1. FHIR规范里有部分内容是多级的,而我们的业务一般都是平级,如病人信息里的地址(address)
  2. FHIR 规范里定义的valueSetcodesystem,就是对应我们日常理解的代码表。
  3. 对于业务内容的部分,我们先整理医院、科室信息,再整理病人信息、就诊信息。
  4. 对于我们现有业务系统的主键或是关联外键值,在FHIR里面可以放在identifier面,做为查询的条件或指向

6. 从HIS业务库里把职业、医院等代码表数据组成JSON对象,与FHIRJSON对象做对照并上传FHIR服务器

7. 再把病人信息和就诊信息从HIS业务库取出数据组成JSON对,与FHIRJSON对象做对照并上传FHIR服务器

 

下面是一个地址(address)字段组成的json格式例子。

"telecom": [
       {"system": "other","value": "PAPMIMobPhone"}],
    "address": [
      {
        "use": "home",
        "type": "both",
        "line": ["aaaa"],
        "state":"PAPMICTProvinceDR",
        "city": "PAPMICityAreaDR",
        "district": "Rainbow",
        "state": "PAPMICTProvinceDR",
        "postalCode": "3999",
        "period": {
          "start": "1974-12-25"
        }
        
      },

 

在这里面以FHIR对象为基准,HIS对象名放在值里面。用一个统一的方法把HIS里的对象转换成FHIR 格式 JSON对象。转换完后上传到FHIR服务器:

  while iter.%GetNext(.key , .value ) {
    s ret=##class(src.comm.comm).objToObj(value,target)
    ;if (value.CTLOCStartTime<value.CTLOCEndTime){set ret.status="inactive"}else{set ret.status="active"}
    s conObj={"resource":"Patient","EntityBody":(ret),"Param":{"identifier":(value.PAPMINo)}}
    s tmpObj=##class(src.comm.comm).operateDataFhir(conObj)
    if (tmpObj.code=1){
      s ret=##class(%DynamicObject).%FromJSON(tmpObj.data)
      d retArr.%Push(ret)
    }
    ;d retArr.%Push(value)
  }
  
  
  

 

转换示例

// 根据对照做对象转换
// s ret=##class(src.comm.comm).objToObj(src,target)
ClassMethod objToObj(src, target) [ Language = objectscript ]
{
  
  
  n (src, target)
  ;s retObj=target
  s iter=target.%GetIterator()
  s valueType=$classname(target)
  if (valueType="%Library.DynamicArray"){s retObj=##class(%Library.DynamicArray).%New()}
  if (valueType="%Library.DynamicObject"){s retObj=##class(%Library.DynamicObject).%New()}
  while iter.%GetNext(.key , .srcKey ) {
    if ($classname(retObj)="%Library.DynamicArray"){d retObj.%Push(srcKey)}
    if ($classname(retObj)="%Library.DynamicObject"){d retObj.%Set(key,srcKey)}
    
    s valueType=$classname(srcKey)
    if ((valueType="%Library.DynamicArray")||(valueType="%Library.DynamicObject")){
      s temp=##class(src.comm.comm).objToObj(src,retObj.%Get(key))
      d retObj.%Set(key,temp)
    }else{
      //为字符时
      IF ($l(srcKey,"---")>1){
        s srcKey=$replace(srcKey,"---","")
        if (src.%IsDefined(srcKey)){
          if (src.%Get(srcKey)=""){d retObj.%Remove(key)}
          else{d retObj.%Set(key,src.%Get(srcKey))}
        }else{
          d retObj.%Remove(key)
        }
      }else{
        if (src.%IsDefined(srcKey)){
          if (src.%Get(srcKey)=""){d retObj.%Set(key,"空")}
          else{ d retObj.%Set(key,src.%Get(srcKey))}
        }
     }
    }
  }
  return retObj
}

 

进行查询

 

可以看到查出了两个符合查询条件的病人。

1 Comment
ディスカッション (1)1
続けるにはログインするか新規登録を行ってください
質問
· 2024年7月23日

Getting service account token for Online Demo GKE workflow

I'm trying to leverage the workflow from this repo so I can create an online demo for my project: https://github.com/intersystems-community/iris-google-run-deploy-template.

I saw that we need to ask for the service account token because I mistakenly tried to obtain my own and obviously was getting permission errors.

So how should I go about getting the token? Thanks!

ディスカッション (0)2
続けるにはログインするか新規登録を行ってください
記事
· 2024年7月23日 4m read

リンクテーブルと外部テーブルについて

これは InterSystems FAQ サイトの記事です。
 

JDBC および ODBC 経由でInterSystemsIRISから外部データベースにアクセスしたい場合、SQLゲートウェイを使用しリンクテーブルを作成して接続できます。

2023.1以降のバージョンでは、リンクテーブルに加えて、外部テーブル/FOREIGN TABLE を使用することが可能となりました(2024.1時点で実験的機能)。

外部テーブルというのは、物理的に別の場所に保存されているデータを IRIS SQL に投影する非常に便利な機能です。

外部テーブルを使用する場合は、Java(2023.1の場合は1.8~)を事前にインストールし、JAVA_HOME環境変数を設定するだけで、簡単に接続することが可能です。

※JAVA_HOME環境変数設定例:
 


外部テーブルの使用方法については、以下の記事で紹介しております。
レシピデータセットを外部テーブルで読み込み、組み込みPythonでLLMを使って分析する (Langchain + OpenAI)
 


こちらの記事では、外部テーブルで作成できる2種類のテーブル(「CSVファイル直接接続」と「外部DBへのJDBCゲートウェイ経由での接続」)の簡単なサンプル作成例と、外部テーブルの特徴を紹介しています。
 

1-1. 簡単なサンプル作成例(CSVファイル編:ファイルから外部テーブル作成)


a. 外部データラッパとする CSVファイルを用意します(例:C:\temp\FT\managers.csv)

 ※サンプルCSV(managers.csv)

ID,Name,Title,HireDate,CompanyCar
111,"Cornish,Irving",Senior Support Manager,1992-02-10,6
222,"Aquino,Aric","Manager, Technical Account Management",1992-07-15,3
333,"Masterson,Arthur","Director, Customer Support",2002-10-01,9
444,"Deyn,Ernest",Director Customer Support,2000-08-15,4
555,"Lee,Eileen","Manager, Product Support",2002-06-17,3
666,"Knapp,Ashtyn",Senior Support Manager,2002-10-01,11
777,"King,Michael",Senior Support Manager,2003-04-10,2


b. 外部サーバ(WRC.Files)を作成します

CREATE FOREIGN SERVER WRC.Files FOREIGN DATA WRAPPER CSV HOST 'C:\temp\FT\'


c. 外部テーブルを作成します

CREATE FOREIGN TABLE WRC.Managers (
  ID INTEGER, 
  Name VARCHAR, 
  Title VARCHAR, 
  HireDate DATE
) SERVER WRC.Files FILE 'managers.csv' USING
{ "from" : {
       "file" : {
          "header": 1
       }
   }
}


1-2. 簡単なサンプル作成例(JDBCゲートウェイ接続経由編)


a. 外部DBへの JDBCゲートウェイ接続を作成します
 管理ポータル:
 [システム管理] > [構成] > [接続性] > [SQLゲートウェイ接続] 新規作成:WRC


b. 接続用の外部サービス(例:WRC.Data)を作成します。  

CREATE FOREIGN SERVER WRC.Data FOREIGN DATA WRAPPER JDBC CONNECTION 'WRC'


c. 外部サーバ内の任意のテーブルに対して外部テーブルを作成します。
  ※外部テーブルは CREATE FOREIGN TABLE コマンドで定義する必要があります。
   クラス定義を作成して外部テーブルを作成することはできません。

CREATE FOREIGN TABLE Remote.Problems SERVER WRC.Data TABLE 'SQLUser.Problem'


d. 作成後、クエリを実行します。

SELECT ProblemOwner, OpenDate FROM Remote.Problems WHERE OpenDate = '2023-03-09'

外部言語サーバ(%Java Server)が起動されていない状態でクエリを実行すると、以下のようなエラーが返ります。

SQLCODE: <-230>:<Foreign table query Execute() failed>]
  [%msg: <Foreign Tables - ERROR #5023: Remote Gateway Error: Connection cannot be established>]


2. 外部テーブルの特徴

・外部テーブルとのJoinが可能

・ローカルテーブルとのJoinが可能

・外部テーブル用に作成されるクラスは非表示となる(SQLテーブルとしては表示可能)

・削除する場合は、DROP FOREIGN TABLE コマンドで行う
 例:DROP FOREIGN TABLE WRC.Advisor

・外部テーブルに対してクエリを実行すると、クエリごとにすべてのフィールドが取得される

・ストリーム(Stream)フィールドの取得方法は、リンクテーブルと同様に substring 関数 を使用可能

例:

select substring(clob1,1,50) from linked.newclass1


※うまく動作しない場合は、%Java Server が問題なく起動できているかご確認ください。
 [システム管理] > [構成] > [接続性] >[外部言語サーバ]  
 %Java Server が Start されているか

 

外部テーブルの詳細については、以下のドキュメントをご覧ください。
外部テーブル


※SQLゲートウェイ/リンクテーブルの使用方法については、以下のような記事をご紹介しております。

(管理ポータルで行う)リンクテーブルをプログラムで行う方法
SQL ゲートウェイを使用した外部データベースへのアクセス方法について
プログラムでSQLゲートウェイ接続設定を作成する方法

ディスカッション (0)0
続けるにはログインするか新規登録を行ってください
ディスカッション (4)2
続けるにはログインするか新規登録を行ってください
質問
· 2024年7月23日

IRIS / INTEROPERABILITY / New DTL - source / target question

I am creating a new HL7 DTL item. I put the from as HL7 and the to as XML, hence, those Ens classes were loaded into the new DTL. However, on the map screen, both the left and the right column only show 'source' and 'target', and no other fields. I pictured that when I included these classes, both columns would pull the corresponding 'schema' to show all the to/from fields, but, they did not. Is that right?

2 Comments
ディスカッション (2)2
続けるにはログインするか新規登録を行ってください