2023年7月26日 星期三

從測試角度看FHIR SDK

近期自行開發了一套FHIR SDK。因是自用不對外,也沒有想著要如何寫使用手冊。雖然整個SDK系統內容還算複雜,但從使用角度似乎沒甚麼好說的。

想要深入研究FHIR,真的從無到有開發一次SDK的方式,就能夠知道所有問題所在。

若要直接從SDK設計思維與程式設計的角度切入,那還真的不知要從何說起。經過一番思考後,何不從測試SDK的思維切入,從測試想法來展開FHIR SDK也許是個好主意。

要如何測試Resource

使用者可能會有三種情境:

  •  收到某個Resource TypeJSON檔案。不管你是使用甚麼樣的JSON程式庫,最終一定可以變成字串(string)
  • 有個很好的物件定義結構,協助我們產生符合FHIR規範的JSON字串。
  • 更新修正JSON內容。

潛在問題

雖然不同ResourceJSON內容不同,但所有測試需求都是一致的。

另外一個就是測試版本問題。FHIR不斷地發展,所以,應該要降低改版時,又得重新設計一次的問題。Resource實在太多了,不可能一改版,就重新寫一次(這邊留個伏筆,相對設計SDK也有同樣問題)。

 解決方法

測試系統結構上會有一個抽象父類別讓個別的Resource來繼承,然後把固定的測試函數,寫在這個父類別上。這部分不受FHIR 改版之影響。

而個別Resource類別,主要就是定義JSON字串、建立Resource物件與更新內容。若FHIR有改版時,那就替換JSON字串。新建Resource部分也有可能需要修改,不過測試主要對象是必要欄位。

程式解說-抽象父類別


行號

說明

1

宣告使用FHIR SDK的基礎套件。

所有Resource都會繼承於DomainResource

2

宣告使用System.Text.Json.Nodes套件。

這個是微軟官方提供處理JSON的套件。請參考System.Text.Json.Nodes Namespace | Microsoft Learn

4

命名空間

6

宣告抽象類別。採用泛型,得知目前所有處理的Resource Type

所有的Resource Type都會繼承於DomainResource

8

宣告 T Resource_TargetObject是用新增物件方式所產生的。

9

宣告 T Resource_TargetString是從字串解析取得的。

10

宣告 T Resource_TargetNode是從JsonNode解析取得的。

11

建構元。輸入Resource TypeJSON字串。

13

指派_TargetObject。呼叫設定物件之函數。SetupObject函數由個別Resource測試類別去編寫內容。

14

指派_TargetString。使用泛型技術,呼叫T Resource類別能接受字串的建構元。

15

指派_TargetNode。使用泛型技術,呼叫T Resource類別能接受JsonNode的建構元。

JsonObject.Parse(jsonString)是將字串解析成JsonNode

注意,實務上我們收到的一定是JSON Object,不會是JSON Array。多筆的一定是Bundle Resource,他是JSON Object

17

宣告一個SetupObject 抽象方法,讓繼承者實做。

這是用來產生一個新的 T Resource

18

宣告一個ChangeDataTest抽象方法,讓繼承者實做。

這是一個測試方法,用來測試更新Resource內容時。

20

-

27

測試方法 ResourceNameTest

這是確認 T ResourceJSON Resource字串所宣告的Resource Type是否一致。

28

-

33

測試方法FromJsonNodeTest

這是確認是否能順利透過JsonNode的方式,來建構T Resource

並且利用T ResourceToJsonString,顯示解析後之結果。

34

-

39

測試方法FromObjectTest

這是確認是否能順利透過設定物件內容的方式,來建構T Resource

並且利用T ResourceToJsonString,顯示解析後之結果。

40

-

45

測試方法FromStringTest

這是確認是否能順利透過解析JSON 字串的方式,來建構T Resource

並且利用T ResourceToJsonString,顯示解析後之結果。

程式解說-Resource測試類別(Account 為例)


行號

說明

1

宣告使用到FHIR SDK Complex的資料型態。

2

宣告使用到FHIR SDK Primitive的資料型態。

3

宣告使用到FHIR SDK Account Resource Type

4

宣告使用到FHIR SDK 用於BindValue Set

6

命名空間

8

宣告這是測試類別。

9

