Stream: việtnam
Topic: V2 to FHIR Stream
Grahame Grieve (Nov 29 2017 at 02:24):
MSH|^~\&|REGADT|MCM|IFENG||199112311501||ADT^A04^ADT_A01|000001|P|2.5||| EVN|A04|200301101500|200301101400|01||200301101410 PID|||191919^^^GENHOS^MR~371-66-9256^^^USSSA^SS|253763|MASSIE^JAMES^A||19560129|M|||171 ZOBER-LEIN^^ISHPEMING^MI^49849^""^||(900)485-5344|(900)485-5344||S|C|10199925^^^GENHOS^AN|371-66-9256||<cr> NK1|1|MASSIE^ELLEN|SPOUSE|171 ZOBERLEIN^^ISHPEMING^MI^49849^""^|(900)485-5344|(900)545-1234~(900)545-1200|EC1^FIRST EMERGENCY CONTACT<cr> NK1|2|MASSIE^MARYLOU|MOTHER|300 ZOBERLEIN^^ISHPEMING^MI^49849^""^|(900)485-5344|(900)545-1234~(900)545-1200|EC2^SECOND EMERGENCY CONTACT<cr> NK1|3<cr> NK1|4|||123 INDUSTRY WAY^^ISHPEMING^MI^49849^""^||(900)545-1200|EM^EMPLOYER|19940605||PROGRAMMER|||ACME SOFTWARE COMPANY<cr> PV1||O|O/R||||0148^ADDISON,JAMES|0148^ADDISON,JAMES|0148^ADDISON,JAMES|AMB|||||||0148^ADDISON,JAMES|S|1400|A|||||||||||||||||||GENHOS|||||199501101410|<cr> PV2||||||||200301101400||||||||||||||||||||||||||200301101400<cr> OBX||ST|1010.1^BODY WEIGHT||62|kg|||||F<cr> OBX||ST|1010.1^HEIGHT||190|cm|||||F<cr> DG1|1|19||BIOPSY||00|<cr> GT1|1||MASSIE^JAMES^""^""^""^""^||171 ZOBERLEIN^^ISHPEMING^MI^49849^""^|(900)485-5344|(900)485-5344||||SE^SELF|371-66-925||||MOOSES AUTO CLINIC|171 ZOBER-LEIN^^ISHPEMING^MI^49849^""|(900)485-5344|<cr> IN1|0|0|BC1|BLUE CROSS|171 ZOBERLEIN^^ISHPEMING^M149849^""^||(900)485-5344|90||||||50 OK|<cr> IN1|2|""|""<cr>
Grahame Grieve (Nov 29 2017 at 02:34):
http://www.healthintersections.com.au/A31ToPerson.js
Grahame Grieve (Nov 29 2017 at 02:35):
function A31ToPerson(aEvent) { // this function mines a A04 message to create a FHIR Patient resource // Mostly the Person resource only covers the information in the PID segment. // // the actual message is the one found in the v2.5 standard (section 3.5.3): //MSH|^~\&|REGADT|MCM|IFENG||199112311501||ADT^A04^ADT_A01|000001|P|2.5||| //EVN|A04|200301101500|200301101400|01||200301101410 //PID|||191919^^^GENHOS^MR~371-66-9256^^^USSSA^SS|253763|MASSIE^JAMES^A||19560129|M|||171 ZOBER-LEIN^^ISHPEMING^MI^49849^""^||(900)485-5344|(900)485-5344||S|C|10199925^^^GENHOS^AN|371-66-9256||<cr> //NK1|1|MASSIE^ELLEN|SPOUSE|171 ZOBERLEIN^^ISHPEMING^MI^49849^""^|(900)485-5344|(900)545-1234~(900)545-1200|EC1^FIRST EMERGENCY CONTACT<cr> //NK1|2|MASSIE^MARYLOU|MOTHER|300 ZOBERLEIN^^ISHPEMING^MI^49849^""^|(900)485-5344|(900)545-1234~(900)545-1200|EC2^SECOND EMERGENCY CONTACT<cr> //NK1|3<cr> //NK1|4|||123 INDUSTRY WAY^^ISHPEMING^MI^49849^""^||(900)545-1200|EM^EMPLOYER|19940605||PROGRAMMER|||ACME SOFTWARE COMPANY<cr> //PV1||O|O/R||||0148^ADDISON,JAMES|0148^ADDISON,JAMES|0148^ADDISON,JAMES|AMB|||||||0148^ADDISON,JAMES|S|1400|A|||||||||||||||||||GENHOS|||||199501101410|<cr> //PV2||||||||200301101400||||||||||||||||||||||||||200301101400<cr> //OBX||ST|1010.1^BODY WEIGHT||62|kg|||||F<cr> //OBX||ST|1010.1^HEIGHT||190|cm|||||F<cr> //DG1|1|19||BIOPSY||00|<cr> //GT1|1||MASSIE^JAMES^""^""^""^""^||171 ZOBERLEIN^^ISHPEMING^MI^49849^""^|(900)485-5344|(900)485-5344||||SE^SELF|371-66-925||||MOOSES AUTO CLINIC|171 ZOBER-LEIN^^ISHPEMING^MI^49849^""|(900)485-5344|<cr> //IN1|0|0|BC1|BLUE CROSS|171 ZOBERLEIN^^ISHPEMING^M149849^""^||(900)485-5344|90||||||50 OK|<cr> //IN1|2|""|""<cr> var v2 = aEvent.Source.Message; var p = FHIR.newPatient; var r = FHIR.makeRequest(); r.Resource = p; aEvent.Dest.FhirRequest = r; // fill out the request r.id = makePatientId(v2); if (r.id == null || r.id == "") r.id = "test"; r.ResourceType = frtPatient; r.CommandType = fcmdUpdate; r.Lang = "en"; // now we have to build the Person resource. fillOutPatientDetails(p, v2); buildNarrative(p, v2); }
Grahame Grieve (Nov 29 2017 at 02:56):
function A31ToPerson(aEvent) { var v2 = aEvent.Source.Message; var p = FHIR.newPatient; var r = FHIR.makeRequest(); r.Resource = p; aEvent.Dest.FhirRequest = r; Bundle b = FHIR.search("Patient?identifier="+MRN); if b.entry.count = 1 patientId = b.entry.resource.id; else if b.entry.count > 1 makeAnError("Multiple matches for Patient "+MRN); else patientId = ""; // we're going to create a new patient // fill out the request r.id = patientId; if (r.id != "") r.CommandType = fcmdUpdate; else r.CommandType = fcmdCreate; r.ResourceType = frtPatient; r.Lang = "vn"; // now we have to build the Person resource. fillOutPatientDetails(p, v2); buildNarrative(p, v2); }
Grahame Grieve (Nov 29 2017 at 02:57):
function A31ToPerson(aEvent) { var v2 = aEvent.Source.Message; var p = FHIR.newPatient; var r = FHIR.makeRequest(); r.Resource = p; aEvent.Dest.FhirRequest = r; MRN = v2["PID-3[5 = 'MR']"]; Bundle b = FHIR.search("Patient?identifier="+MRN); if b.entry.count = 1 patientId = b.entry.resource.id; else if b.entry.count > 1 makeAnError("Multiple matches for Patient "+MRN); else patientId = ""; // we're going to create a new patient // fill out the request r.id = patientId; if (r.id != "") r.CommandType = fcmdUpdate; else r.CommandType = fcmdCreate; r.ResourceType = frtPatient; r.Lang = "vn"; // now we have to build the Person resource. fillOutPatientDetails(p, v2); buildNarrative(p, v2); }
Grahame Grieve (Nov 29 2017 at 04:05):
function buildNarrative(p, v2) { p.text = FHIR.newNarrative; p.text.statusST = NarrativeStatusGenerated; var n2 = v2.element("PID-5"); var name = n2.component(1)+" "+n2.component(2); var a2 = v2.element("PID-11"); var address = a2.component(1)+", "+a2.component(3)+" "+a2.component(4)+" "+a2.component(5); var mrn = v2.Element("PID-3[@5 = \"MR\"].1"); var dob = v2.element("PID-7"); var gender = v2.element("PID-8").DisplayForCode;; p.text.div = new xhtml( "<div>Patient "+name+" ("+mrn+"): "+gender+", born "+dob+".<br/>Address: "+address+"</div>" ); }
Grahame Grieve (Nov 29 2017 at 04:05):
function fillOutPatientDetails(p, v2) { // MRN. We need to assign a global scope to the mrn. It has to be known based on local config p.identifier.add(new Identifier{ value = v2.Element("PID-3[@5 = \"MR\"].1"), // system = lookupURL(v2.Element("PID-3[@5 = \"MR\"].4")), system = "http://hospitals.vn/"+v2.Element("PID-3[@5 = \"MR\"].4")+"/mrn", type = new CodeableConcept(){ coding.add( new Coding( system="http://hl7.org/fhir/v2/0203", code="MR"){})} }); p.identifier.add(new Identifier{ value = v2.Element("PID-3[@5 = \"MR\"].1"), system = "http://moh.gov.vn/vss", type = new CodeableConcept(){ coding.add( new Coding( system="http://hl7.org/fhir/v2/0203", code="NIIP"){})} }); // Nguyen^Thi Phuong Trang^ var n2 = v2["PID-5"]; var n = new HumanName(){ text = n2.component(1)+" "+n2.component(3); }; n.family.add(new string(n2.component(1))); for (s : n2.component(2).split(" ")) n.given.add(new string(s)); // 171 ZOBER-LEIN^^ISHPEMING^MI^49849^""^ var a2 = v2.element("PID-11"); var a = new Address() { text = a2.component(1)+"\r\n"+a2.component(3)+" "+a2.component(4)+" "+a2.component(5)) } a.line.add(new string(a2.component(1))); a.city = a2.component(3); a.state = a2.component(4); p.addressList.addItem(a); // (900)485-5344|(900)485-5344 p.telecom.add(new ContactPoint() {use = "phone", value= v2.element("PID-13"), type= "home"}; p.telecom.add(new ContactPoint() {use = "phone", value= v2.element("PID-14"), type= "work"}; p.birthDate = v2.element("PID-7").copy(0, 10).reformat; // gender & religion: code is also original text. That may not be appropriate, depending on the source of the message string g = v2.element("PID-8"); if (g = "M") p.gender = "male"; else if (g = "F") p.gender = "female"; else if (g = "U") p.gender = "unknown"; else // if (g = "O") p.gender = "other"; }
Grahame Grieve (Nov 29 2017 at 05:03):
// run this every minute procedure PushProcess() { sql = select * from my_database_queue where handled = 0 for each row in fetch(sql) do { // transaction: Bundle b = makeResources(row); Bundle resp = client.submitTransaction(b); for entry in resp.entry do if we_have_no_national_id update our record with the national id runsql("update row set handled = 1"); } } procedure makeResources(row) { Bundle b = new Bundle(); b.type = transaction; b.id = makeUUID; // this row is a changed problem! Condition c = new Condition(); BundleEntry be = b.entry.add; be.resource = c; if my_record_has_national_id c.id = national_id else c.id = ""; we're going to create it fill out the condition from your databases } Security: 3 options to authenticate the client (push process): - username/password = Basic Authentication Header - Mutual TLS certificates (SSL) - Certificate exchange (smart on FHIR "backend services" http://docs.smarthealthit.org/authorization/backend-services/)
Grahame Grieve (Nov 29 2017 at 07:21):
Pull on Search Patient do { check the user has access to which patients build base sql for selecting from patients in scope (select * from patients, user_patients where patients.patientkey = userPaitents.paitnetkey and userpatients.userkey = [user]) for each parameter: add to sql statement (?name=thi -> and patients.name = "%thi%") create response bundle, type = searchset for each row in fetch(sql) add entry to bundle fill out the resource from your database } on read patient do { read the id check the user has access to the patient [id] if so fill out the resource from your database } on Update patient do { read the id check the user has access to the patient [id] check submitted resource is valid check meets business rules if so fill out your database from the resource } on Create patient do { check the user has access to create patients check submitted resource is valid check meets business rules (check ids unique etc) if so assign an id (next primary key, uuid etc) fill out your database from the resource return the id in the location header } key pull issue: - only want data that has changed GET Patient/[id]/$everything?_lastUpdated=2017-10-23T13:14:15+07:00 Security on pull: A: Securing systems: 3 options to authenticate the client (push process): - username/password = Basic Authentication Header - Mutual TLS certificates (SSL) - Certificate exchange (smart on FHIR "backend services" http://docs.smarthealthit.org/authorization/backend-services/) B: Securing the user: - OAuth (smart on FHIR) Authorization: Bearer [token]
Grahame Grieve (Nov 29 2017 at 07:25):
Grahame's server: http://www.healthintersections.com.au/FhirServer
Raymond Francis Sarmiento (Nov 29 2017 at 07:26):
Command Line Tool for HAPI FHIR -- http://hapifhir.io/doc_cli.html
Raymond Francis Sarmiento (Nov 29 2017 at 07:39):
https://fhirblog.com/2016/10/19/setting-up-your-own-fhir-server-for-profiling/
Raymond Francis Sarmiento (Nov 29 2017 at 07:45):
James Agnew's HAPI FHIR github:
https://github.com/jamesagnew/hapi-fhir
Tuan Le (Nov 29 2017 at 07:47):
How to build a health database
https://www.openhealthhub.org/t/howto-build-a-health-database-and-fhir-api-server-in-15-mins-using-open-source/155
Raymond Francis Sarmiento (Nov 29 2017 at 07:47):
How to build a health database and FHIR server in 15 mins using open source
https://goo.gl/28pkTz
This is essentially HAPI FHIR and MySQL
Hanh Dang (Nov 29 2017 at 08:29):
:thumbs_up: thanks
Grahame Grieve (Nov 29 2017 at 10:41):
by everyone.... see you next time!
Raymond Francis Sarmiento (Nov 29 2017 at 10:46):
Great stuff, @Grahame Grieve
We learned a lot! Have a safe flight :)
Trần Ngọc Quốc (Nov 30 2017 at 01:48):
thanks
Last updated: Apr 12 2022 at 19:14 UTC