Compare commits
13 Commits
86c18927e8
...
main
Author | SHA1 | Date | |
---|---|---|---|
46ef48b0ae | |||
1d276e7bec | |||
18d6b6f19c | |||
5d2a8f464f | |||
fcd8447658 | |||
e648988b53 | |||
4708e5e0eb | |||
badb26c9fc | |||
5db3091470 | |||
ace1407715 | |||
32de0bdd56 | |||
fa25bf3c5c | |||
053053825c |
201
XWBSSOi.py
Normal file
201
XWBSSOi.py
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import ctypes
|
||||||
|
import ctypes.wintypes
|
||||||
|
import winreg
|
||||||
|
import contextlib
|
||||||
|
from typing import Any, Optional, Generator
|
||||||
|
|
||||||
|
DEFAULT_USER_AGENT = 'Borland SOAP 1.2'
|
||||||
|
DEFAULT_ISSUER = 'https://ssoi.sts.va.gov/Issuer/smtoken/SAML2'
|
||||||
|
|
||||||
|
HCERTSTORE = ctypes.c_void_p
|
||||||
|
PCERT_INFO = ctypes.c_void_p
|
||||||
|
HCRYPTPROV_LEGACY = ctypes.c_void_p
|
||||||
|
|
||||||
|
CERT_STORE_PROV_MEMORY = b'Memory'
|
||||||
|
X509_ASN_ENCODING = 0x00000001
|
||||||
|
PKCS_7_ASN_ENCODING = 0x00010000
|
||||||
|
CERT_COMPARE_ANY = 0
|
||||||
|
CERT_COMPARE_SHIFT = 16
|
||||||
|
CERT_FIND_ANY = CERT_COMPARE_ANY<<CERT_COMPARE_SHIFT
|
||||||
|
CERT_NAME_FRIENDLY_DISPLAY_TYPE = 5
|
||||||
|
CERT_DIGITAL_SIGNATURE_KEY_USAGE = 0x80
|
||||||
|
CERT_STORE_ADD_ALWAYS = 4
|
||||||
|
CERT_HASH_PROP_ID = CERT_SHA1_HASH_PROP_ID = 3
|
||||||
|
|
||||||
|
class CERT_CONTEXT(ctypes.Structure):
|
||||||
|
_fields_ = [
|
||||||
|
('dwCertEncodingType', ctypes.wintypes.DWORD),
|
||||||
|
('pbCertEncoded', ctypes.POINTER(ctypes.wintypes.BYTE)),
|
||||||
|
('cbCertEncoded', ctypes.wintypes.DWORD),
|
||||||
|
('pCertInfo', PCERT_INFO),
|
||||||
|
('hCertStore', HCERTSTORE),
|
||||||
|
]
|
||||||
|
PCCERT_CONTEXT = ctypes.POINTER(CERT_CONTEXT)
|
||||||
|
|
||||||
|
crypt32 = ctypes.WinDLL('crypt32')
|
||||||
|
|
||||||
|
CertOpenStore = crypt32.CertOpenStore
|
||||||
|
CertOpenStore.restype = HCERTSTORE
|
||||||
|
CertOpenStore.argtypes = (ctypes.wintypes.LPCSTR, ctypes.wintypes.DWORD, HCRYPTPROV_LEGACY, ctypes.wintypes.DWORD, ctypes.c_void_p)
|
||||||
|
|
||||||
|
CertOpenSystemStoreW = crypt32.CertOpenSystemStoreW
|
||||||
|
CertOpenSystemStoreW.restype = HCERTSTORE
|
||||||
|
CertOpenSystemStoreW.argtypes = (HCRYPTPROV_LEGACY, ctypes.wintypes.LPCWSTR)
|
||||||
|
|
||||||
|
CertCloseStore = crypt32.CertCloseStore
|
||||||
|
CertCloseStore.restype = ctypes.wintypes.BOOL
|
||||||
|
CertCloseStore.argtypes = (HCERTSTORE, ctypes.wintypes.DWORD)
|
||||||
|
|
||||||
|
CertEnumCertificatesInStore = crypt32.CertEnumCertificatesInStore
|
||||||
|
CertEnumCertificatesInStore.restype = PCCERT_CONTEXT
|
||||||
|
CertEnumCertificatesInStore.argtypes = (HCERTSTORE, PCCERT_CONTEXT)
|
||||||
|
|
||||||
|
CertFindCertificateInStore = crypt32.CertFindCertificateInStore
|
||||||
|
CertFindCertificateInStore.restype = PCCERT_CONTEXT
|
||||||
|
CertFindCertificateInStore.argtypes = (HCERTSTORE, ctypes.wintypes.DWORD, ctypes.wintypes.DWORD, ctypes.wintypes.DWORD, ctypes.c_void_p, PCCERT_CONTEXT)
|
||||||
|
|
||||||
|
CertAddCertificateContextToStore = crypt32.CertAddCertificateContextToStore
|
||||||
|
CertAddCertificateContextToStore.restype = ctypes.wintypes.BOOL
|
||||||
|
CertAddCertificateContextToStore.argtypes = (HCERTSTORE, PCCERT_CONTEXT, ctypes.wintypes.DWORD, ctypes.POINTER(PCCERT_CONTEXT))
|
||||||
|
|
||||||
|
CertGetNameStringW = crypt32.CertGetNameStringW
|
||||||
|
CertGetNameStringW.restype = ctypes.wintypes.DWORD
|
||||||
|
CertGetNameStringW.argtypes = (PCCERT_CONTEXT, ctypes.wintypes.DWORD, ctypes.wintypes.DWORD, ctypes.c_void_p, ctypes.wintypes.LPWSTR, ctypes.wintypes.DWORD)
|
||||||
|
|
||||||
|
CertGetIntendedKeyUsage = crypt32.CertGetIntendedKeyUsage
|
||||||
|
CertGetIntendedKeyUsage.restype = ctypes.wintypes.BOOL
|
||||||
|
CertGetIntendedKeyUsage.argtypes = (ctypes.wintypes.DWORD, PCERT_INFO, ctypes.POINTER(ctypes.wintypes.BYTE), ctypes.wintypes.DWORD)
|
||||||
|
|
||||||
|
CertGetCertificateContextProperty = crypt32.CertGetCertificateContextProperty
|
||||||
|
CertGetCertificateContextProperty.restype = ctypes.wintypes.BOOL
|
||||||
|
CertGetCertificateContextProperty.argtypes = (PCCERT_CONTEXT, ctypes.wintypes.DWORD, ctypes.c_void_p, ctypes.POINTER(ctypes.wintypes.DWORD))
|
||||||
|
|
||||||
|
CertVerifyTimeValidity = crypt32.CertVerifyTimeValidity
|
||||||
|
CertVerifyTimeValidity.restype = ctypes.wintypes.LONG
|
||||||
|
CertVerifyTimeValidity.argtypes = (ctypes.wintypes.LPFILETIME, PCERT_INFO)
|
||||||
|
|
||||||
|
cryptui = ctypes.WinDLL('cryptui')
|
||||||
|
|
||||||
|
CryptUIDlgSelectCertificateFromStore = cryptui.CryptUIDlgSelectCertificateFromStore
|
||||||
|
CryptUIDlgSelectCertificateFromStore.restype = PCCERT_CONTEXT
|
||||||
|
CryptUIDlgSelectCertificateFromStore.argtypes = (HCERTSTORE, ctypes.wintypes.HWND, ctypes.wintypes.LPCWSTR, ctypes.wintypes.LPCWSTR, ctypes.wintypes.DWORD, ctypes.wintypes.DWORD, ctypes.c_void_p)
|
||||||
|
|
||||||
|
GetConsoleWindow = ctypes.windll.kernel32.GetConsoleWindow
|
||||||
|
GetConsoleWindow.restype = ctypes.wintypes.HWND
|
||||||
|
|
||||||
|
@contextlib.contextmanager
|
||||||
|
def ManagedCertOpenStore(lpszStoreProvider: ctypes.wintypes.LPCSTR, dwEncodingType: ctypes.wintypes.DWORD, hCryptProv: HCRYPTPROV_LEGACY, dwFlags: ctypes.wintypes.DWORD, pvPara: ctypes.c_void_p) -> Generator[HCERTSTORE, None, None]:
|
||||||
|
res = CertOpenStore(lpszStoreProvider, dwEncodingType, hCryptProv, dwFlags, pvPara)
|
||||||
|
try:
|
||||||
|
yield res
|
||||||
|
finally:
|
||||||
|
CertCloseStore(res, 0)
|
||||||
|
|
||||||
|
@contextlib.contextmanager
|
||||||
|
def ManagedCertOpenSystemStore(hProv: HCRYPTPROV_LEGACY, szSubsystemProtocol: ctypes.wintypes.LPCWSTR) -> Generator[HCERTSTORE, None, None]:
|
||||||
|
res = CertOpenSystemStoreW(hProv, szSubsystemProtocol)
|
||||||
|
try:
|
||||||
|
yield res
|
||||||
|
finally:
|
||||||
|
CertCloseStore(res, 0)
|
||||||
|
|
||||||
|
def get_vista_certificate(show_cert_dialog: bool=True, hwnd: Optional[int]=0) -> PCCERT_CONTEXT:
|
||||||
|
with ManagedCertOpenSystemStore(0, 'MY') as store_system, ManagedCertOpenStore(CERT_STORE_PROV_MEMORY, 0, None, 0, None) as store_memory:
|
||||||
|
cert_selection = cert_iter = None
|
||||||
|
while cert_iter := CertEnumCertificatesInStore(store_system, cert_iter):
|
||||||
|
if cert_valid := CertFindCertificateInStore(store_system, X509_ASN_ENCODING|PKCS_7_ASN_ENCODING, 0, CERT_FIND_ANY, None, None):
|
||||||
|
name_bufsz = CertGetNameStringW(cert_iter, CERT_NAME_FRIENDLY_DISPLAY_TYPE, 0, None, None, 0)
|
||||||
|
buf = ctypes.create_unicode_buffer(name_bufsz)
|
||||||
|
CertGetNameStringW(cert_iter, CERT_NAME_FRIENDLY_DISPLAY_TYPE, 0, None, buf, name_bufsz)
|
||||||
|
name_string = buf.value
|
||||||
|
key_usage_bits = ctypes.wintypes.BYTE()
|
||||||
|
CertGetIntendedKeyUsage(X509_ASN_ENCODING|PKCS_7_ASN_ENCODING, cert_iter.contents.pCertInfo, ctypes.byref(key_usage_bits), ctypes.sizeof(key_usage_bits))
|
||||||
|
valid_date = CertVerifyTimeValidity(None, cert_iter.contents.pCertInfo)
|
||||||
|
if ((key_usage_bits.value&CERT_DIGITAL_SIGNATURE_KEY_USAGE) == CERT_DIGITAL_SIGNATURE_KEY_USAGE) and (valid_date == 0) and ('Card Authentication' not in name_string) and ('0,' not in name_string) and ('Signature' not in name_string):
|
||||||
|
CertAddCertificateContextToStore(store_memory, cert_iter, CERT_STORE_ADD_ALWAYS, ctypes.byref(cert_valid))
|
||||||
|
cert_selection = cert_valid
|
||||||
|
return CryptUIDlgSelectCertificateFromStore(store_memory, hwnd if hwnd is not None else GetConsoleWindow(), 'VistA Logon - Certificate Selection', 'Select a certificate for VistA authentication', 0, 0, None) if show_cert_dialog else cert_selection
|
||||||
|
|
||||||
|
def get_certificate_thumbprint(certificate: PCCERT_CONTEXT) -> str:
|
||||||
|
bufsz = ctypes.wintypes.DWORD()
|
||||||
|
CertGetCertificateContextProperty(certificate, CERT_HASH_PROP_ID, None, ctypes.byref(bufsz))
|
||||||
|
buffer = ctypes.create_string_buffer(bufsz.value)
|
||||||
|
CertGetCertificateContextProperty(certificate, CERT_HASH_PROP_ID, buffer, ctypes.byref(bufsz))
|
||||||
|
return buffer.value
|
||||||
|
|
||||||
|
def get_certificate_friendly_display_name(certificate: PCCERT_CONTEXT) -> str:
|
||||||
|
name_bufsz = CertGetNameStringW(certificate, CERT_NAME_FRIENDLY_DISPLAY_TYPE, 0, None, None, 0)
|
||||||
|
buffer = ctypes.create_unicode_buffer(name_bufsz)
|
||||||
|
CertGetNameStringW(certificate, CERT_NAME_FRIENDLY_DISPLAY_TYPE, 0, None, buffer, name_bufsz)
|
||||||
|
return buffer.value
|
||||||
|
|
||||||
|
# WS-Trust STS endpoints from VDL documentation
|
||||||
|
# https://www.va.gov/vdl/documents/Infrastructure/KAAJEE/kaajee_ssowap_8_791_depg_r.pdf
|
||||||
|
# https://www.va.gov/vdl/documents/Financial_Admin/Bed_Management_Solution_(BMS)/bms_4_0_tm.pdf
|
||||||
|
# https://www.va.gov/vdl/documents/VistA_GUI_Hybrids/National_Utilization_Management_Integration_Archive/numi_server_setup_guide_v15_9.pdf
|
||||||
|
get_registry_iam = lambda: get_registry_value(winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Vista\\Common\\IAM', default='https://services.eauth.va.gov:9301/STS/RequestSecurityToken')
|
||||||
|
get_registry_iam_ad = lambda: get_registry_value(winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Vista\\Common\\IAM_AD', default='https://services.eauth.va.gov:9201/STS/RequestSecurityToken')
|
||||||
|
get_registry_rioserver = lambda: get_registry_value(winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Vista\\Common\\RIOSERVER', default='SecurityTokenService')
|
||||||
|
get_registry_rioport = lambda: get_registry_value(winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Vista\\Common\\RIOPORT', default='RequestSecurityToken')
|
||||||
|
def get_registry_value(hkey: int, subkey: str, value: Optional[str]=None, default: Any=None) -> Any:
|
||||||
|
try:
|
||||||
|
with winreg.OpenKey(hkey, subkey) as key:
|
||||||
|
return winreg.QueryValueEx(key, value)[0]
|
||||||
|
except FileNotFoundError:
|
||||||
|
return default
|
||||||
|
|
||||||
|
def get_iam_request(application: str, issuer: str) -> str:
|
||||||
|
return f'''<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns="http://docs.oasis-open.org/ws-sx/ws-trust/200512">
|
||||||
|
<soapenv:Header/>
|
||||||
|
<soapenv:Body>
|
||||||
|
<ns:RequestSecurityToken>
|
||||||
|
<ns:Base>
|
||||||
|
<wss:TLS xmlns:wss="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"/>
|
||||||
|
</ns:Base>
|
||||||
|
<wsp:AppliesTo xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
|
||||||
|
<wsa:EndpointReference xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing">
|
||||||
|
<wsa:Address>{application}</wsa:Address>
|
||||||
|
</wsa:EndpointReference>
|
||||||
|
</wsp:AppliesTo>
|
||||||
|
<ns:Issuer>
|
||||||
|
<wsa:Address xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing">{issuer}</wsa:Address>
|
||||||
|
</ns:Issuer>
|
||||||
|
<ns:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Validate</ns:RequestType>
|
||||||
|
</ns:RequestSecurityToken>
|
||||||
|
</soapenv:Body>
|
||||||
|
</soapenv:Envelope>'''
|
||||||
|
|
||||||
|
def get_local_computer_name() -> str:
|
||||||
|
import socket
|
||||||
|
return socket.getfqdn()
|
||||||
|
|
||||||
|
def get_app_name() -> str:
|
||||||
|
import sys, os
|
||||||
|
return os.path.basename(sys.argv[0])
|
||||||
|
|
||||||
|
def get_sso_token(iam: Optional[str]=None, ua: Optional[str]=None, certificate: Optional[str]=None, issuer: Optional[str]=None, hostname: Optional[str]=None, application: Optional[str]=None) -> str:
|
||||||
|
import sys, subprocess
|
||||||
|
if certificate is None:
|
||||||
|
if choice := get_vista_certificate():
|
||||||
|
certificate = get_certificate_thumbprint(choice).hex()
|
||||||
|
if certificate is not None:
|
||||||
|
res = subprocess.run(['curl', '-fsSL', '-X', 'POST', iam or get_registry_iam(), '--ca-native', '--cert', 'CurrentUser\\MY\\' + certificate, '-A', ua or DEFAULT_USER_AGENT, '-H', 'Content-Type: application/xml', '-H', 'Accept: application/xml', '-d', get_iam_request(f"https://{hostname or get_local_computer_name()}/Delphi_RPC_Broker/{application or get_app_name()}", issuer or DEFAULT_ISSUER)], capture_output=True)
|
||||||
|
print(res.stderr.decode('utf8'), end='', file=sys.stderr)
|
||||||
|
return res.stdout.decode('utf8')
|
||||||
|
|
||||||
|
async def get_sso_token_async(iam: Optional[str]=None, ua: Optional[str]=None, certificate: Optional[str]=None, issuer: Optional[str]=None, hostname: Optional[str]=None, application: Optional[str]=None) -> str:
|
||||||
|
import sys, asyncio
|
||||||
|
if certificate is None:
|
||||||
|
certificate = get_certificate_thumbprint(get_vista_certificate()).hex()
|
||||||
|
res = await (await asyncio.create_subprocess_exec('curl', '-fsSL', '-X', 'POST', iam or get_registry_iam(), '--ca-native', '--cert', 'CurrentUser\\MY\\' + certificate, '-A', ua or DEFAULT_USER_AGENT, '-H', 'Content-Type: application/xml', '-H', 'Accept: application/xml', '-d', get_iam_request(f"https://{hostname or get_local_computer_name()}/Delphi_RPC_Broker/{application or get_app_name()}", issuer or DEFAULT_ISSUER), stdin=None, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE)).communicate()
|
||||||
|
print(res[1].decode('utf8'), end='', file=sys.stderr)
|
||||||
|
return res[0].decode('utf8')
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
try:
|
||||||
|
print(get_sso_token())
|
||||||
|
except OSError:
|
||||||
|
exit(1)
|
17
auth.py
17
auth.py
@ -1,17 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
|
|
||||||
import ctypes
|
|
||||||
|
|
||||||
# Load DLL
|
|
||||||
XUIAMSSOi = ctypes.WinDLL('C:\\Program Files (x86)\\Micro Focus\\Reflection\\XUIAMSSOi.dll')
|
|
||||||
XUIAMSSOi.MySsoTokenVBA.restype = ctypes.c_long
|
|
||||||
XUIAMSSOi.MySsoTokenVBA.argtypes = (ctypes.c_wchar_p, ctypes.c_long)
|
|
||||||
|
|
||||||
# Authenticate against smartcard
|
|
||||||
def XUIAMSSOi_MySsoTokenVBA(bufsize=15000):
|
|
||||||
buf = ctypes.create_unicode_buffer(bufsize)
|
|
||||||
sz = XUIAMSSOi.MySsoTokenVBA(buf, bufsize)
|
|
||||||
if sz <= bufsize:
|
|
||||||
return buf.value.encode('utf-16')[2:].decode('latin-1')
|
|
||||||
else:
|
|
||||||
return XUIAMSSOi_MySsoTokenVBA(sz)
|
|
@ -53,8 +53,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function timeshift_month(date, diff) {
|
function timeshift_month(date, diff) {
|
||||||
var month = date.getMonth() + diff;
|
var month = date.getMonth() + diff, month_mod12 = month%12;
|
||||||
return new Date(date.getFullYear() + Math.floor(month/12), month >= 0 ? (month%12) : (month%12 + 12), date.getDate());
|
return new Date(date.getFullYear() + Math.floor(month/12), month_mod12 >= 0 ? (month_mod12) : (month_mod12 + 12), date.getDate());
|
||||||
}
|
}
|
||||||
|
|
||||||
function datecalc(date, range, direction) {
|
function datecalc(date, range, direction) {
|
||||||
|
@ -205,12 +205,27 @@
|
|||||||
id: 'OR_PN:' + time.getTime() + ':' + x[2],
|
id: 'OR_PN:' + time.getTime() + ':' + x[2],
|
||||||
emblem: 'emblem-notes',
|
emblem: 'emblem-notes',
|
||||||
title: [x[4], x[5], '#' + x[2]],
|
title: [x[4], x[5], '#' + x[2]],
|
||||||
detail: escape_html(x[6])
|
detail: escape_html(collapse_lines(x[6]))
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
loader: reportloader_chunk,
|
loader: reportloader_chunk,
|
||||||
enabled: true
|
enabled: true
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'Discharge',
|
||||||
|
rpt_id: 'OR_DS:DISCHARGE SUMMARY~TIUDCS;ORDV04;57;',
|
||||||
|
map: flow(f_parse_columns, function(x) {
|
||||||
|
var time = new Date(x[3]);
|
||||||
|
return {
|
||||||
|
time,
|
||||||
|
id: 'OR_DS:' + time.getTime() + ':' + x[2],
|
||||||
|
emblem: 'emblem-notes',
|
||||||
|
title: ['DISCHARGE SUMMARY', x[4], x[2], x[3]],
|
||||||
|
detail: escape_html(collapse_lines(x[7]))
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
loader: reportloader_chunk
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'Labs',
|
name: 'Labs',
|
||||||
rpt_id: 'OR_OV_R:LAB OVERVIEW (COLLECTED SPECIMENS)~OV;ORDV02C;32;',
|
rpt_id: 'OR_OV_R:LAB OVERVIEW (COLLECTED SPECIMENS)~OV;ORDV02C;32;',
|
||||||
@ -224,7 +239,7 @@
|
|||||||
detail: escape_html(x[15])
|
detail: escape_html(x[15])
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
loader: reportloader_alpha
|
loader: reportloader_chunk
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Microbiology',
|
name: 'Microbiology',
|
||||||
@ -412,6 +427,10 @@
|
|||||||
return fn;
|
return fn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function collapse_lines(s) {
|
||||||
|
return s.replace(/(\S)[^\S\r\n]+\r?\n([^\s\.,\/#!$%\^&\*;:=\-_`~])/g, '$1 $2').replace(/([\.,!;])\r?\n([^\s\.,\/#!$%\^&\*;:=\-_`~])/g, '$1 $2');
|
||||||
|
}
|
||||||
|
|
||||||
const escape_div = document.createElement('div');
|
const escape_div = document.createElement('div');
|
||||||
function escape_html(s) {
|
function escape_html(s) {
|
||||||
escape_div.textContent = s;
|
escape_div.textContent = s;
|
||||||
|
@ -177,7 +177,7 @@
|
|||||||
if((satisfied) && (updated)) {
|
if((satisfied) && (updated)) {
|
||||||
item = calculation.calc(...calculation.deps.map(x => history[x].value), history[calculation.name] && history[calculation.name].value);
|
item = calculation.calc(...calculation.deps.map(x => history[x].value), history[calculation.name] && history[calculation.name].value);
|
||||||
if((item !== undefined) && (item !== null) && (item === item) && (item != 'NaN')) { // item === item if not NaN
|
if((item !== undefined) && (item !== null) && (item === item) && (item != 'NaN')) { // item === item if not NaN
|
||||||
results.push(history[calculation.name] = update[calculation.name] = Object.assign({ time: group.key, value: item }, calculation));
|
results.push(history[calculation.name] = update[calculation.name] = item = Object.assign({ time: group.key, value: item }, calculation));
|
||||||
if((item.hasOwnProperty('rangeL')) && (item.value < item.rangeL)) item.flag = 'L';
|
if((item.hasOwnProperty('rangeL')) && (item.value < item.rangeL)) item.flag = 'L';
|
||||||
else if((item.hasOwnProperty('rangeH')) && (item.value > item.rangeH)) item.flag = 'H';
|
else if((item.hasOwnProperty('rangeH')) && (item.value > item.rangeH)) item.flag = 'H';
|
||||||
}
|
}
|
||||||
@ -226,7 +226,7 @@
|
|||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
async resultset(value) {
|
async resultset(value) {
|
||||||
this.$nextTick(() => this.$refs.headers ? this.$refs.headers.scrollIntoView({ block: 'nearest', inline: 'end' }) : null);
|
this.$nextTick(() => (this.$refs.headers) && (this.$refs.headers.length > 0) ? this.$refs.headers[this.$refs.headers.length - 1].scrollIntoView({ block: 'nearest', inline: 'end' }) : null);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -22,7 +22,9 @@
|
|||||||
{ name: 'BMI', unit: 'kg/m²', rangeL: 18.5, rangeH: 24.9, range: '18.5 - 24.9', deps: ['Ht', 'Wt'], calc: (Ht, Wt) => (10000*Wt/(Ht*Ht)).toPrecision(3) },
|
{ name: 'BMI', unit: 'kg/m²', rangeL: 18.5, rangeH: 24.9, range: '18.5 - 24.9', deps: ['Ht', 'Wt'], calc: (Ht, Wt) => (10000*Wt/(Ht*Ht)).toPrecision(3) },
|
||||||
{ name: 'BSA', unit: 'm²', deps: ['Ht', 'Wt'], calc: (Ht, Wt) => (0.007184*Math.pow(Ht, 0.725)*Math.pow(Wt, 0.425)).toPrecision(3) },
|
{ name: 'BSA', unit: 'm²', deps: ['Ht', 'Wt'], calc: (Ht, Wt) => (0.007184*Math.pow(Ht, 0.725)*Math.pow(Wt, 0.425)).toPrecision(3) },
|
||||||
{ name: 'CrCl', unit: 'mL/min', deps: ['Age', 'Sex', 'Wt', 'CREATININE'], calc: (Age, Sex, Wt, CREATININE) => (((140 - Age) * Wt)/(72*CREATININE)*(Sex == 'M' ? 1 : 0.85)).toPrecision(4) },
|
{ name: 'CrCl', unit: 'mL/min', deps: ['Age', 'Sex', 'Wt', 'CREATININE'], calc: (Age, Sex, Wt, CREATININE) => (((140 - Age) * Wt)/(72*CREATININE)*(Sex == 'M' ? 1 : 0.85)).toPrecision(4) },
|
||||||
{ name: 'RETICYLOCYTE#', unit: 'K/cmm', rangeL: 50, rangeH: 100, range: '50 - 100', deps: ['RBC', 'RETICULOCYTES'], calc: (RBC, RETICULOCYTES) => (10*RBC*RETICULOCYTES).toPrecision(3) }
|
{ name: 'RETICYLOCYTE#', unit: 'K/cmm', rangeL: 50, rangeH: 100, range: '50 - 100', deps: ['RBC', 'RETICULOCYTES'], calc: (RBC, RETICULOCYTES) => (10*RBC*RETICULOCYTES).toPrecision(3) },
|
||||||
|
{ name: 'CALCIUM CORRECTED', unit: 'mg/dL', rangeL: 8.9, rangeH: 10.3, range: '8.9 - 10.3', deps: ['CALCIUM', 'ALBUMIN'], calc: (CALCIUM, ALBUMIN) => ALBUMIN < 4 ? (+CALCIUM + 0.8*(4 - ALBUMIN)).toPrecision(3) : undefined },
|
||||||
|
{ name: 'IRON SATURATION', unit: '%', rangeL: 15, rangeH: 55, range: '15 - 55', comment: 'IRON/TIBC', deps: ['IRON', 'TIBC'], calc: (IRON, TIBC) => (100*IRON/TIBC).toPrecision(3) },
|
||||||
];
|
];
|
||||||
|
|
||||||
const reports = [
|
const reports = [
|
||||||
@ -30,9 +32,9 @@
|
|||||||
{ name: 'CBC', value: ['HGB', 'MCV', 'RETICYLOCYTE#', 'PLT', 'WBC', 'NEUTROPHIL#'], selected: false },
|
{ name: 'CBC', value: ['HGB', 'MCV', 'RETICYLOCYTE#', 'PLT', 'WBC', 'NEUTROPHIL#'], selected: false },
|
||||||
{ name: 'Renal', value: ['CREATININE', 'UREA NITROGEN', 'EGFR CKD-EPI 2021', 'Estimated GFR dc\'d 3/30/2022', 'CrCl'], selected: false },
|
{ name: 'Renal', value: ['CREATININE', 'UREA NITROGEN', 'EGFR CKD-EPI 2021', 'Estimated GFR dc\'d 3/30/2022', 'CrCl'], selected: false },
|
||||||
{ name: 'Hepatic', value: ['SGOT', 'SGPT', 'LDH', 'ALKALINE PHOSPHATASE', 'GAMMA-GTP', 'TOT. BILIRUBIN', 'DIR. BILIRUBIN', 'ALBUMIN'], selected: false },
|
{ name: 'Hepatic', value: ['SGOT', 'SGPT', 'LDH', 'ALKALINE PHOSPHATASE', 'GAMMA-GTP', 'TOT. BILIRUBIN', 'DIR. BILIRUBIN', 'ALBUMIN'], selected: false },
|
||||||
{ name: 'Electrolytes', value: ['SODIUM', 'CHLORIDE', 'CO2', 'CALCIUM', 'IONIZED CALCIUM (LABCORP)', 'POTASSIUM', 'MAGNESIUM', 'PO4', 'ANION GAP', 'OSMOBLD'], selected: false },
|
{ name: 'Electrolytes', value: ['SODIUM', 'CHLORIDE', 'CO2', 'CALCIUM', 'CALCIUM CORRECTED', 'IONIZED CALCIUM (LABCORP)', 'POTASSIUM', 'MAGNESIUM', 'PO4', 'ANION GAP', 'OSMOBLD'], selected: false },
|
||||||
{ name: 'Coagulation', value: ['PT', 'INR', 'PTT'], selected: false },
|
{ name: 'Coagulation', value: ['PT', 'INR', 'PTT'], selected: false },
|
||||||
{ name: 'Vitamins', value: ['FERRITIN', 'IRON', 'TIBC', 'B 12', 'FOLATE', 'VITAMIN D TOTAL 25-OH'], selected: false },
|
{ name: 'Vitamins', value: ['FERRITIN', 'IRON', 'TIBC', 'IRON SATURATION', 'B 12', 'FOLATE', 'VITAMIN D TOTAL 25-OH'], selected: false },
|
||||||
{ name: 'Thyroid', value: ['TSH', 'T4 (THYROXINE)'], selected: false },
|
{ name: 'Thyroid', value: ['TSH', 'T4 (THYROXINE)'], selected: false },
|
||||||
{ name: 'Myeloma', value: ['PROTEIN,TOT SER (LC)', 'ALBUMIN [for SPEP](LC)', 'ALPHA-1 GLOBULIN S (LC)', 'ALPHA-2 GLOBULIN S (LC)', 'BETA GLOBULIN S (LC)', 'GAMMA GLOBULIN S (LC)', 'GLOBULIN,TOTAL S (LC)', 'A/G RATIO S (LC)', 'M-SPIKE S (LC)', 'IMMUNOFIXATION SERUM (LC)', 'FREE KAPPA LT CHAIN, S (LC)', 'FREE LAMBDA LT CHAIN, S (LC)', 'KAPPA/LAMBDA RATIO, S (LC)', 'KLRATIO', 'IMMUNOGLOBULIN G,QN (LC)', 'IMMUNOGLOBULIN A,QN (LC)', 'IMMUNOGLOBULIN M,QN (LC)', 'IGG', 'IGA', 'IGM', 'ALBUMIN [for RAND UR](LC):U', 'ALPHA-1 GLOB RAND UR(LC):U', 'ALPHA-2 GLOB RAND UR(LC):U', 'BETA GLOB RAND UR(LC):U', 'GAMMA GLOB RAND UR(LC):U', 'M-SPIKE% RAND UR(LC):U', 'PROTEIN,TOT UR(LC):U', 'FKLCUR:U', 'FLLCUR:U', 'KAPPA/LAMBDA RATIO, UR (LC):U', 'KLRATIO:U', 'PROTEIN,24H CALC(LC):U', 'ALBUMIN [for 24UPEP](LC):U', 'ALPHA-1 GLOBULIN 24H(LC):U', 'ALPHA-2 GLOBULIN 24H(LC):U', 'BETA GLOBULIN 24H(LC):U', 'GAMMA GLOBULIN 24H(LC):U', 'M-SPIKE% 24H(LC):U', 'M-SPIKE mg/24hr(LC):U', 'FR KAPPA LTCH:U', 'FR LAMBDA LTCH:U'], selected: false }
|
{ name: 'Myeloma', value: ['PROTEIN,TOT SER (LC)', 'ALBUMIN [for SPEP](LC)', 'ALPHA-1 GLOBULIN S (LC)', 'ALPHA-2 GLOBULIN S (LC)', 'BETA GLOBULIN S (LC)', 'GAMMA GLOBULIN S (LC)', 'GLOBULIN,TOTAL S (LC)', 'A/G RATIO S (LC)', 'M-SPIKE S (LC)', 'IMMUNOFIXATION SERUM (LC)', 'FREE KAPPA LT CHAIN, S (LC)', 'FREE LAMBDA LT CHAIN, S (LC)', 'KAPPA/LAMBDA RATIO, S (LC)', 'KLRATIO', 'IMMUNOGLOBULIN G,QN (LC)', 'IMMUNOGLOBULIN A,QN (LC)', 'IMMUNOGLOBULIN M,QN (LC)', 'IGG', 'IGA', 'IGM', 'ALBUMIN [for RAND UR](LC):U', 'ALPHA-1 GLOB RAND UR(LC):U', 'ALPHA-2 GLOB RAND UR(LC):U', 'BETA GLOB RAND UR(LC):U', 'GAMMA GLOB RAND UR(LC):U', 'M-SPIKE% RAND UR(LC):U', 'PROTEIN,TOT UR(LC):U', 'FKLCUR:U', 'FLLCUR:U', 'KAPPA/LAMBDA RATIO, UR (LC):U', 'KLRATIO:U', 'PROTEIN,24H CALC(LC):U', 'ALBUMIN [for 24UPEP](LC):U', 'ALPHA-1 GLOBULIN 24H(LC):U', 'ALPHA-2 GLOBULIN 24H(LC):U', 'BETA GLOBULIN 24H(LC):U', 'GAMMA GLOBULIN 24H(LC):U', 'M-SPIKE% 24H(LC):U', 'M-SPIKE mg/24hr(LC):U', 'FR KAPPA LTCH:U', 'FR LAMBDA LTCH:U'], selected: false }
|
||||||
];
|
];
|
||||||
@ -47,13 +49,14 @@
|
|||||||
|
|
||||||
function vitals_normalize(rs) {
|
function vitals_normalize(rs) {
|
||||||
return rs.map(function(x) {
|
return rs.map(function(x) {
|
||||||
|
var comment = x.comment && x.comment.replace(/^\s+|\s+$/g, '').replace(/\s+/g, ' ');
|
||||||
var res = {
|
var res = {
|
||||||
time: x.datetime,
|
time: x.datetime,
|
||||||
name: x.name,
|
name: x.name,
|
||||||
unit: x.unit,
|
unit: x.unit,
|
||||||
value: x.value,
|
value: x.value,
|
||||||
flag: x.flag,
|
flag: x.flag,
|
||||||
comment: x.user
|
comment: comment ? x.user + ' • ' + comment : x.user
|
||||||
};
|
};
|
||||||
return vitals_mapping[x.name] ? Object.assign(res, vitals_mapping[x.name]) : res;
|
return vitals_mapping[x.name] ? Object.assign(res, vitals_mapping[x.name]) : res;
|
||||||
});
|
});
|
||||||
|
@ -53,15 +53,15 @@ 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(x = line.match(/^\b(?<name>.*?)(?<value>(?:positive|negative|reactive|nonreactive|not detected|collected - specimen in lab|test not performed))(?: (?<flag>L\*|L|H\*|H))?\s+(?:(?<unit>.{10}) (?<range>.*?)(?: \[(?<site>\d+)\])?)?$/i)) m = x;
|
if(x = line.match(/^\b(?<name>.*?)(?<value>(?:positive|negative|reactive|nonreactive|detected|not detected|comment|collected - specimen in lab|test not performed))(?: (?<flag>L\*|L|H\*|H))?\s+(?:(?<unit>.{10}) (?<range>.*?)(?: \[(?<site>\d+)\])?)?$/i)) m = x;
|
||||||
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|detected|not detected|comment|collected - specimen in lab|test not performed))\s*$/i))) {
|
||||||
x.name = m.groups.name;
|
x.name = m.groups.name;
|
||||||
x.value = m.groups.value;
|
x.value = m.groups.value;
|
||||||
}
|
}
|
||||||
for(var k in x) if(x[k]) x[k] = x[k] ? x[k].replace(/^\s+|\s+$/g, '') : undefined;
|
for(var k in x) if(x[k]) x[k] = x[k] ? x[k].replace(/^\s+|\s+$/g, '') : undefined;
|
||||||
} else if(m = line.match(/^\b(?<name>.*?)(?<value>(?:[\d\.]+|positive|negative|reactive|nonreactive|not detected|collected - specimen in lab|test not performed))\s*$/i)) {
|
} else if(m = line.match(/^\b(?<name>.*?)(?<value>(?:[\d\.]+|positive|negative|reactive|nonreactive|detected|not detected|comment|collected - specimen in lab|test not performed))(?: (?<flag>L\*|L|H\*|H))?\s*$/i)) {
|
||||||
results.push(x = m.groups);
|
results.push(x = m.groups);
|
||||||
for(var k in x) if(x[k]) x[k] = x[k] ? x[k].replace(/^\s+|\s+$/g, '') : undefined;
|
for(var k in x) if(x[k]) x[k] = x[k] ? x[k].replace(/^\s+|\s+$/g, '') : undefined;
|
||||||
} else if(line.startsWith(' [')) {
|
} else if(line.startsWith(' [')) {
|
||||||
@ -95,7 +95,7 @@ function lab_parse1default(data) {
|
|||||||
value: x = (results.hasOwnProperty('SEGS') ? +results.SEGS.value : 0) + (results.hasOwnProperty('BANDS') ? +results.BANDS.value : 0),
|
value: x = (results.hasOwnProperty('SEGS') ? +results.SEGS.value : 0) + (results.hasOwnProperty('BANDS') ? +results.BANDS.value : 0),
|
||||||
flag: (x < 42.2 ? 'L' : x > 75.2 ? 'H' : undefined)
|
flag: (x < 42.2 ? 'L' : x > 75.2 ? 'H' : undefined)
|
||||||
});
|
});
|
||||||
results.push(results['NEUTROPHIL#'] = {
|
if(results.WBC) results.push(results['NEUTROPHIL#'] = {
|
||||||
name: 'NEUTROPHIL#', unit: 'K/cmm', range: '1.4 - 6.5',
|
name: 'NEUTROPHIL#', unit: 'K/cmm', range: '1.4 - 6.5',
|
||||||
value: +(x = 0.01*x*results.WBC.value).toFixed(3),
|
value: +(x = 0.01*x*results.WBC.value).toFixed(3),
|
||||||
flag: (x < 1.4 ? 'L' : x > 6.5 ? 'H' : undefined)
|
flag: (x < 1.4 ? 'L' : x > 6.5 ? 'H' : undefined)
|
||||||
@ -107,7 +107,7 @@ function lab_parse1default(data) {
|
|||||||
value: x = +results.EOSINO.value,
|
value: x = +results.EOSINO.value,
|
||||||
flag: (x < 0 ? 'L' : x > 10 ? 'H' : undefined)
|
flag: (x < 0 ? 'L' : x > 10 ? 'H' : undefined)
|
||||||
});
|
});
|
||||||
results.push(results['EOSINOPHIL#'] = {
|
if(results.WBC) results.push(results['EOSINOPHIL#'] = {
|
||||||
name: 'EOSINOPHIL#', unit: 'K/cmm', range: '0.0 - 0.7',
|
name: 'EOSINOPHIL#', unit: 'K/cmm', range: '0.0 - 0.7',
|
||||||
value: +(x = 0.01*x*results.WBC.value).toFixed(3),
|
value: +(x = 0.01*x*results.WBC.value).toFixed(3),
|
||||||
flag: (x < 0 ? 'L' : x > 0.7 ? 'H' : undefined)
|
flag: (x < 0 ? 'L' : x > 0.7 ? 'H' : undefined)
|
||||||
@ -119,7 +119,7 @@ function lab_parse1default(data) {
|
|||||||
value: x = +results.BASO.value,
|
value: x = +results.BASO.value,
|
||||||
flag: (x < 0 ? 'L' : x > 2 ? 'H' : undefined)
|
flag: (x < 0 ? 'L' : x > 2 ? 'H' : undefined)
|
||||||
});
|
});
|
||||||
results.push(results['BASOPHIL#'] = {
|
if(results.WBC) results.push(results['BASOPHIL#'] = {
|
||||||
name: 'BASOPHIL#', unit: 'K/cmm', range: '0.0 - 0.2',
|
name: 'BASOPHIL#', unit: 'K/cmm', range: '0.0 - 0.2',
|
||||||
value: +(x = 0.01*x*results.WBC.value).toFixed(3),
|
value: +(x = 0.01*x*results.WBC.value).toFixed(3),
|
||||||
flag: (x < 0 ? 'L' : x > 0.2 ? 'H' : undefined)
|
flag: (x < 0 ? 'L' : x > 0.2 ? 'H' : undefined)
|
||||||
@ -131,7 +131,7 @@ function lab_parse1default(data) {
|
|||||||
value: x = +results.MONOS.value,
|
value: x = +results.MONOS.value,
|
||||||
flag: (x < 1.7 ? 'L' : x > 9.3 ? 'H' : undefined)
|
flag: (x < 1.7 ? 'L' : x > 9.3 ? 'H' : undefined)
|
||||||
});
|
});
|
||||||
results.push(results['MONOCYTE#'] = {
|
if(results.WBC) results.push(results['MONOCYTE#'] = {
|
||||||
name: 'MONOCYTE#', unit: 'K/cmm', range: '0.11 - 0.59',
|
name: 'MONOCYTE#', unit: 'K/cmm', range: '0.11 - 0.59',
|
||||||
value: +(x = 0.01*x*results.WBC.value).toFixed(3),
|
value: +(x = 0.01*x*results.WBC.value).toFixed(3),
|
||||||
flag: (x < 0.11 ? 'L' : x > 0.59 ? 'H' : undefined)
|
flag: (x < 0.11 ? 'L' : x > 0.59 ? 'H' : undefined)
|
||||||
@ -143,7 +143,7 @@ function lab_parse1default(data) {
|
|||||||
value: x = (results.hasOwnProperty('LYMPHS') ? +results.LYMPHS.value : 0) + (results.hasOwnProperty('ATYPICAL LYMPHOCYTES') ? +results['ATYPICAL LYMPHOCYTES'].value : 0),
|
value: x = (results.hasOwnProperty('LYMPHS') ? +results.LYMPHS.value : 0) + (results.hasOwnProperty('ATYPICAL LYMPHOCYTES') ? +results['ATYPICAL LYMPHOCYTES'].value : 0),
|
||||||
flag: (x < 15 ? 'L' : x > 41 ? 'H' : undefined)
|
flag: (x < 15 ? 'L' : x > 41 ? 'H' : undefined)
|
||||||
});
|
});
|
||||||
results.push(results['LYMPHOCYTE#'] = {
|
if(results.WBC) results.push(results['LYMPHOCYTE#'] = {
|
||||||
name: 'LYMPHOCYTE#', unit: 'K/cmm', range: '1.2 - 3.4',
|
name: 'LYMPHOCYTE#', unit: 'K/cmm', range: '1.2 - 3.4',
|
||||||
value: +(x = 0.01*x*results.WBC.value).toFixed(3),
|
value: +(x = 0.01*x*results.WBC.value).toFixed(3),
|
||||||
flag: (x < 1.2 ? 'L' : x > 3.4 ? 'H' : undefined)
|
flag: (x < 1.2 ? 'L' : x > 3.4 ? 'H' : undefined)
|
||||||
@ -195,20 +195,36 @@ export function measurement_parse(data) {
|
|||||||
res.name = row.substring(idx + 3, idx = row.indexOf(': ', idx));
|
res.name = row.substring(idx + 3, idx = row.indexOf(': ', idx));
|
||||||
value = row.substring(idx + 4, idx = row.indexOf(' _', idx));
|
value = row.substring(idx + 4, idx = row.indexOf(' _', idx));
|
||||||
res.user = row.substring(idx + 2);
|
res.user = row.substring(idx + 2);
|
||||||
m = value.match(/^(?:(.*?)(?: (\S+))?)(\*)?(?: \((?:(.*?)(?: (\S+))?)\))?\s*$/);
|
if(m = value.match(/(?:^(?<value>[\d\.\/%]+)(?: (?<unit>\w+) \((?<value2>[\d\.\/%]+) (?<unit2>\w+)\))?(?<flag>\*)? (?: (?<comment>.*))?$)|(?:^(?<value3>[\d\.\/%]+)(?<flag3>\*)?\s*(?<comment3>.*)$)/)) {
|
||||||
res.value = m[4] ? m[4] : m[1];
|
if(m.groups.value2) {
|
||||||
res.unit = m[4] ? m[5] : m[2];
|
res.value = m.groups.value2;
|
||||||
res.flag = m[3];
|
res.unit = m.groups.unit2;
|
||||||
res.value_american = m[4] ? m[1] : m[4];
|
res.value_american = m.groups.value;
|
||||||
res.unit_american = m[4] ? m[2] : m[5];
|
res.unit_american = m.groups.unit;
|
||||||
if(res.value.charAt(res.value.length - 1) == '%') {
|
res.flag = m.groups.flag;
|
||||||
res.unit = '%';
|
res.comment = m.groups.comment;
|
||||||
res.value = res.value.substring(0, res.value.length - 1);
|
} else if(m.groups.value) {
|
||||||
|
res.value = m.groups.value;
|
||||||
|
res.unit = m.groups.unit;
|
||||||
|
res.flag = m.groups.flag;
|
||||||
|
res.comment = m.groups.comment;
|
||||||
|
} else if(m.groups.value3) {
|
||||||
|
res.value = m.groups.value3;
|
||||||
|
res.flag = m.groups.flag3;
|
||||||
|
res.comment = m.groups.comment3;
|
||||||
|
} else res.comment = value;
|
||||||
|
if(res.comment) res.comment = res.comment.replace(/^\s+|\s+$/g, '').replace(/\s+/g, ' ');
|
||||||
}
|
}
|
||||||
if(res.name == 'B/P') {
|
if(res.value) {
|
||||||
var bpsplit = res.value.split('/');
|
if(res.value.charAt(res.value.length - 1) == '%') {
|
||||||
extras.push({...res, name: 'SBP', range: '90 - 120', unit: 'mmHg', value: bpsplit[0] });
|
res.unit = '%';
|
||||||
extras.push({...res, name: 'DBP', range: '60 - 80', unit: 'mmHg', value: bpsplit[1] });
|
res.value = res.value.substring(0, res.value.length - 1);
|
||||||
|
}
|
||||||
|
if(res.name == 'B/P') {
|
||||||
|
var bpsplit = res.value.split('/');
|
||||||
|
extras.push({...res, name: 'SBP', range: '90 - 120', unit: 'mmHg', value: bpsplit[0] });
|
||||||
|
extras.push({...res, name: 'DBP', range: '60 - 80', unit: 'mmHg', value: bpsplit[1] });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
4
main.py
4
main.py
@ -112,8 +112,8 @@ def application():
|
|||||||
client._cache_persistent(persistent=util.Store(f'cache.{client._server["volume"].lower()}.{client._server["uci"].lower()}.{user[0]}.db', journal_mode='WAL').memo)
|
client._cache_persistent(persistent=util.Store(f'cache.{client._server["volume"].lower()}.{client._server["uci"].lower()}.{user[0]}.db', journal_mode='WAL').memo)
|
||||||
return jsonify_result(user, id=request.json.get('id'))
|
return jsonify_result(user, id=request.json.get('id'))
|
||||||
else:
|
else:
|
||||||
from auth import XUIAMSSOi_MySsoTokenVBA
|
import XWBSSOi
|
||||||
if token := XUIAMSSOi_MySsoTokenVBA():
|
if token := XWBSSOi.get_sso_token(application='CPRSChart.exe'):
|
||||||
user = client.authenticate(token)
|
user = client.authenticate(token)
|
||||||
client._cache_persistent(persistent=util.Store(f'cache.{client._server["volume"].lower()}.{client._server["uci"].lower()}.{user[0]}.db', journal_mode='WAL').memo)
|
client._cache_persistent(persistent=util.Store(f'cache.{client._server["volume"].lower()}.{client._server["uci"].lower()}.{user[0]}.db', journal_mode='WAL').memo)
|
||||||
return jsonify_result(user, id=request.json.get('id'))
|
return jsonify_result(user, id=request.json.get('id'))
|
||||||
|
4
rpc.py
4
rpc.py
@ -237,13 +237,13 @@ class ClientAsync(object):
|
|||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
import getpass, code
|
import getpass, code
|
||||||
from auth import XUIAMSSOi_MySsoTokenVBA
|
import XWBSSOi
|
||||||
|
|
||||||
client = ClientSync(host='test.northport.med.va.gov', port=19009)
|
client = ClientSync(host='test.northport.med.va.gov', port=19009)
|
||||||
#client = ClientSync(host='vista.northport.med.va.gov', port=19209)
|
#client = ClientSync(host='vista.northport.med.va.gov', port=19209)
|
||||||
threading.Thread(target=client.keepalive, daemon=True).start()
|
threading.Thread(target=client.keepalive, daemon=True).start()
|
||||||
print('\r\n'.join(client.XUS_INTRO_MSG()))
|
print('\r\n'.join(client.XUS_INTRO_MSG()))
|
||||||
if token := XUIAMSSOi_MySsoTokenVBA():
|
if token := XWBSSOi.get_sso_token(application='CPRSChart.exe'):
|
||||||
print('authenticate', repr(client.authenticate(token)))
|
print('authenticate', repr(client.authenticate(token)))
|
||||||
else:
|
else:
|
||||||
print('authenticate', repr(client.authenticate(f"{getpass.getpass('ACCESS CODE: ')};{getpass.getpass('VERIFY CODE: ')}")))
|
print('authenticate', repr(client.authenticate(f"{getpass.getpass('ACCESS CODE: ')};{getpass.getpass('VERIFY CODE: ')}")))
|
||||||
|
Reference in New Issue
Block a user