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

satusehat: add job examples #651

Open
wants to merge 14 commits into
base: main
Choose a base branch
from

Conversation

hunterachieng
Copy link
Contributor

Short Description

Add job examples to the satusehat adaptor

Closes #971

Details

Add any more details that may be relevant to the reviewer. How did you approach
these docs? Are there any parts you struggled with, or that need particularly
careful review?

AI Usage

Please disclose how you've used AI in this work (it's cool, we just want to
know!):

  • Code generation (copilot but not intellisense)
  • Learning or fact checking
  • Strategy / design
  • Optimisation / refactoring
  • Translation / spellchecking / doc gen
  • Other
  • I have not used AI

You can read more details in our
Responsible AI Policy

Signed-off-by: Hunter Achieng <[email protected]>
Copy link
Contributor

@josephjclark josephjclark left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we try and do something a bit more narrative in these examples?

These single-statement examples don't really show much more usage than the docs examples.

And actually for satusehat, what we really want to show in examples is how to take data from local health system, convert it into fhir, and then upload it. That's a really really valuable process to show to users out here with real problems.

Ok, so we can't do a whole workflow, but I'm sure we can simulate one. Or just cover the hard bits.

Maybe @AishaHassen can help you tell this convert-to-fhir story in a single example? I want to see some mapping code examples, some state manipulation, some fn blocks and maybe some each.

We don't necessarily need to see a whole resource getting built (because that's a lot of data) but it would be nice to see the whole pipeline for part of a resource. Like just mapping an identifier and address or something.

Signed-off-by: Hunter Achieng <[email protected]>
@@ -0,0 +1,134 @@
// Create a medication from visit data and lookup table

function generateUUID() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not keen on spreading this pattern! It's not a nice function at all

Common should have a util.uuid. Can we use that instead? If it's not exported by satusehat, let's do a quick patch (just make sure common.util is exported by satusehat)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@josephjclark We are not exporting the util.uuid from common in satusehat

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then let's do a patch and do that :) Should only take a minute an it'll make this example significantly cleaner

I've only had a light look over this btw - I'll get back to you on the rest of it later

@josephjclark
Copy link
Contributor

@hunterachieng so is this literally just a copy and paste of an existing workflow?

My worry with this is that it's still not really telling the story in a very clear way. Like the first line of create medication is:

fnIf(
  !state.errors &&
    state.visit.properties['prescription_1'] &&
    state.visit.properties['prescription_1'].length > 0,

But like, what have all those conditions got to do with anything?

That's part of the workflow story. But here we only have a single step.

For this example, we can probably assume that state.visit is what we need.

But we need a little bit of commentary in comments, like: state.visit is a Commcare Visit record. And then the step name is something like "Create a FHIR Encounter from a Commcare Visit"

Our job isn't to capture a whole workflow, but to show how a non-fhir object might be converted into fhir, and then uploaded to Satusehat. An even then we don't need to do a whole commcare record - just enough to show the sort of patterns needed.

For example: the mapMedications function is really interesting. It shows how we can declare helper functions in job code, it shows how arbitrary commcare strings can be mapped into proper FHIR resources, it shows how to do looping in a functional programming style.

createMedicationResource is also super interesting, because it shows how you can create all the FHIR boiler-plate and map resources into it. It shows a technique for building a fhir object. This is also super useful!

So we need to priorise what's important and interesting about the example, document it at a high level so that users can understand it, and clear out anything that's noisy or confusing

Signed-off-by: Hunter Achieng <[email protected]>
Copy link
Contributor

@AishaHassen AishaHassen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @hunterachieng , we're missing the example for conditions like we talked about yesterday. General comments for all the js functions:

  1. add comments wherever relevant
  2. use prettier when you are done

Copy link
Contributor

@josephjclark josephjclark left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left a few comments but maybe I'm being too picky? It's come on a long way :)

@@ -0,0 +1,97 @@
// Create a diagnosis in satusehat

fn(state => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe a comment like "Map diagnosis objects [from commcare?] into Condition resources", just to set some context

value.forEach(value => {
const result = {
[valueType]: {
coding: [JSON.parse(value.snomed_code)],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious as to why we have all this JSON parsing? Is that really needed? Can we cheat in this example and pretend the input is well formatted?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need to show them how we create the different mappings since it is an important aspect when creating an obervation. cc @AishaHassen

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right but the actual JSON.parse doens't feel like it's part of the mapping. That suggests that value.snomed_code is a string containing a JSON object. And that's kind weird? Feels like bad practice. If snowmed_code is JSON it should really have been parsed properly earlier.

Also, if snomed_code is as string, it shouldn't need to be JSON parsed at all

I don't know what's happening in the original implementation. It doesn't matter. For the sake of this example, I think we should remove the JSON.parse and pretend the input data is well formatted.

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

Successfully merging this pull request may close these issues.

3 participants