宣告測試類別,繼承於ResourceTypeTester,用來測試Account Resource Type

11

宣告_jsonString其內容為Resource TypeJSON 字串。

可從FHIR官方文件中取得。

66

建構元。丟Resource TypeJSON字串給父類別處理。

67

複寫父類別之SetupObject函數。

69

目標回傳一個新的Account物件。

71

設定有Id欄位。資料型態是Id,屬Primitive

FHIR SDK有針對string寫了擴增方法來轉換成FHIR的資料型態。

為了避免與程式語言的資料型態相衝突,FHIRPrimitive資料型態,前面都加上FHIR



72

-

76

設定Text欄位。資料型態是Narrative,屬Complex

因為是Complex所以,是採用新增物件的方式處理。

其中Status的部分,因為有BindNarrativeStatus,所以可以取值。


Div雖然資料型態是XHtml,但可透過字串方式轉換。

77

-

84

設定Identifier欄位,資料型態是Identifier,屬Complex,多筆。

利用C#起始建構元特性來新增物件。

SystemValue都是從字串轉成對應之FHIR Data Type

85

設定Status欄位。資料型態是Code

BindAccountStatus

86

設定Name欄位。資料型態是String

90

宣告ChangeDataTest是測試方法。

91

複寫父類別的抽象方法。

93

-

105

JSON字串產生Resource

隨即進行欄位更新。

Name欄位,也可以使用new 物件的方式來建構。

Subject欄位其資料型態是Reference,且為多筆。

106

測試是否可以正常產生物件。

107

使用FHIR SDK ResourceToJsonString方法,倒出資料。

測試結果

測試前


測試後






 

2022年11月5日 星期六

HL7 FHIR 模組說明 - Level 2 Conformance

本系列要討論的是FHIR Level 2 Conformance與標準一致性有關之資源。


這類相關Resource都是用來確保標準制訂過程中的一致與相容性性,也提供了實做層級保持一致性。算是核心類Resource。下圖說明了各Resource關係。

要瞭解這些應用,得先去參考另一份文件:Profiling FHIR  文件中會說明如何製作一份Profile檔。 
ImplemantationGuide是一份針對特定應用情境下,把所有與此相關所引發進一步詮釋Resource的文件,進行整合在一起。
其實,所有的Resource都可由StructureDefinition這個Resource來表達。當然,不只如此,data type,extension與constraint等都可以取得元素定義(ElementDefinition)。
接著的問題是,我們要進行存取得FHIR Server,他會支援到哪些東西呢?透過呼叫metadta,取得CapabilityStatement就可探知一些訊息。
例如,可以知道有支援哪些IG。
就Resource層級,知道支援哪些Resource,有哪些存取方法、參數(SearchParameter),操作子(OprerationDefinition)、套組(CompartmentDefinition)。

這系列的Resource要切割成幾個使用者角度來思考。
1. 標準制訂者,也就是IG作者,責無旁貸他必須充分瞭解在這個模組下的所有Resource。
2. 前端標準實做者,也就是開發APP來應用FHIR Server的人。CapabilitySatement是一定要懂,這是你瞭解FHIR Server的第一步。接著是IG,不過是以看懂前者寫的文件比較重要。
3. FHIR Server開發者,需要研究的深度當然要跟標準制訂者相當,差異只是開發者是配合標準制訂者,除完成FHIR Server基本功能外,就是配合IG,實做出相容性高的服務。

2021年6月1日 星期二

HL7 FHIR 模組說明 - Level 4 Medications - Immunization

 官方文件:

簡述:

用來描述個案接種疫苗之相關資訊,偏重於事件描述。

