Compare commits

...

3 Commits

Author SHA1 Message Date
85fabfcc92 Restricted record check 2022-10-04 11:40:12 -04:00
4ccb4ca3db Hidden full SSNs 2022-10-04 09:44:04 -04:00
555308e97f Improved lab report edge case parsing 2022-10-04 09:42:54 -04:00
5 changed files with 28 additions and 17 deletions

View File

@ -1,7 +1,12 @@
<template> <template>
<div v-if="(sensitive) && (!info)" class="alert alert-danger text-center mb-3 shadow" role="alert">
<h1>Warning: Restricted Record</h1>
<p>This record is protected by the Privacy Act of 1974 and the Health Insurance Portability and Accountability Act of 1996. If you elect to proceed, you will be required to prove you have a need to know. Accessing this patient is tracked, and your station Security Officer will contact you for your justification.</p>
<router-link class="btn btn-danger" :to="'/patient/' + dfn + '?viewsensitive'">Proceed</router-link>
</div>
<div v-if="info"> <div v-if="info">
<div class="card mb-3 shadow"> <div class="card mb-3 shadow">
<div class="card-header">{{info.name}} #{{dfn}} ${{info.pid}}</div> <div class="card-header">{{info.name}} <span :title="info.pid">{{info.pid.slice(-4)}}</span> #{{dfn}}</div>
<div class="card-body row" style="font-family: monospace;"> <div class="card-body row" style="font-family: monospace;">
<div class="col" v-if="info.dob"><strong>DOB:</strong> {{strptime_vista(info.dob).toLocaleDateString('en-CA')}}</div> <div class="col" v-if="info.dob"><strong>DOB:</strong> {{strptime_vista(info.dob).toLocaleDateString('en-CA')}}</div>
<div class="col" v-if="info.age"><strong>Age:</strong> {{info.age}}</div> <div class="col" v-if="info.age"><strong>Age:</strong> {{info.age}}</div>
@ -52,6 +57,7 @@
data() { data() {
return { return {
dfn: null, dfn: null,
sensitive: false,
info: null, info: null,
report_date: now, report_date: now,
report_date_begin: now, report_date_begin: now,
@ -66,7 +72,12 @@
} }
}, },
methods: { methods: {
strptime_vista strptime_vista,
async loadinfo(dfn, viewsensitive) {
this.dfn = dfn;
this.sensitive = viewsensitive ? false : await this.client.ORWPT_SELCHK(dfn);
this.info = this.sensitive ? null : await this.client.ORWPT16_ID_INFO(dfn);
}
}, },
async mounted() { async mounted() {
if(this.$route.params.id.startsWith('$')) { if(this.$route.params.id.startsWith('$')) {
@ -82,14 +93,10 @@
break; break;
} }
} }
} else { } else this.loadinfo(this.$route.params.id, this.$route.query.hasOwnProperty('viewsensitive'));
this.dfn = this.$route.params.id;
this.info = await this.client.ORWPT16_ID_INFO(this.$route.params.id);
}
}, },
async beforeRouteUpdate(to, from, next) { async beforeRouteUpdate(to, from, next) {
this.dfn = to.params.id; this.loadinfo(to.params.id, to.query.hasOwnProperty('viewsensitive'));
this.info = await this.client.ORWPT16_ID_INFO(to.params.id);
next(); next();
} }
}; };

View File

@ -17,8 +17,8 @@
</thead> </thead>
<tbody> <tbody>
<tr v-for="row in patients_lost" :style="{ backgroundColor: strHashHSL(row.Clinic, '90%') }"> <tr v-for="row in patients_lost" :style="{ backgroundColor: strHashHSL(row.Clinic, '90%') }">
<td v-if="production"><router-link :to="'/patient/$' + row.key">{{row.Name}} ${{row.key}}</router-link></td> <td v-if="production"><router-link :to="'/patient/$' + row.key">{{row.Name}} <span :title="row.key">{{row.key.slice(-4)}}</span></router-link></td>
<td v-else><router-link :title="strtr_unscramble(row.Name)" :to="'/patient/$' + row.Name.charAt(0) + row.key.slice(-4) + '?name=' + row.Name">{{row.Name}} ${{row.key}}</router-link></td> <td v-else><router-link :title="strtr_unscramble(row.Name)" :to="'/patient/$' + row.Name.charAt(0) + row.key.slice(-4) + '?name=' + row.Name">{{row.Name}} <span :title="row.key">{{row.key.slice(-4)}}</span></router-link></td>
<td>{{datefmt(row.TimeLast)}} {{dow[row.TimeLast.getDay()]}}</td> <td>{{datefmt(row.TimeLast)}} {{dow[row.TimeLast.getDay()]}}</td>
<td>{{row.Clinic}}</td> <td>{{row.Clinic}}</td>
<td>{{Math.round(row.TimeLastDiff/86400000)}}</td> <td>{{Math.round(row.TimeLastDiff/86400000)}}</td>
@ -31,8 +31,8 @@
</thead> </thead>
<tbody> <tbody>
<tr v-for="row in patients_outstanding" :style="{ backgroundColor: strHashHSL(row.Clinic, '90%') }"> <tr v-for="row in patients_outstanding" :style="{ backgroundColor: strHashHSL(row.Clinic, '90%') }">
<td v-if="production"><router-link :to="'/patient/$' + row.key">{{row.Name}} ${{row.key}}</router-link></td> <td v-if="production"><router-link :to="'/patient/$' + row.key">{{row.Name}} <span :title="row.key">{{row.key.slice(-4)}}</span></router-link></td>
<td v-else><router-link :title="strtr_unscramble(row.Name)" :to="'/patient/$' + row.Name.charAt(0) + row.key.slice(-4) + '?name=' + row.Name">{{row.Name}} ${{row.key}}</router-link></td> <td v-else><router-link :title="strtr_unscramble(row.Name)" :to="'/patient/$' + row.Name.charAt(0) + row.key.slice(-4) + '?name=' + row.Name">{{row.Name}} <span :title="row.key">{{row.key.slice(-4)}}</span></router-link></td>
<td>{{datefmt(row.TimeNext)}} {{dow[row.TimeNext.getDay()]}}</td> <td>{{datefmt(row.TimeNext)}} {{dow[row.TimeNext.getDay()]}}</td>
<td>{{row.Clinic}}</td> <td>{{row.Clinic}}</td>
<td>{{Math.round(row.TimeNextDiff/86400000)}}</td> <td>{{Math.round(row.TimeNextDiff/86400000)}}</td>

View File

@ -7,7 +7,7 @@
<tr v-for="row in appointments" :style="{ backgroundColor: strHashHSL(row.Clinic, '90%') }"> <tr v-for="row in appointments" :style="{ backgroundColor: strHashHSL(row.Clinic, '90%') }">
<td>{{row.ApptDate}}</td> <td>{{row.ApptDate}}</td>
<td>{{row.Clinic}}</td> <td>{{row.Clinic}}</td>
<td v-if="production"><router-link :to="'/patient/$' + row.HRN">{{row.Name}} ${{row.HRN}}</router-link></td> <td v-if="production"><router-link :to="'/patient/$' + row.HRN">{{row.Name}} <span :title="row.HRN">{{row.HRN.slice(-4)}}</span></router-link></td>
<td v-else><router-link :title="strtr_unscramble(row.Name)" :to="'/patient/$' + row.Name.charAt(0) + row.HRN.slice(-4) + '?name=' + row.Name">{{row.Name}} ${{row.HRN}}</router-link></td> <td v-else><router-link :title="strtr_unscramble(row.Name)" :to="'/patient/$' + row.Name.charAt(0) + row.HRN.slice(-4) + '?name=' + row.Name">{{row.Name}} ${{row.HRN}}</router-link></td>
<td>{{row.NOTE}} [{{row.APPT_MADE_BY}} on {{row.DATE_APPT_MADE}}]</td> <td>{{row.NOTE}} [{{row.APPT_MADE_BY}} on {{row.DATE_APPT_MADE}}]</td>
<td><Autocomplete :value="practitioner[row.Name]" @update:value="x => set_practitioner(row.Name, x)" :items="practitioner_list" /></td> <td><Autocomplete :value="practitioner[row.Name]" @update:value="x => set_practitioner(row.Name, x)" :items="practitioner_list" /></td>

View File

