- Filtered to show exclusively this patient's teleconsult telemetry. Once the Contec CMS7000PLUS monitor connects or transmits data, live waveforms and vitals will stream automatically.
+
+ Live waveforms and telemetry stream automatically once the Contec CMS7000PLUS monitor connects and transmits telemetry matching all requested demographics (MRN, Name, DOB).
Listening for live connection...
@@ -1774,65 +1775,110 @@
function getTargetPatientBadge() {
const items = [];
- if (targetMrn) items.push(`MRN:
${targetMrn}`);
- if (targetName) items.push(`Name:
${targetName}`);
- if (targetDob) items.push(`DOB:
${targetDob}`);
- if (targetGender) items.push(`Gender:
${targetGender}`);
- return items.length > 0 ? items.join(' • ') : 'Target Patient';
+ if (targetMrn) items.push(`
📋 MRN: ${targetMrn}`);
+ if (targetName) items.push(`
👤 Name: ${targetName}`);
+ if (targetDob) items.push(`
🎂 DOB: ${targetDob}`);
+ if (targetGender) items.push(`
⚧ Gender: ${targetGender}`);
+ return items.length > 0 ? items.join('') : '
Target Patient';
+ }
+
+ // Helper: Extract 4-digit year and digits for robust DOB matching
+ function normalizeDob(str) {
+ if (!str) return { raw: '', digits: '', year: null };
+ const clean = String(str).trim();
+ const yearMatch = clean.match(/\b(19\d\d|20[0-2]\d)\b/);
+ const digits = clean.replace(/\D/g, '');
+ return {
+ raw: clean.toLowerCase(),
+ digits: digits,
+ year: yearMatch ? yearMatch[1] : null
+ };
+ }
+
+ // Helper: Extract individual words for flexible name matching
+ function normalizeNameTokens(str) {
+ if (!str) return [];
+ return String(str)
+ .toLowerCase()
+ .replace(/[^a-z0-9\s]/g, '')
+ .trim()
+ .split(/\s+/)
+ .filter(w => w.length >= 2);
+ }
+
+ // Check verification for each requested parameter (MRN, Name, DOB, Gender)
+ function checkPatientVerification(dev) {
+ const res = {
+ mrnMatched: !targetMrn,
+ nameMatched: !targetName,
+ dobMatched: !targetDob,
+ genderMatched: !targetGender,
+ allMatched: false
+ };
+ if (!dev) return res;
+
+ const pMrn = String(dev?.mrn || dev?.patient_id || '').toLowerCase().trim();
+ const pName = String(dev?.patient_name || dev?.name || '').toLowerCase().trim();
+ const pDob = String(dev?.patient_dob || dev?.dob || '').toLowerCase().trim();
+ const pGender = String(dev?.patient_gender || dev?.gender || '').toLowerCase().trim();
+
+ // 1. MRN verification
+ if (targetMrn) {
+ const tm = targetMrn.toLowerCase().trim();
+ if (pMrn && (pMrn === tm || pMrn.includes(tm) || tm.includes(pMrn))) {
+ res.mrnMatched = true;
+ } else {
+ const numT = parseInt(tm, 10);
+ const numMrn = parseInt(pMrn, 10);
+ if (!isNaN(numT) && !isNaN(numMrn) && numT === numMrn && tm.length >= 3 && pMrn.length >= 3) {
+ res.mrnMatched = true;
+ }
+ }
+ }
+
+ // 2. Name verification (supports "Shahil", "Shahil Kumar", "Kumar Shahil")
+ if (targetName) {
+ const tn = targetName.toLowerCase().trim();
+ const targetTokens = normalizeNameTokens(targetName);
+ const patientTokens = normalizeNameTokens(pName);
+ if (pName && (pName.includes(tn) || tn.includes(pName))) {
+ res.nameMatched = true;
+ } else if (targetTokens.length > 0 && targetTokens.some(t => patientTokens.includes(t))) {
+ res.nameMatched = true;
+ }
+ }
+
+ // 3. DOB verification (supports 1960, 1960-06-22, 22/06/1960, 19600622)
+ if (targetDob) {
+ const td = targetDob.toLowerCase().trim();
+ const tNorm = normalizeDob(targetDob);
+ const pNorm = normalizeDob(pDob);
+ if (pDob && (pDob.includes(td) || td.includes(pDob))) {
+ res.dobMatched = true;
+ } else if (tNorm.year && pNorm.year && tNorm.year === pNorm.year) {
+ res.dobMatched = true;
+ } else if (tNorm.digits && pNorm.digits && (tNorm.digits.includes(pNorm.digits) || pNorm.digits.includes(tNorm.digits))) {
+ res.dobMatched = true;
+ }
+ }
+
+ // 4. Gender verification
+ if (targetGender) {
+ const tg = targetGender.toLowerCase().trim();
+ if (pGender && (pGender.startsWith(tg[0]) || pGender.includes(tg))) {
+ res.genderMatched = true;
+ }
+ }
+
+ res.allMatched = res.mrnMatched && res.nameMatched && res.dobMatched && res.genderMatched;
+ return res;
}
// Helper function: Check if a device or patient matches the target demographics (MRN, Name, DOB, Gender)
function matchesTargetPatient(dev, devId) {
if (!hasTargetFilter) return true;
if (!dev && !devId) return false;
-
- const pMrn = String(dev?.mrn || dev?.patient_id || '').toLowerCase().trim();
- const pName = String(dev?.patient_name || dev?.name || '').toLowerCase().trim();
- const pDob = String(dev?.patient_dob || dev?.dob || '').toLowerCase().trim();
- const pGender = String(dev?.patient_gender || dev?.gender || '').toLowerCase().trim();
- const pBed = String(dev?.bed_id || dev?.bedLabel || '').toLowerCase().trim();
- const dId = String(devId || dev?.device_id || '').toLowerCase().trim();
-
- // 1. If targetName is explicitly specified: must match patient name
- if (targetName) {
- const tn = targetName.toLowerCase().trim();
- const nameMatch = pName && (pName.includes(tn) || tn.includes(pName));
- if (!nameMatch) return false;
- }
-
- // 2. If targetDob is explicitly specified: must match patient DOB / birth year
- if (targetDob) {
- const td = targetDob.toLowerCase().trim();
- const dobMatch = pDob && (pDob.includes(td) || td.includes(pDob));
- if (!dobMatch) return false;
- }
-
- // 3. If targetGender is explicitly specified: must match gender
- if (targetGender) {
- const tg = targetGender.toLowerCase().trim();
- const genderMatch = pGender && (pGender.startsWith(tg[0]) || pGender.includes(tg));
- if (!genderMatch) return false;
- }
-
- // 4. If targetMrn is specified: strictly match patient MRN only
- if (targetMrn) {
- const tm = targetMrn.toLowerCase().trim();
- let mrnMatch = false;
-
- if (pMrn) {
- if (pMrn === tm) mrnMatch = true;
- // Also support numeric equivalence (e.g. "09091102" vs "9091102") with minimum length 3
- const numT = parseInt(tm, 10);
- const numMrn = parseInt(pMrn, 10);
- if (!isNaN(numT) && !isNaN(numMrn) && numT === numMrn && tm.length >= 3 && pMrn.length >= 3) {
- mrnMatch = true;
- }
- }
-
- if (!mrnMatch) return false;
- }
-
- return true;
+ return checkPatientVerification(dev).allMatched;
}
// Backward-compatible alias
@@ -2121,8 +2167,13 @@
}
// 2. Load latest readings to backfill vitals for each device
- const backfillUrl = hasTargetFilter && targetMrn
- ? `${API_BASE}/latest-readings?limit=50&mrn=${encodeURIComponent(targetMrn)}`
+ const backfillParams = new URLSearchParams();
+ backfillParams.set('limit', '50');
+ if (targetMrn) backfillParams.set('mrn', targetMrn);
+ if (targetName) backfillParams.set('name', targetName);
+ if (targetDob) backfillParams.set('dob', targetDob);
+ const backfillUrl = hasTargetFilter
+ ? `${API_BASE}/latest-readings?${backfillParams.toString()}`
: `${API_BASE}/latest-readings?limit=100`;
const readResp = await fetch(backfillUrl);
if (readResp.ok) {
@@ -2560,7 +2611,7 @@
if (waitingState) {
waitingState.style.display = 'flex';
const badge = document.getElementById('targetWaitingMrnBadge');
- if (badge) badge.innerHTML = `Target Patient: ${getTargetPatientBadge()}`;
+ if (badge) badge.innerHTML = getTargetPatientBadge();
}
if (patientBanner) patientBanner.style.display = 'none';
if (vitalsGrid) vitalsGrid.style.display = 'none';
@@ -2584,10 +2635,11 @@
const bannerEl = document.getElementById('singlePatientBanner');
if (bannerEl) {
- document.getElementById('bannerPatientName').textContent = pName;
- document.getElementById('bannerMrn').textContent = `MRN: ${pMrn}`;
- document.getElementById('bannerGender').textContent = `Gender: ${pGender}`;
- document.getElementById('bannerDob').textContent = `DOB: ${pDob}`;
+ const verifiedTag = '
✓ Verified';
+ document.getElementById('bannerPatientName').innerHTML = `${pName} ${targetName ? verifiedTag : ''}`;
+ document.getElementById('bannerMrn').innerHTML = `MRN: ${pMrn} ${targetMrn ? verifiedTag : ''}`;
+ document.getElementById('bannerGender').innerHTML = `Gender: ${pGender} ${targetGender ? verifiedTag : ''}`;
+ document.getElementById('bannerDob').innerHTML = `DOB: ${pDob} ${targetDob ? verifiedTag : ''}`;
document.getElementById('bannerBed').textContent = `Location: ${pBed}`;
}
diff --git a/routers/dashboard.py b/routers/dashboard.py
index acffcb2..3b43f32 100644
--- a/routers/dashboard.py
+++ b/routers/dashboard.py
@@ -112,26 +112,30 @@ def get_latest_readings(
if device_id:
query = query.filter(VitalReading.device_id == device_id)
- patient_filters = []
+ patient_criteria = []
if mrn:
mrn_clean = mrn.strip()
- patient_filters.append(Patient.mrn == mrn_clean)
- patient_filters.append(Patient.patient_id == mrn_clean)
+ patient_criteria.append(or_(Patient.mrn == mrn_clean, Patient.patient_id == mrn_clean))
if name:
name_clean = name.strip()
- patient_filters.append(Patient.name.ilike(f"%{name_clean}%"))
+ patient_criteria.append(Patient.name.ilike(f"%{name_clean}%"))
if dob:
- dob_clean = dob.strip()
- patient_filters.append(Patient.dob.ilike(f"%{dob_clean}%"))
+ dob_clean = dob.strip().replace("-", "").replace("/", "")
+ patient_criteria.append(or_(
+ Patient.dob.ilike(f"%{dob.strip()}%"),
+ Patient.dob.ilike(f"%{dob_clean}%"),
+ Patient.dob.ilike(f"%{dob.strip()[:4]}%") if len(dob.strip()) >= 4 else False
+ ))
- if patient_filters:
- matching_patients = db.query(Patient).filter(or_(*patient_filters)).all()
+ if patient_criteria:
+ from sqlalchemy import and_
+ matching_patients = db.query(Patient).filter(and_(*patient_criteria)).all()
+ if not matching_patients:
+ return []
matching_dev_ids = [p.device_id for p in matching_patients if p.device_id]
matching_mrns = [p.mrn for p in matching_patients if p.mrn] + [p.patient_id for p in matching_patients if p.patient_id]
conds = []
- if mrn:
- conds.append(VitalReading.patient_id == mrn.strip())
if matching_mrns:
conds.append(VitalReading.patient_id.in_(matching_mrns))
if matching_dev_ids:
@@ -139,6 +143,8 @@ def get_latest_readings(
if conds:
query = query.filter(or_(*conds))
+ else:
+ return []
readings = query.order_by(VitalReading.timestamp.desc()).limit(limit).all()
return readings
diff --git a/static/dashboard.html b/static/dashboard.html
index 18a6f21..3ffdc4f 100644
--- a/static/dashboard.html
+++ b/static/dashboard.html
@@ -1425,13 +1425,14 @@
-
-
+
+
⏳
Waiting for Contec Monitor Data
-
Patient MRN: ---
-
- Filtered to show exclusively this patient's teleconsult telemetry. Once the Contec CMS7000PLUS monitor connects or transmits data, live waveforms and vitals will stream automatically.
+
Verifying incoming monitor telemetry against required teleconsult patient:
+
Patient Demographics: ---
+
+ Live waveforms and telemetry stream automatically once the Contec CMS7000PLUS monitor connects and transmits telemetry matching all requested demographics (MRN, Name, DOB).
Listening for live connection...
@@ -1774,65 +1775,110 @@
function getTargetPatientBadge() {
const items = [];
- if (targetMrn) items.push(`MRN: ${targetMrn}`);
- if (targetName) items.push(`Name: ${targetName}`);
- if (targetDob) items.push(`DOB: ${targetDob}`);
- if (targetGender) items.push(`Gender: ${targetGender}`);
- return items.length > 0 ? items.join(' • ') : 'Target Patient';
+ if (targetMrn) items.push(`📋 MRN: ${targetMrn}`);
+ if (targetName) items.push(`👤 Name: ${targetName}`);
+ if (targetDob) items.push(`🎂 DOB: ${targetDob}`);
+ if (targetGender) items.push(`⚧ Gender: ${targetGender}`);
+ return items.length > 0 ? items.join('') : 'Target Patient';
+ }
+
+ // Helper: Extract 4-digit year and digits for robust DOB matching
+ function normalizeDob(str) {
+ if (!str) return { raw: '', digits: '', year: null };
+ const clean = String(str).trim();
+ const yearMatch = clean.match(/\b(19\d\d|20[0-2]\d)\b/);
+ const digits = clean.replace(/\D/g, '');
+ return {
+ raw: clean.toLowerCase(),
+ digits: digits,
+ year: yearMatch ? yearMatch[1] : null
+ };
+ }
+
+ // Helper: Extract individual words for flexible name matching
+ function normalizeNameTokens(str) {
+ if (!str) return [];
+ return String(str)
+ .toLowerCase()
+ .replace(/[^a-z0-9\s]/g, '')
+ .trim()
+ .split(/\s+/)
+ .filter(w => w.length >= 2);
+ }
+
+ // Check verification for each requested parameter (MRN, Name, DOB, Gender)
+ function checkPatientVerification(dev) {
+ const res = {
+ mrnMatched: !targetMrn,
+ nameMatched: !targetName,
+ dobMatched: !targetDob,
+ genderMatched: !targetGender,
+ allMatched: false
+ };
+ if (!dev) return res;
+
+ const pMrn = String(dev?.mrn || dev?.patient_id || '').toLowerCase().trim();
+ const pName = String(dev?.patient_name || dev?.name || '').toLowerCase().trim();
+ const pDob = String(dev?.patient_dob || dev?.dob || '').toLowerCase().trim();
+ const pGender = String(dev?.patient_gender || dev?.gender || '').toLowerCase().trim();
+
+ // 1. MRN verification
+ if (targetMrn) {
+ const tm = targetMrn.toLowerCase().trim();
+ if (pMrn && (pMrn === tm || pMrn.includes(tm) || tm.includes(pMrn))) {
+ res.mrnMatched = true;
+ } else {
+ const numT = parseInt(tm, 10);
+ const numMrn = parseInt(pMrn, 10);
+ if (!isNaN(numT) && !isNaN(numMrn) && numT === numMrn && tm.length >= 3 && pMrn.length >= 3) {
+ res.mrnMatched = true;
+ }
+ }
+ }
+
+ // 2. Name verification (supports "Shahil", "Shahil Kumar", "Kumar Shahil")
+ if (targetName) {
+ const tn = targetName.toLowerCase().trim();
+ const targetTokens = normalizeNameTokens(targetName);
+ const patientTokens = normalizeNameTokens(pName);
+ if (pName && (pName.includes(tn) || tn.includes(pName))) {
+ res.nameMatched = true;
+ } else if (targetTokens.length > 0 && targetTokens.some(t => patientTokens.includes(t))) {
+ res.nameMatched = true;
+ }
+ }
+
+ // 3. DOB verification (supports 1960, 1960-06-22, 22/06/1960, 19600622)
+ if (targetDob) {
+ const td = targetDob.toLowerCase().trim();
+ const tNorm = normalizeDob(targetDob);
+ const pNorm = normalizeDob(pDob);
+ if (pDob && (pDob.includes(td) || td.includes(pDob))) {
+ res.dobMatched = true;
+ } else if (tNorm.year && pNorm.year && tNorm.year === pNorm.year) {
+ res.dobMatched = true;
+ } else if (tNorm.digits && pNorm.digits && (tNorm.digits.includes(pNorm.digits) || pNorm.digits.includes(tNorm.digits))) {
+ res.dobMatched = true;
+ }
+ }
+
+ // 4. Gender verification
+ if (targetGender) {
+ const tg = targetGender.toLowerCase().trim();
+ if (pGender && (pGender.startsWith(tg[0]) || pGender.includes(tg))) {
+ res.genderMatched = true;
+ }
+ }
+
+ res.allMatched = res.mrnMatched && res.nameMatched && res.dobMatched && res.genderMatched;
+ return res;
}
// Helper function: Check if a device or patient matches the target demographics (MRN, Name, DOB, Gender)
function matchesTargetPatient(dev, devId) {
if (!hasTargetFilter) return true;
if (!dev && !devId) return false;
-
- const pMrn = String(dev?.mrn || dev?.patient_id || '').toLowerCase().trim();
- const pName = String(dev?.patient_name || dev?.name || '').toLowerCase().trim();
- const pDob = String(dev?.patient_dob || dev?.dob || '').toLowerCase().trim();
- const pGender = String(dev?.patient_gender || dev?.gender || '').toLowerCase().trim();
- const pBed = String(dev?.bed_id || dev?.bedLabel || '').toLowerCase().trim();
- const dId = String(devId || dev?.device_id || '').toLowerCase().trim();
-
- // 1. If targetName is explicitly specified: must match patient name
- if (targetName) {
- const tn = targetName.toLowerCase().trim();
- const nameMatch = pName && (pName.includes(tn) || tn.includes(pName));
- if (!nameMatch) return false;
- }
-
- // 2. If targetDob is explicitly specified: must match patient DOB / birth year
- if (targetDob) {
- const td = targetDob.toLowerCase().trim();
- const dobMatch = pDob && (pDob.includes(td) || td.includes(pDob));
- if (!dobMatch) return false;
- }
-
- // 3. If targetGender is explicitly specified: must match gender
- if (targetGender) {
- const tg = targetGender.toLowerCase().trim();
- const genderMatch = pGender && (pGender.startsWith(tg[0]) || pGender.includes(tg));
- if (!genderMatch) return false;
- }
-
- // 4. If targetMrn is specified: strictly match patient MRN only
- if (targetMrn) {
- const tm = targetMrn.toLowerCase().trim();
- let mrnMatch = false;
-
- if (pMrn) {
- if (pMrn === tm) mrnMatch = true;
- // Also support numeric equivalence (e.g. "09091102" vs "9091102") with minimum length 3
- const numT = parseInt(tm, 10);
- const numMrn = parseInt(pMrn, 10);
- if (!isNaN(numT) && !isNaN(numMrn) && numT === numMrn && tm.length >= 3 && pMrn.length >= 3) {
- mrnMatch = true;
- }
- }
-
- if (!mrnMatch) return false;
- }
-
- return true;
+ return checkPatientVerification(dev).allMatched;
}
// Backward-compatible alias
@@ -2121,8 +2167,13 @@
}
// 2. Load latest readings to backfill vitals for each device
- const backfillUrl = hasTargetFilter && targetMrn
- ? `${API_BASE}/latest-readings?limit=50&mrn=${encodeURIComponent(targetMrn)}`
+ const backfillParams = new URLSearchParams();
+ backfillParams.set('limit', '50');
+ if (targetMrn) backfillParams.set('mrn', targetMrn);
+ if (targetName) backfillParams.set('name', targetName);
+ if (targetDob) backfillParams.set('dob', targetDob);
+ const backfillUrl = hasTargetFilter
+ ? `${API_BASE}/latest-readings?${backfillParams.toString()}`
: `${API_BASE}/latest-readings?limit=100`;
const readResp = await fetch(backfillUrl);
if (readResp.ok) {
@@ -2560,7 +2611,7 @@
if (waitingState) {
waitingState.style.display = 'flex';
const badge = document.getElementById('targetWaitingMrnBadge');
- if (badge) badge.innerHTML = `Target Patient: ${getTargetPatientBadge()}`;
+ if (badge) badge.innerHTML = getTargetPatientBadge();
}
if (patientBanner) patientBanner.style.display = 'none';
if (vitalsGrid) vitalsGrid.style.display = 'none';
@@ -2584,10 +2635,11 @@
const bannerEl = document.getElementById('singlePatientBanner');
if (bannerEl) {
- document.getElementById('bannerPatientName').textContent = pName;
- document.getElementById('bannerMrn').textContent = `MRN: ${pMrn}`;
- document.getElementById('bannerGender').textContent = `Gender: ${pGender}`;
- document.getElementById('bannerDob').textContent = `DOB: ${pDob}`;
+ const verifiedTag = '✓ Verified';
+ document.getElementById('bannerPatientName').innerHTML = `${pName} ${targetName ? verifiedTag : ''}`;
+ document.getElementById('bannerMrn').innerHTML = `MRN: ${pMrn} ${targetMrn ? verifiedTag : ''}`;
+ document.getElementById('bannerGender').innerHTML = `Gender: ${pGender} ${targetGender ? verifiedTag : ''}`;
+ document.getElementById('bannerDob').innerHTML = `DOB: ${pDob} ${targetDob ? verifiedTag : ''}`;
document.getElementById('bannerBed').textContent = `Location: ${pBed}`;
}