extract well visit schedule data
This commit is contained in:
parent
03285532cb
commit
b4d7420f22
5 changed files with 4152 additions and 5 deletions
4003
public/data/well-visit/schedule.json
Normal file
4003
public/data/well-visit/schedule.json
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -475,7 +475,6 @@
|
||||||
integrity="sha384-JUh163oCRItcbPme8pYnROHQMC6fNKTBWtRG3I3I0erJkzNgL7uxKlNwcrcFKeqF"
|
integrity="sha384-JUh163oCRItcbPme8pYnROHQMC6fNKTBWtRG3I3I0erJkzNgL7uxKlNwcrcFKeqF"
|
||||||
crossorigin="anonymous" referrerpolicy="no-referrer" defer></script>
|
crossorigin="anonymous" referrerpolicy="no-referrer" defer></script>
|
||||||
<script defer src="/js/milestonesData.js"></script>
|
<script defer src="/js/milestonesData.js"></script>
|
||||||
<script defer src="/js/pediatricScheduleData.js"></script>
|
|
||||||
<script defer src="/js/audioBackup.js"></script>
|
<script defer src="/js/audioBackup.js"></script>
|
||||||
<script defer src="/js/browserWhisper.js"></script>
|
<script defer src="/js/browserWhisper.js"></script>
|
||||||
<script defer src="/js/speechRecognition.js"></script>
|
<script defer src="/js/speechRecognition.js"></script>
|
||||||
|
|
@ -498,7 +497,7 @@
|
||||||
<script defer src="/js/diagrams.js"></script>
|
<script defer src="/js/diagrams.js"></script>
|
||||||
<script type="module" src="/js/clinicalAssistant.js"></script>
|
<script type="module" src="/js/clinicalAssistant.js"></script>
|
||||||
<script defer src="/js/nextcloud.js"></script>
|
<script defer src="/js/nextcloud.js"></script>
|
||||||
<script defer src="/js/wellVisit.js"></script>
|
<script type="module" src="/js/wellVisit.js"></script>
|
||||||
<script defer src="/js/shadess.js"></script>
|
<script defer src="/js/shadess.js"></script>
|
||||||
<script defer src="/js/sickVisit.js"></script>
|
<script defer src="/js/sickVisit.js"></script>
|
||||||
<script defer src="/js/ed-encounters.js"></script>
|
<script defer src="/js/ed-encounters.js"></script>
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { applyWellVisitScheduleGlobals, loadWellVisitScheduleData } from './wellVisit/scheduleData.js';
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// WELL VISIT / PREVENTIVE CARE TAB
|
// WELL VISIT / PREVENTIVE CARE TAB
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
@ -77,6 +79,25 @@
|
||||||
// ─── Module-level state for visit statuses ────────────────────────────────
|
// ─── Module-level state for visit statuses ────────────────────────────────
|
||||||
// keyed by visitId + '.' + itemKey — persisted to localStorage
|
// keyed by visitId + '.' + itemKey — persisted to localStorage
|
||||||
var _visitStatuses = {};
|
var _visitStatuses = {};
|
||||||
|
var _scheduleReady = null;
|
||||||
|
|
||||||
|
function ensureScheduleData() {
|
||||||
|
if (typeof VISIT_AGES !== 'undefined' && typeof PERIODICITY !== 'undefined') return Promise.resolve();
|
||||||
|
if (!_scheduleReady) {
|
||||||
|
_scheduleReady = loadWellVisitScheduleData().then(function(data) {
|
||||||
|
applyWellVisitScheduleGlobals(data, window);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return _scheduleReady;
|
||||||
|
}
|
||||||
|
|
||||||
|
function showScheduleLoadError(panelId, err) {
|
||||||
|
var panel = document.getElementById(panelId);
|
||||||
|
if (panel) {
|
||||||
|
panel.innerHTML = '<div class="wv-empty">Unable to load well-visit schedule data. Please refresh and try again.</div>';
|
||||||
|
}
|
||||||
|
console.error('Failed to load well-visit schedule data', err);
|
||||||
|
}
|
||||||
|
|
||||||
// SSHADESS is only relevant for 12+ year visits
|
// SSHADESS is only relevant for 12+ year visits
|
||||||
var SSHADESS_VISITS = ['12y','13y','14y','15y','16y','17y','18y','19y','20y','21y'];
|
var SSHADESS_VISITS = ['12y','13y','14y','15y','16y','17y','18y','19y','20y','21y'];
|
||||||
|
|
@ -721,9 +742,18 @@
|
||||||
var _catchupInited = false;
|
var _catchupInited = false;
|
||||||
document.addEventListener('tabChanged', function(e) {
|
document.addEventListener('tabChanged', function(e) {
|
||||||
var tab = e.detail.tab;
|
var tab = e.detail.tab;
|
||||||
if (tab === 'wellvisit' && !_inited) { _inited = true; init(); }
|
if (tab === 'wellvisit' && !_inited) {
|
||||||
if (tab === 'vaxschedule' && !_vaxInited) { _vaxInited = true; renderFullSchedule(); }
|
_inited = true;
|
||||||
if (tab === 'catchup' && !_catchupInited) { _catchupInited = true; renderCatchUp(); }
|
ensureScheduleData().then(init).catch(function(err) { showScheduleLoadError('wv-visit-detail', err); });
|
||||||
|
}
|
||||||
|
if (tab === 'vaxschedule' && !_vaxInited) {
|
||||||
|
_vaxInited = true;
|
||||||
|
ensureScheduleData().then(renderFullSchedule).catch(function(err) { showScheduleLoadError('wv-full-schedule', err); });
|
||||||
|
}
|
||||||
|
if (tab === 'catchup' && !_catchupInited) {
|
||||||
|
_catchupInited = true;
|
||||||
|
ensureScheduleData().then(renderCatchUp).catch(function(err) { showScheduleLoadError('wv-catchup', err); });
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
|
|
||||||
32
public/js/wellVisit/scheduleData.js
Normal file
32
public/js/wellVisit/scheduleData.js
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
var scheduleDataCache = null;
|
||||||
|
|
||||||
|
export function loadWellVisitScheduleData() {
|
||||||
|
if (scheduleDataCache) return Promise.resolve(scheduleDataCache);
|
||||||
|
|
||||||
|
return fetch('/data/well-visit/schedule.json', { credentials: 'same-origin' })
|
||||||
|
.then(function(response) {
|
||||||
|
if (!response.ok) throw new Error('Failed to load well-visit schedule data');
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then(function(data) {
|
||||||
|
scheduleDataCache = data;
|
||||||
|
return scheduleDataCache;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyWellVisitScheduleGlobals(data, target) {
|
||||||
|
var scope = target || globalThis;
|
||||||
|
scope.VISIT_AGES = data.visitAges;
|
||||||
|
scope.WELL_VISIT_CODES = data.wellVisitCodes;
|
||||||
|
scope.VACCINES = data.vaccines;
|
||||||
|
scope.PERIODICITY = data.periodicity;
|
||||||
|
scope.CATCH_UP_SCHEDULE = data.catchUpSchedule;
|
||||||
|
scope.ANTICIPATORY_GUIDANCE = data.anticipatoryGuidance;
|
||||||
|
scope.AAP_FOOTNOTES = data.aapFootnotes;
|
||||||
|
scope.DYSLIPIDEMIA_SCHEDULE = data.dyslipidemiaSchedule;
|
||||||
|
scope.NEWBORN_SCREENING = data.newbornScreening;
|
||||||
|
scope.GROWTH_REFERENCE = data.growthReference;
|
||||||
|
scope.REFLEXES_REFERENCE = data.reflexesReference;
|
||||||
|
scope.BMI_CLASSIFICATION = data.bmiClassification;
|
||||||
|
return scope;
|
||||||
|
}
|
||||||
83
test/well-visit-data.test.js
Normal file
83
test/well-visit-data.test.js
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
const { test } = require('node:test');
|
||||||
|
const assert = require('node:assert/strict');
|
||||||
|
const fs = require('node:fs');
|
||||||
|
const path = require('node:path');
|
||||||
|
|
||||||
|
function loadScheduleJson() {
|
||||||
|
const raw = fs.readFileSync(path.join(__dirname, '..', 'public', 'data', 'well-visit', 'schedule.json'), 'utf8');
|
||||||
|
return JSON.parse(raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadLegacyScheduleData() {
|
||||||
|
return require('../public/js/pediatricScheduleData.js');
|
||||||
|
}
|
||||||
|
|
||||||
|
test('schedule.json exactly mirrors existing pediatric schedule constants', () => {
|
||||||
|
const json = loadScheduleJson();
|
||||||
|
const legacy = loadLegacyScheduleData();
|
||||||
|
|
||||||
|
assert.equal(json.version, '2025.1');
|
||||||
|
assert.deepEqual(json.visitAges, legacy.VISIT_AGES);
|
||||||
|
assert.deepEqual(json.wellVisitCodes, legacy.WELL_VISIT_CODES);
|
||||||
|
assert.deepEqual(json.vaccines, legacy.VACCINES);
|
||||||
|
assert.deepEqual(json.periodicity, legacy.PERIODICITY);
|
||||||
|
assert.deepEqual(json.catchUpSchedule, legacy.CATCH_UP_SCHEDULE);
|
||||||
|
assert.deepEqual(json.anticipatoryGuidance, legacy.ANTICIPATORY_GUIDANCE);
|
||||||
|
assert.deepEqual(json.aapFootnotes, legacy.AAP_FOOTNOTES);
|
||||||
|
assert.deepEqual(json.dyslipidemiaSchedule, legacy.DYSLIPIDEMIA_SCHEDULE);
|
||||||
|
assert.deepEqual(json.newbornScreening, legacy.NEWBORN_SCREENING);
|
||||||
|
assert.deepEqual(json.growthReference, legacy.GROWTH_REFERENCE);
|
||||||
|
assert.deepEqual(json.reflexesReference, legacy.REFLEXES_REFERENCE);
|
||||||
|
assert.deepEqual(json.bmiClassification, legacy.BMI_CLASSIFICATION);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('schedule.json preserves expected well-visit anchors', () => {
|
||||||
|
const data = loadScheduleJson();
|
||||||
|
|
||||||
|
assert.equal(data.visitAges.length, 32);
|
||||||
|
assert.deepEqual(data.visitAges[0], { id: 'prenatal', label: 'Prenatal', era: 'prenatal' });
|
||||||
|
assert.deepEqual(data.visitAges[data.visitAges.length - 1], { id: '21y', label: '21 Years', era: 'adolescence' });
|
||||||
|
assert.equal(data.wellVisitCodes['2mo'].cpt, '99391');
|
||||||
|
assert.equal(data.periodicity['12mo'].procedures.lead, 'dot_or_risk');
|
||||||
|
assert.equal(data.periodicity['11y'].developmental.depressionSuicideRisk, 'dot');
|
||||||
|
assert.equal(data.catchUpSchedule.DTaP.series[4].minimumAge, '4 years');
|
||||||
|
assert.equal(data.growthReference['2mo'].feeding.length, 4);
|
||||||
|
assert.equal(data.reflexesReference.newborn.reflexes[2].name, 'Moro (startle)');
|
||||||
|
assert.equal(data.bmiClassification.categories[3].label, 'Obesity (Class I)');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('well-visit schedule loader fetches JSON and can apply legacy globals', async () => {
|
||||||
|
const data = loadScheduleJson();
|
||||||
|
const previousFetch = globalThis.fetch;
|
||||||
|
globalThis.fetch = async function(url, options) {
|
||||||
|
assert.equal(url, '/data/well-visit/schedule.json');
|
||||||
|
assert.deepEqual(options, { credentials: 'same-origin' });
|
||||||
|
return {
|
||||||
|
ok: true,
|
||||||
|
async json() {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const module = await import('../public/js/wellVisit/scheduleData.js');
|
||||||
|
const loaded = await module.loadWellVisitScheduleData();
|
||||||
|
assert.strictEqual(loaded, data);
|
||||||
|
|
||||||
|
const target = {};
|
||||||
|
module.applyWellVisitScheduleGlobals(loaded, target);
|
||||||
|
assert.strictEqual(target.VISIT_AGES, data.visitAges);
|
||||||
|
assert.strictEqual(target.PERIODICITY, data.periodicity);
|
||||||
|
assert.strictEqual(target.CATCH_UP_SCHEDULE, data.catchUpSchedule);
|
||||||
|
assert.strictEqual(target.BMI_CLASSIFICATION, data.bmiClassification);
|
||||||
|
} finally {
|
||||||
|
globalThis.fetch = previousFetch;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('index loads wellVisit as a module without the legacy schedule data script', () => {
|
||||||
|
const indexHtml = fs.readFileSync(path.join(__dirname, '..', 'public', 'index.html'), 'utf8');
|
||||||
|
assert.match(indexHtml, /<script type="module" src="\/js\/wellVisit\.js"><\/script>/);
|
||||||
|
assert.doesNotMatch(indexHtml, /src="\/js\/pediatricScheduleData\.js"/);
|
||||||
|
});
|
||||||
Loading…
Reference in a new issue