欄位討論:

  • status [0..1]: code
  • statusReason [0..1]: CodeableConcept
    • 延續前項,沒有完成的原因。
    • 參考:Immunization Status Reason Codes (Example)
      • http://terminology.hl7.org/CodeSystem/v3-ActReason
        • {IMMUNE | MEDPREC | OSTOCK | OATOBJ} --> {已免疫 | 因病患違反施打要求 | 缺貨 | 病患反對}
      • http://snomed.info/sct
        • concept is-a 310376006 (Immunization consent not given)
  • vaccineCode [1..1]: CodeableConcept
  • patient [1..1]: Reference(Patient)
    • 施打之對象。
  • encounter [0..1]: Reference(Encounter)
    • 就診紀錄。
  • occurrence[x] [1..1]: {dateTime | string}
    • 施打日期。
  • recorded [0..1]: dateTime
    • 第一次施打時間。若本次是第一次施打(相同的vaccineCode時),那時間就該與occurrence相同。
  • primarySource [0..1]: boolean
    • 代表這份Resource的紀錄是來自第一時間施打者所寫。而非由其他系統所產生的紀錄。
    • 主要是代表這份文檔的可靠度(reliability)。
  • reportOrigin [0..1]: CodeableConcept
    • 若不是第一時間施打者之紀錄,那本紀錄來自哪裡。
    • 參考:Immunization Origin Codes (Example)
      • http://terminology.hl7.org/CodeSystem/immunization-origin
        • {provider | record | recall | school} --> {來自其他提供者 | 來是病患書面報告 | 來自病患本身、父母或監護人之回憶 | 來自學校紀錄}
  • location [0..1]: Reference(Locatioin)
    • 施打地點。
  • manufacturer [0..1]: Reference(Organization)
    • 疫苗製造商。
  • lotNumber [0..1]: string
    • 疫苗批量編號。
  • expirationDate [0..1]: date
    • 疫苗到期日。
  • site [0..1]: CodeableConcept
  • route [0..1]: CodeableConcept
    • 以什麼途徑方法進入人的身體。
    • 參考:Immunization Route Codes (Example)
      • http://terminology.hl7.org/CodeSystem/v3-RouteOfAdministration
    • 例如:
      • IDINJ --> 皮內注射
      • IM --> 肌內注射
      • NASINHLC --> 鼻吸入
      • IVINJ --> 靜脈注射
      • PO --> 口服
      • SQ --> 皮下注射
      • TRNSDERM --> 經皮
  • doseQuantity [0..1]: SimpleQuantity
    • 疫苗注射量。
    • 注意單位代碼。
  • performer [0..*]: BackboneElement
    • 進行本次施打疫苗事件的執行者。
    • 注意一個施打事件可能由很多人共同執行。
    • 欄位討論:
  • note [0..*]: Annotation
    • 註記。
  • reasonCode [0..*]: CodeableConcept
  • reasonReference [0..*]: Reference(Condition | Observation | DiagnosticReport)
    • 為何要施打此疫苗詳細原因之參考資訊。
  • isSubpotent [0..1]: boolean
    • 劑量的效力。應該是指本次施打並無得到完整效能。
  • subpotentReason [0..*]: CodeableConcept
    • 未達全效的原因。
    • 參考:Immunization Subpotent Reason (Example)
      • http://terminology.hl7.org/CodeSystem/immunization-subpotent-reason
    • 例如:
      • partial --> 並沒給予足夠劑量。
      • coldchainbreak --> 疫苗有經歷冷鏈損壞。
      • recall --> 此疫苗是被製造商招回的。
  • education [0..*]: BackboneElement
    • 要給施打此疫苗病患之教育訓練教材。
    • 欄位討論:
      • documentType [0..1]: string
        • 教材代碼。
      • reference [0..1]: uri
        • 教材的參考網址。
      • publicationDate [0..1]: dateTime
        • 教材出版日期。
      • presentationDate [0..1]: dateTime
        • 向病患展示教材的日期。
  • programEligibility [0..*]: CodeableConcept
    • 病患接種此疫苗的資格,或者說他是有資格取得贊助費用。
    • 參考:Immunization Program Eligibility (Example)
      • http://terminology.hl7.org/CodeSystem/immunization-program-eligibility
    • 例如:
      • ineligible --> 沒有資格獲取贊助費用。
      • uninsured --> 可獲贊助費用,因為病患無保險。
  • fundingSource
    • 贊助施打疫苗的費用來源。
    • 參考:Immunization Funding Source (Example)
      • http://terminology.hl7.org/CodeSystem/immunization-funding-source
    • 例如:
      • private --> 來自私人採購贊助。
      • public --> 來自政府採購贊助。
  • reaction [0..*]: BackboneElement
    • 詳細紀錄施打後的各種副作用反應。
    • 欄位討論:
      • date [0..1]: dateTime
        • 副作用開始時間日期。
      • detail [0..1]: Reference(Observation)
        • 詳細描述情況。
      • reported [0..1]: boolean
        • 用來註記是否為自行回報。
  • protocalApplied [0..*]: BackboneElement
    • 來自提供者的建議(協議)。
    • 太過專業,不方便闡述。

