projecttacoma / fqm-execution

fqm-execution is a library that allows users to calculate FHIR-based electronic Clinical Quality Measures (eCQMs) and retrieve the results in a variety of formats
https://projecttacoma.github.io/fqm-execution/
Apache License 2.0
18 stars 6 forks source link

Update source parsing for query filter parsing #280

Closed sarahmcdougall closed 11 months ago

sarahmcdougall commented 1 year ago

Summary

For measures authored using QI-Core, the sources array is unpopulated due to unhandled scenarios related to parsing query sources. Updates parseSources to drill into expressions on a given query that are not immediate retrieves.

New behavior

The sources arrays on the query info should now be populated appropriately. The approach to source parsing is as follows:

“source”: [
  {
    “localId”: 2,
    “alias”: “MyAlias”,
    “resultTypeSpecifier”: {
      “type”: “ListTypeSpecifier”,
      “elementType”: {
        “name”: “{http//hl7.org/fhir}Encounter”,
        “type”: “NamedTypeSpecifier”
      }
    },
    “expression”: {
      “localId”: “1”,
      “type”: “Union”,
      “resultTypeSpecifier”: {
        “type”: “ListTypeSpecifier”,
        “elementType”: {
          “name”: “{http://hl7.org/fhir}Encounter”,
          “type”: “NamedTypeSpecifier”
        }
      }
    }
  }
]

Since there is no datatype on the expression, the “elementType” is parsed to retrieve the associated datatype.

A potential side effect of the current query filter parsing implementation is that incorrect attribute paths are assigned to resource types during data requirements calculation. Note that this issue is not directly addressed in this pull request, as there are other issues involved, such as the “query stacking” approach that is implemented and how it handles the “Union” type that is seen in the ELM of QI-Core measures.

More information about the ELM structure is available at https://cql.hl7.org/04-logicalspecification.html.

Code changes

QueryFilterParser

Testing guidance

It is important to test against both QI-Core and non-QI-Core measures. Some potential steps that can be taken for testing:

Open questions to check for when testing

image

However, the original query info does not contain the Procedure and instead contains an Encounter:

image

When we set queryInfo.sources = innerQueryInfo.sources, we lose this Procedure source.

github-actions[bot] commented 1 year ago

Coverage report

St.:grey_question:
Category Percentage Covered / Total
🟢 Statements
86.33% (-0.25% 🔻)
2362/2736
🟡 Branches
73.64% (-0.19% 🔻)
2190/2974
🟢 Functions
88.89% (-0.16% 🔻)
424/477
🟢 Lines
86.67% (-0.27% 🔻)
2282/2633
Show files with reduced coverage 🔻
|
St.:grey_question:
| File | Statements | Branches | Functions | Lines | | :----------------------------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------ | :------------------------------------------------------------- | :------------------------------------------------------------- | :----------------------------------------------------------- | :------------------------------------------------------------- | | 🟢 |
`...` / GapsReportBuilder.ts
|
93.66% (-0.31% 🔻)
| 83.91% | 100% |
93.63% (-0.34% 🔻)
| | 🟢 |
`...` / QueryFilterParser.ts
|
85.31% (-1.56% 🔻)
|
78.57% (-1.86% 🔻)
|
97.06% (-2.94% 🔻)
|
85.07% (-1.58% 🔻)
|

Test suite run success

447 tests passing in 31 suites.

Report generated by 🧪jest coverage report action from bead5ad9aa02267d0fceaa6cbaaa5c0bbc276bc4

sarahmcdougall commented 11 months ago

Summary of changes made in newest commits:

  1. Before adding filters to data requirement, GapsReportBuilder now checks that a queryInfo source exists whose alias matches the alias of the data type query. If no source exists, filters are not added to the data requirement.
  2. Before adding filters to data requirement, GapsReportBuilder now checks that the alias of the queryInfo source matches the alias of the filter. If the aliases do not match, filters are not added to the data requirement. All the filter types in the function extend from AttributeFilter, which has alias as a required field. I did notice a few filters during testing that do not have alias populated but it seems that this is the case when the type is ‘unknown’ and ‘withError’ is populated.
  3. In the QueryFilterParser, we now only copy over the first index of the inner query info source rather than the entire array of sources. While it usually is the case that there is only one source, it is possible for there to be multiple sources (like when we are working with relationships), and so we want to only replace the first index and copy over the rest of the sources so that we do not lose any information.
  4. parseSources was updated to consolidate the logic between sources and relationships since we are essentially treating them the same. The function now parses sources and relationships in the same loop.
  5. Type handling added for parseElementType.

Changes to the output: We no longer receive the “Procedure.period” error (or similar errors depending on the measure under test). This is due to the additional query alias handling that was added to GapsReportBuilder. The correct filter does not get added to the data requirements because we never actually parse the relationship during the query filter parsing. Is this still in scope of this task? It looks like the relationships do get parsed in RetrievesFinder but the suchThat expressions do not get parsed in the query parser. I believe that significant re-working of the parseQueryInfo function will be required to dig down into the relationship clauses.