@ -53,10 +53,7 @@ function lab_parse1default(data) {
else x.comment = [line.substring(12)]; else x.comment = [line.substring(12)];
} else console.log('DANGLING:', line); } else console.log('DANGLING:', line);
} else if(m = line.match(/^\b(?<name>.*?)\s{2,}(?<value>.*?)(?: (?<flag>L\*|L|H\*|H))?\s+(?:(?<unit>.{10}) (?<range>.*?)(?: \[(?<site>\d+)\])?)?$/)) { } else if(m = line.match(/^\b(?<name>.*?)\s{2,}(?<value>.*?)(?: (?<flag>L\*|L|H\*|H))?\s+(?:(?<unit>.{10}) (?<range>.*?)(?: \[(?<site>\d+)\])?)?$/)) {
if(m.groups.name.startsWith('COVID-19 SCR (CEPHEID-RAPID)')) { if(x = line.match(/^\b(?<name>.*?)(?<value>(?:positive|negative|reactive|not detected|collected - specimen in lab|test not performed))(?: (?<flag>L\*|L|H\*|H))?\s+(?:(?<unit>.{10}) (?<range>.*?)(?: \[(?<site>\d+)\])?)?$/i)) m = x;
m = line.substring(28).match(/^(?<value>.*?)(?: (?<flag>L\*|L|H\*|H))?\s+(?:(?<unit>.{10}) (?<range>.*?)(?: \[(?<site>\d+)\])?)?$/);
m.groups.name = 'COVID-19 SCR (CEPHEID-RAPID)';
}
if((m.groups.range) && (m.groups.range.startsWith('Ref: '))) m.groups.range = m.groups.range.substring(5); if((m.groups.range) && (m.groups.range.startsWith('Ref: '))) m.groups.range = m.groups.range.substring(5);
results.push(x = m.groups); results.push(x = m.groups);
if((x.value === '') && (m = x.name.match(/^(?<name>.*?)(?<value>(?:[\d\.]+|positive|negative|reactive|not detected|collected - specimen in lab|test not performed))\s*$/i))) { if((x.value === '') && (m = x.name.match(/^(?<name>.*?)(?<value>(?:[\d\.]+|positive|negative|reactive|not detected|collected - specimen in lab|test not performed))\s*$/i))) {

View File

@ -48,6 +48,12 @@ export function memoized(fn) {
} }
} }
export function converted_boolean(fn, columns=null) {
return async function(...args) {
return await fn(...args) == '1';
}
}
export function caretseparated(fn, columns=null) { export function caretseparated(fn, columns=null) {
return async function(...args) { return async function(...args) {
if(columns) return (await fn(...args)).map(function(row) { if(columns) return (await fn(...args)).map(function(row) {
@ -142,6 +148,7 @@ export function Client(cid, secret) {
this.ORWPT_FULLSSN = memoized(caretseparated(unwrapped(logged((...args) => this.callctx(['OR CPRS GUI CHART'], 'ORWPT_FULLSSN', ...args), 'ORWPT_FULLSSN')), ['dfn', 'name', 'date', 'pid'])); this.ORWPT_FULLSSN = memoized(caretseparated(unwrapped(logged((...args) => this.callctx(['OR CPRS GUI CHART'], 'ORWPT_FULLSSN', ...args), 'ORWPT_FULLSSN')), ['dfn', 'name', 'date', 'pid']));
this.ORWPT_LAST5 = memoized(caretseparated(unwrapped(logged((...args) => this.callctx(['OR CPRS GUI CHART'], 'ORWPT_LAST5', ...args), 'ORWPT_LAST5')), ['dfn', 'name', 'date', 'pid'])); this.ORWPT_LAST5 = memoized(caretseparated(unwrapped(logged((...args) => this.callctx(['OR CPRS GUI CHART'], 'ORWPT_LAST5', ...args), 'ORWPT_LAST5')), ['dfn', 'name', 'date', 'pid']));
this.ORWPT_ID_INFO = memoized(caretseparated1(unwrapped(logged((...args) => this.callctx(['OR CPRS GUI CHART'], 'ORWPT_ID_INFO', ...args), 'ORWPT_ID_INFO')), ['pid', 'dob', 'sex', 'vet', 'sc_percentage', 'ward', 'room_bed', 'name'])); this.ORWPT_ID_INFO = memoized(caretseparated1(unwrapped(logged((...args) => this.callctx(['OR CPRS GUI CHART'], 'ORWPT_ID_INFO', ...args), 'ORWPT_ID_INFO')), ['pid', 'dob', 'sex', 'vet', 'sc_percentage', 'ward', 'room_bed', 'name']));
this.ORWPT_SELCHK = memoized(converted_boolean(unwrapped(logged((...args) => this.callctx(['OR CPRS GUI CHART'], 'ORWPT_SELCHK', ...args), 'ORWPT_SELCHK'))));
this.ORWPT16_LOOKUP = memoized(caretseparated(unwrapped(logged((...args) => this.callctx(['OR CPRS GUI CHART'], 'ORWPT16_LOOKUP', ...args), 'ORWPT16_LOOKUP')), ['dfn', 'name', 'pid'])); this.ORWPT16_LOOKUP = memoized(caretseparated(unwrapped(logged((...args) => this.callctx(['OR CPRS GUI CHART'], 'ORWPT16_LOOKUP', ...args), 'ORWPT16_LOOKUP')), ['dfn', 'name', 'pid']));
this.ORWPT16_ID_INFO = memoized(caretseparated1(unwrapped(logged((...args) => this.callctx(['OR CPRS GUI CHART'], 'ORWPT16_ID_INFO', ...args), 'ORWPT16_ID_INFO')), ['pid', 'dob', 'age', 'sex', 'sc_percentage', 'type', 'ward', 'room_bed', 'name'])); this.ORWPT16_ID_INFO = memoized(caretseparated1(unwrapped(logged((...args) => this.callctx(['OR CPRS GUI CHART'], 'ORWPT16_ID_INFO', ...args), 'ORWPT16_ID_INFO')), ['pid', 'dob', 'age', 'sex', 'sc_percentage', 'type', 'ward', 'room_bed', 'name']));