Giter Site home page Giter Site logo

stanfordbdhg / researchkitonfhir Goto Github PK

View Code? Open in Web Editor NEW
11.0 12.0 2.0 238 KB

HL7 FHIR Structured Data Capture with ResearchKit on iOS

Home Page: https://swiftpackageindex.com/StanfordBDHG/ResearchKitOnFHIR/documentation

License: MIT License

Swift 97.56% ANTLR 2.25% Shell 0.19%
researchkit fhir questionnaire swift swiftui stanford

researchkitonfhir's People

Contributors

luppoalberto111 avatar pschmiedmayer avatar supereg avatar vishnuravi avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

researchkitonfhir's Issues

πŸ› Bug report: Custom error messages not displayed for number and text validation

Description

I've noticed an issue where custom error messages for both Number and Text type questions in our survey forms aren't being displayed as expected. While the validations are functioning correctly, the custom error messages we've set up aren’t showing up when a user fails to meet the validation criteria.

Reproduction

To give you a better idea, here’s how you can reproduce the issue:

For Number Question

  • Create a Number question with a validation rule limiting the number between 0 and 10.
  • Include a custom error message in the FHIR Questionnaire for failed validations.
  • In the application, enter a number outside the specified range.

For Text Question

  • Create a Text question with a regular expression validation for a maximum word count.
  • Enter text exceeding the word limit.
  • Notice the absence of the custom error message.

JSON sample

{
  "title": "Test custom error message",
  "resourceType": "Questionnaire",
  "language": "en-US",
  "status": "draft",
  "publisher": "Stanford Biodesign Digital Health",
  "meta": {
    "profile": [
      "http://spezi.health/fhir/StructureDefinition/sdf-Questionnaire"
    ],
    "tag": [
      { "system": "urn:ietf:bcp:47", "code": "en-US", "display": "English" }
    ]
  },
  "useContext": [
    {
      "code": {
        "system": "http://hl7.org/fhir/ValueSet/usage-context-type",
        "code": "focus",
        "display": "Clinical Focus"
      },
      "valueCodeableConcept": {
        "coding": [
          {
            "system": "urn:oid:2.16.578.1.12.4.1.1.8655",
            "display": "Test custom error message"
          }
        ]
      }
    }
  ],
  "contact": [{ "name": "http://spezi.health" }],
  "subjectType": ["Patient"],
  "url": "http://spezi.health/fhir/questionnaire/dd859ee0-4e7a-4131-f6fa-2239f0ad08b2",
  "item": [
    {
      "linkId": "282ca91c-5123-4c68-9755-8b5aab297dac",
      "type": "integer",
      "extension": [
        {
          "url": "http://hl7.org/fhir/StructureDefinition/minValue",
          "valueInteger": 0
        },
        {
          "url": "http://hl7.org/fhir/StructureDefinition/maxValue",
          "valueInteger": 10
        },
        {
          "url": "http://ehelse.no/fhir/StructureDefinition/validationtext",
          "valueString": "Custom error message..."
        }
      ],
      "required": false,
      "text": "Please, rate your attention"
    },
    {
      "linkId": "1d0a234f-bbd2-4ef4-8b26-1110cce45efe",
      "type": "string",
      "text": "Please describe the characteristics of you pain?",
      "required": false,
      "extension": [
        {
          "url": "http://ehelse.no/fhir/StructureDefinition/validationtext",
          "valueString": "Custom error message..."
        },
        {
          "url": "http://hl7.org/fhir/StructureDefinition/regex",
          "valueString": "^\\s*\\S+(\\s+\\S+){0,2}\\s*$"
        }
      ]
    }
  ]
}

Expected behavior

Ideally, when a user inputs data that doesn't meet the validation criteria, our custom error message should be displayed to guide them.

Additional context

No response

Code of Conduct

  • I agree to follow this project's Code of Conduct and Contributing Guidelines

Group is skippable even if all questions are required.

Description

When you create a questionnaire with the Phoenix Questionnaire Builder with a group of required questions, the ResearchKit view is skippable (e.g., the Skip button is shown).

