2021年1月31日 星期日

CDS Hooks - 規範 - 4

今天討論「CDS Response」

CDS Response

CDS Service --> CDS Client

Response的前置作業

這個過程其實沒有這麼簡單。當CDS Client決定要引用CDS Server哪個Service時,就得遵守規定,把FHIR相關資源準備好後,叫用Service時,用POST帶上。如果用Postman測試時,在BODY的部分可選擇x-www-form-urlencoded(可減少設定)。
CDS Server從Endpoint接受到資料後,要開始解析。整個作業流程應該要分成四個重要階段。
  1. 檢核Post 資料正確性。也就是說,傳回來的Hook跟當初規定的是否一樣。
  2. 要解析fhirAuthorization。你要設計好流程,到底還要再跟FHIR Server要什麼東西。因為Access Token一般都是有時間限制(當然,還會有個Refresh Token的機制)。另外一點就是,他會有Scope的問題,就是Resource.Interactions。
  3. 要解析prefetch的內容。這塊要小心。一般而言,若Read,原則上你要什麼Resource就會回給你什麼Resource。若你是用Search,那就會是Bundle,要再進一步解析entry才是你想要的Resource。
  4. 取你CDS真正要的資料進行運算處理,然後得到結果。這個結果才是用來組出Response Object的資料。
這段講得輕鬆,但FHIR技術關鍵就在這兒。
Web的技術關鍵是OAuth與CORS。

Response Object

回傳一個物件包含兩個欄位cards與systemActions,兩個皆是陣列。
systemActions在1.0版沒有。所以,最簡單的範例:這個Card,是由CDS Client決定要如何呈現在他們的系統中。在EPIC他們稱之為Best Practice Advisory,會將Card的內容轉成HTML Web介面。
{ "cards": [] }

Card Object


這個是有點複雜,還會衍生出幾個物件出來。我就簡單描述,會做的應該一點就懂。

uuid

這個是可選欄位。若你有處理到feedback階段的話,那這個就很重要。在C#有個GUID物件,很簡單處理。

summary

這個是必要欄位。將你的結果簡單描述。字串長度要小於140個字元(我實作時有踢到這個鐵板) 。

detail

結果的詳細說明。若是Debug階段,一些訊息就放在這(別像我,以為summary是必要就放在那兒,結果是Bug的Bug)。

indicator

說明這個結果的重要程度。目前支援有info、warning與critical。理想上,應該是不同程度畫面要有所區別,但是要看CDS Client要不要實作。

source

這是用來宣告版權的,為必要欄位。其結構為:(不多加解釋,coding結構就是FHIR的資料型態)

suggestions

這個整個card的核心,卻是Optional。他的內容是suggestion物件陣列。用來描述,CDS所提供的可採取行動之建議。其結構為:

其中action欄位是實際描述「當CDS Client接受這個Suggestion時要如何處理」。其結構為:


 處理的方式不外乎create、update與delete(怎麼沒有retrive?啊你前面在幹嘛?)。resource就是處理的對象。就是Resource啦。這裡要注意你要處理的Resource,當初的Scope都要宣告到。不過,還是得看CDS Client有沒有支援啦(坦白說,FHIR的真還在發展中,就是因為發展中,那還不趕快跟上)。貼個EPIC支援的給各位參考。(剪圖關係未能全部)


 Source: https://fhir.epic.com/

selectionBehavior

如果你有suggestion欄位,那這個欄位就是必要。他只有兩種選項at-most-one,單選。與any,多選。哈~還是要看CDS Client有沒有支援啦。

overrideReasons

當CDS Client不接受你任何suggestion時,你可以用這個欄位讓他選擇不接受的理由。他是coding的陣列。還是一樣,要看CDS Client要不要支援。

links

這是提供給CDS Client一些建議網站或者APP實用的。他是Link物件陣列。其結構為:


範例

官方範例提供參考

{ "cards": [ { "summary": "Example Card", "indicator": "info", "detail": "This is an example card.", "source": { "label": "Static CDS Service Example", "url": "https://example.com", "icon": "https://example.com/img/icon-100px.png" }, "links": [ { "label": "Google", "url": "https://google.com", "type": "absolute" }, { "label": "Github", "url": "https://github.com", "type": "absolute" }, { "label": "SMART Example App", "url": "https://smart.example.com/launch", "type": "smart", "appContext": "{\"session\":3456356,\"settings\":{\"module\":4235}}" } ] }, { "summary": "Another card", "indicator": "warning", "source": { "label": "Static CDS Service Example" } } ] }

在實作的過程這個階段很痛苦。因為每一個CDS Client的支援程度與方式不盡相同,甚至容錯能力也不同。

2021年1月28日 星期四

CDS Hooks - 規範 - 3

 複習一下,CDS的實作流程有四個階段的規範,「Discovery」、「Request CDS」、「CDS Response」與「Feedback」。今天討論Request CDS。

Request CDS

CDS Client 呼叫 --> CDS Server實作

 Endpoint: POST {baseURL}/cds-services/{service.id}

Request Object


註:
1. 由CDS Client負責組合物件。
2. CDS Server得解析物件。

 hooks

叫用這個service所規定的hooks標準。大部分都是patient-view。

hookInstance

這是由CDS Client產生的UUID碼,他必須需Globally unique。CDS Server端,每一個hookInstance都是單一事件,都得單獨處理。我這次實作比較像寫範例,所以這個值我沒有記錄下來。其實,回應訊息時也不會用到。

 fhirServer

