Find

Article
· Oct 13 13m read

Writing a REST api service for exporting the generated FHIR bundle in JSON

 

Hi all,

 

Let's do some more work about the testing data generation and export the result by REST API.😁

Here, I would like to reuse the datagen.restservice class which built in the pervious article Writing a REST api service for exporting the generated patient data in .csv

This time, we are planning to generate a FHIR bundle include multiple resources for testing the FHIR repository.

Here is some reference for you, if you want to know mare about FHIR The Concept of FHIR: A Healthcare Data Standard Designed for the Future

 

OK... Let's start😆

1. Create a new utility class datagen.utli.genfhirjson.cls for hosting the FHIR data generation functions

Class datagen.utli.genfhirjson Extends %RegisteredObject
{
}

 

2. Write a Python function genfhirbundle to generate FHIR bundle in JSON string (this part I asked Chat-GPT to help me😂🤫)

- here, I added an argument isirisfhir (default value is 0) to indicate if this FHIR bundle is sending to IRIS FHIR repository or not.

The reason of doing this is the different syntax of resource reference while using uuid 

For example some of the FHIR repository by referencing a patient resource using uuid looks like the following

 "subject": {
          "reference": "Patient/3ce18a5b-b904-4c77-8a33-b09c3f8c79cc"
        }

 While IRIS FHIR repository by referencing a patient resource using uuid looks like the following

 "subject": {
          "reference": "3ce18a5b-b904-4c77-8a33-b09c3f8c79cc"
        }

As a result, an argument is added as a selector