2021年5月30日 星期日

HL7 FHIR 模組說明 - Level 4 Medications - Medication

 官方文件: 

  • http://www.hl7.org/fhir/medication.html (R4)
  • http://hl7.org/fhir/2021May/medication.html (R5)

簡述:

淺顯易懂,就是放藥品資訊的地方。注意,他沒有服藥相關資訊,就是單純的代表一種藥的編碼,廠商、樣式、成分、包裝,批量等資訊。

欄位討論:

  • code [0..1]: CodeableConcept
  • status [0..1]: code
  • manufacturer [0..1]: Reference(Organization)
    • 製造生產商。
  • form [0..1]: CodeableConcept
  • amount [0..1]: Ratio
    • 每一份包裝的數量。
    • 注意,他的資料型態是Ratio,就會有分子與分母。若為每一份,所以分母為1。
  • Ingredient [0..*]: BackboneElement
    • 藥品成分清單。
    • 欄位討論:
      • item[x] [1..1]: {CodeableConcept | Reference(Substance | Medication)}
        • 成分代碼。
        • 像複方藥,成分也可能是藥。
      • IsActive [0..1]: boolean
        • 是否可用。
      • Strength [0..1]: Ratio
        • 強度。此成分所佔的比率。
  • batch [0..1]: BackboneElement
    • 批量相關資訊。
    • 欄位討論:
      • lotNumber [0..1]: string
        • 批量序號。
      • expirationDate [0..1]: dateTime
        • 到期日

2021年5月27日 星期四

HL7 FHIR 模組說明 - Level 4 Medications - MedicationRequest - Dosage

延續前一篇提到MedicationRequest.dosageInstruction的資料型態是Dosage。他有點複雜,需要專頁來說明。其實,他並不適合放在這,Dosage在R4,他是被很尷尬的獨立成一篇。


到了R5,重新架構了資料型態,將Dosage歸入Special Purpose Types中(R4是隱晦在文件中),感覺就舒服多了。
言歸正傳,這邊討論的是R4版本。其實與R5就欄位內容是一樣的,只是R4是繼承BackboneElement,而R5是繼承BackboneType(實際上就是Element)。至於兩者有何差異,不在本篇討論範圍。(迫不及待想知道,那就參考R5的Type Framework)。
====================================

簡述:

關於藥物服用的指引。

欄位討論:

  • sequency [0..1]: integer
    • MedicationRequest.dosageInstruction是[0..*],表示一張MequestionRequest所要求的一種藥,可以有多筆用藥說明。
    • 這個欄位就是用來當序號的。
  • text [0..1]: string
    • 非結構描述用藥資訊。
  • additionalInstruction [0..*]: CodeableConcept
  • patientInstruction [0..1]: string
    • 針對此病患提供額外服藥指引。
  • timing [0..1]: Timing
    • 服藥資訊與時間有關者。
    • Timing這個資料型態在R4比Dosage更尷尬。他是被放在General-Purpose DataType中,卻是唯一繼承於BackboneElement者。
    • 到了R5,就與Dosage一樣,繼承於BackboneType。但是,仍放在General-Purpose DataType。
    • 為什麼?因為BackboneElement已經不繼承於DataType而是直接繼承於Element。
    • 這個資料型態有點複雜,可參考之前的文章HL7 FHIR 資料型態篇 - Complex Types - 時間類
  • asNeeded[x] [0..1] {Boolean | CodeableConcept}
  • site [0..1]: CodeableConcept
  • route [0..1]: CodeableConcept
    • 使用什麼途徑將藥進入身體(在什麼地方,就看site)。
    • 參考:SNOMED CT Route Codes (Example)
      • Code System http://snomed.info/sct
        • Concept is-a 284009009 (Route of administration values)
    • 例如:
  • method [0..1]: CodeableConcept
    • 以什麼方法技術來使用藥進行服藥。 
    • 參考:SNOMED CT Administration Method Codes (Example)
      • Code System http://snomed.info/sct
        • Concept is-a 422096002 (Dosing instruction fragment)
    • 例如:
      • 419747000 Chew
      • 421521009 Swallow
      • 421538008 Instill
      • 422145002 Inject
      • 421682005 Dissolve
  • doseAndRate [0..*]: Element
    • 服藥資訊與數量/速率有關者。
    • 注意他的資料型態是Element。而非常見的BackboneElement。
    • 欄位討論:
      • type [0..1]: CodeableConcept
        • 數量/速率的值是怎麼來的。
        • 參考:DoseAndRateType (Example)
          • Code System http://terminology.hl7.org/CodeSystem/dose-rate-type
            • Concept is calculated
            • Concept is ordered
      • dose[x] [0..1]: { Range | SimpleQuantity }
        • 每一劑的使用數量(可數)。
        • 是範圍值,還是特定值。
        • 每一個值要提供單位。常用之Code System為http://unitsofmeasure.org
      • rate[x] [0..1]: {Ratio | Range | SimpleQuantity}
        • 每一劑使用之速率(不可數)
        • 例如掉點滴。
  • maxDosePerPeriod [0..1]: Ratio
    • 每單位時間內的最大劑量
  • maxDosePerAdministrtion [0..1]: SimpleQuantity
    • 每次服藥之最大劑量。
  • maxDosePerLifetime [0..1]: SimpleQuantity
    • 病患此身最大使用劑量。

