From 177579d1a5d8dd3dbfce176971b9ec0bb9b3d919 Mon Sep 17 00:00:00 2001 From: ckawell-sb Date: Tue, 16 Jul 2024 09:27:27 -0500 Subject: [PATCH 1/5] feat: QPPA-9026 cleanup measures scripts --- .../measures/{ => 2022}/build-2022-measures | 0 .../{ => archive/2018}/build-2018-measures | 2 +- .../validate-measures-past-existence.js | 4 +- .../{ => archive/2019}/build-2019-measures | 0 .../{ => archive/2020}/build-2020-measures | 0 .../{ => archive/2021}/build-2021-measures | 0 .../validate-json-data-lib.js | 101 ------------------ 7 files changed, 3 insertions(+), 104 deletions(-) rename scripts/measures/{ => 2022}/build-2022-measures (100%) rename scripts/measures/{ => archive/2018}/build-2018-measures (97%) rename scripts/measures/archive/{ => 2018}/validate-measures-past-existence.js (96%) rename scripts/measures/{ => archive/2019}/build-2019-measures (100%) rename scripts/measures/{ => archive/2020}/build-2020-measures (100%) rename scripts/measures/{ => archive/2021}/build-2021-measures (100%) delete mode 100644 scripts/validate-json-data/validate-json-data-lib.js diff --git a/scripts/measures/build-2022-measures b/scripts/measures/2022/build-2022-measures similarity index 100% rename from scripts/measures/build-2022-measures rename to scripts/measures/2022/build-2022-measures diff --git a/scripts/measures/build-2018-measures b/scripts/measures/archive/2018/build-2018-measures similarity index 97% rename from scripts/measures/build-2018-measures rename to scripts/measures/archive/2018/build-2018-measures index 46637144..ae9d2d0c 100755 --- a/scripts/measures/build-2018-measures +++ b/scripts/measures/archive/2018/build-2018-measures @@ -57,4 +57,4 @@ cat measures/$currentPerformanceYear/measures-data.json | \ # 3. Validate that measures actually existed in their listed firstPerformanceYear and the years after echo "validating that measures actually existed in their listed firstPerformanceYear and the years after" cat measures/$currentPerformanceYear/measures-data.json | \ - node scripts/measures/archive/validate-measures-past-existence.js $currentPerformanceYear + node scripts/measures/archive/2018/validate-measures-past-existence.js $currentPerformanceYear diff --git a/scripts/measures/archive/validate-measures-past-existence.js b/scripts/measures/archive/2018/validate-measures-past-existence.js similarity index 96% rename from scripts/measures/archive/validate-measures-past-existence.js rename to scripts/measures/archive/2018/validate-measures-past-existence.js index 34dd0fb6..f1a02fa3 100644 --- a/scripts/measures/archive/validate-measures-past-existence.js +++ b/scripts/measures/archive/2018/validate-measures-past-existence.js @@ -1,6 +1,6 @@ const _ = require('lodash'); -const QppMeasuresData = require('../../../index.js'); -const Constants = require('../../../constants.js'); +const QppMeasuresData = require('../../../../index.js'); +const Constants = require('../../../../constants.js'); const currentYear = Number(process.argv[2]); if (!Constants.validPerformanceYears.includes(currentYear)) { diff --git a/scripts/measures/build-2019-measures b/scripts/measures/archive/2019/build-2019-measures similarity index 100% rename from scripts/measures/build-2019-measures rename to scripts/measures/archive/2019/build-2019-measures diff --git a/scripts/measures/build-2020-measures b/scripts/measures/archive/2020/build-2020-measures similarity index 100% rename from scripts/measures/build-2020-measures rename to scripts/measures/archive/2020/build-2020-measures diff --git a/scripts/measures/build-2021-measures b/scripts/measures/archive/2021/build-2021-measures similarity index 100% rename from scripts/measures/build-2021-measures rename to scripts/measures/archive/2021/build-2021-measures diff --git a/scripts/validate-json-data/validate-json-data-lib.js b/scripts/validate-json-data/validate-json-data-lib.js deleted file mode 100644 index a9957bc9..00000000 --- a/scripts/validate-json-data/validate-json-data-lib.js +++ /dev/null @@ -1,101 +0,0 @@ -const Ajv = require('ajv'); -const ajvKeywords = require('ajv-keywords'); -const jsonPatch = require('fast-json-patch'); -const jsonMergePatch = require('json-merge-patch'); -const url = require('url'); - -const ajv = new Ajv({ verbose: true }); -ajvKeywords(ajv, 'uniqueItemProperties'); - -/** - * Implementations for `$merge` and `$patch` are adapted from the `ajv-merge-patch` package: - * https://github.com/ajv-validator/ajv-merge-patch/tree/master - * Due to a security vulnerability in the original package, we've integrated these functionalities directly. - */ -function addKeyword(ajv, keyword, jsonPatch, patchSchema) { - ajv.addKeyword({ - keyword: keyword, - macro: function (schema, parentSchema, it) { - var source = schema.source; - var patch = schema.with; - if (source.$ref) source = JSON.parse(JSON.stringify(getSchema(source.$ref, it))); - if (patch.$ref) patch = getSchema(patch.$ref, it); - jsonPatch.call(null, source, patch, true); - return source; - - function getSchema($ref, it) { - var id = it.baseId && it.baseId !== '#' ? url.resolve(it.baseId, $ref) : $ref; - var validate = ajv.getSchema(id); - if (validate) return validate.schema; - throw new ajv.constructor.MissingRefError(it.baseId, $ref); - } - }, - metaSchema: { - "type": "object", - "required": ["source", "with"], - "additionalProperties": false, - "properties": { - "source": { - "anyOf": [ - { - "type": "object", - "required": ["$ref"], - "additionalProperties": false, - "properties": {"$ref": {"type": "string", "format": "uri"}} - }, - {"$ref": "http://json-schema.org/draft-07/schema#"} - ] - }, - "with": patchSchema - } - } - }); -} - -// Adapted for $merge -addKeyword(ajv, '$merge', jsonMergePatch.apply, { "type": "object" }); - -// Adapted for $patch -addKeyword(ajv, '$patch', jsonPatch.applyPatch, { - "type": "array", - "items": { - "type": "object", - "required": ["op", "path"], - "properties": { - "op": { "type": "string" }, - "path": { "type": "string", "format": "json-pointer" } - }, - "anyOf": [ - { - "properties": {"op": {"enum": ["add", "replace", "test"]}}, - "required": ["value"] - }, - { - "properties": {"op": {"enum": ["remove"]}} - }, - { - "properties": { - "op": {"enum": ["move", "copy"]}, - "from": {"type": "string", "format": "json-pointer"} - }, - "required": ["from"] - } - ] - } -}); - -const ValidateLib = {}; - -ValidateLib.validate = function(schema, data) { - const valid = ajv.validate(schema, data); - - return { - valid, - // Summary of errors, this is a string. If valid this is "No errors" - errors: ajv.errorsText(ajv.errors), - // Full error details, this is an array of objects - details: ajv.errors - }; -}; - -module.exports = ValidateLib; From 16bf08868b16e5710e3e7ebbb05b46d1097e4ffa Mon Sep 17 00:00:00 2001 From: ckawell-sb Date: Tue, 16 Jul 2024 10:47:52 -0500 Subject: [PATCH 2/5] feat: QPPA-9026 update validation to ts --- benchmarks/2023/benchmarks-schema.yaml | 2 +- package-lock.json | 37 +++++-- package.json | 2 + scripts/benchmarks/archive/build-benchmarks | 2 +- scripts/benchmarks/build-benchmarks | 4 +- .../clinical-clusters/build-clinical-clusters | 2 +- scripts/measures/2022/build-2022-measures | 3 +- .../measures/archive/2018/build-2018-measures | 3 +- .../measures/archive/2019/build-2019-measures | 3 +- .../measures/archive/2020/build-2020-measures | 3 +- .../measures/archive/2021/build-2021-measures | 3 +- scripts/measures/update-measures | 3 +- .../{validate-data.js => validate-data.ts} | 40 +++---- .../validate-json-data-lib.ts | 103 ++++++++++++++++++ 14 files changed, 158 insertions(+), 52 deletions(-) rename scripts/{validate-data.js => validate-data.ts} (54%) create mode 100644 scripts/validate-json-data/validate-json-data-lib.ts diff --git a/benchmarks/2023/benchmarks-schema.yaml b/benchmarks/2023/benchmarks-schema.yaml index f9020faa..eb872386 100644 --- a/benchmarks/2023/benchmarks-schema.yaml +++ b/benchmarks/2023/benchmarks-schema.yaml @@ -77,5 +77,5 @@ definitions: enum: [claims, registry, cmsWebInterface, administrativeClaims, electronicHealthRecord, certifiedSurveyVendor] averagePerformanceRate: description: The Average Performance Rate for the Measure and CollectionType. - type: [number, string, 'null'] + type: [number, 'null'] required: [measureId, performanceYear, benchmarkYear, submissionMethod, percentiles] \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 2140f777..bb55f960 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "packages": { "": { "name": "qpp-measures-data", - "version": "v7.5.1", + "version": "v7.6.0", "license": "MIT", "dependencies": { "@types/fs-extra": "^11.0.4", @@ -21,6 +21,7 @@ "@eslint/js": "^9.4.0", "@types/eslint__js": "^8.42.3", "@types/jest": "^29.0.3", + "@types/json-merge-patch": "^0.0.10", "@types/lodash": "^4.14.184", "@types/node": "^18.7.16", "adm-zip": "0.4.13", @@ -39,6 +40,7 @@ "nyc": "^15.0.0", "officeparser": "^4.0.8", "rimraf": "2.6.1", + "toad-uri-js": "^5.0.1", "ts-jest": "^29.1.2", "typescript": "^4.9.5", "typescript-eslint": "^7.13.0", @@ -1527,6 +1529,12 @@ "pretty-format": "^29.0.0" } }, + "node_modules/@types/json-merge-patch": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/@types/json-merge-patch/-/json-merge-patch-0.0.10.tgz", + "integrity": "sha512-2OkvrOzPq/0XmXKdz9ImSOS28GUVLbH9vv0UDZoeX8qj/zJVCaBbJ6iDlZMOqOc5Y2zvzYoTJDoQ8FiV1JeL5g==", + "dev": true + }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -5921,6 +5929,15 @@ "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/pure-rand": { "version": "6.0.4", "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.4.tgz", @@ -6529,6 +6546,15 @@ "node": ">=8.0" } }, + "node_modules/toad-uri-js": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/toad-uri-js/-/toad-uri-js-5.0.1.tgz", + "integrity": "sha512-r2c5hs10O0tcRvjUpgJdJf5CalaOZhY7oS9kvYBDu/rPg+02PWa1QAOb7+tvKtpmNCkW6w6F5WZt9BDWLCNHkQ==", + "dev": true, + "dependencies": { + "punycode": "^2.3.1" + } + }, "node_modules/token-types": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/token-types/-/token-types-4.2.1.tgz", @@ -6902,15 +6928,6 @@ "punycode": "^2.1.0" } }, - "node_modules/uri-js/node_modules/punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", diff --git a/package.json b/package.json index ed879a21..3e6a588a 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "@eslint/js": "^9.4.0", "@types/eslint__js": "^8.42.3", "@types/jest": "^29.0.3", + "@types/json-merge-patch": "^0.0.10", "@types/lodash": "^4.14.184", "@types/node": "^18.7.16", "adm-zip": "0.4.13", @@ -51,6 +52,7 @@ "nyc": "^15.0.0", "officeparser": "^4.0.8", "rimraf": "2.6.1", + "toad-uri-js": "^5.0.1", "ts-jest": "^29.1.2", "typescript": "^4.9.5", "typescript-eslint": "^7.13.0", diff --git a/scripts/benchmarks/archive/build-benchmarks b/scripts/benchmarks/archive/build-benchmarks index 36761008..16afc3aa 100755 --- a/scripts/benchmarks/archive/build-benchmarks +++ b/scripts/benchmarks/archive/build-benchmarks @@ -70,5 +70,5 @@ else # validate benchmarks echo 'Validating new benchmarks JSON.' - cat $benchmarkFilename | node scripts/validate-data.js benchmarks $performanceYear + node dist/validate-data.js benchmarks $performanceYear $benchmarkFilename fi diff --git a/scripts/benchmarks/build-benchmarks b/scripts/benchmarks/build-benchmarks index 1fd954dc..ac883677 100755 --- a/scripts/benchmarks/build-benchmarks +++ b/scripts/benchmarks/build-benchmarks @@ -49,7 +49,7 @@ if (( $currentPerformanceYear >= 2023 )) && (( $currentPerformanceYear <= $maxPe # validate the JSON files echo -e "Validating new $FILE.json" - cat benchmarks/$currentPerformanceYear/${BASH_REMATCH[2]}.json | node scripts/validate-data.js ${BASH_REMATCH[2]} $currentPerformanceYear benchmarks + node dist/validate-data.js ${BASH_REMATCH[2]} $currentPerformanceYear benchmarks/$currentPerformanceYear/${BASH_REMATCH[2]}.json benchmarks exitOnFailure fi @@ -64,7 +64,7 @@ if (( $currentPerformanceYear >= 2023 )) && (( $currentPerformanceYear <= $maxPe # Validate benchmarks. echo -e "Validating new benchmarks JSON." - cat benchmarks/$currentPerformanceYear.json | node scripts/validate-data.js benchmarks $currentPerformanceYear + node dist/validate-data.js benchmarks $currentPerformanceYear benchmarks/$currentPerformanceYear.json exitOnFailure echo -e "${Green}\n${bold}Update complete." diff --git a/scripts/clinical-clusters/build-clinical-clusters b/scripts/clinical-clusters/build-clinical-clusters index b9d42b82..ac5fcfd9 100755 --- a/scripts/clinical-clusters/build-clinical-clusters +++ b/scripts/clinical-clusters/build-clinical-clusters @@ -12,4 +12,4 @@ node dist/clinical-clusters/ema-clinical-cluster-builder.js $currentPerformanceY # validate EMA clusters echo "" echo "Validating clinical-clusters.json produced: " -cat clinical-clusters/$currentPerformanceYear/clinical-clusters.json | node scripts/validate-data.js clinical-clusters $currentPerformanceYear +node dist/validate-data.js clinical-clusters $currentPerformanceYear clinical-clusters/$currentPerformanceYear/clinical-clusters.json diff --git a/scripts/measures/2022/build-2022-measures b/scripts/measures/2022/build-2022-measures index 56601894..2c55b3b4 100755 --- a/scripts/measures/2022/build-2022-measures +++ b/scripts/measures/2022/build-2022-measures @@ -85,5 +85,4 @@ node scripts/measures/$currentPerformanceYear/import-qcdr-measures.js \ # 10. Validate the resulting measures-data.json file: echo "validating new measures-data.json" -cat measures/$currentPerformanceYear/measures-data.json | \ - node scripts/validate-data.js measures $currentPerformanceYear +node dist/validate-data.js measures $currentPerformanceYear measures/$currentPerformanceYear/measures-data.json diff --git a/scripts/measures/archive/2018/build-2018-measures b/scripts/measures/archive/2018/build-2018-measures index ae9d2d0c..68b634a0 100755 --- a/scripts/measures/archive/2018/build-2018-measures +++ b/scripts/measures/archive/2018/build-2018-measures @@ -51,8 +51,7 @@ node scripts/measures/$currentPerformanceYear/import-qcdr-measures.js \ # 2. Validate the resulting measures-data.json file: echo "validating new measures-data.json against schema" -cat measures/$currentPerformanceYear/measures-data.json | \ - node scripts/validate-data.js measures $currentPerformanceYear +node dist/validate-data.js measures $currentPerformanceYear measures/$currentPerformanceYear/measures-data.json # 3. Validate that measures actually existed in their listed firstPerformanceYear and the years after echo "validating that measures actually existed in their listed firstPerformanceYear and the years after" diff --git a/scripts/measures/archive/2019/build-2019-measures b/scripts/measures/archive/2019/build-2019-measures index 23f30d24..3b0a2515 100755 --- a/scripts/measures/archive/2019/build-2019-measures +++ b/scripts/measures/archive/2019/build-2019-measures @@ -59,5 +59,4 @@ node scripts/measures/$currentPerformanceYear/import-qcdr-measures.js \ # 8. Validate the resulting measures-data.json file: echo "validating new measures-data.json" -cat measures/$currentPerformanceYear/measures-data.json | \ - node scripts/validate-data.js measures $currentPerformanceYear +node dist/validate-data.js measures $currentPerformanceYear measures/$currentPerformanceYear/measures-data.json diff --git a/scripts/measures/archive/2020/build-2020-measures b/scripts/measures/archive/2020/build-2020-measures index 9853c4ab..324560de 100755 --- a/scripts/measures/archive/2020/build-2020-measures +++ b/scripts/measures/archive/2020/build-2020-measures @@ -80,5 +80,4 @@ node scripts/measures/$currentPerformanceYear/import-qcdr-measures.js \ # 9. Validate the resulting measures-data.json file: echo "validating new measures-data.json" -cat measures/$currentPerformanceYear/measures-data.json | \ - node scripts/validate-data.js measures $currentPerformanceYear +node dist/validate-data.js measures $currentPerformanceYear measures/$currentPerformanceYear/measures-data.json diff --git a/scripts/measures/archive/2021/build-2021-measures b/scripts/measures/archive/2021/build-2021-measures index 9dd11901..e21b7cc3 100755 --- a/scripts/measures/archive/2021/build-2021-measures +++ b/scripts/measures/archive/2021/build-2021-measures @@ -80,5 +80,4 @@ node scripts/measures/$currentPerformanceYear/import-qcdr-measures.js \ # 9. Validate the resulting measures-data.json file: echo "validating new measures-data.json" -cat measures/$currentPerformanceYear/measures-data.json | \ - node scripts/validate-data.js measures $currentPerformanceYear +node dist/validate-data.js measures $currentPerformanceYear measures/$currentPerformanceYear/measures-data.json diff --git a/scripts/measures/update-measures b/scripts/measures/update-measures index 78823e78..47b072b8 100755 --- a/scripts/measures/update-measures +++ b/scripts/measures/update-measures @@ -58,8 +58,7 @@ if (( $currentPerformanceYear >= 2022 )) && (( $currentPerformanceYear <= $maxPe exitOnFailure echo -e "Validating new measures-data.json..." - cat measures/$currentPerformanceYear/measures-data.json | \ - node scripts/validate-data.js measures $currentPerformanceYear + node dist/validate-data.js measures $currentPerformanceYear measures/$currentPerformanceYear/measures-data.json exitOnFailure # mvps don't exist prior to PY2023. diff --git a/scripts/validate-data.js b/scripts/validate-data.ts similarity index 54% rename from scripts/validate-data.js rename to scripts/validate-data.ts index 91b04fdc..e26e47af 100755 --- a/scripts/validate-data.js +++ b/scripts/validate-data.ts @@ -5,24 +5,26 @@ * the validation error. * * This script can be used as follows: - * cat [json file path] | node [this file's path] [schemaType] [performanceYear] + * node [this file's path] [schemaType] [performanceYear] [json file path] * Example: - * cat measures/measures-data.json | node scripts/validate-data.js measures 2018 + * node dist/validate-data.js measures 2018 measures/measures-data.json **/ -const path = require('path'); -const YAML = require('yaml'); -const fs = require('fs'); +import path from 'path'; +import appRoot from 'app-root-path'; +import YAML from 'yaml'; +import fs from 'fs'; -const Constants = require('../constants.js'); -const ValidateLib = require('./validate-json-data/validate-json-data-lib'); +import Constants from '../constants'; +import { ValidateLib } from './validate-json-data/validate-json-data-lib'; const schemaType = process.argv[2]; const performanceYear = (process.argv[3] || Constants.currentPerformanceYear).toString(); -const optionalPath = process.argv[4] +const jsonToValidate = fs.readFileSync(path.join(appRoot + '', process.argv[4]), 'utf8'); +const optionalPath = process.argv[5] -function validate(schema, json) { - const data = JSON.parse(json, 'utf8'); +function validate(schema: any, json: any) { + const data = JSON.parse(json); const {valid, errors, details} = ValidateLib.validate(schema, data); @@ -36,25 +38,13 @@ function validate(schema, json) { } if (!schemaType) { - console.log('Please provide schema type, e.g. measures or benchmarks\nExample Command: cat ../measures/measures-data.json | node validate-data.js measures'); + console.log('Please provide schema type, e.g. measures or benchmarks\nExample Command: node dist/validate-data.js measures 2023 measures/measures-data.json'); process.exit(1); } // Load the schema file const schema = YAML.parse(fs.readFileSync(path.join(__dirname, '../', optionalPath || schemaType, performanceYear, schemaType + '-schema.yaml'), 'utf8')); -// Read stdin into a string -let json = ''; -process.stdin.setEncoding('utf8'); +// Validate the json against the schema +validate(schema, jsonToValidate); -process.stdin.on('readable', function() { - const chunk = this.read(); - if (chunk !== null) { - json += chunk; - } -}); - -// Finally, validate the json against the schema -process.stdin.on('end', function() { - validate(schema, json); -}); diff --git a/scripts/validate-json-data/validate-json-data-lib.ts b/scripts/validate-json-data/validate-json-data-lib.ts new file mode 100644 index 00000000..ad06bbdb --- /dev/null +++ b/scripts/validate-json-data/validate-json-data-lib.ts @@ -0,0 +1,103 @@ +import Ajv, { + AnySchemaObject, + MissingRefError, + SchemaCxt +} from 'ajv'; +import ajvKeywords from 'ajv-keywords'; +import jsonPatch from 'fast-json-patch'; +import jsonMergePatch from 'json-merge-patch'; +import * as URI from 'toad-uri-js'; + +const ajv = new Ajv({ verbose: true }); +ajvKeywords(ajv, 'uniqueItemProperties'); + +/** + * Implementations for `$merge` and `$patch` are adapted from the `ajv-merge-patch` package: + * https://github.com/ajv-validator/ajv-merge-patch/tree/master + * Due to a security vulnerability in the original package, we've integrated these functionalities directly. + */ +function addKeyword(ajv: Ajv, keyword: string, jsonPatch: Function, patchSchema: any) { + ajv.addKeyword({ + keyword: keyword, + macro: function (schema: any, parentSchema: AnySchemaObject, it: SchemaCxt) { + var source = schema.source; + var patch = schema.with; + if (source.$ref) source = JSON.parse(JSON.stringify(getSchema(source.$ref, it))); + if (patch.$ref) patch = getSchema(patch.$ref, it); + jsonPatch.call(null, source, patch, true); + return source; + + function getSchema($ref: string, it: SchemaCxt) { + var id = it.baseId && it.baseId !== '#' ? URI.resolve(it.baseId, $ref) : $ref; + var validate = ajv.getSchema(id); + if (validate) return validate.schema; + throw new MissingRefError(URI, it.baseId, $ref); + } + }, + metaSchema: { + type: "object", + required: ["source", "with"], + additionalProperties: false, + properties: { + source: { + anyOf: [ + { + type: "object", + required: ["$ref"], + additionalProperties: false, + properties: { "$ref": { "type": "string", "format": "uri" } } + }, + { $ref: "http://json-schema.org/draft-07/schema#" } + ] + }, + with: patchSchema + } + } + }); +} + +// Adapted for $merge +addKeyword(ajv, '$merge', jsonMergePatch.apply, { type: "object" }); + +// Adapted for $patch +addKeyword(ajv, '$patch', jsonPatch.applyPatch, { + type: "array", + items: { + type: "object", + required: ["op", "path"], + properties: { + op: { type: "string" }, + path: { type: "string", format: "json-pointer" } + }, + anyOf: [ + { + properties: { op: { enum: ["add", "replace", "test"] } }, + required: ["value"] + }, + { + properties: { op: { enum: ["remove"] } } + }, + { + properties: { + op: { enum: ["move", "copy"] }, + from: { type: "string", format: "json-pointer" } + }, + required: ["from"] + } + ] + } +}); + +export const ValidateLib = { + validate: function (schema: any, data: any) { + const valid = ajv.validate(schema, data); + + return { + valid, + // Summary of errors, this is a string. If valid this is "No errors" + errors: ajv.errorsText(ajv.errors), + // Full error details, this is an array of objects + details: ajv.errors + }; + } +}; From a5261fb4a04d4cbccaa58ba1f4deae721f8d01f3 Mon Sep 17 00:00:00 2001 From: ckawell-sb Date: Tue, 16 Jul 2024 11:25:04 -0500 Subject: [PATCH 3/5] feat: QPPA-9026 cleanup --- scripts/validate-data.ts | 4 ++-- .../validate-json-data-lib.ts => validate-lib.ts} | 0 2 files changed, 2 insertions(+), 2 deletions(-) rename scripts/{validate-json-data/validate-json-data-lib.ts => validate-lib.ts} (100%) diff --git a/scripts/validate-data.ts b/scripts/validate-data.ts index e26e47af..c3c56fda 100755 --- a/scripts/validate-data.ts +++ b/scripts/validate-data.ts @@ -16,12 +16,12 @@ import YAML from 'yaml'; import fs from 'fs'; import Constants from '../constants'; -import { ValidateLib } from './validate-json-data/validate-json-data-lib'; +import { ValidateLib } from './validate-lib'; const schemaType = process.argv[2]; const performanceYear = (process.argv[3] || Constants.currentPerformanceYear).toString(); const jsonToValidate = fs.readFileSync(path.join(appRoot + '', process.argv[4]), 'utf8'); -const optionalPath = process.argv[5] +const optionalPath = process.argv[5]; function validate(schema: any, json: any) { const data = JSON.parse(json); diff --git a/scripts/validate-json-data/validate-json-data-lib.ts b/scripts/validate-lib.ts similarity index 100% rename from scripts/validate-json-data/validate-json-data-lib.ts rename to scripts/validate-lib.ts From e17ef98c6829cce023e4d3651b195063e5a1b435 Mon Sep 17 00:00:00 2001 From: ckawell-sb Date: Tue, 16 Jul 2024 12:59:19 -0500 Subject: [PATCH 4/5] feat: QPPA-0000 measures removal cleanup --- mvp/2024/mvp-raw.json | 63 -------------------- mvp/2024/mvp.csv | 7 --- staging/2024/benchmarks/benchmarks.csv | 2 - staging/2024/benchmarks/json/benchmarks.json | 50 ---------------- 4 files changed, 122 deletions(-) diff --git a/mvp/2024/mvp-raw.json b/mvp/2024/mvp-raw.json index dc70d1f3..97c165f5 100644 --- a/mvp/2024/mvp-raw.json +++ b/mvp/2024/mvp-raw.json @@ -5426,42 +5426,6 @@ "MVP Reporting Category": "Quality", "Measure Id": "503" }, - { - "MVP Title": "Optimal Care for Patients with Episodic Neurological Conditions", - "MVP ID": "M0003", - "MVP Description": "The Optimal Care for Patients with Episodic Neurological Conditions MVP focuses on the clinical theme of promoting quality care for patients suffering from episodic neurological conditions.", - "Most Applicable Medical Specialties": "Neurology", - "Clinical Topic": "Episodic neurological conditions", - "MVP Reporting Category": "Quality", - "Measure Id": "AAN22" - }, - { - "MVP Title": "Optimal Care for Patients with Episodic Neurological Conditions", - "MVP ID": "M0003", - "MVP Description": "The Optimal Care for Patients with Episodic Neurological Conditions MVP focuses on the clinical theme of promoting quality care for patients suffering from episodic neurological conditions.", - "Most Applicable Medical Specialties": "Neurology", - "Clinical Topic": "Episodic neurological conditions", - "MVP Reporting Category": "Quality", - "Measure Id": "AAN31" - }, - { - "MVP Title": "Optimal Care for Patients with Episodic Neurological Conditions", - "MVP ID": "M0003", - "MVP Description": "The Optimal Care for Patients with Episodic Neurological Conditions MVP focuses on the clinical theme of promoting quality care for patients suffering from episodic neurological conditions.", - "Most Applicable Medical Specialties": "Neurology", - "Clinical Topic": "Episodic neurological conditions", - "MVP Reporting Category": "Quality", - "Measure Id": "AAN32" - }, - { - "MVP Title": "Optimal Care for Patients with Episodic Neurological Conditions", - "MVP ID": "M0003", - "MVP Description": "The Optimal Care for Patients with Episodic Neurological Conditions MVP focuses on the clinical theme of promoting quality care for patients suffering from episodic neurological conditions.", - "Most Applicable Medical Specialties": "Neurology", - "Clinical Topic": "Episodic neurological conditions", - "MVP Reporting Category": "Quality", - "Measure Id": "AAN5" - }, { "MVP Title": "Supportive Care for Neurodegenerative Conditions", "MVP ID": "M0004", @@ -6074,33 +6038,6 @@ "MVP Reporting Category": "Quality", "Measure Id": "487" }, - { - "MVP Title": "Supportive Care for Neurodegenerative Conditions", - "MVP ID": "M0004", - "MVP Description": "The Supportive Care for Neurodegenerative Conditions MVP focuses on the clinical theme of promoting quality care for patients with cognitive-based neurological disorders such as dementia, Parkinson’s Disease (PD), and Amyotrophic Lateral Sclerosis (ALS).", - "Most Applicable Medical Specialties": "Neurology", - "Clinical Topic": "Cognitive-based neurological disorders", - "MVP Reporting Category": "Quality", - "Measure Id": "AAN22" - }, - { - "MVP Title": "Supportive Care for Neurodegenerative Conditions", - "MVP ID": "M0004", - "MVP Description": "The Supportive Care for Neurodegenerative Conditions MVP focuses on the clinical theme of promoting quality care for patients with cognitive-based neurological disorders such as dementia, Parkinson’s Disease (PD), and Amyotrophic Lateral Sclerosis (ALS).", - "Most Applicable Medical Specialties": "Neurology", - "Clinical Topic": "Cognitive-based neurological disorders", - "MVP Reporting Category": "Quality", - "Measure Id": "AAN34" - }, - { - "MVP Title": "Supportive Care for Neurodegenerative Conditions", - "MVP ID": "M0004", - "MVP Description": "The Supportive Care for Neurodegenerative Conditions MVP focuses on the clinical theme of promoting quality care for patients with cognitive-based neurological disorders such as dementia, Parkinson’s Disease (PD), and Amyotrophic Lateral Sclerosis (ALS).", - "Most Applicable Medical Specialties": "Neurology", - "Clinical Topic": "Cognitive-based neurological disorders", - "MVP Reporting Category": "Quality", - "Measure Id": "AAN9" - }, { "MVP Title": "Value in Primary Care", "MVP ID": "M0005", diff --git a/mvp/2024/mvp.csv b/mvp/2024/mvp.csv index 56119d28..4eca1131 100644 --- a/mvp/2024/mvp.csv +++ b/mvp/2024/mvp.csv @@ -602,10 +602,6 @@ Optimal Care for Patients with Episodic Neurological Conditions ,M0003,The Optim Optimal Care for Patients with Episodic Neurological Conditions ,M0003,The Optimal Care for Patients with Episodic Neurological Conditions MVP focuses on the clinical theme of promoting quality care for patients suffering from episodic neurological conditions. ,Neurology,Episodic neurological conditions,Quality,419 Optimal Care for Patients with Episodic Neurological Conditions ,M0003,The Optimal Care for Patients with Episodic Neurological Conditions MVP focuses on the clinical theme of promoting quality care for patients suffering from episodic neurological conditions. ,Neurology,Episodic neurological conditions,Quality,487 Optimal Care for Patients with Episodic Neurological Conditions ,M0003,The Optimal Care for Patients with Episodic Neurological Conditions MVP focuses on the clinical theme of promoting quality care for patients suffering from episodic neurological conditions. ,Neurology,Episodic neurological conditions,Quality,503 -Optimal Care for Patients with Episodic Neurological Conditions ,M0003,The Optimal Care for Patients with Episodic Neurological Conditions MVP focuses on the clinical theme of promoting quality care for patients suffering from episodic neurological conditions. ,Neurology,Episodic neurological conditions,Quality,AAN22 -Optimal Care for Patients with Episodic Neurological Conditions ,M0003,The Optimal Care for Patients with Episodic Neurological Conditions MVP focuses on the clinical theme of promoting quality care for patients suffering from episodic neurological conditions. ,Neurology,Episodic neurological conditions,Quality,AAN31 -Optimal Care for Patients with Episodic Neurological Conditions ,M0003,The Optimal Care for Patients with Episodic Neurological Conditions MVP focuses on the clinical theme of promoting quality care for patients suffering from episodic neurological conditions. ,Neurology,Episodic neurological conditions,Quality,AAN32 -Optimal Care for Patients with Episodic Neurological Conditions ,M0003,The Optimal Care for Patients with Episodic Neurological Conditions MVP focuses on the clinical theme of promoting quality care for patients suffering from episodic neurological conditions. ,Neurology,Episodic neurological conditions,Quality,AAN5 Supportive Care for Neurodegenerative Conditions ,M0004,"The Supportive Care for Neurodegenerative Conditions MVP focuses on the clinical theme of promoting quality care for patients with cognitive-based neurological disorders such as dementia, Parkinson’s Disease (PD), and Amyotrophic Lateral Sclerosis (ALS).",Neurology,Cognitive-based neurological disorders,Cost,MSPB_1 Supportive Care for Neurodegenerative Conditions ,M0004,"The Supportive Care for Neurodegenerative Conditions MVP focuses on the clinical theme of promoting quality care for patients with cognitive-based neurological disorders such as dementia, Parkinson’s Disease (PD), and Amyotrophic Lateral Sclerosis (ALS).",Neurology,Cognitive-based neurological disorders,Foundational,479 Supportive Care for Neurodegenerative Conditions ,M0004,"The Supportive Care for Neurodegenerative Conditions MVP focuses on the clinical theme of promoting quality care for patients with cognitive-based neurological disorders such as dementia, Parkinson’s Disease (PD), and Amyotrophic Lateral Sclerosis (ALS).",Neurology,Cognitive-based neurological disorders,Foundational,484 @@ -674,9 +670,6 @@ Supportive Care for Neurodegenerative Conditions ,M0004,"The Supportive Care fo Supportive Care for Neurodegenerative Conditions ,M0004,"The Supportive Care for Neurodegenerative Conditions MVP focuses on the clinical theme of promoting quality care for patients with cognitive-based neurological disorders such as dementia, Parkinson’s Disease (PD), and Amyotrophic Lateral Sclerosis (ALS).",Neurology,Cognitive-based neurological disorders,Quality,293 Supportive Care for Neurodegenerative Conditions ,M0004,"The Supportive Care for Neurodegenerative Conditions MVP focuses on the clinical theme of promoting quality care for patients with cognitive-based neurological disorders such as dementia, Parkinson’s Disease (PD), and Amyotrophic Lateral Sclerosis (ALS).",Neurology,Cognitive-based neurological disorders,Quality,386 Supportive Care for Neurodegenerative Conditions ,M0004,"The Supportive Care for Neurodegenerative Conditions MVP focuses on the clinical theme of promoting quality care for patients with cognitive-based neurological disorders such as dementia, Parkinson’s Disease (PD), and Amyotrophic Lateral Sclerosis (ALS).",Neurology,Cognitive-based neurological disorders,Quality,487 -Supportive Care for Neurodegenerative Conditions ,M0004,"The Supportive Care for Neurodegenerative Conditions MVP focuses on the clinical theme of promoting quality care for patients with cognitive-based neurological disorders such as dementia, Parkinson’s Disease (PD), and Amyotrophic Lateral Sclerosis (ALS).",Neurology,Cognitive-based neurological disorders,Quality,AAN22 -Supportive Care for Neurodegenerative Conditions ,M0004,"The Supportive Care for Neurodegenerative Conditions MVP focuses on the clinical theme of promoting quality care for patients with cognitive-based neurological disorders such as dementia, Parkinson’s Disease (PD), and Amyotrophic Lateral Sclerosis (ALS).",Neurology,Cognitive-based neurological disorders,Quality,AAN34 -Supportive Care for Neurodegenerative Conditions ,M0004,"The Supportive Care for Neurodegenerative Conditions MVP focuses on the clinical theme of promoting quality care for patients with cognitive-based neurological disorders such as dementia, Parkinson’s Disease (PD), and Amyotrophic Lateral Sclerosis (ALS).",Neurology,Cognitive-based neurological disorders,Quality,AAN9 Value in Primary Care,M0005,"The Value in Primary Care MVP focuses on the clinical theme of promoting quality care for patients in order to reduce the risk of diseases, disabilities, and death. ",Preventive Medicine; Internal Medicine; Family Medicine; Geriatrics,Preventive Care,Cost,COST_ACOPD_1 Value in Primary Care,M0005,"The Value in Primary Care MVP focuses on the clinical theme of promoting quality care for patients in order to reduce the risk of diseases, disabilities, and death. ",Preventive Medicine; Internal Medicine; Family Medicine; Geriatrics,Preventive Care,Cost,COST_D_1 Value in Primary Care,M0005,"The Value in Primary Care MVP focuses on the clinical theme of promoting quality care for patients in order to reduce the risk of diseases, disabilities, and death. ",Preventive Medicine; Internal Medicine; Family Medicine; Geriatrics,Preventive Care,Cost,COST_DEP_1 diff --git a/staging/2024/benchmarks/benchmarks.csv b/staging/2024/benchmarks/benchmarks.csv index 2524c501..f1fc07cc 100644 --- a/staging/2024/benchmarks/benchmarks.csv +++ b/staging/2024/benchmarks/benchmarks.csv @@ -94,8 +94,6 @@ Otitis Media with Effusion: Systemic Antimicrobials - Avoidance of Inappropriate Percentage of Patients Who Died from Cancer Admitted to Hospice for Less than 3 days (lower score - better),457,MIPS CQM,Outcome,11.57648986980493,11.02,No,No,0.0,0.0,1.96,3.56,5.665,8.895,11.74,13.645,19.48,22.58,77.78,Y,Y,singlePerformanceRate Prevention of Post-Operative Vomiting (POV) - Combination Therapy (Pediatrics),463,MIPS CQM,Process,5.039167666359465,98.81,Yes,Yes,58.11,97.73,99.605,99.87,100.0,100.0,100.0,100.0,100.0,100.0,100.0,Y,N,singlePerformanceRate Melanoma: – Appropriate Surgical Margins,AAD12,QCDR Measure,Intermediate Outcome,7.207450741453331,97.55,Yes,No,75.51,96.67,100.0,100.0,100.0,100.0,100.0,100.0,100.0,100.0,100.0,Y,N,registrySinglePerformanceRate -Treatment Prescribed For Acute Migraine Attacks,AAN5,QCDR Measure,Process,21.146308027485016,77.53,No,No,0.59,62.5,73.74,78.13,79.845,81.52,83.26,85.9,89.96,95.61,98.25,N,N,registrySinglePerformanceRate -Exercise and Appropriate Physical Activity Counseling for Patients with MS,AAN8,QCDR Measure,Process,17.995335374949725,80.48,No,No,30.3,53.03,70.64,76.32,78.63,85.71,90.11,92.54,95.79,97.18,100.0,N,N,registrySinglePerformanceRate Age-Related Hearing Loss: Comprehensive Audiometric Evaluation,AAO16,QCDR Measure,Process,11.677787941458508,91.57,Yes,Yes,45.96,73.29,86.16,91.27,94.44,96.77,97.15,99.32,100.0,100.0,100.0,Y,N,registrySinglePerformanceRate Tympanostomy Tubes: Comprehensive Audiometric Evaluation,AAO20,QCDR Measure,Process,30.01790502905446,74.14,No,No,10.92,20.38,49.0,63.04,85.725,91.17,93.445,94.44,96.025,99.36,100.0,N,N,registrySinglePerformanceRate Allergic Rhinitis: Intranasal Corticosteroids or Oral Antihistamines,AAO23,QCDR Measure,Process,25.011888270888896,64.08,No,No,8.9,24.46,39.03,54.02,63.37,70.36,77.65,80.27,87.06,94.7,100.0,N,N,registrySinglePerformanceRate diff --git a/staging/2024/benchmarks/json/benchmarks.json b/staging/2024/benchmarks/json/benchmarks.json index 12f48124..cfb592ea 100644 --- a/staging/2024/benchmarks/json/benchmarks.json +++ b/staging/2024/benchmarks/json/benchmarks.json @@ -2374,56 +2374,6 @@ }, "averagePerformanceRate": 97.55 }, - { - "measureId": "AAN5", - "benchmarkYear": 2022, - "performanceYear": 2024, - "submissionMethod": "registry", - "isToppedOut": false, - "isHighPriority": false, - "isInverse": false, - "metricType": "registrySinglePerformanceRate", - "isToppedOutByProgram": false, - "percentiles": { - "1": 0.59, - "10": 62.5, - "20": 73.74, - "30": 78.13, - "40": 79.845, - "50": 81.52, - "60": 83.26, - "70": 85.9, - "80": 89.96, - "90": 95.61, - "99": 98.25 - }, - "averagePerformanceRate": 77.53 - }, - { - "measureId": "AAN8", - "benchmarkYear": 2022, - "performanceYear": 2024, - "submissionMethod": "registry", - "isToppedOut": false, - "isHighPriority": false, - "isInverse": false, - "metricType": "registrySinglePerformanceRate", - "isToppedOutByProgram": false, - "percentiles": { - "1": 30.3, - "10": 53.03, - "20": 70.64, - "30": 76.32, - "40": 78.63, - "50": 85.71, - "60": 90.11, - "70": 92.54, - "80": 95.79, - "90": 97.18, - "99": 100 - }, - "averagePerformanceRate": 80.48 - }, { "measureId": "AAO16", "benchmarkYear": 2022, From e4550b873a666777a17138f31c2cfc6cad7ef4c2 Mon Sep 17 00:00:00 2001 From: ckawell-sb Date: Tue, 16 Jul 2024 13:11:00 -0500 Subject: [PATCH 5/5] feat: QPPA-9026 cleanup --- scripts/validate-data.ts | 3 ++- scripts/validate-lib.ts | 14 +++++++------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/scripts/validate-data.ts b/scripts/validate-data.ts index c3c56fda..5033ec60 100755 --- a/scripts/validate-data.ts +++ b/scripts/validate-data.ts @@ -17,13 +17,14 @@ import fs from 'fs'; import Constants from '../constants'; import { ValidateLib } from './validate-lib'; +import { AnySchemaObject } from 'ajv'; const schemaType = process.argv[2]; const performanceYear = (process.argv[3] || Constants.currentPerformanceYear).toString(); const jsonToValidate = fs.readFileSync(path.join(appRoot + '', process.argv[4]), 'utf8'); const optionalPath = process.argv[5]; -function validate(schema: any, json: any) { +function validate(schema: AnySchemaObject, json: any) { const data = JSON.parse(json); const {valid, errors, details} = ValidateLib.validate(schema, data); diff --git a/scripts/validate-lib.ts b/scripts/validate-lib.ts index ad06bbdb..032ee539 100644 --- a/scripts/validate-lib.ts +++ b/scripts/validate-lib.ts @@ -16,20 +16,20 @@ ajvKeywords(ajv, 'uniqueItemProperties'); * https://github.com/ajv-validator/ajv-merge-patch/tree/master * Due to a security vulnerability in the original package, we've integrated these functionalities directly. */ -function addKeyword(ajv: Ajv, keyword: string, jsonPatch: Function, patchSchema: any) { +function addKeyword(ajv: Ajv, keyword: string, jsonPatch: any, patchSchema: { type: string, items?: any }) { ajv.addKeyword({ keyword: keyword, - macro: function (schema: any, parentSchema: AnySchemaObject, it: SchemaCxt) { - var source = schema.source; - var patch = schema.with; + macro: function (schema: AnySchemaObject, parentSchema: AnySchemaObject, it: SchemaCxt) { + let source = schema.source; + let patch = schema.with; if (source.$ref) source = JSON.parse(JSON.stringify(getSchema(source.$ref, it))); if (patch.$ref) patch = getSchema(patch.$ref, it); jsonPatch.call(null, source, patch, true); return source; function getSchema($ref: string, it: SchemaCxt) { - var id = it.baseId && it.baseId !== '#' ? URI.resolve(it.baseId, $ref) : $ref; - var validate = ajv.getSchema(id); + const id = it.baseId && it.baseId !== '#' ? URI.resolve(it.baseId, $ref) : $ref; + const validate = ajv.getSchema(id); if (validate) return validate.schema; throw new MissingRefError(URI, it.baseId, $ref); } @@ -89,7 +89,7 @@ addKeyword(ajv, '$patch', jsonPatch.applyPatch, { }); export const ValidateLib = { - validate: function (schema: any, data: any) { + validate: function (schema: AnySchemaObject, data: any) { const valid = ajv.validate(schema, data); return {