diff --git a/htdocs/App.vue b/htdocs/App.vue index fb6d667..a774379 100644 --- a/htdocs/App.vue +++ b/htdocs/App.vue @@ -49,6 +49,7 @@ import RoutePatientReports from './RoutePatientReports.vue'; import RoutePatientDocuments from './RoutePatientDocuments.vue'; import RoutePatientConsults from './RoutePatientConsults.vue'; + import RoutePatientImaging from './RoutePatientImaging.vue'; import RoutePlanner from './RoutePlanner.vue'; import RouteRecall from './RouteRecall.vue'; import RouteInbox from './RouteInbox.vue'; @@ -100,6 +101,8 @@ { path: 'document/:tiu_da', component: RoutePatientDocuments }, { path: 'consult', component: RoutePatientConsults }, { path: 'consult/:ien', component: RoutePatientConsults }, + { path: 'imaging', component: RoutePatientImaging }, + { path: 'imaging/:ien', component: RoutePatientImaging }, ] }, { path: '/planner', component: RoutePlanner }, { path: '/recall', component: RouteRecall }, diff --git a/htdocs/RoutePatient.vue b/htdocs/RoutePatient.vue index 3078d15..7b011a5 100644 --- a/htdocs/RoutePatient.vue +++ b/htdocs/RoutePatient.vue @@ -61,6 +61,7 @@ { name: 'Reports', href: '/patient/' + this.patient_dfn + '/reports' }, { name: 'Documents', href: '/patient/' + this.patient_dfn + '/document' }, { name: 'Consults', href: '/patient/' + this.patient_dfn + '/consult' }, + { name: 'Imaging', href: '/patient/' + this.patient_dfn + '/imaging' }, ] } : null; } diff --git a/htdocs/RoutePatientImaging.vue b/htdocs/RoutePatientImaging.vue new file mode 100644 index 0000000..a8b1acb --- /dev/null +++ b/htdocs/RoutePatientImaging.vue @@ -0,0 +1,228 @@ + + + + + diff --git a/htdocs/vistax.mjs b/htdocs/vistax.mjs index ccfe89a..4b24690 100644 --- a/htdocs/vistax.mjs +++ b/htdocs/vistax.mjs @@ -50,6 +50,37 @@ export const d_parse_array = data => data !== '' ? data : []; export const d_parse_authinfo = data => data ? { duz: data[0] != '0' ? data[0] : null, device_lock: data[1] != '0', change_verify: data[2] != '0', message: data[3], reserved: data[4], greeting_lines: data[5], greeting: data.slice(6), success: (data[0] != '0') && (data[2] == '0') } : { success: false } +export const d_parse_imagelist = data => { + var descriptor = d_split1(data[0], '^', 'success', 'filter', 'more'), res; + if(descriptor.success == '1') { + var headers = d_split1(data[1], '^'); + res = data.slice(2).map(function(row) { + row = row.split('|'); + var values = headers.reduce((acc, val, idx) => (acc[val] = acc[idx], acc), row[0].split('^')) + values.Info = d_split1(row[1], '^', 'IEN', 'Image Path', 'Abstract Path', 'Short Desc', 'Procedure Time', 'Object Type', 'Procedure', 'Display Date', 'Pointer', 'Abs Type', 'Availability', 'DICOM Series', 'DICOM Image', 'Count', 'Site IEN', 'Site', 'Error', 'BIGPath', 'Patient DFN', 'Patient Name', 'Image Class', 'Cap Dt', 'Document Date', 'Group IEN', 'Group Ch1', 'RPC Server', 'RPC Port', 'Controlled Image', 'Viewable Status', 'Status', 'Image Annotated', 'Image TIU Note Completed', 'Annotation Operation Status', 'Annotation Operation Status Description', 'Package'); + values.Info['Group Ch1'] = values.Info['Group Ch1'] ? d_split1(':', 'IEN', 'Type') : null + return values; + }); + res.descriptor = descriptor; + } else { + res = data.slice(1); + res.descriptor = descriptor; + } + return res; +}; + +export const d_parse_imageinfo = data => { + var res = d_split1(data[0], '^', 'Code', 'IEN', 'Image Path', 'Abstract Path', 'Short Desc', 'Procedure Time', 'Object Type', 'Procedure', 'Display Date', 'Pointer', 'Abs Type', 'Availability', 'DICOM Series', 'DICOM Image', 'Count', 'Site IEN', 'Site', 'Error', 'BIGPath', 'Patient DFN', 'Patient Name', 'Image Class', 'Cap Dt', 'Document Date', 'Group IEN', 'Group Ch1', 'RPC Server', 'RPC Port', 'Controlled Image', 'Viewable Status', 'Status', 'Image Annotated', 'Image TIU Note Completed', 'Annotation Operation Status', 'Annotation Operation Status Description', 'Package'); + res.Patient = d_split1(data[1], '^', 'IEN', 'name'); + return res; +}; + +export const d_parse_imagegroup = data => { + var res = d_split(data.slice(1), '^', 'B2', 'IEN', 'Image Path', 'Abstract Path', 'Short Desc', 'Procedure Time', 'Object Type', 'Procedure', 'Display Date', 'Pointer', 'Abs Type', 'Availability', 'DICOM Series', 'DICOM Image', 'Count', 'Site IEN', 'Site', 'Error', 'BIGPath', 'Patient DFN', 'Patient Name', 'Image Class', 'Cap Dt', 'Document Date', 'Group IEN', 'Group Ch1', 'RPC Server', 'RPC Port', 'Controlled Image', 'Viewable Status', 'Status', 'Image Annotated', 'Image TIU Note Completed', 'Annotation Operation Status', 'Annotation Operation Status Description', 'Package'); + res.Result = d_split1(data[0], '^', 'code', 'count'); + return res; +}; + export const d_parse_orderdialogs = (data, columns=['IEN', 'windowFormId', 'displayGroupId', 'type', 'displayText']) => data.map(function(row) { row = row.split('^'); row = [...row[0].split(';'), row[1]]; @@ -344,6 +375,11 @@ export function Client(cid, secret) { this.ORWPCE_NOTEVSTR = aflow((...args) => this.call({ method: 'ORWPCE_NOTEVSTR', context: ['OR CPRS GUI CHART'], ttl: 86400, stale: true }, ...args), d_log, d_unwrap); this.ORWPCE_DELETE = aflow((...args) => this.call({ method: 'ORWPCE_DELETE', context: ['OR CPRS GUI CHART'], ttl: 0, stale: false }, ...args), d_log, d_unwrap); + this.MAG4_IMAGE_LIST = memoized(aflow((...args) => this.call({ method: 'MAG4_IMAGE_LIST', context: ['MAG WINDOWS'], ttl: 60, stale: false }, ...args), d_log, d_unwrap, d_parse_array, d_parse_imagelist)); + this.MAG4_GET_IMAGE_INFO = memoized(aflow((...args) => this.call({ method: 'MAG4_GET_IMAGE_INFO', context: ['MAG WINDOWS'], ttl: 60, stale: false }, ...args), d_log, d_unwrap, d_parse_text)); + this.MAGG_IMAGE_INFO = memoized(aflow((...args) => this.call({ method: 'MAGG_IMAGE_INFO', context: ['MAG WINDOWS'], ttl: 60, stale: false }, ...args), d_log, d_unwrap, d_parse_array, d_parse_imageinfo)); + this.MAGG_GROUP_IMAGES = memoized(aflow((...args) => this.call({ method: 'MAGG_GROUP_IMAGES', context: ['MAG WINDOWS'], ttl: 60, stale: false }, ...args), d_log, d_unwrap, d_parse_array, d_parse_imagegroup)); + this.ORWCV_VST = memoized(aflow((...args) => this.callctx(['OR CPRS GUI CHART'], 'ORWCV_VST', ...args), d_log, d_unwrap, d_parse_array, f_split('^', 'apptinfo', 'datetime', 'location', 'status'))); this.ORWU_NEWPERS = memoized(aflow((...args) => this.call({ method: 'ORWU_NEWPERS', context: ['OR CPRS GUI CHART'], ttl: 86400, stale: true }, ...args), d_log, d_unwrap, f_split('^', 'DUZ', 'name', 'description'))); diff --git a/main.py b/main.py index 9384b97..e5f374a 100644 --- a/main.py +++ b/main.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 +import os import json import secrets import string @@ -160,6 +161,29 @@ def application(): logger.exception(request.url) return jsonify_error(ex, id=request.json.get('id')) + @app.get('/v1/vista//imaging/') + def cb_imaging(cid, path): + if 'view' in request.args: + adapter = 'view.' + path.rsplit('.', 1)[1].lower() + '.html' + if os.path.isfile('./htdocs/adapter/' + adapter): + return send_from_directory('./htdocs/adapter', adapter) + client = clients[cid] + frag = path.replace('\\', '/').strip('/').split('/') + winshare = '\\\\' + '\\'.join(frag[:2]).upper() + for item in client.MAG_GET_NETLOC('ALL', context=['MAG WINDOWS']): + if item.split('^')[1] == winshare: + break + else: + raise PermissionError(path) + try: + open('//' + '/'.join(frag)).close() + except PermissionError as ex: + credentials = client.MAGGUSER2(context=['MAG WINDOWS'])[2].split('^') + import subprocess, XWBHash + subprocess.run(['net', 'use', winshare, '/d']) + subprocess.run(['net', 'use', winshare, '/user:' + credentials[0], XWBHash.decrypt(credentials[1])]) + return send_from_directory('//' + '/'.join(frag[:2]), '/'.join(frag[2:]), as_attachment=('dl' in request.args)) + @app.get('/') def cb_static(path): return send_from_directory('./htdocs', path if '.' in path.rsplit('/', 1)[-1] else 'index.html')