這個是CDS Client告訴CDS Server我的FHIR Server在哪裡。他是一個完整路徑。(切到資源)

fhirAuthorization

這個有點複雜是OAuth 2.0內容。主要是給你Access Token,讓你可以直接向CDS Client的FHIR Server抓取資料。不過,都得要事先宣告Scope,其流程沒有那麼簡單。也不是CDS Client都會給你。

context

hooks是規範,只有描述需要哪些鍵值欄位。CDS Client就把這次POST資料實際鍵值內容放在這裡。如果Client採用Prefetch方式,已經這個內容不重要。但是,採fhirAuthorization的話,這個就很重要。每次來回的資料總得是同一個病人吧。 

prefetch

這的內容很重要。他就是CDS Server用prefetch template告知CDS Client要準備的FHIR內容。CDS Server端在解析的時候要小心。當初你可以同時要了好幾條不同的Resource。另外,你可能走Search 方式,那你收到的可能是Bundle回來的東西,解析時要小心。

 Request 範例


 這是CDS Client發出的資料(部分)

至於這個sandbox介面後續會介紹。網址先給各位了:https://sandbox.cds-hooks.org/

CDS Server端就根據自己所需要資料到prefetch裡的FHIR Resource去抓囉。

其實,如果CDS Client如果沒有給你fhirAuthorization的資訊,那就隱含告訴你,去他們的FHIR Server抓資料無須Access Token。

至於要怎麼解析,程式面的問題,應該會找適當時間來寫寫。如果沒有保密問題的話。

至於CDS要怎麼運用資料,那就超出本系列的範圍了。




CDS Hooks - 規範 - 2

 進入無聊的階段,卻是實作最重要的階段。

實作CDS的整個過程,可以粗分「Discovery」、「Request CDS」、「CDS Response」與「Feedback」四個階段。(先把OAuth的部分屏除)

Discovery

CDS Client呼叫 <--> CDS Server實作

 Endpoint: GET {baseURL}/cds-services

Response Object:


註:

  1.  想看完整有Swagger文件:Swagger Editor
  2. 都是JSON格式。
  3. 注意Type是Primitive type, Object還是Array。 

hook

這個欄位是宣告兩端要共同遵守的鍵值變數組合(Context)。最常見的就是patient-view,他要求雙方有userId(R)、patientId(R)與encounterId(O)。這些鍵值欄位會以「prefetch tokens」的形式應用於prefetch欄位(後敘)中。所謂prefetch tokens就是用{{ }}包覆鍵值變數。

title

這個欄位可有可無,純粹用來給人看,由CDS Client決定要不要去呈現他。

description 

這個欄位反而是必要欄位,用來描述這個Hook服務的主要任務是什麼。

id

這個欄位非常重要,而且要小心設計。這個會是URL的一部分,也就是呼叫這個服務的路徑。所以,他必須要唯一。{baseUrl}/cds-services/{id}。

prefetch

這個欄位雖為Optional,卻是非常重要的欄位。CDS Server端告知CDS Client端,若要使用此服務,請你要提供哪些FHIR Resource。這個欄位內容的表達方式,有一個專有名稱為「Prefetch Template」是否能看懂且會用,又要用得好,就是看你FHIR的功力了。後面會專節說明。 

Discovery 範例

為避免一頭霧水,提供規範書的範例參考。這由CDS Server端實作,為/cds-services的回應內容。
{ "services": [ { "hook": "patient-view", "title": "Static CDS Service Example", "description": "An example of a CDS Service that returns a static set of cards", "id": "static-patient-greeter", "prefetch": { "patientToGreet": "Patient/{{context.patientId}}" } }, { "hook": "medication-prescribe", "title": "Medication Echo CDS Service", "description": "An example of a CDS Service that simply echos the medication being prescribed", "id": "medication-echo", "prefetch": { "patient": "Patient/{{context.patientId}}", "medications": "MedicationRequest?patient={{context.patientId}}" } } ] }
提醒,這個id與prefetch必須配對好,Server端要記住。當Client用某Id過來時,要知道他可能會帶什麼prefetch,別接錯了。

Prefetch Template

這段可能是CDS Hook最關鍵的地方。講到這先補充一下FHIR的Interactions。

CDS Hooks的Prefetch Template他就是FHIR read與search的機制。(另外支援的機制在後面規範會提到)所以,懂FHIR愈多,就能寫個漂亮的Prefetch Template,CDS Server端能要來的資料就更精確。
在官方文件就提供了一個範例:
{ "prefetch": { "hemoglobin-a1c": "Observation?patient={{context.patientId}}&code=4548-4&_count=1&sort:desc=date" } }
我非臨床人員,就不便多作解釋。不過,我可以跟妳說上述稱之為search機制。
一般最常用的就是
{ "prefetch": { "patient": "Patient/{{context.patientId}}", "user": "{{context.userId}}" } }
這是read機制。那個被{{ }}圍起來的就是prefetch Token。能有哪些可用,就看你是引用哪個Hooks規範。

先偷偷展示,回應會是長什麼樣子?(這是由CDS Client負責產生的)
{ "prefetch": { "patient":{ "resourceType": "Patient", "gender": "male", "birthDate": "1974-12-25", "...": "<snipped for brevity>" }, "hemoglobin-a1c": { "resourceType": "Bundle", "type": "searchset", "entry": [{ "resource": { "resourceType": "Observation", "code": { "coding": [{ "system": "http://loinc.org", "code": "4548-4", "display": "Hemoglobin A1c" }] }, "...": "<snipped for brevity>" } }] } } }