2021年5月25日 星期二

HL7 FHIR 模組說明 - Level 4 Medications - MedicationRequest

官方文件:http://hl7.org/fhir/medicationrequest.html

簡述:

這個其實不能被視為等同開立處方籤,因為我們對處方籤的概念是一張單子上,會列出很多筆藥。依據欄位內容定義,Medication[1..1]僅能描述一種藥,所以,他是醫師開立一項藥品的請求單。

他會經由EMR系統啟動一系列的流程,進而產生其他的本系列有關之Resource。

欄位討論:

  • 他繼承於DomainResource。(大部分的Resource都會繼承於DomainResource,但有些不是,例如Bundle,Binary...。)

  • status [1..1]: code
    • 限定:Medicationrequest status (Required) --> http://hl7.org/fhir/ValueSet/medicationrequest-status#{active | on-hold | cancelled | completed | entered-in-error | stopped | draft | unknown}
    • 從CDS Hooks的角度來說,開立一張訂單,要經過查核後才能確認。此時,收到MedicationRequest的狀態就會是draft。active是指此Request可能還有些步驟未完成。Completed表示所有步驟接已完成。
  • intent [1..1]: code
    • 限定:Medication request intent (Required) --> http://hl7.org/fhir/ValueSet/medicationrequest-intent#{proposal | plan | order | original-order | reflex-order | filler-order | instance-order | option}
    • 這份Request的意圖。一般而言,都是放order。
  • category [0..*]: CodeableConcept
    • 此Resource用於何處。
    • 參考:Medication request category codes (Example)  --> http://terminology.hl7.org/CodeSystem/medicationrequest-category。
    • 門診、住院、社區照護、出院。
  • priority [0..*]: code
    • 此Resource的優先序。
    • 限定:Request priority (Required) --> http://hl7.org/fhir/ValueSet/request-priority#{routine | urgent | asap | stat}
    • {常規 | 緊急 | 愈快愈好 | 立刻} 低-->高
  • doNotPerform [0..1]: boolean
    • 若為true,反而是告知此MedicationRequest不要執行。
  • reported[x] [0..1]: {boolean | Reference(Patient | Practitioner | PractitionRole | RelatedPerson | Organization)}
    • 設定此MedicationRequest是報告,而非主要紀錄。
    • 主要紀錄是當時情境。報告是事後情境,那就會有一份引發此報告的來源。 
  • medication[x] [1..1]: {CodeableConcept | Reference(Medication)}
    • 這份MedicationRequest所指稱的藥品資料。
    • 因為是Choice資料型態,所以要注意抓回來的是哪一種資料型態。
    • 這個是依紀錄不同,千萬別以為同一台FHIR Server都會給固定資料型態。
    • 無論哪種,最終目標還要抓到code。
    • 但要注意,這個code是哪種sytem。
    • 國外會用RxNorm。
  • subject [1..1]: Reference(Patient | Group)
    • 這份MedicationRequest為誰|那哪些人而申請的。
    • Group類似Bundle的功能,用來打包其他Resource。但是有很大差異。Group只能打包固定六種類的東西(person | animal | practitioner | device | medication | substance)。可接受固定的Resource(Patient | Practitioner | PractitionerRole | Device | Medication | Substance | Group)
  • encounter [0..1]: Reference(Encounter)
    • encounter在國外的定義是很複雜。只要有醫病交流之情境,就可以產生一份Encounter,所以Encounter會有Location。很多個Encounter會衍生自同一個Appointment。
    • 此處就是紀錄這份MedicationRequest是發生在哪一次的Encounter。
  • supportingInformation [0..*]: Reference(Any)
    • 其他有關開立處方之支持性資源。
  • authoredOn [0..1]: dateTime
    • 此MedicationRequest開立日期。
  • requester [0..1]: Reference(Practitioner | PractitionerRole | Organization | Patient | RelatedPerson | Device)
    • 提出此處方需求之來源。
  • performer [0..1]: Reference(Practitioner | PractitionerRole | Organization | Patient | Device | RelatedPerson | CareTeam)
    • 執行處方的人。
    • 也就是會拿藥去餵subject的人。
  • performerType [0..1]: CodeableConcept
    • 適合來執行此處方的角色要求。
    • 參考:Procedure Performer Role Codes (Example) -->  http://hl7.org/fhir/ValueSet/performer-role
    • CodeSystem
      • http://snomed.info/sct | 223366009(Healthcare professional)
  • recorder [0..1]: Reference(Practitioner | PractitionerRole)
    • 真正開立此MedicationRequest的人。
    • 注意不要與requester混淆,這個是想要處方的人,所以才會有Patient。
    • 而此處是有資格開立處方的人,當然只能是有醫師資格的人。
  • reasonCode [0..*]: CodeableConcept
    • 定義開立或不開立此藥的原因或指示。
    • 參考:Procedure Performer Role Codes (Example) --> http://hl7.org/fhir/ValueSet/condition-code
    • CodeSystem
      • http://snomed.info/sct | 404684003 (Clinical finding)
      • http://snomed.info/sct | #160245001 (No current problems or disability)
  • reasonReference [0..*]: Reference(Condition | Observation)
    • 用來支持證明需要開立這份MedicationRequest的Condition或Observation。
    • 一般而言,這個Condition的category會是encounter-diagnosis。
  • basedOn [0..*]: Reference(CarePlan | MedicationRequest | ServiceRequest | ImmunizationRecommendation)
    • 是基於哪一個流程中所開立的MedicationRequestion。也就是,為了滿足哪一個流程而開立。
  • dosageInstruction [0..*]: Dosage
    • 如何服用此藥物的指示。
    • Dodage資料型態有點複雜,得專頁說明。