Reproduction

  1. Create a new phoenix questionnaire (like M_CHAT) that has a Group of required questions.
  2. When displaying it within the QuestionnaireView ResearchKit will render a "Skip" button allowing to skip all required questions.

Expected behavior

Required questions should not be able to be skipped.

Additional context

No response

Code of Conduct

  • I agree to follow this project's Code of Conduct and Contributing Guidelines

🏎 Minor feature request: Support for Scale/Slider Response Types

Problem

A common survey response type is one in which the participant response belongs to an ordered set of values, e.g.

  • What best describes your level of stress right now on a scale of 0-10 from not at all stressed "0" to Extremely stressed "10"? Here, the answer is an integer between 0 and 10.
  • During today at work, how would you rate the passage of time? Here, the answer belongs to a 7-point Likert scale: ["Very slow", "Moderately slow", "Slightly Slow", "Neither slow nor fast", "Slightly fast", "Moderately fast", "Very fast"]

ResearchKit supports scale responses with the ORKScaleAnswerFormat (for integers) and the ORKTextScaleAnswerFormat, both of a which display a slider in the user interface.

Currently, researchers have to encode such questionnaires as a choice FHIR QuestionnaireItemType, which is converted to a ORKTextChoice ResearchKit type. For example, this would display the options 0-10 as a list of questionnaire options rather than a slider.

Solution

Provide support for choice questionnaire items that are on an ordered scale to be mapped to the appropriate ResearchKit type (either ORKScaleAnswerFormat or ORKTextScaleAnswerFormat). I am unsure if FHIR differentiates between choice types and ordered choice types, but adding the option to display a slider could be achieved with metadata fields on a ValueSet or on the survey item itself.

Additional context

Here is an example of a survey item we are currently using in our study:

{
    "linkId": "70133a34-8597-4cdf-ec4a-6fb0f8ede585",
    "type": "choice",
    "text": "Please choose the value that best describes how you felt on average today about your workplace on a scale of 1-7, from I do not fit in my workplace \"0\" to I definitely fit in my workplace \"7\".",
    "required": false,
    "answerOption":
    [
        {
            "valueCoding":
            {
                "id": "6f5be914-eee6-458a-805e-a5cd84fac028",
                "code": "1",
                "system": "urn:uuid:8ac26004-8e67-4916-8f0a-0d70a70626e2",
                "display": "1"
            }
        },
        {
            "valueCoding":
            {
                "id": "8004d543-ea5d-4824-8832-9ac81c9c6235",
                "code": "2",
                "system": "urn:uuid:8ac26004-8e67-4916-8f0a-0d70a70626e2",
                "display": "2"
            }
        },
        {
            "valueCoding":
            {
                "id": "2b81b474-9c02-438e-9f22-13be890dc230",
                "code": "3",
                "system": "urn:uuid:8ac26004-8e67-4916-8f0a-0d70a70626e2",
                "display": "3"
            }
        },
        {
            "valueCoding":
            {
                "id": "40845d67-f5b8-49d8-8aac-65913160e7b1",
                "code": "4",
                "system": "urn:uuid:8ac26004-8e67-4916-8f0a-0d70a70626e2",
                "display": "4"
            }
        },
        {
            "valueCoding":
            {
                "id": "6dc6c684-10f1-48c6-9565-647aa2e3fc9e",
                "code": "5",
                "system": "urn:uuid:8ac26004-8e67-4916-8f0a-0d70a70626e2",
                "display": "5"
            }
        },
        {
            "valueCoding":
            {
                "id": "c03a4eda-801a-448c-8ff4-d83f655ba218",
                "code": "6",
                "system": "urn:uuid:8ac26004-8e67-4916-8f0a-0d70a70626e2",
                "display": "6"
            }
        },
        {
            "valueCoding":
            {
                "id": "ee1e918e-1dd5-4133-8f0c-a6cce0303cfd",
                "code": "7",
                "system": "urn:uuid:8ac26004-8e67-4916-8f0a-0d70a70626e2",
                "display": "7"
            }
        }
    ]
}

