Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generate fhir-jembi adaptor #734

Closed
wants to merge 101 commits into from
Closed

Generate fhir-jembi adaptor #734

wants to merge 101 commits into from

Conversation

josephjclark
Copy link
Collaborator

@josephjclark josephjclark commented Aug 23, 2024

This PR contains code to generate a jembi-fhir adaptor based on the fhir specification.

It's fairly early days but I think definitions for Patient, Observation and Encounter will be ready very soon

How it works

Basically rather than having an adaptor here, we have an adaptor generator. A mix of hand-written and automated code.

The repo will download all the fhir specs from build.fhir.org/ig/jembi/ethiopia-hiv/branches/master/.

For each type defined there, we generate a "builder". Well, actually, there's a little handmade mapping object whichtells us a) which resourceTypes to map, and b) hints and rules how to map certain keys. 80% of the mapping data should come purely from the spec - but there are a few places where we'll take control.

The generated functions are saved to dist, alongside typings (for code assist). The stuff in dist should look just like any other adaptor.

The generated code is supported by a few handwritten function to make it easier to build FHIR types, like CodeableConcepts and Identifiers and things (these can be used in job code)

Usage

Basically given an input object from the upstream server, we manually map keys and values into a constructor function.

Once the adaptor is built, you'll use it in job code to map input objects to jembi objects. Keys and values must be manually mapped in a constructor function, like this:

 const jembiEncounter = createEncounter({
      id: input.id,
      identifier: input.identifier[0],
      type: input.type
      visitType: input.type[0].coding[0].display
      // ... etc
    });

Note that visitType is a non-standard fhir property. I've not handled extensions yet but basically the adaptor will know how to map an aribtrary key like valueType into the jembi extension format.

Anyway, the constructor knows how to map, eg, identifier: 'mtuchi' into a full fhir identifier object, so it'll generate all the static data for you.

(Right now the generated code is just plain old JS functions, not Operations. I think that's more appropriate because you'll want to do an each in the job code, and in the callback map every type).

How to Build

At the moment the built code is checked in. But how do we build it? And if the spec changes, how do we re-generate teh built code?

I've already written build scripts to:

  • Download all the JSON definitions from jembi-fhir
  • Map the desired resource types into builders in dist

So basically you can do pnpm load-schema to download the schemas locally, and then pnpm build to generate the actual adaptor based on those schemas.

Unit tests

Oh, this is all unit testable by the way. So if you want to check whether my builders work, you can see the tests!

For each resource type, we'll have a number of input structures (saved to test/fixtures/input). We'll also capture the desired output, using jembi's own examples with a few tweaks (to make values match. The tests will then take an input, map it with a builder, and ensure the result matches the expected structure.

Those tests should also be a really strong guideline for how to write the actual job code - you can probably just copy and paste the mappings. Or maybe I'll even build them into the adaptor so that the adaptor handles conversions too.

Code Assist

Because I'm generating full type definitions for each builder function, we should get code assist in lightning and in the adaptors monorepo (eg while writing tests).

It's not working yet but I think it's only a couple of hours work to plug it in - I've got a working prototype in basic fhir.

You won't get code assist in VSC because VSC can't "read" a job file properly. We need an extension for that....

FHIR diffs

I've branched this work off of another branch where I was working on getting type definitions into fhir. Some of that should probably be useful here.

I don't know what I'll do with the FHIR diffs yet. Ask me next week.

Issues

  • The text isn't defaulted in CarePlan. This is easily overridden in the mapping so its not urgent, but should be done
  • Handle Backbone Elements (need a plan for this)
  • Work out a nice extension for CarePlan.activity It's a bit hard to work out a nice mapping but quite easy to do it manually, so idk
  • If a type is "decimal", but comes in as a string, convert it
  • Many category properties need to include a text label, but sadly this isn't in the destination schema. So we'll have to manually set the category (or find a way to append it). See arv-regimen-changed-observation
  • Document everything!

@josephjclark
Copy link
Collaborator Author

josephjclark commented Aug 23, 2024

For the Encounter example, I'm currently generating this:

{
  resourceType: 'Encounter',
  id: 'e84781ed-5f02-40ac-8c97-e7280fb153e3',
  identifier: [
    {
      system: 'http://moh.gov.et/fhir/hiv/identifier/encounter',
      value: '7834'
    }
  ],
  status: 'finished',
  subject: { reference: 'Patient/1216a6e180f840b88a43e68d56dcm910' },
  period: { start: '2022-04-11', end: '2022-04-11' },
  serviceProvider: { reference: 'Organization/Patient.managingOrganization' },
  meta: {
    profile: [
      'http://moh.gov.et/fhir/hiv/StructureDefinition/target-facility-encounter'
    ]
  }
}

I get that by passing the input object in verbatim (ie, no mappings in the job code).

What I still need to do is:

  • map the type (easy)
  • map the extension to the type (tricky)
  • map the class as a Coding (easy)
  • map serviceType as a codeable concept (easy I think)

Once extensions, codings and codeable concepts have been mapped once, they should apply pretty generally across all types

@josephjclark
Copy link
Collaborator Author

This needs some documentation an a bump to 0.1.0 before it can be merged

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant