|
| 1 | +import {fileAsLines, readAsJson} from "./files.ts" |
| 2 | +import {groupBy} from "./functional.ts" |
| 3 | + |
| 4 | + |
| 5 | +const tableLines = fileAsLines("table.tsv") |
| 6 | +/* |
| 7 | + * The contents of the 'table.tsv' file are copied from the table in Annex A of the guidelines document, |
| 8 | + * after which some spurious newlines are cleaned up. |
| 9 | + */ |
| 10 | + |
| 11 | + |
| 12 | +const cleanUp = (str: string) => { |
| 13 | + const match = str.match(/^"\s*(.+?)\s*"$/) |
| 14 | + return match |
| 15 | + ? match[1] |
| 16 | + : str |
| 17 | +} |
| 18 | + |
| 19 | + |
| 20 | +type Mapping = { |
| 21 | + "vaccine-code": string |
| 22 | + "vaccine-manufacturer": string |
| 23 | + "sct-codes": string[] |
| 24 | + "note": string |
| 25 | +} |
| 26 | + |
| 27 | +const lineAsEncoding = (line: string): [string, Mapping] => { |
| 28 | + const parts = line.split(/\t/).map(cleanUp) |
| 29 | + return [ |
| 30 | + parts[0], |
| 31 | + { |
| 32 | + "vaccine-code": parts[1], |
| 33 | + "vaccine-manufacturer": parts[2], |
| 34 | + "sct-codes": /* parts[3] === undefined ? [] : */parts[3].split(/\s+or\s+/), |
| 35 | + "note": parts[4] |
| 36 | + } |
| 37 | + ] |
| 38 | +} |
| 39 | + |
| 40 | +const keyedEncodings: [string, Mapping][] = tableLines |
| 41 | + .slice(1) |
| 42 | + .map(lineAsEncoding) |
| 43 | + |
| 44 | + |
| 45 | +// check against other value sets: |
| 46 | +const medicinalProduct = readAsJson("../vaccine-medicinal-product.json") |
| 47 | +const manufacturers = readAsJson("../vaccine-mah-manf.json") |
| 48 | +const prophylaxis = readAsJson("../vaccine-prophylaxis.json") |
| 49 | + |
| 50 | +const deprecationPostfix = " (deprecated)" |
| 51 | +keyedEncodings.forEach(([key, mapping], i) => { |
| 52 | + |
| 53 | + const rowInfix = `@ mapping row ${i+2} for "${key}"` |
| 54 | + const warnHeader = `[WARN] ${rowInfix}:` |
| 55 | + |
| 56 | + const vaccineCode = mapping["vaccine-code"] |
| 57 | + const withoutDeprecation = vaccineCode.endsWith(deprecationPostfix) ? vaccineCode.substring(0, vaccineCode.length - deprecationPostfix.length) : vaccineCode |
| 58 | + if (vaccineCode.endsWith(deprecationPostfix)) { |
| 59 | + console.info(`[INFO] ${rowInfix}: vaccine with code "${withoutDeprecation}" occurs even though the vaccine's been deprecated`) |
| 60 | + } |
| 61 | + |
| 62 | + if (!(withoutDeprecation in medicinalProduct.valueSetValues)) { |
| 63 | + console.warn(`${warnHeader} vaccine code "${mapping["vaccine-code"]}" doesn't exist in value set "vaccines-covid-19-names"`) |
| 64 | + } |
| 65 | + |
| 66 | + if (!(mapping["vaccine-manufacturer"] in manufacturers.valueSetValues)) { |
| 67 | + console.warn(`${warnHeader} vaccine manufacturer with ID "${mapping["vaccine-manufacturer"]}" doesn't exist in value set "vaccines-covid-19-auth-holders"`) |
| 68 | + } |
| 69 | + |
| 70 | + if (mapping["sct-codes"].length === 0) { |
| 71 | + console.warn(`${warnHeader} no mapping to prophylaxes`) |
| 72 | + } |
| 73 | + if (new Set(...mapping["sct-codes"]).size < mapping["sct-codes"].length) { |
| 74 | + console.warn(`${warnHeader} mappings to duplicate prophylaxes occur`) |
| 75 | + } |
| 76 | + mapping["sct-codes"].forEach((sctCode) => { |
| 77 | + if (sctCode in prophylaxis.valueSetValues) { |
| 78 | + const sct = prophylaxis.valueSetValues[sctCode] |
| 79 | + if (!sct["active"]) { |
| 80 | + console.warn(`${warnHeader} prophylaxis code "${sctCode}" is used despite it being deprecated`) |
| 81 | + } |
| 82 | + } else { |
| 83 | + console.warn(`${warnHeader} prophylaxis code "${sctCode}" doesn't exist in value set "sct-vaccines-covid-19"`) |
| 84 | + } |
| 85 | + }) |
| 86 | + |
| 87 | +}) |
| 88 | + |
| 89 | + |
| 90 | +const manufacturersWithMultipleVaccines = |
| 91 | + groupBy(keyedEncodings, ([_, mapping]) => mapping["vaccine-manufacturer"]) |
| 92 | + .filter(([_, encodings]) => encodings.length > 1) |
| 93 | + .map(([key, encodings]) => [key, encodings.map(([_, mapping]) => mapping["vaccine-code"])]) |
| 94 | + |
| 95 | +if (manufacturersWithMultipleVaccines.length > 0) { |
| 96 | + console.log() |
| 97 | + console.log(`[INFO] manufacturers with more than 1 vaccine:`) |
| 98 | + console.dir(manufacturersWithMultipleVaccines) |
| 99 | +} |
| 100 | + |
| 101 | + |
| 102 | +// check for duplicate keys: |
| 103 | +const encodingsPerKey = groupBy(keyedEncodings, ([key]) => key) |
| 104 | +const duplicateKeyEntries = encodingsPerKey.filter(([_, group]) => group.length > 1) |
| 105 | +if (duplicateKeyEntries.length > 0) { |
| 106 | + console.error(`[ERROR] key occurs more than once: ${duplicateKeyEntries.map(([key]) => `"${key}"`).join(", ")}`) |
| 107 | + console.error(` - (exiting before saving)`) |
| 108 | + Deno.exit(1) |
| 109 | +} |
| 110 | + |
| 111 | + |
| 112 | +const valueSet = { |
| 113 | + "valueSetId": "vaccines-covid-19-encoding-instructions", |
| 114 | + "valueSetDate": new Date().toISOString().slice(0, 10), |
| 115 | + "valueSetValues": Object.fromEntries(keyedEncodings) |
| 116 | +} |
| 117 | + |
| 118 | +Deno.writeTextFileSync( |
| 119 | + "../vaccine-encoding-instructions.json", |
| 120 | + JSON.stringify(valueSet, null, 2) |
| 121 | +) |
| 122 | + |
0 commit comments