FHIR Chat · V2 to FHIR Stream · việtnam

Stream: việtnam

Topic: V2 to FHIR Stream


view this post on Zulip 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>

view this post on Zulip Grahame Grieve (Nov 29 2017 at 02:34):

http://www.healthintersections.com.au/A31ToPerson.js

view this post on Zulip 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);
  }

view this post on Zulip 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);
  }

view this post on Zulip 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);
  }

view this post on Zulip 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>"
  );
}

view this post on Zulip 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";
}

view this post on Zulip 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/)

view this post on Zulip 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]

view this post on Zulip Grahame Grieve (Nov 29 2017 at 07:25):

Grahame's server: http://www.healthintersections.com.au/FhirServer

view this post on Zulip Raymond Francis Sarmiento (Nov 29 2017 at 07:26):

Command Line Tool for HAPI FHIR -- http://hapifhir.io/doc_cli.html

view this post on Zulip Raymond Francis Sarmiento (Nov 29 2017 at 07:39):

https://fhirblog.com/2016/10/19/setting-up-your-own-fhir-server-for-profiling/

view this post on Zulip Raymond Francis Sarmiento (Nov 29 2017 at 07:45):

James Agnew's HAPI FHIR github:
https://github.com/jamesagnew/hapi-fhir

view this post on Zulip 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

view this post on Zulip 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

view this post on Zulip Hanh Dang (Nov 29 2017 at 08:29):

:thumbs_up: thanks

view this post on Zulip Grahame Grieve (Nov 29 2017 at 10:41):

by everyone.... see you next time!

view this post on Zulip Raymond Francis Sarmiento (Nov 29 2017 at 10:46):

Great stuff, @Grahame Grieve
We learned a lot! Have a safe flight :)

view this post on Zulip Trần Ngọc Quốc (Nov 30 2017 at 01:48):

thanks


Last updated: Apr 12 2022 at 19:14 UTC