Class datagen.utli.genfhirjson Extends %RegisteredObject
{

ClassMethod genfhirbundle(isirisfhir = 0) As %String [ Language = python ]
{
    # w ##class(datagen.utli.genfhirjson).genfhirbundle(0)
    # w ##class(datagen.utli.genfhirjson).genfhirbundle(1)
    from faker import Faker
    import random
    import json
    import uuid
    from datetime import datetime
    import base64

    fake = Faker()


    # ----------------------
    # Patient
    # ----------------------
    def generate_fhir_patient():
        patient_id = str(uuid.uuid4())
        return {
            "resourceType": "Patient",
            "id": patient_id,
            "identifier": [{
                "use": "usual",
                "system": "http://hospital.smarthealth.org/patient-ids",
                "value": str(random.randint(100000, 999999))
            }],
            "name": [{
                "use": "official",
                "family": fake.last_name(),
                "given": [fake.first_name()]
            }],
            "gender": random.choice(["male", "female"]),
            "birthDate": fake.date_of_birth(minimum_age=0, maximum_age=100).strftime("%Y-%m-%d"),
            "address": [{
                "use": "home",
                "line": [fake.street_address()],
                "city": fake.city(),
                "state": fake.state(),
                "postalCode": fake.postcode(),
                "country": fake.country()
            }],
            "telecom": [{
                "system": "phone",
                "value": fake.phone_number(),
                "use": "mobile"
            }],
            "meta": {"lastUpdated": datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ")}
        }


    # ----------------------
    # Practitioner
    # ----------------------
    def generate_fhir_practitioner():
        practitioner_id = str(uuid.uuid4())
        return {
            "resourceType": "Practitioner",
            "id": practitioner_id,
            "identifier": [{
                "system": "http://hospital.smarthealth.org/staff-ids",
                "value": str(random.randint(1000, 9999))
            }],
            "name": [{
                "family": fake.last_name(),
                "given": [fake.first_name()],
                "prefix": ["Dr."]
            }],
            "telecom": [{
                "system": "phone",
                "value": fake.phone_number(),
                "use": "work"
            }],
            "meta": {"lastUpdated": datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ")}
        }


    # ----------------------
    # Encounter
    # ----------------------
    def generate_fhir_encounter(patient_id, practitioner_id):
        encounter_id = str(uuid.uuid4())
        if isirisfhir==0:
            patient_ref=f"Patient/{patient_id}"
            practitioner_ref=f"Practitioner/{practitioner_id}"
        else:
            patient_ref=patient_id
            practitioner_ref=practitioner_id
        return {
            "resourceType": "Encounter",
            "id": encounter_id,
            "status": "finished",
            "class": {
                "system": "http://terminology.hl7.org/CodeSystem/v3-ActCode",
                "code": "AMB",
                "display": "ambulatory"
            },
            "subject": {"reference": patient_ref},
            "participant": [{
                "individual": {"reference": practitioner_ref}
            }],
            "period": {
                "start": datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ"),
                "end": datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ")
            }
        }


    # ----------------------
    # Condition
    # ----------------------
    def generate_fhir_condition(patient_id, encounter_id, practitioner_id):
        condition_id = str(uuid.uuid4())
        code_choice = random.choice([
            ("44054006", "Diabetes mellitus"),
            ("195967001", "Asthma"),
            ("38341003", "Hypertension"),
            ("254637007", "Viral upper respiratory tract infection")
        ])
        if isirisfhir==0:
            patient_ref=f"Patient/{patient_id}"
            practitioner_ref=f"Practitioner/{practitioner_id}"
            encounter_ref=f"Encounter/{encounter_id}"
        else:
            patient_ref=patient_id
            practitioner_ref=practitioner_id
            encounter_ref=encounter_id
        return {
            "resourceType": "Condition",
            "id": condition_id,
            "clinicalStatus": {
                "coding": [{
                    "system": "http://terminology.hl7.org/CodeSystem/condition-clinical",
                    "code": "active"
                }]
            },
            "code": {
                "coding": [{
                    "system": "http://snomed.info/sct",
                    "code": code_choice[0],
                    "display": code_choice[1]
                }]
            },
            "subject": {"reference": patient_ref},
            "encounter": {"reference": encounter_ref},
            "asserter": {"reference": practitioner_ref},
            "onsetDateTime": datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ")
        }


    # ----------------------
    # Vitals Observation
    # ----------------------
    def generate_fhir_vitals(patient_id, encounter_id, practitioner_id):
        observation_id = str(uuid.uuid4())
        if isirisfhir==0:
            patient_ref=f"Patient/{patient_id}"
            practitioner_ref=f"Practitioner/{practitioner_id}"
            encounter_ref=f"Encounter/{encounter_id}"
        else:
            patient_ref=patient_id
            practitioner_ref=practitioner_id
            encounter_ref=encounter_id
        return {
            "resourceType": "Observation",
            "id": observation_id,
            "status": "final",
            "category": [{
                "coding": [{
                    "system": "http://terminology.hl7.org/CodeSystem/observation-category",
                    "code": "vital-signs",
                    "display": "Vital Signs"
                }]
            }],
            "code": {
                "coding": [{
                    "system": "http://loinc.org",
                    "code": "85354-9",
                    "display": "Blood pressure panel"
                }]
            },
            "subject": {"reference": patient_ref},
            "encounter": {"reference": encounter_ref},
            "performer": [{"reference": practitioner_ref}],
            "effectiveDateTime": datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ"),
            "component": [
                {
                    "code": {
                        "coding": [{
                            "system": "http://loinc.org",
                            "code": "8480-6",
                            "display": "Systolic blood pressure"
                        }]
                    },
                    "valueQuantity": {
                        "value": random.randint(100, 140),
                        "unit": "mmHg",
                        "system": "http://unitsofmeasure.org",
                        "code": "mm[Hg]"
                    }
                },
                {
                    "code": {
                        "coding": [{
                            "system": "http://loinc.org",
                            "code": "8462-4",
                            "display": "Diastolic blood pressure"
                        }]
                    },
                    "valueQuantity": {
                        "value": random.randint(60, 90),
                        "unit": "mmHg",
                        "system": "http://unitsofmeasure.org",
                        "code": "mm[Hg]"
                    }
                }
            ]
        }


    # ----------------------
    # Lab Observations
    # ----------------------
    def generate_fhir_lab(patient_id, encounter_id, practitioner_id):
        labs = [
            ("2339-0", "Glucose [Mass/volume] in Blood", "mg/dL", random.randint(70, 200)),
            ("2093-3", "Cholesterol [Mass/volume] in Serum", "mg/dL", random.randint(150, 250)),
            ("4548-4", "Hemoglobin A1c/Hemoglobin.total in Blood", "%", round(random.uniform(4.5, 10.0), 1))
        ]
        lab_obs = []
        if isirisfhir==0:
            patient_ref=f"Patient/{patient_id}"
            practitioner_ref=f"Practitioner/{practitioner_id}"
            encounter_ref=f"Encounter/{encounter_id}"
        else:
            patient_ref=patient_id
            practitioner_ref=practitioner_id
            encounter_ref=encounter_id
        for loinc, display, unit, value in labs:
            obs_id = str(uuid.uuid4())
            lab_obs.append({
                "resourceType": "Observation",
                "id": obs_id,
                "status": "final",
                "category": [{
                    "coding": [{
                        "system": "http://terminology.hl7.org/CodeSystem/observation-category",
                        "code": "laboratory",
                        "display": "Laboratory"
                    }]
                }],
                "code": {
                    "coding": [{
                        "system": "http://loinc.org",
                        "code": loinc,
                        "display": display
                    }]
                },
                "subject": {"reference": patient_ref},
                "encounter": {"reference": encounter_ref},
                "performer": [{"reference": practitioner_ref}],
                "effectiveDateTime": datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ"),
                "valueQuantity": {
                    "value": value,
                    "unit": unit,
                    "system": "http://unitsofmeasure.org",
                    "code": unit
                }
            })
        return lab_obs


    # ----------------------
    # Medication + MedicationRequest
    # ----------------------
    def generate_fhir_medication(patient_id, encounter_id, practitioner_id):
        meds = [
            ("860975", "Metformin 500mg tablet"),
            ("860976", "Lisinopril 10mg tablet"),
            ("860977", "Salbutamol inhaler"),
            ("860978", "Atorvastatin 20mg tablet")
        ]
        med_code, med_name = random.choice(meds)
        med_id = str(uuid.uuid4())
        med_request_id = str(uuid.uuid4())
        if isirisfhir==0:
            patient_ref=f"Patient/{patient_id}"
            practitioner_ref=f"Practitioner/{practitioner_id}"
            encounter_ref=f"Encounter/{encounter_id}"
        else:
            patient_ref=patient_id
            practitioner_ref=practitioner_id
            encounter_ref=encounter_id

        medication = {
            "resourceType": "Medication",
            "id": med_id,
            "code": {
                "coding": [{
                    "system": "http://www.nlm.nih.gov/research/umls/rxnorm",
                    "code": med_code,
                    "display": med_name
                }]
            }
        }

        medication_request = {
            "resourceType": "MedicationRequest",
            "id": med_request_id,
            "status": "active",
            "intent": "order",
            "medicationReference": {"reference": f"Medication/{med_id}"},
            "subject": {"reference": patient_ref},
            "encounter": {"reference": encounter_ref},
            "requester": {"reference": practitioner_ref},
            "authoredOn": datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ"),
            "dosageInstruction": [{
                "text": f"Take {random.randint(1,2)} tablet(s) daily"
            }]
        }

        return [medication, medication_request]


    # ----------------------
    # DocumentReference (Discharge Summary)
    # ----------------------
    def generate_fhir_documentreference(patient_id, encounter_id, practitioner_id):
        doc_id = str(uuid.uuid4())
        note_text = f"Discharge summary for patient {patient_id}. Diagnosis: Stable. Follow-up in 2 weeks."
        encoded_note = base64.b64encode(note_text.encode("utf-8")).decode("utf-8")
        if isirisfhir==0:
            patient_ref=f"Patient/{patient_id}"
            practitioner_ref=f"Practitioner/{practitioner_id}"
            encounter_ref=f"Encounter/{encounter_id}"
        else:
            patient_ref=patient_id
            practitioner_ref=practitioner_id
            encounter_ref=encounter_id

        return {
            "resourceType": "DocumentReference",
            "id": doc_id,
            "status": "current",
            "type": {
                "coding": [{
                    "system": "http://loinc.org",
                    "code": "18842-5",
                    "display": "Discharge summary"
                }]
            },
            "subject": {"reference": patient_ref},
            "author": [{"reference": practitioner_ref}],
            "context": {"encounter": [{"reference": encounter_ref}]},
            "content": [{
                "attachment": {
                    "contentType": "text/plain",
                    "language": "en",
                    "data": encoded_note,
                    "title": "Discharge Summary Note"
                }
            }]
        }


    # ----------------------
    # Bundle Generator (transaction)
    # ----------------------
    def generate_patient_bundle(n=1):
        bundle = {
            "resourceType": "Bundle",
            "type": "transaction",
            "id": str(uuid.uuid4()),
            "meta": {"lastUpdated": datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ")},
            "entry": []
        }

        for _ in range(n):
            patient = generate_fhir_patient()
            practitioner = generate_fhir_practitioner()
            encounter = generate_fhir_encounter(patient["id"], practitioner["id"])
            condition = generate_fhir_condition(patient["id"], encounter["id"], practitioner["id"])
            vitals = generate_fhir_vitals(patient["id"], encounter["id"], practitioner["id"])
            labs = generate_fhir_lab(patient["id"], encounter["id"], practitioner["id"])
            meds = generate_fhir_medication(patient["id"], encounter["id"], practitioner["id"])
            docref = generate_fhir_documentreference(patient["id"], encounter["id"], practitioner["id"])

            all_resources = [patient, practitioner, encounter, condition, vitals] + labs + meds + [docref]

            for resource in all_resources:
                bundle["entry"].append({
                    "fullUrl": f"urn:uuid:{resource['id']}",
                    "resource": resource,
                    "request": {
                        "method": "POST",
                        "url": resource["resourceType"]
                    }
                })

        return bundle


    # ----------------------
    # Example Run
    # ----------------------
    fhir_bundle = generate_patient_bundle(1)

    return json.dumps(fhir_bundle, indent=2)
}

}

Let's test the function by the following script, for generating FHIR bundle for non IRIS FHIR repository

w ##class(datagen.utli.genfhirjson).genfhirbundle(0)

and check the reference syntax

 

Now test the function by the following script, for generating FHIR bundle for IRIS FHIR repository😁

w ##class(datagen.utli.genfhirjson).genfhirbundle(1)

and check the reference syntax

 


Great! Now go back the pervious article Writing a REST api service for exporting the generated patient data in .csv

We would like to add a new function and update the route for the datagen.restservice class.

 

1. Add a new function GenFHIRBundle, with an parameter isirisfhir for indicating which kind of reference syntax we prefer.

If no parameters are input, it will out put the reference syntax for non IRIS FHIR repository

ClassMethod GenFHIRBundle() As %Status
{
    // get the parameter isirisfhir
    set isirisfhir=$GET(%request.Data("isirisfhir",1),"")
    // default value for non - IRIS repository
    if isirisfhir="" set isirisfhir=0
    // gen the FHIR bundle
    w ##class(datagen.utli.genfhirjson).genfhirbundle(isirisfhir)
    
    return $$$OK
}

2. Then we add a route for the REST service and compile😀

<Route Url="/gen/fhirbundle" Method="GET" Call="GenFHIRBundle"/>

the updated  datagen.restservice class will look like the following.

 


Great!! That's all we need to do!😁

Let's test in postman!!

Try to GET by the following URL for generating FHIR bundle for non IRIS FHIR repository

localhost/irishealth/csp/mpapp/gen/fhirbundle

 

 

Looks good!!

Now Try to GET by the following URL for generating FHIR bundle for IRIS FHIR repository
 

localhost/irishealth/csp/mpapp/gen/fhirbundle?isirisfhir=1

 

Works well!!!😆😉

Thank you very much for reading. 😘

6 Comments
Discussion (6)3
Log in or sign up to continue
Digest
· Oct 13
Announcement
· Oct 12

Key Questions of the Month: September 2025

Hey Community,

It's time for the new batch of #KeyQuestions from the previous month.

150+ Common Sense Questions That'll Make You Think Twice | ClassPoint

Here are the Key Questions of September chosen by InterSystems Experts within all Communities:

📌 Is there any kind of garbage collector? by @Matheus Augusto (EN)

📌 Plotly Bar Graph is NOT showing expected values by @Oliver Wilms (EN)

These questions will be highlighted with the #Key Question tag, and their authors will get the Key Question badge on Global Masters.

If you find the key question(s) from other communities interesting, just drop us a line in the comments, and we will translate the question(s) and the accepted answer(s).

Congrats, and thank you all for your interesting questions. Keep them coming!

See you next month😉

2 Comments
Discussion (2)3
Log in or sign up to continue
Article
· Oct 12 2m read

Como e por que fazer perguntas à IA da Comunidade?

Olá desenvolvedores!

Conhecem a IA da Comunidade? Se não, estão perdendo uma das melhores aplicações que nos trouxeram em 2024. E vocês devem se perguntar: o que é isso? É uma ferramenta muito intuitiva para obter respostas sobre a tecnologia InterSystems. 

Onde a encontro?

 


Ao lado direito da página, logo acima da coluna de eventos, onde há "Fazer uma pergunta a IA". Vamos seguir o passo a passo e vocês verão como é simples.

 

  • Uma vez dentro, vocês observarão que ainda está em fase beta. Nossos companheiros estão trabalhando em incluir manuais de suporte oficiais e incorporar o feedback que vocês vão fornecendo. Vocês verão um disclaimer que avisa que o conteúdo que recebem vêm dos textos da Comunidade de Desenvolvedores. Isso é genial, porque junto vem o artigo e a fonte da resposta.  

 

  • Quando fizerem suas perguntas, receberão respostas esquematizadas, fáceis de ler e resumidas. É uma boa opção para uma primeira busca sobre um tema. Podem filtrar se a resposta é de conteúdo da comunidade ou da documentação oficial.

   

  • Vocês podem avaliar se a resposta foi boa ou não (polegar acima ou abaixo), consultar as fontes (eu adoro isso) e, se for como eu, talvez possa seguir preferindo a ajuda de uma pessoa. Ainda que esta ferramenta esteja incrível, é complementar a ajuda que lhes oferecem nossos experts. Se quiser acrescentar algo mais específico a respeito do seu caso, simplesmente clique em "perguntar à comunidade" e desenvolva sua pergunta sem problemas.

 

  • No seu caso, a IA funcionou? Se te ajudou, pode avaliar positivamente. Isso ajuda a seguir melhorando a BETA.

Futuramente comentarei outras funções interessantes que teremos esse ano na comunidade, assim como ideias e propostas que podem incorporar no seu dia a dia para aproveitar o máximo da tecnologia InterSystems.

Obrigado por ler!

Discussion (0)1
Log in or sign up to continue
Discussion
· Oct 12

Data quality is the main piece to get assertive AI responses?

This anthropic article made me think of several InterSystems presentations and articles on the topic of data quality for AI applications. InterSystems is right that data quality is crucial for AI, but I imagined there would be room for small errors, but this study suggests otherwise. That small errors can lead to big hallucinations. What do you think of this? And how can InterSystems technology help?

https://www.anthropic.com/research/small-samples-poison

Discussion (0)1
Log in or sign up to continue