Find

Digest
· Nov 3

Nuevas publicaciones en la Comunidad de InterSystems, 27 octubre - 2 noviembre

27 octubre - 2 noviembreWeek at a GlanceInterSystems Developer Community
Digest
· Nov 3

Publications des développeurs d'InterSystems, semaine Octobre 27 - Novembre 02, 2025, Résumé

Octobre 27 - Novembre 02, 2025Week at a GlanceInterSystems Developer Community
Article
· Nov 3 13m read

Création d'un service d'API REST pour exporter le bundle FHIR généré au format JSON

Bonjour à tous,

Continuons à travailler sur la génération de données de test et l'exportation des résultats via une API REST. 😁

Ici, je souhaite réutiliser la classe `datagen.restservice` créée dans l'article précédent : « Écriture d'un service API REST pour exporter les données patient générées au format .csv ».

Cette fois-ci, nous prévoyons de générer un bundle FHIR incluant plusieurs ressources pour tester le référentiel FHIR.

Voici une référence si vous souhaitez en savoir plus sur FHIR : « The Concept of FHIR: A Healthcare Data Standard Designed for the Future ».

C'est parti ! 😆

1. Créez une nouvelle classe utilitaire `datagen.util.genfhirjson.cls` pour héberger les fonctions de génération de données FHIR.

Class datagen.utli.genfhirjson Extends %RegisteredObject
{
}

2. Écrivez une fonction Python `genfhirbundle` pour générer un bundle FHIR au format JSON (j'ai demandé de l'aide au Chat-GPT pour cette partie 😂🤫).

J'ai ajouté ici un argument `isirisfhir` (valeur par défaut : 0) pour indiquer si ce bundle FHIR est destiné au référentiel FHIR IRIS.

Ceci est nécessaire en raison de la syntaxe différente des références de ressources utilisant un UUID.

Par exemple, voici à quoi ressemble une référence FHIR pour une ressource patient utilisant un UUID.

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

Le référentiel IRIS FHIR, qui référence une ressource patient à l'aide de son UUID, ressemble à ceci :

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

Par conséquent, un argument est ajouté en tant que sélecteur.

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)
}

}

Testons la fonction à l'aide du script suivant, pour générer un bundle FHIR pour un dépôt FHIR autre qu'IRIS.

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

et vérifiez la syntaxe de référence

Testez maintenant la fonction avec le script suivant, pour générer un bundle FHIR pour le dépôt IRIS FHIR 😁

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

et vérifiez la syntaxe de référence


Parfait ! Revenons à l'article précédent : « Écriture d'un service API REST pour exporter les données patient générées au format .csv ».

Nous souhaitons ajouter une nouvelle fonction et mettre à jour la route de la classe `datagen.restservice`.

1. Ajoutez une nouvelle fonction `GenFHIRBundle`, avec un paramètre `isirisfhir` indiquant la syntaxe de référence souhaitée.

Si aucun paramètre n'est fourni, la fonction renverra la syntaxe de référence pour un référentiel FHIR autre qu'IRIS.

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. Ensuite, nous ajoutons une route pour le service REST et nous compilons 😀

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

La classe datagen.restservice mise à jour ressemblera à ceci :


Super ! C'est tout ce qu'il nous reste à faire ! 😁

Testons avec Postman !

Essayez d'effectuer une requête GET à l'URL suivante pour générer un bundle FHIR pour un dépôt FHIR autre qu'IRIS.

localhost/irishealth/csp/mpapp/gen/fhirbundle

 

Parfait !

Essayez maintenant d'effectuer une requête GET à l'URL suivante pour générer un bundle FHIR pour le dépôt IRIS FHIR.

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

Ça marche super bien !!! 😆😉

Merci beaucoup d'avoir lu. 😘

Discussion (0)1
Log in or sign up to continue
Announcement
· Nov 2

InterSystems Open Exchange Applications Digest, October 2025

Hello and welcome to the October 2025 Open Exchange Recap.
General Stats:
26 new apps in October
579 downloads in October
1,158 applications all time
43,414 downloads all time
3,410 developers joined
New Applications
quarkus-iris-monitor-system
By Davi Massaru Teixeira Muta
IRIStool and Data Manager
By Pietro Di Leo
yaml-adaptor
By Yuri Marx
DevHub
By Ashok Kumar T
golang-fiber-iris-realworld-example-app
By Dmitry Maslennikov
python-iris-audio-query
By Yu Han Eng
Interop-LookupTable
By Ashok Kumar T
IRIS ESB
By Andrew Sklyarov
Erralyzer
By Ashok Kumar T
sanitary-surveillance
By Vinícius Sena
gorm-iris
By Dmitry Maslennikov
irisconns
By Eric Fortenberry
java-iris-python-heart-diagnosis-system
By Joseph Ekpe
gj :: configExplorer
By John Murray
GBLSizeMonitor
By Ashok Kumar T
Reusable-Web-Interface-for-Data-Lookup-Table
By Sanjib Pandey
Free DBsize with Swagger
By Robert Cemper
TablePluse
By Ashok Kumar T
InterSystems IRIS GraphRAG
By Fan Ji
swaggertools-jsonviewer
By Ashok Kumar T
iris-docdb-ui
By Ashok Kumar T
inquisidor
By Luis Angel Pérez Ramos
FocusBoard
By Ashok Kumar T
iris-haos
By Ron Sweeney
ObjectScript-Native-API-demo
By Robert Cemper
Stream-Sample-for-Beginners
By Robert Cemper
New Releases
MDX2JSON by Eduard Lebedyuk
v3.2.49
FIX: when axis with "agg" type received a children object
SQL-for-ERRORS-Global by Robert Cemper
v1.0.9
fix iris.script to use zpm
Intersystems-Monitoring by Teunis Stolker
v1.0.25
  • Improved System Monitoring by sending explicit properties
  • Allow sending 500 i.s.o. 100 Events and Message Headers per request
  • No longer sending Info events
  • No longer sending AuditEvents for UnknownUser
  • Added headers for Monitoring Package version and IRIS version in each request
  • Now compatible with newer ZPM versions (IPM)
v1.0.26
EventLogService: Moved condition TraceCat <> 'user' into SQL query
IntegrityLog-Web by Ashok Kumar T
v1.0.2
Enhanced the user interface and implemented new features.
IRIS internal WebSocket Client by Robert Cemper
v1.2.0
  • Added SSL for WSS support
  • new Demo with available
    • "wss://echo.websocket.org/"
  • SSL requires manual installation in ZPM as this might cause serious conflicts
JsonTraceViewer by Guillaume Rongier
v1.2.0
Fix issue with new json format form IRIS
Support YAML too now.
Thanks to AshokThangavel for his PR.
Most downloaded
MDX2JSON
By Eduard Lebedyuk
ObjectScript-Math
By Peter Steiwer
DeepSeeWeb
By Anton Gnibeda
zpm-registry
By Evgeny Shvarov
yaml-utils
By Benjamin De Boe
WebTerminal
By Nikita Savchenko
Intersystems-Monitoring
By Teunis Stolker
Test Coverage Tool
By Timothy Leavitt
ssl-client
By Evgeny Shvarov
Embedded Git
By Pravin Barton
October, 2025Month at a GlanceInterSystems Open Exchange
Discussion (0)1
Log in or sign up to continue