This currently displays as below
IMG_4109

It would be nice if this answer could display as a slider

Code of Conduct

  • I agree to follow this project's Code of Conduct and Contributing Guidelines

🏎 Minor feature request: Questionnaire URL as an optional parameter

Problem

According to FHIR standard URL attribute has a cardinality between 0..1. Unfortunately, ResearchKitOnFHIR is strict in this case and doesn't accept questionnaires without URL even though the standard is still fulfilled.

Currently, this kind of restriction doesn't exist in equivalent libraries for Android and FE.

Also, there are currently 3rd party services that provide questionnaires without URL and are not presentable by this library.

Solution

The best solution to this issue would be to make URL parameter of ORKNavigableOrderedTask optional.
In this case FHIR URL serves as an identifier of ORKNavigableOrderedTask.

Instead of throwing an error, we can put any non-url placeholder - static String or dynamic- for example UUID.

In the public var fhirResponse: QuestionnaireResponse we can put something like:

if let questionnaireURL = questionnaireID.flatMap(URL.init) {
    questionnaireResponse.questionnaire = FHIRPrimitive(Canonical(questionnaireURL))
}

Additional context

Even though, this is a small improvement, it can be a dealbreaker for other people who use this library.
Of course, I can help with the implementation and currently, I'm not aware of any blocker.

Small sidenote: I tried to inject URL to the Questionnaire but it causes crashes on Android in case you'd want to display responses from iOS on Android. You see, Android compares URL of Questionnaire vs Responses. And the discrepancy between these URLs is causing runtime crash on Android.

Code of Conduct

  • I agree to follow this project's Code of Conduct and Contributing Guidelines

πŸ› Bug report: Error when using `enableWhen`

Description

On certain occasions, some questions that should be displayed based on the answers to previous questions within a questionnaire are inadvertently omitted. This means that while they should appear in the questionnaire flow based on previous answers, these questions are not presented as expected.

This suggests some kind of issue with the navigation logic, probably in the translation of the attributes of the ResearchKit navigation rules.

To verify the situation, UI tests were developed, which were executed in a multi-run test plan obtaining the following results:

  1. In a first scenario, a JSON in FHIR format was used with a question that has two conditions that depend on the answer to a previous question, obtaining a 9.1% error. Example of running 100 tests, meeting only one of the conditions. For example, nine times out of 100 executions the question with dependency was not shown, following the same flow.

image

This shows that in nine out of every hundred executions, the dependent question was not displayed, even following the same flow.

  1. In a second scenario, two tests are run, one for each possible condition, but this time the dependent question is duplicated by arranging each question with a single enableWhen attribute. The following results are obtained:

image

Although the error rate decreased in this scenario, failures still occurred.

Reproduction

The questionnaire in JSON FHIR format used for the above tests, and with which the incident is being presented, is as follows:

