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

Constraint support (local and error case only) #2199

Merged
merged 20 commits into from
Mar 27, 2024

Conversation

FikriMilano
Copy link
Collaborator

@FikriMilano FikriMilano commented Sep 26, 2023

IMPORTANT: All PRs must be linked to an issue (except for extremely trivial and straightforward changes).

Fixes #1849

Description

  • Add questionnaire constraint as part of the validator
  • The constraint predicate can use variables
  • Add JSON sample to the catalog app

Alternative(s) considered
N/A

Type
Feature

Demo Video

Screen_recording_20230927_111829.webm

Catalog Behavior Image
@shelaghm

Checklist

  • I have read and acknowledged the Code of conduct.
  • I have read the Contributing page.
  • I have signed the Google Individual CLA, or I am covered by my company's Corporate CLA.
  • I have discussed my proposed solution with code owners in the linked issue(s) and we have agreed upon the general approach.
  • I have run ./gradlew spotlessApply and ./gradlew spotlessCheck to check my code follows the style guide of this project.
  • I have run ./gradlew check and ./gradlew connectedCheck to test my changes locally.
  • I have built and run the demo app(s) to verify my change fixes the issue and/or does not break the demo app(s).

@FikriMilano FikriMilano marked this pull request as ready for review September 27, 2023 04:21
@FikriMilano
Copy link
Collaborator Author

@jingtang10 @santosh-pingle
this is ready for review

@FikriMilano
Copy link
Collaborator Author

FikriMilano commented Sep 27, 2023

@shelaghm we might need a new icon in the catalog app for this new behavior

@shelaghm
Copy link

shelaghm commented Sep 29, 2023

@FikriMilano How about this function icon? I recommend just pulling from this list of open source icons if you ever need one since these are easy to implement and open source.

@FikriMilano
Copy link
Collaborator Author

@shelaghm That's fine, thanks!

@FikriMilano
Copy link
Collaborator Author

@shelaghm Done.

@shelaghm
Copy link

shelaghm commented Oct 2, 2023

@FikriMilano Could you make the icon slightly smaller: 48px? Thanks!

@jingtang10 jingtang10 changed the title constraint support Constraint support Oct 2, 2023
@jingtang10
Copy link
Collaborator

not sure i am super keen on the sigma sign - it means sum in mathamatics so i think it's a bit weird to use that for constraint.

how about this: https://fonts.google.com/icons?selected=Material+Symbols+Outlined:rule:FILL@0;wght@400;GRAD@0;opsz@24&icon.query=rule?

@jingtang10
Copy link
Collaborator

@FikriMilano how are you determining where to show the error message since constraints are global?

@shelaghm
Copy link

shelaghm commented Oct 2, 2023

not sure i am super keen on the sigma sign - it means sum in mathamatics so i think it's a bit weird to use that for constraint.

how about this: https://fonts.google.com/icons?selected=Material+Symbols+Outlined:rule:FILL@0;wght@400;GRAD@0;opsz@24&icon.query=rule?

"Rule' one is better, thanks @jingtang10 @FikriMilano

@FikriMilano
Copy link
Collaborator Author

FikriMilano commented Oct 3, 2023

how are you determining where to show the error message since constraints are global?

@jingtang10
As we may know, the constraint extension can be used used in the Questionnaire itself (global) and it's item.
If the extension is declared inside an item, then this validator will show the error message below the field (as usual).

I kind of forgot that the extension can be applied to the Questionnaire / globally, so I haven't add the code to do that yet. The solution would be:

use a sub-extension called location that contains the Relative paths to the questions this rule is enforced against / Link ID of the item. Using this path, we can find the item where the rule belongs to, then show the error message below the field of that item.

or

we can use a snackbar/toast/modal to show an error message if the constraint extension is declared in the Questionnaire itself / globally.

@FikriMilano FikriMilano changed the title Constraint support Constraint support (local case) Oct 20, 2023
@jingtang10
Copy link
Collaborator

i think in the global case, i'm happy for it to be displayed on top of the questionnaire or in the confirmation popup dialog before submission. i feel deduction of where the error message should be could end up being unreliable / not what the questionnaire author expects.

how are you determining where to show the error message since constraints are global?

@jingtang10 As we may know, the constraint extension can be used used in the Questionnaire itself (global) and it's item. If the extension is declared inside an item, then this validator will show the error message below the field (as usual).

I kind of forgot that the extension can be applied to the Questionnaire / globally, so I haven't add the code to do that yet. The solution would be:

use a sub-extension called location that contains the Relative paths to the questions this rule is enforced against / Link ID of the item. Using this path, we can find the item where the rule belongs to, then show the error message below the field of that item.

or

we can use a snackbar/toast/modal to show an error message if the constraint extension is declared in the Questionnaire itself / globally.

Copy link
Collaborator

@jingtang10 jingtang10 left a comment

Choose a reason for hiding this comment

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

Thanks @FikriMilano!

I see that you have created another validator that takes in some addition contextual data. Wouldn't another approach be that we pass the questionnaire response and expression evaluator and context to the validator itself upon constrution so to make the evaluation of any expression contextual within a particular questionnaire?

That way, i think you don't really need to create a new interface? as the interface would just be the same as the QuestionnaireResponseItemConstraintValidator?

Have you considered this? Can you briefly analyse the pros and cons of this approach? Thanks!

@ndegwamartin
Copy link
Collaborator

For this 'local case' it's probably a good idea to refactor this to use the FHIRPath supplement %qItem to get the references since it was already merged into master by the PR here #2346.

It might be cleaner and more maintainable maybe?

