281 lines
14 KiB
JavaScript
281 lines
14 KiB
JavaScript
import { reactive, watch } from 'vue';
|
|
|
|
import vista from './vista.mjs';
|
|
import cookie from './cookie.mjs';
|
|
import { debounce } from './util.mjs';
|
|
import { lab_parse, lab_reparse_results, measurement_parse, order_parse } from './reportparser.mjs';
|
|
import { TplFS, EncFS, randpassword as tplfs_randpassword } from './tplfs.mjs';
|
|
|
|
export const localstate = reactive(cookie.get('state') ? JSON.parse(cookie.get('state')) : {});
|
|
window.addEventListener('storage', function(evt) {
|
|
if((evt.storageArea == window.localStorage) && (evt.key == 'state') && (evt.newValue)) Object.assign(localstate, JSON.parse(evt.newValue));
|
|
});
|
|
watch(localstate, function(value) {
|
|
cookie.set('state', value = JSON.stringify(value), 45);
|
|
window.localStorage.setItem('state', value);
|
|
}, { immediate: true, deep: true });
|
|
|
|
function RPCError(type, ...args) {
|
|
this.name = type;
|
|
this.message = args;
|
|
}
|
|
RPCError.prototype = Object.create(Error.prototype);
|
|
|
|
export function logged(fn, name) {
|
|
return async function(...args) {
|
|
var res = await fn(...args);
|
|
console.log(name, ...args, res);
|
|
return res;
|
|
}
|
|
}
|
|
|
|
export function unwrapped(fn) {
|
|
return async function(...args) {
|
|
var res = await fn(...args);
|
|
if(res.error) throw new RPCError(res.error.type, ...res.error.args);
|
|
else return res.result;
|
|
}
|
|
}
|
|
|
|
export function memoized(fn) {
|
|
var cache = {};
|
|
return async function(...args) {
|
|
var key = JSON.stringify(args);
|
|
return cache.hasOwnProperty(key) ? cache[key] : (cache[key] = await fn(...args));
|
|
}
|
|
}
|
|
|
|
export function converted_boolean(fn, columns=null) {
|
|
return async function(...args) {
|
|
return await fn(...args) == '1';
|
|
}
|
|
}
|
|
|
|
export function parsed_nullarray(fn) {
|
|
return async function(...args) {
|
|
var res = await fn(...args);
|
|
return res !== '' ? res : [];
|
|
}
|
|
}
|
|
|
|
export function parsed_text(fn) {
|
|
return async function(...args) {
|
|
var res = await fn(...args);
|
|
return res !== '' ? res.join('\r\n') : res;
|
|
}
|
|
}
|
|
|
|
export function caretseparated(fn, columns=null) {
|
|
return async function(...args) {
|
|
if(columns) return (await fn(...args)).map(function(row) {
|
|
row = row.split('^');
|
|
for(var i = columns.length - 1; i >= 0; --i) if(columns[i]) row[columns[i]] = row[i];
|
|
return row;
|
|
});
|
|
else return (await fn(...args)).map(function(row) { return row.split('^'); });
|
|
}
|
|
}
|
|
|
|
export function caretseparated1(fn, columns=null) {
|
|
return async function(...args) {
|
|
var res = (await fn(...args)).split('^');
|
|
if(columns) for(var i = columns.length - 1; i >= 0; --i) if(columns[i]) res[columns[i]] = res[i];
|
|
return res;
|
|
}
|
|
}
|
|
|
|
export function sliced(fn, start, end) {
|
|
return async function(...args) {
|
|
return (await fn(...args)).slice(start, end);
|
|
}
|
|
}
|
|
|
|
export function labreportparsed(fn) {
|
|
return async function(...args) {
|
|
return lab_parse(await fn(...args));
|
|
}
|
|
}
|
|
|
|
export function orderparsed(fn) {
|
|
return async function(...args) {
|
|
return order_parse(await fn(...args));
|
|
}
|
|
}
|
|
|
|
export function tabulated(fn, mapping) {
|
|
return async function(...args) {
|
|
var res = (await fn(...args)).map(function(row) { return row.slice(); }), nrow = res.length;
|
|
for(var i = 0; i < nrow; ++i) {
|
|
var row = res[i], ncol = row.length;
|
|
for(var j = 0; j < ncol; ++j) if(mapping.hasOwnProperty(j)) row[mapping[j]] = row[j];
|
|
res.push()
|
|
}
|
|
return res;
|
|
}
|
|
}
|
|
|
|
export function Client(cid, secret) {
|
|
var heartbeat = null;
|
|
|
|
this.secret = secret;
|
|
this.cid = cid;
|
|
this.connected = reactive({ value: true });
|
|
|
|
this.close = function() {
|
|
console.log('CLOSE', cid);
|
|
if(heartbeat) window.clearInterval(heartbeat);
|
|
this.connected.value = false;
|
|
return vista.close(cid);
|
|
};
|
|
this.call = async function(method, ...params) {
|
|
var res = await vista.call(cid, method, ...params);
|
|
if((res.error) && (res.error.type == 'ConnectionResetError')) this.close();
|
|
return res;
|
|
};
|
|
this.callctx = async function(context, method, ...params) {
|
|
var res = vista.callctx(cid, context, method, ...params);
|
|
if((res.error) && (res.error.type == 'ConnectionResetError')) this.close();
|
|
return res;
|
|
};
|
|
this.heartbeat = async function(interval=null) {
|
|
if(!interval) interval = 0.45*1000*(await this.XWB_GET_BROKER_INFO())[0];
|
|
if(heartbeat) window.clearInterval(heartbeat);
|
|
this.XWB_IM_HERE();
|
|
return heartbeat = window.setInterval(this.XWB_IM_HERE, interval);
|
|
}
|
|
this.serverinfo = () => vista.serverinfo(cid);
|
|
this.userinfo = () => vista.userinfo(cid);
|
|
this.authenticate = (avcode=null) => vista.authenticate(cid, avcode);
|
|
|
|
if(!localstate.encfs) localstate.encfs = tplfs_randpassword();
|
|
this.tplfs = async () => this._tplfs ? this._tplfs : (this._tplfs = await TplFS.fromUser(this, (await this.userinfo()).result[0]));
|
|
this.encfs = async () => this._encfs ? this._encfs : (this._encfs = await EncFS.fromPassword(await this.tplfs(), localstate.encfs));
|
|
|
|
this.remotestate = reactive({});
|
|
this.setup_remotestate = async () => {
|
|
var fs = await this.encfs(), file;
|
|
try {
|
|
file = await fs.open('state');
|
|
Object.assign(this.remotestate, JSON.parse(await file.cat(null)));
|
|
} catch(ex) {
|
|
console.error(ex);
|
|
file = await fs.create('state', JSON.stringify(this.remotestate));
|
|
}
|
|
watch(this.remotestate, debounce(function(value) {
|
|
file.update(null, JSON.stringify(value));
|
|
}, 1000), { deep: true });
|
|
if((!this.remotestate.resources) && (localstate.resources)) this.remotestate.resources = localstate.resources;
|
|
if((!this.remotestate.practitioner) && (localstate.practitioner)) this.remotestate.practitioner = localstate.practitioner;
|
|
if(localstate.resources) delete localstate.resources;
|
|
if(localstate.practitioner) delete localstate.practitioner;
|
|
};
|
|
|
|
this.XWB_IM_HERE = unwrapped(logged(() => this.call('XWB_IM_HERE'), 'XWB_IM_HERE'));
|
|
|
|
this.XUS_INTRO_MSG = memoized(unwrapped(logged(() => this.callctx(['XUCOMMAND'], 'XUS_INTRO_MSG'), 'XUS_INTRO_MSG')));
|
|
this.XWB_GET_BROKER_INFO = memoized(unwrapped(logged(() => this.callctx(['XUCOMMAND'], 'XWB_GET_BROKER_INFO'), 'XWB_GET_BROKER_INFO')));
|
|
this.XUS_GET_USER_INFO = memoized(unwrapped(logged(() => this.call('XUS_GET_USER_INFO'), 'XUS_GET_USER_INFO')));
|
|
|
|
this.SDEC_RESOURCE = memoized(unwrapped(logged(() => this.callctx(['SDECRPC'], 'SDEC_RESOURCE'), 'SDEC_RESOURCE')));
|
|
this.SDEC_CLINLET = memoized(unwrapped(logged((...args) => this.callctx(['SDECRPC'], 'SDEC_CLINLET', ...args), 'SDEC_CLINLET')));
|
|
|
|
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_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_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.ORQQVI_VITALS = memoized(caretseparated(unwrapped(logged((...args) => this.callctx(['OR CPRS GUI CHART'], 'ORQQVI_VITALS', ...args), 'ORQQVI_VITALS')), ['measurement_ien', 'type', 'value', 'datetime', 'value_american', 'value_metric']));
|
|
this.ORQQVI_VITALS_FOR_DATE_RANGE = memoized(caretseparated(unwrapped(logged((...args) => this.callctx(['OR CPRS GUI CHART'], 'ORQQVI_VITALS_FOR_DATE_RANGE', ...args), 'ORQQVI_VITALS_FOR_DATE_RANGE')), ['measurement_ien', 'type', 'value', 'datetime']));
|
|
|
|
this.GMV_EXTRACT_REC = memoized(async (dfn, oredt, orsdt) => measurement_parse(await unwrapped(logged((...args0) => this.callctx(['OR CPRS GUI CHART'], 'GMV_EXTRACT_REC', args0.join('^')), 'GMV_EXTRACT_REC'))(dfn, oredt, '', orsdt)));
|
|
|
|
this.ORWLRR_INTERIM = memoized(labreportparsed(unwrapped(logged((...args) => this.callctx(['OR CPRS GUI CHART'], 'ORWLRR_INTERIM', ...args), 'ORWLRR_INTERIM'))));
|
|
this.ORWLRR_INTERIM_RESULTS = memoized(async (...args) => lab_reparse_results(await this.ORWLRR_INTERIM(...args)));
|
|
|
|
this.ORWORDG_ALLTREE = memoized(caretseparated(unwrapped(logged(() => this.callctx(['OR CPRS GUI CHART'], 'ORWORDG_ALLTREE'), 'ORWORDG_ALLTREE')), ['ien', 'name', 'parent', 'has_children']));
|
|
this.ORWORDG_REVSTS = memoized(caretseparated(unwrapped(logged(() => this.callctx(['OR CPRS GUI CHART'], 'ORWORDG_REVSTS'), 'ORWORDG_REVSTS')), ['ien', 'name', 'parent', 'has_children']));
|
|
this.ORWORR_AGET = memoized(caretseparated(sliced(unwrapped(logged((...args) => this.callctx(['OR CPRS GUI CHART'], 'ORWORR_AGET', ...args), 'ORWORR_AGET')), 1), ['ifn', 'dgrp', 'time']));
|
|
this.ORWORR_GET4LST = memoized(orderparsed(unwrapped(logged((...args) => this.callctx(['OR CPRS GUI CHART'], 'ORWORR_GET4LST', ...args), 'ORWORR_GET4LST'))));
|
|
|
|
this.TIU_TEMPLATE_GETROOTS = caretseparated(unwrapped(logged((...args) => this.callctx(['OR CPRS GUI CHART'], 'TIU_TEMPLATE_GETROOTS', ...args), 'TIU_TEMPLATE_GETROOTS')), ['IEN', 'type', 'status', 'name', 'exclude_from_group_boilerplate', 'blank_lines', 'personal_owner', 'has_children_flag', 'dialog', 'display_only', 'first_line', 'one_item_only', 'hide_dialog_items', 'hide_tree_items', 'indent_items', 'reminder_dialog_ien', 'reminder_dialog_name', 'locked', 'com_object_pointer', 'com_object_parameter', 'link_pointer', 'reminder_dialog_patient_specific_value']);
|
|
this.TIU_TEMPLATE_GETPROOT = caretseparated(unwrapped(logged((...args) => this.callctx(['OR CPRS GUI CHART'], 'TIU_TEMPLATE_GETPROOT', ...args), 'TIU_TEMPLATE_GETPROOT')), ['IEN', 'type', 'status', 'name', 'exclude_from_group_boilerplate', 'blank_lines', 'personal_owner', 'has_children_flag', 'dialog', 'display_only', 'first_line', 'one_item_only', 'hide_dialog_items', 'hide_tree_items', 'indent_items', 'reminder_dialog_ien', 'reminder_dialog_name', 'locked', 'com_object_pointer', 'com_object_parameter', 'link_pointer', 'reminder_dialog_patient_specific_value']);
|
|
this.TIU_TEMPLATE_GETBOIL = parsed_text(unwrapped(logged((...args) => this.callctx(['OR CPRS GUI CHART'], 'TIU_TEMPLATE_GETBOIL', ...args), 'TIU_TEMPLATE_GETBOIL')));
|
|
this.TIU_TEMPLATE_GETITEMS = caretseparated(parsed_nullarray(unwrapped(logged((...args) => this.callctx(['OR CPRS GUI CHART'], 'TIU_TEMPLATE_GETITEMS', ...args), 'TIU_TEMPLATE_GETITEMS'))), ['IEN', 'type', 'status', 'name', 'exclude_from_group_boilerplate', 'blank_lines', 'personal_owner', 'has_children_flag', 'dialog', 'display_only', 'first_line', 'one_item_only', 'hide_dialog_items', 'hide_tree_items', 'indent_items', 'reminder_dialog_ien', 'reminder_dialog_name', 'locked', 'com_object_pointer', 'com_object_parameter', 'link_pointer', 'reminder_dialog_patient_specific_value']);
|
|
this.TIU_TEMPLATE_SET_ITEMS = unwrapped(logged((...args) => this.callctx(['OR CPRS GUI CHART'], 'TIU_TEMPLATE_SET_ITEMS', ...args), 'TIU_TEMPLATE_SET_ITEMS'));
|
|
this.TIU_TEMPLATE_CREATE_MODIFY = unwrapped(logged((...args) => this.callctx(['OR CPRS GUI CHART'], 'TIU_TEMPLATE_CREATE/MODIFY', ...args), 'TIU_TEMPLATE_CREATE/MODIFY'));
|
|
this.TIU_TEMPLATE_DELETE = unwrapped(logged((...args) => this.callctx(['OR CPRS GUI CHART'], 'TIU_TEMPLATE_DELETE', ...args), 'TIU_TEMPLATE_DELETE'));
|
|
this.TIU_TEMPLATE_LOCK = unwrapped(logged((...args) => this.callctx(['OR CPRS GUI CHART'], 'TIU_TEMPLATE_LOCK', ...args), 'TIU_TEMPLATE_LOCK'));
|
|
this.TIU_TEMPLATE_UNLOCK = unwrapped(logged((...args) => this.callctx(['OR CPRS GUI CHART'], 'TIU_TEMPLATE_UNLOCK', ...args), 'TIU_TEMPLATE_UNLOCK'));
|
|
|
|
return this;
|
|
}
|
|
Client._registry = {};
|
|
|
|
Client.fromID = function(cid, secret) {
|
|
if(Client._registry[cid]) return Client._registry[cid];
|
|
return Client._registry[cid] = new Client(cid, secret);
|
|
};
|
|
|
|
Client.fromScratch = async function(secret, host='vista.northport.med.va.gov', port=19209) {
|
|
var data = await vista.connect(secret, host, port);
|
|
if(data.result) return Client.fromID(data.result, secret);
|
|
};
|
|
|
|
Client.fromCookie = async function(secret, defaulthost='vista.northport.med.va.gov:19209') {
|
|
if(!secret) secret = localstate.secret;
|
|
if(secret) {
|
|
var host = localstate.host;
|
|
host = (host || defaulthost).split(':');
|
|
if(secret != localstate.secret) {
|
|
console.log('Using new secret', secret);
|
|
var client = await Client.fromScratch(secret, host[0], host[1]);
|
|
if(client) {
|
|
localstate.host = host.join(':');
|
|
localstate.secret = secret;
|
|
localstate.cid = client.cid;
|
|
console.log('Established connection', client.cid);
|
|
return client;
|
|
} else {
|
|
delete localstate.secret;
|
|
delete localstate.cid;
|
|
console.log('Failed to connect');
|
|
return null;
|
|
}
|
|
} else if(!localstate.cid) {
|
|
console.log('Using saved secret', secret);
|
|
var client = await Client.fromScratch(secret, host[0], host[1]);
|
|
if(client) {
|
|
localstate.host = host.join(':');
|
|
localstate.secret = secret;
|
|
localstate.cid = client.cid;
|
|
console.log('Established connection', client.cid);
|
|
return client;
|
|
} else {
|
|
delete localstate.secret;
|
|
delete localstate.cid;
|
|
console.log('Failed connection');
|
|
return null;
|
|
}
|
|
} else {
|
|
console.log('Using saved secret and connection', secret);
|
|
var cid = localstate.cid;
|
|
var client = Client.fromID(cid, secret);
|
|
if((await vista.call(cid, 'XWB_IM_HERE')).result == '1') {
|
|
var server = await client.serverinfo();
|
|
if((host[0] == server.result.host) && (host[1] == server.result.port)) {
|
|
localstate.host = host.join(':');
|
|
return client;
|
|
} else console.log('Rejecting previous connection to different server', server);
|
|
}
|
|
delete localstate.cid;
|
|
return await Client.fromCookie(secret, host.join(':'));
|
|
}
|
|
}
|
|
};
|
|
|
|
export default window.vistax = {
|
|
localstate, RPCError, Client, connect: Client.fromCookie
|
|
};
|