{
  "resourceType": "Questionnaire",
  "language": "en-US",
  "title": "Report on tests",
  "status": "draft",
  "publisher": "Tester",
  "meta": {
    "profile": [
      "http://spezi.health/fhir/StructureDefinition/sdf-Questionnaire"
    ],
    "tag": [
      { "system": "urn:ietf:bcp:47", "code": "en-US", "display": "English" }
    ]
  },
  "useContext": [
    {
      "code": {
        "system": "http://hl7.org/fhir/ValueSet/usage-context-type",
        "code": "focus",
        "display": "Clinical Focus"
      },
      "valueCodeableConcept": {
        "coding": [
          {
            "system": "urn:oid:2.16.578.1.12.4.1.1.8655",
            "display": "Untitled"
          }
        ]
      }
    }
  ],
  "contact": [{ "name": "http://testing.test" }],
  "subjectType": ["Patient"],
  "url": "http://testing.test",
  "item": [
    {
      "linkId": "1.1",
      "type": "choice",
      "text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit?",
      "required": true,
      "answerOption": [
        {
          "valueCoding": {
            "code": "very-severe",
            "system": "urn:uuid:b8ef0384-7163-44fa-e977-d5c64bce8cb8",
            "display": "Very severe"
          }
        },
        {
          "valueCoding": {
            "code": "severe",
            "system": "urn:uuid:b8ef0384-7163-44fa-e977-d5c64bce8cb8",
            "display": "Severe"
          }
        },
        {
          "valueCoding": {
            "code": "moderate",
            "system": "urn:uuid:b8ef0384-7163-44fa-e977-d5c64bce8cb8",
            "display": "Moderate"
          }
        },
        {
          "valueCoding": {
            "code": "mild",
            "system": "urn:uuid:b8ef0384-7163-44fa-e977-d5c64bce8cb8",
            "display": "Mild"
          }
        },
        {
          "valueCoding": {
            "code": "none",
            "system": "urn:uuid:b8ef0384-7163-44fa-e977-d5c64bce8cb8",
            "display": "None"
          }
        }
      ]
    },
    {
      "linkId": "1.2",
      "type": "choice",
      "required": true,
      "answerOption": [
        {
          "valueCoding": {
            "code": "yes",
            "system": "urn:uuid:2ab65962-d3f0-48d4-8c27-d042bfffafc3",
            "display": "Yes"
          }
        },
        {
          "valueCoding": {
            "code": "no",
            "system": "urn:uuid:2ab65962-d3f0-48d4-8c27-d042bfffafc3",
            "display": "No"
          }
        }
      ],
      "text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit??",
      "enableBehavior": "any",
      "enableWhen": [
        {
          "question": "1.1",
          "operator": "=",
          "answerCoding": {
            "system": "urn:uuid:b8ef0384-7163-44fa-e977-d5c64bce8cb8",
            "code": "very-severe"
          }
        },
        {
          "question": "1.1",
          "operator": "=",
          "answerCoding": {
            "system": "urn:uuid:b8ef0384-7163-44fa-e977-d5c64bce8cb8",
            "code": "severe"
          }
        }
      ]
    },
    {
      "linkId": "1.3",
      "type": "choice",
      "required": true,
      "answerOption": [
        {
          "valueCoding": {
            "code": "severe-abdominal-pain",
            "system": "urn:uuid:190d3b6e-015f-415e-9325-48f7b6275108",
            "display": "Severe abdominal pain"
          }
        },
        {
          "valueCoding": {
            "id": "05a9c9ec-1781-479f-e302-6283a66474ef",
            "code": "havent-passed-gas",
            "system": "urn:uuid:190d3b6e-015f-415e-9325-48f7b6275108",
            "display": "Haven't passed gas from my bottom (or ostomy)"
          }
        },
        {
          "valueCoding": {
            "id": "4446b2d6-c19f-4e33-e536-f4ef3cb0aa90",
            "code": "vomiting",
            "system": "urn:uuid:190d3b6e-015f-415e-9325-48f7b6275108",
            "display": "Vomiting"
          }
        }
      ],
      "extension": [
        {
          "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl",
          "valueCodeableConcept": {
            "coding": [
              {
                "system": "http://hl7.org/fhir/questionnaire-item-control",
                "code": "check-box",
                "display": "Checkbox"
              }
            ],
            "text": "Checkbox"
          }
        }
      ],
      "text": "Lorem ipsum dolor sit amet, consectetur adipiscing.",
      "enableWhen": [
        {
          "question": "1.2",
          "operator": "=",
          "answerCoding": {
            "system": "urn:uuid:2ab65962-d3f0-48d4-8c27-d042bfffafc3",
            "code": "yes"
          }
        }
      ],
      "item": [
        {
          "extension": [
            {
              "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-displayCategory",
              "valueCodeableConcept": {
                "coding": [
                  {
                    "system": "http://hl7.org/fhir/questionnaire-display-category",
                    "code": "instructions"
                  }
                ]
              }
            }
          ],
          "linkId": "1-select-one",
          "text": "Select all that apply",
          "type": "display"
        }
      ]
    },
    {
      "linkId": "1.4.1",
      "type": "dateTime",
      "text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit?",
      "required": true,
      "enableBehavior": "any"
    },
    {
      "linkId": "1.4.2",
      "type": "choice",
      "required": true,
      "answerOption": [
        {
          "valueCoding": {
            "code": "very-large",
            "system": "urn:uuid:190d3b6e-015f-415e-9325-48f7b6275108",
            "display": "Very large"
          }
        },
        {
          "valueCoding": {
            "code": "large",
            "system": "urn:uuid:190d3b6e-015f-415e-9325-48f7b6275108",
            "display": "Large"
          }
        },
        {
          "valueCoding": {
            "code": "medium",
            "system": "urn:uuid:190d3b6e-015f-415e-9325-48f7b6275108",
            "display": "Medium"
          }
        },
        {
          "valueCoding": {
            "code": "small",
            "system": "urn:uuid:190d3b6e-015f-415e-9325-48f7b6275108",
            "display": "Small"
          }
        },
        {
          "valueCoding": {
            "code": "very-small",
            "system": "urn:uuid:190d3b6e-015f-415e-9325-48f7b6275108",
            "display": "Very small"
          }
        }
      ],
      "text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit?"
    },
    {
      "linkId": "1.5",
      "type": "dateTime",
      "text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit?",
      "required": true,
      "enableBehavior": "any",
      "enableWhen": [
        {
          "question": "1.4.2",
          "operator": "=",
          "answerCoding": {
            "system": "urn:uuid:190d3b6e-015f-415e-9325-48f7b6275108",
            "code": "small"
          }
        },
        {
          "question": "1.4.2",
          "operator": "=",
          "answerCoding": {
            "system": "urn:uuid:190d3b6e-015f-415e-9325-48f7b6275108",
            "code": "very-small"
          }
        }
      ]
    },
    {
      "linkId": "1.6",
      "type": "choice",
      "required": true,
      "answerOption": [
        {
          "valueCoding": {
            "code": "hard-or-lumpy",
            "system": "urn:uuid:190d3b6e-015f-415e-9325-48f7b6275108",
            "display": "Hard or lumpy"
          }
        },
        {
          "valueCoding": {
            "code": "soft-or-unformed",
            "system": "urn:uuid:190d3b6e-015f-415e-9325-48f7b6275108",
            "display": "Soft or unformed"
          }
        },
        {
          "valueCoding": {
            "code": "loose-or-watery",
            "system": "urn:uuid:190d3b6e-015f-415e-9325-48f7b6275108",
            "display": "Loose or watery"
          }
        },
        {
          "valueCoding": {
            "code": "unfinished",
            "system": "urn:uuid:190d3b6e-015f-415e-9325-48f7b6275108",
            "display": "Unfinished"
          }
        },
        {
          "valueCoding": {
            "code": "difficult-to-get-out",
            "system": "urn:uuid:190d3b6e-015f-415e-9325-48f7b6275108",
            "display": "Difficult to get out"
          }
        },
        {
          "valueCoding": {
            "code": "painful",
            "system": "urn:uuid:190d3b6e-015f-415e-9325-48f7b6275108",
            "display": "Painful"
          }
        }
      ],
      "text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit?",
      "enableBehavior": "any",
      "enableWhen": [
        {
          "question": "1.4.2",
          "operator": "=",
          "answerCoding": {
            "system": "urn:uuid:190d3b6e-015f-415e-9325-48f7b6275108",
            "code": "very-large"
          }
        },
        {
          "question": "1.4.2",
          "operator": "=",
          "answerCoding": {
            "system": "urn:uuid:190d3b6e-015f-415e-9325-48f7b6275108",
            "code": "large"
          }
        },
        {
          "question": "1.4.2",
          "operator": "=",
          "answerCoding": {
            "system": "urn:uuid:190d3b6e-015f-415e-9325-48f7b6275108",
            "code": "medium"
          }
        },
        { "question": "1.5", "operator": "exists", "answerBoolean": true }
      ]
    }
  ]
}


Expected behavior

It is expected that question 1.2 will be activated whenever the 'very severe' or 'severe' option is selected.

Additional context

No response

Code of Conduct

  • I agree to follow this project's Code of Conduct and Contributing Guidelines

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.