@ndegwamartin
Copy link
Collaborator

Additionally perhaps as part of this PR we should enforce that qItem & questionnaire are reserved keywords within the ExpressionEvalutor.kt class when populating the dependent variables data structure see comment here.

Copy link
Collaborator

@joiskash joiskash left a comment

Choose a reason for hiding this comment

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

Left some comments, in general I was wondering if we could re-use the existing interface.
It is also not clear as to what changes will need to be done to support global mode of this extension. It might be worthwhile adding a TODO with a small explanation of what changes will be needed with an issue link.

@FikriMilano FikriMilano changed the title Constraint support (local case) Constraint support (local and error case only) Mar 14, 2024
@FikriMilano FikriMilano force-pushed the 2003-questionnaire-constraint branch from da8cf0e to a3497d1 Compare March 14, 2024 09:10
@FikriMilano
Copy link
Collaborator Author

@jingtang10 @ellykits @joiskash
This is ready for review

@FikriMilano FikriMilano requested a review from MJ1998 March 26, 2024 04:54
@FikriMilano
Copy link
Collaborator Author

@MJ1998 this is ready for review

Copy link
Collaborator

@MJ1998 MJ1998 left a comment

Choose a reason for hiding this comment

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

LGTM

@MJ1998 MJ1998 merged commit b157fd4 into google:master Mar 27, 2024
4 checks passed
@FikriMilano
Copy link
Collaborator Author

I see that you have created another validator that takes in some addition contextual data. Wouldn't another approach be that we pass the questionnaire response and expression evaluator and context to the validator itself upon constrution so to make the evaluation of any expression contextual within a particular questionnaire?

That way, i think you don't really need to create a new interface? as the interface would just be the same as the QuestionnaireResponseItemConstraintValidator?

Have you considered this? Can you briefly analyse the pros and cons of this approach? Thanks!

@MJ1998 @jingtang10
The pros of your approach is:
Based on the interface name: QuestionnaireResponseItemConstraintValidator, it's the proper interface for the new ConstraintItemExtensionValidator class.

The cons is:
QuestionnaireResponseItemConstraintValidator is not written in a way it's suitable for ConstraintItemExtensionValidator.

So I ended up taking a similar approach:
We are now passing only the expression evaluator to the constructor, and yes I'm re-using QuestionnaireResponseItemConstraintValidator, but I did some update in that interface so that it can be used in a make sense way for it's child classes: RequiredValidator and the new ConstraintItemExtensionValidator. And I created a ConstraintValidator interface, which is the base interface for all validator classes and interface, to avoid code duplication of the Result data class on other current interfaces.

The changes are:

  1. we changed the answers parameter in the validate method to questionnaireResponseItem in QuestionnaireResponseItemConstraintValidator, because we don't use answers in the ConstraintItemExtensionValidator class and to make it general following the interface name
  2. we changed the return type of the validate method from Result to List<ConstraintValidator.Result> because there can be multiple constraint extension in a single item, those validation result will then combined in the QuestionnaireResponseItemValidator class into a single error message
  3. we removed the Result data class from QuestionnaireResponseItemConstraintValidator and other interfaces that has it's own Result data class, to avoid a code duplication
  4. we created a ConstraintValidator interface, which is the base interface for all validator classes and interface, and it only contains the Result data class that we removed from other validator interfaces

Before my change:

/**
 * Validates [QuestionnaireResponse.QuestionnaireResponseItemComponent] against a particular
 * constraint.
 */
internal interface QuestionnaireResponseItemConstraintValidator {
  /**
   * Validates that [answers] satisfy a particular constraint of the [questionnaireItem] according
   * to the [structured data capture implementation guide]
   * (http://build.fhir.org/ig/HL7/sdc/behavior.html).
   *
   * This does not validate the consistency between the structure of the [answers] and their
   * descendants and that of the [questionnaireItem] and its descendants.
   *
   * [Learn more](https://www.hl7.org/fhir/questionnaireresponse.html#link).
   */
  fun validate(
    questionnaireItem: Questionnaire.QuestionnaireItemComponent,
    answers: List<QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent>,
    context: Context,
  ): Result

  /**
   * The validation result containing whether the response item is valid and any error message if it
   * is not valid.
   */
  data class Result(val isValid: Boolean, val errorMessage: String?)
}

After my change:

/**
 * Validates [QuestionnaireResponse.QuestionnaireResponseItemComponent] against a particular
 * constraint.
 */
internal interface QuestionnaireResponseItemConstraintValidator : ConstraintValidator {
  /**
   * Validates that [questionnaireResponseItem] satisfy a particular constraint of the
   * [questionnaireItem] according to the [structured data capture implementation guide]
   * (http://build.fhir.org/ig/HL7/sdc/behavior.html).
   *
   * This does not validate the consistency between the structure of the [questionnaireResponseItem]
   * and their descendants and that of the [questionnaireItem] and its descendants.
   *
   * [Learn more](https://www.hl7.org/fhir/questionnaireresponse.html#link).
   */
  suspend fun validate(
    questionnaireItem: Questionnaire.QuestionnaireItemComponent,
    questionnaireResponseItem: QuestionnaireResponse.QuestionnaireResponseItemComponent,
    context: Context,
  ): List<ConstraintValidator.Result>
}
/** Validator base interface. */
internal interface ConstraintValidator {

  /**
   * The validation result containing whether the response item is valid and any error message if it
   * is not valid.
   */
  data class Result(val isValid: Boolean, val errorMessage: String?)
}

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

Successfully merging this pull request may close these issues.

Support questionnaire-constraint Extension
7 participants