P.S. 實在無法把所有欄位都描述說明。有些沒碰到過,還真不知道是幹嘛用的。我也不是/不能搞翻譯,就我所知盡量分享了。

2021年5月17日 星期一

HL7 FHIR 模組說明 - Level 4 Medications Module

本系列要討論的是FHIR Level 4 Medications 相關的Resource。

本系列的Resource主要用於:

  • 處方籤相關流程,如開立、配藥、給藥、服藥等紀錄。
  • 紀錄防疫,評估接種疫苗時間點等。
  • 藥品資訊與用藥知識。
Resource 主要分成兩大類:Medications與Immunizations。

Medications 系列簡單地說就是:
開藥-->配藥-->服藥 
(MedicationRequest-->MedicationDispense-->MedicationAdministration)
藥資訊-->藥更多資訊
(Medication--> MedicationKnowledge)
在吃什麼藥
(MedicationStatement)

Immunizations系列簡單地說就是:
打疫苗前-->打了什麼疫苗-->打疫苗後
(ImmunizationRecommendation-->Immunization-->ImmunizationEvaluation)

說實在,要介紹Resource的內容,真的是吃力不討好。講多了像翻譯,講少了還不如自己去看。

所以,這系列僅會針對重要的欄位進行說明。一些背景知識,就不再多加說明。網路上搜尋,中文的資料也越來越多了。(我寫的東西,已經不在搜尋的前幾條中,這是好現象吧。)