2 * @package OpenEMR CCDAServer
3 * @link http://www.open-emr.org
5 * @author Jerry Padgett <sjpadgett@gmail.com>
6 * @copyright Copyright (c) 2016-2024 Jerry Padgett <sjpadgett@gmail.com>
7 * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3
12 const enableDebug = false;
14 const net = require('net');
15 const server = net.createServer();
16 const to_json = require('xmljson').to_json;
17 const bbg = require(__dirname + '/oe-blue-button-generate');
18 const fs = require('fs');
19 const { DataStack } = require('./data-stack/data-stack');
20 const { cleanCode } = require('./utils/clean-code/clean-code');
21 const { safeTrim } = require('./utils/safe-trim/safe-trim');
22 const { headReplace } = require('./utils/head-replace/head-replace');
23 const { fDate, templateDate } = require('./utils/date/date');
24 const { countEntities } = require('./utils/count-entities/count-entities');
25 const { populateTimezones } = require('./utils/timezones/timezones');
29 } = require('./utils/demographics/populate-demographics');
30 const { populateProvider } = require('./utils/providers/providers');
32 var conn = ''; // make our connection scope global to script
38 var authorDateTime = '';
39 var documentLocation = '';
41 function populateProviders(all) {
42 let providerArray = [];
44 let provider = populateProvider(all.primary_care_provider.provider, all);
45 providerArray.push(provider);
46 let count = countEntities(all.care_team.provider);
48 provider = populateProvider(all.care_team.provider, all);
49 providerArray.push(provider);
50 } else if (count > 1) {
51 for (let i in all.care_team.provider) {
52 provider = populateProvider(all.care_team.provider[i], all);
53 providerArray.push(provider);
61 "date": fDate(all.time_start) || fDate(""),
65 "date": fDate(all.time_end) || fDate(""),
70 "name": all.primary_diagnosis.text || "",
71 "code": cleanCode(all.primary_diagnosis.code || ""),
72 "code_system_name": all.primary_diagnosis.code_type || ""
74 "provider": providerArray,
80 function populateCareTeamMember(provider) {
82 //"function_code": provider.physician_type ? "PP" : "",
84 "xmlns": "urn:hl7-org:sdtc",
85 "name": provider.taxonomy_description || "",
86 "code": cleanCode(provider.taxonomy) || "",
87 "code_system": "2.16.840.1.113883.6.101",
88 "code_system_name": "NUCC Health Care Provider Taxonomy"
93 "date": fDate(provider.provider_since) || fDate(""),
99 "identifier": provider.npi ? "2.16.840.1.113883.4.6" : oidFacility,
100 "extension": provider.npi || provider.table_id
103 "full_name": provider.fname + " " + provider.lname,
105 "last": provider.lname || "",
106 "first": provider.fname || ""
112 "city": provider.city,
113 "state": provider.state,
115 "country": all.encounter_provider.facility_country_code || "US"
119 "number": provider.telecom,
126 function populateAuthorFromAuthorContainer(pd) {
127 let author = pd.author || {};
130 "name": author.physician_type || '',
131 "code": author.physician_type_code || '',
132 "code_system": author.physician_type_system,
133 "code_system_name": author.physician_type_system_name
137 "date": fDate(author.time),
143 "identifier": author.npi ? "2.16.840.1.113883.4.6" : author.id,
144 "extension": author.npi ? author.npi : 'NI'
149 "last": author.lname || "",
150 "first": author.fname || ""
157 "root": author.facility_oid || "2.16.840.1.113883.4.6",
158 "extension": author.facility_npi || "NI"
162 author.facility_name || ""
169 function populateCareTeamMembers(pd) {
170 let providerArray = [];
172 let primaryCareProvider = pd.primary_care_provider || {provider: {}};
173 let providerSince = fDate(primaryCareProvider.provider.provider_since || '');
174 if (pd.primary_care_provider) {
175 let provider = populateCareTeamMember(pd.primary_care_provider.provider);
176 providerArray.push(provider);
177 let count = countEntities(pd.care_team.provider);
179 provider = populateCareTeamMember(pd.care_team.provider);
180 providerSince = providerSince || fDate(provider.provider_since);
181 providerArray.push(provider);
182 } else if (count > 1) {
183 for (let i in pd.care_team.provider) {
184 provider = populateCareTeamMember(pd.care_team.provider[i]);
185 providerSince = providerSince || fDate(provider.provider_since);
186 providerArray.push(provider);
193 "provider": providerArray,
198 "date": providerSince || fDate(""),
202 // we treat this author a bit differently since we are working at the main pd object instead of the sub pd.care_team
203 "author": populateAuthorFromAuthorContainer(pd.care_team)
207 function populateMedication(pd) {
208 pd.status = 'Completed'; //@todo invoke prescribed
212 "date": fDate(pd.start_date),
216 "date": fDate(pd.end_date),
221 "identifier": pd.sha_extension,
222 "extension": pd.extension || ""
228 "identifier": pd.sha_extension || "2a620155-9d11-439e-92b3-5d9815ff4ee8",
229 "extension": pd.extension + 1 || ""
231 "unencoded_name": pd.drug,
234 "code": cleanCode(pd.rxnorm),
235 "code_system_name": "RXNORM"
239 "code_system_name": "RXNORM"
246 "name": pd.author.physician_type || '',
247 "code": pd.author.physician_type_code || '',
248 "code_system": pd.author.physician_type_system, "code_system_name": pd.author.physician_type_system_name
252 "date": fDate(pd.author.time),
258 "identifier": pd.author.npi ? "2.16.840.1.113883.4.6" : pd.author.id,
259 "extension": pd.author.npi ? pd.author.npi : 'NI'
264 "last": pd.author.lname,
265 "first": pd.author.fname
272 "root": pd.author.facility_oid || "2.16.840.1.113883.4.6",
273 "extension": pd.author.facility_npi || "NI"
277 pd.author.facility_name
285 "date": fDate(pd.start_date),
289 "date": fDate(pd.end_date),
297 "identifier": pd.sha_extension || "2a620155-9d11-439e-92b3-5d9815ff4ee8",
298 "extension": pd.extension + 1 || ""
300 "unencoded_name": pd.drug,
303 "code": cleanCode(pd.rxnorm),
307 "code_system_name": "RXNORM"
309 "code_system_name": "RXNORM"
315 "name": all.author.physician_type || '',
316 "code": all.author.physician_type_code || '',
317 "code_system": all.author.physician_type_system, "code_system_name": all.author.physician_type_system_name
321 "date": authorDateTime,
327 "identifier": all.author.npi ? "2.16.840.1.113883.4.6" : all.author.id,
328 "extension": all.author.npi ? all.author.npi : 'NI'
333 "last": all.author.lname,
334 "first": all.author.fname
341 "root": oidFacility || "2.16.840.1.113883.4.6",
342 "extension": npiFacility || ""
346 all.encounter_provider.facility_name
353 "name": "instruction",
355 "code_system_name": "SNOMED CT"
357 "free_text": pd.instructions || "No Instructions"
362 "name": pd.route || "",
363 "code": cleanCode(pd.route_code) || "",
364 "code_system_name": "Medication Route FDA"
368 "code": cleanCode(pd.form_code),
369 "code_system_name": "Medication Route FDA"
372 "value": parseFloat(pd.size),
376 "value": parseFloat(pd.dosage),
381 "value": parseFloat(pd.dosage),
389 "identifier": "2.16.840.1.113883.4.6",
390 "extension": pd.npi || ""
394 "identifier": pd.sha_extension,
395 "extension": pd.extension || ""
397 "name": [pd.performer_name]
402 "code": cleanCode(pd.form_code),
403 "code_system_name": "RXNORM"
408 "code_system_name": "ActCode"
413 "code_system_name": "SNOMED CT"
418 "identifier": "db734647-fc99-424c-a864-7e3cda82e703",
424 "code_system_name": "SNOMED CT"
428 "date": fDate(pd.start_date),
433 "name": pd.indications,
434 "code": pd.indications_code,
435 "code_system_name": "SNOMED CT"
440 "identifier": "1.2.3.4.56789.1",
441 "extension": "cb734647-fc99-424c-a864-7e3cda82e704"
445 "identifier": "2.16.840.1.113883.19.5.9999.456",
446 "extension": "2981823"
449 "street_lines": [pd.address],
457 "identifier": "2.16.840.1.113883.19.5.9999.1393"
459 "name": [pd.performer_name]
464 "identifier": "2a620155-9d11-439e-92b3-5d9815ff4ee8"
466 "unencoded_name": pd.drug,
473 "code_system_name": "RXNORM"
475 "code_system_name": "RXNORM"
483 function getFinding(pd, problem) {
486 "identifier": pd.sha_extension,
492 "code_system_name": ''
501 "reason": pd.encounter_reason,
504 "name": all.author.physician_type || '',
505 "code": all.author.physician_type_code || '',
506 "code_system": all.author.physician_type_system, "code_system_name": all.author.physician_type_system_name
510 "date": authorDateTime,
516 "identifier": all.author.npi ? "2.16.840.1.113883.4.6" : all.author.id,
517 "extension": all.author.npi ? all.author.npi : 'UNK'
522 "last": all.author.lname,
523 "first": all.author.fname
530 "root": oidFacility || "2.16.840.1.113883.4.6",
531 "extension": npiFacility || ""
535 all.encounter_provider.facility_name
542 finding.identifiers["0"].extension = problem.extension;
543 finding.date_time.low.date = fDate(problem.date);
544 finding.value.name = problem.text;
545 finding.value.code = cleanCode(problem.code);
546 finding.value.code_system_name = problem.code_type;
547 finding.status = problem.status;
551 function populateEncounter(pd) {
552 // just to get diagnosis. for findings..
557 count = countEntities(pd.encounter_problems.problem);
562 for (let i in pd.encounter_problems.problem) {
563 theone[i] = getFinding(pd, pd.encounter_problems.problem[i]);
564 findingObj.push(theone[i]);
566 } else if (count !== 0 && pd.encounter_problems.problem.code > '') {
567 let finding = getFinding(pd, pd.encounter_problems.problem);
568 findingObj.push(finding);
573 "name": pd.visit_category ? (pd.visit_category + " | " + pd.encounter_reason) : pd.code_description,
574 "code": pd.code || "185347001",
575 //"code_system": "2.16.840.1.113883.6.96",
576 "code_system_name": pd.code_type || "SNOMED CT",
578 "name": "Ambulatory",
580 "code_system_name": "ActCode"
584 "identifier": pd.sha_extension,
585 "extension": pd.extension
589 "date": fDate(pd.date),
595 "identifier": "2.16.840.1.113883.4.6",
596 "extension": pd.npi || ""
599 "name": pd.physician_type,
600 "code": cleanCode(pd.physician_type_code),
601 "code_system_name": pd.physician_code_type
605 "last": pd.lname || "",
606 "first": pd.fname || ""
611 "number": pd.work_phone,
619 "name": pd.location_details,
621 "code_system_name": "HealthcareServiceLocation"
624 "street_lines": [pd.facility_address],
625 "city": pd.facility_city,
626 "state": pd.facility_state,
627 "zip": pd.facility_zip,
628 "country": pd.facility_country || "US"
632 "number": pd.facility_phone,
637 "findings": findingObj
641 function populateAllergy(pd) {
645 "no_know_allergies": "No Known Allergies",
647 "low": templateDate("", "day"),
648 //"high": templateDate(pd.enddate, "day")
652 let allergyAuthor = {
654 "name": pd.author.physician_type || '',
655 "code": pd.author.physician_type_code || '',
656 "code_system": pd.author.physician_type_system, "code_system_name": pd.author.physician_type_system_name
660 "date": fDate(pd.author.time),
666 "identifier": pd.author.npi ? "2.16.840.1.113883.4.6" : pd.author.id,
667 "extension": pd.author.npi ? pd.author.npi : 'NI'
672 "last": pd.author.lname,
673 "first": pd.author.fname
680 "root": pd.author.facility_oid || "2.16.840.1.113883.4.6",
681 "extension": pd.author.facility_npi || "NI"
685 pd.author.facility_name
693 "identifier": pd.sha_id,
694 "extension": pd.id || ""
697 "low": templateDate(pd.startdate, "day"),
698 //"high": templateDate(pd.enddate, "day")
700 "author": allergyAuthor,
703 "identifier": pd.sha_extension || "2a620155-9d11-439e-92b3-5d9815ff4ee8",
704 "extension": pd.id + 1 || ""
706 "author": allergyAuthor,
708 "name": pd.title || "",
709 "code": pd.rxnorm_code_text ? cleanCode(pd.rxnorm_code) : pd.snomed_code_text ? cleanCode(pd.snomed_code) : cleanCode(""),
710 "code_system_name": pd.rxnorm_code_text ? "RXNORM" : pd.snomed_code_text ? "SNOMED CT" : ""
714 "date": fDate(pd.startdate) || fDate(""),
719 "name": "Propensity to adverse reactions to drug",
721 "code_system_name": "SNOMED CT"
725 "name": pd.outcome || "",
726 "code": cleanCode(pd.outcome_code) || "",
727 "code_system_name": "SNOMED CT"
731 "name": pd.status_table || "",
732 "code": cleanCode(pd.status_code),
733 "code_system_name": "SNOMED CT"
737 "identifier": "4adc1020-7b14-11db-9fe1-0800200c9a64"
740 "low": templateDate(pd.startdate, "day"),
741 "high": templateDate(pd.enddate, "day")
744 "name": pd.reaction_text,
745 "code": cleanCode(pd.reaction_code) || "",
746 "code_system_name": pd.reaction_code_type || "SNOMED CT"
750 "name": pd.outcome || "",
751 "code": cleanCode(pd.outcome_code),
752 "code_system_name": "SNOMED CT"
760 function populateProblem(pd) {
761 let primary_care_provider = all.primary_care_provider || {provider: {}};
765 "date": fDate(pd.start_date_table),
769 "date": fDate(pd.end_date),
774 "identifier": pd.sha_extension,
775 "extension": pd.extension || ""
780 "code_system_name": "LOINC"
784 "name": safeTrim(pd.title),
785 "code": cleanCode(pd.code),
786 "code_system_name": safeTrim(pd.code_type)
790 "date": fDate(pd.start_date),
794 "date": fDate(pd.end_date),
795 "precision": getPrecision()
801 "name": pd.author.physician_type || '',
802 "code": pd.author.physician_type_code || '',
803 "code_system": pd.author.physician_type_system, "code_system_name": pd.author.physician_type_system_name
807 "date": fDate(pd.author.time),
813 "identifier": pd.author.npi ? "2.16.840.1.113883.4.6" : pd.author.id,
814 "extension": pd.author.npi ? pd.author.npi : 'NI'
819 "last": pd.author.lname,
820 "first": pd.author.fname
827 "root": pd.author.facility_oid || "2.16.840.1.113883.4.6",
828 "extension": pd.author.facility_npi || "NI"
832 pd.author.facility_name
841 "identifier": "2.16.840.1.113883.4.6",
842 "extension": primary_care_provider.provider.npi || ""
847 "last": primary_care_provider.provider.lname || "",
848 "first": primary_care_provider.provider.fname || ""
853 "onset_age_unit": "Year",
855 "name": pd.status_table,
858 "date": fDate(pd.start_date),
862 "date": fDate(pd.end_date),
863 "precision": getPrecision()
867 "patient_status": pd.observation,
868 "source_list_identifiers": [{
869 "identifier": pd.sha_extension,
870 "extension": pd.extension || ""
876 function populateProcedure(pd) {
879 "name": pd.description,
880 "code": cleanCode(pd.code),
881 //"code_system": "2.16.840.1.113883.6.12",
882 "code_system_name": pd.code_type
885 "identifier": "d68b7e32-7810-4f5b-9cc2-acd54b0fd85d",
886 "extension": pd.extension
888 "status": "completed",
891 "date": fDate(pd.date),
898 "code_system_name": ""
902 "identifier": "c2ee9ee9-ae31-4628-a919-fec1cbb58683"
907 "code_system_name": "SNOMED CT"
912 "identifier": "2.16.840.1.113883.4.6",
913 "extension": pd.npi || ""
916 "street_lines": [pd.address],
923 "number": pd.work_phone,
928 "identifier": pd.facility_sha_extension,
929 "extension": pd.facility_extension
931 "name": [pd.facility_name],
933 "street_lines": [pd.facility_address],
934 "city": pd.facility_city,
935 "state": pd.facility_state,
936 "zip": pd.facility_zip,
937 "country": pd.facility_country || "US"
940 "number": pd.facility_phone,
945 "author": populateAuthorFromAuthorContainer(pd),
946 "procedure_type": "procedure"
950 function populateMedicalDevice(pd) {
953 "identifier": pd.sha_extension,
954 "extension": pd.extension
958 "date": fDate(pd.start_date),
962 "date": fDate(pd.end_date),
966 "device_type": "UDI",
968 "name": pd.code_text,
969 "code": cleanCode(pd.code),
970 "code_system_name": "SNOMED CT",
972 "identifier": "2.16.840.1.113883.3.3719",
975 "status": "completed",
979 "code_system_name": ""
985 "name": pd.author.physician_type || '',
986 "code": pd.author.physician_type_code || '',
987 "code_system": pd.author.physician_type_system, "code_system_name": pd.author.physician_type_system_name
991 "date": fDate(pd.author.time),
997 "identifier": pd.author.npi ? "2.16.840.1.113883.4.6" : pd.author.id,
998 "extension": pd.author.npi ? pd.author.npi : 'NI'
1003 "last": pd.author.lname,
1004 "first": pd.author.fname
1011 "root": pd.author.facility_oid || "2.16.840.1.113883.4.6",
1012 "extension": pd.author.facility_npi || "NI"
1016 pd.author.facility_name
1024 function populateResult(pd) {
1025 let icode = pd.subtest.abnormal_flag;
1026 let value = parseFloat(pd.subtest.result_value) || pd.subtest.result_value || "";
1027 let type = isNaN(value) ? "ST" : "PQ";
1028 type = !pd.subtest.unit ? "ST" : type;
1030 let range_type = pd.subtest.range.toUpperCase() == "NEGATIVE" ? "CO" : type;
1031 type = value.toUpperCase() == "NEGATIVE" ? "CO" : type;
1033 switch (pd.subtest.abnormal_flag.toUpperCase()) {
1046 "identifier": pd.subtest.root,
1047 "extension": pd.subtest.extension
1051 "code": cleanCode(pd.subtest.result_code) || "",
1052 "code_system_name": "LOINC"
1056 "date": fDate(pd.date_ordered),
1060 "status": pd.order_status,
1061 "reference_range": {
1062 "low": pd.subtest.low,
1063 "high": pd.subtest.high,
1064 "unit": pd.subtest.unit,
1066 "range_type": range_type
1068 "value": value + "",
1069 "unit": pd.subtest.unit,
1071 "range": pd.subtest.range,
1072 "range_type": range_type
1074 // interpretation cannot be an empty value so we skip it if it is
1075 // empty as Observation.interpretationCode is [0..*]
1077 result["interpretations"] = [icode];
1082 function getResultSet(results) {
1084 if (!results) return '';
1086 // not sure if the result set should be grouped better on the backend as the author information needs to be more nuanced here
1087 let tResult = results.result[0] || results.result;
1090 "identifier": tResult.root,
1091 "extension": tResult.extension
1093 "author": populateAuthorFromAuthorContainer(tResult),
1095 "name": tResult.test_name,
1096 "code": cleanCode(tResult.test_code),
1097 "code_system_name": "LOINC"
1106 count = countEntities(results.result);
1111 for (let i in results.result) {
1112 theone[i] = populateResult(results.result[i]);
1113 many.results.push(theone[i]);
1115 } else if (count !== 0) {
1116 theone = populateResult(results.result);
1117 many.results.push(theone);
1119 rs.results = Object.assign(resultSet);
1120 rs.results.results = Object.assign(many.results);
1124 function getPlanOfCare(pd) {
1127 let code_system_name = "";
1128 let status = "Active";
1132 let planType = "observation";
1133 switch (pd.care_plan_type) {
1134 case 'plan_of_care':
1135 planType = "observation"; // mood code INT. sets code in template
1137 case 'test_or_order':
1138 planType = "observation"; // mood code RQO
1141 planType = "procedure";
1143 case 'appointments':
1144 planType = "encounter";
1146 case 'instructions':
1147 planType = "instructions";
1150 planType = ""; // for now exclude. unsure how to template.
1153 planType = "observation";
1155 if (pd.code_type === 'RXCUI') {
1156 pd.code_type = 'RXNORM';
1158 if (pd.code_type === 'RXNORM') {
1159 planType = "substanceAdministration";
1161 if (planType === "") {
1165 for (let key in all.encounter_list.encounter) {
1166 // skip loop if the property is from prototype
1167 if (!Object.prototype.hasOwnProperty.call(all.encounter_list.encounter, key)) {
1170 encounter = all.encounter_list.encounter[key];
1171 if (pd.encounter == encounter.encounter_id) {
1173 name = encounter.encounter_diagnosis.text;
1174 code = cleanCode(encounter.encounter_diagnosis.code);
1175 code_system_name = encounter.encounter_diagnosis.code_type;
1176 status = encounter.encounter_diagnosis.status;
1177 encounter = all.encounter_list.encounter[key]; // to be sure.
1183 if (all.encounter_list && all.encounter_list.encounter && all.encounter_list.encounter.encounter_diagnosis) {
1184 value = all.encounter_list.encounter.encounter_diagnosis;
1187 code = cleanCode(value.code);
1188 code_system_name = value.code_type;
1189 status = value.status;
1190 encounter = all.encounter_list.encounter;
1195 "name": pd.code_text || "",
1196 "code": cleanCode(pd.code) || "",
1197 "code_system_name": pd.code_type || "SNOMED CT"
1200 "identifier": pd.sha_extension,
1201 "extension": pd.extension || ""
1204 "code": cleanCode(pd.code) || "",
1205 "name": safeTrim(pd.description) || ""
1209 "date": fDate(pd.date),
1215 "code": cleanCode(pd.status)
1217 "author": populateAuthorFromAuthorContainer(pd),
1220 "identifier": "2.16.840.1.113883.4.6",
1221 "extension": encounter.npi || ""
1224 "name": encounter.physician_type,
1225 "code": cleanCode(encounter.physician_type_code),
1226 "code_system_name": "SNOMED CT"
1230 "last": encounter.lname || "",
1231 "first": encounter.fname || ""
1236 "number": encounter.work_phone,
1237 "type": "work place"
1242 "name": encounter.location,
1244 "name": encounter.location_details,
1246 "code_system_name": "HealthcareServiceLocation"
1249 "street_lines": [encounter.facility_address],
1250 "city": encounter.facility_city,
1251 "state": encounter.facility_state,
1252 "zip": encounter.facility_zip,
1253 "country": encounter.facility_country || "US"
1257 "number": encounter.facility_phone,
1258 "type": "work place"
1264 "identifier": encounter.sha_extension,
1265 "extension": encounter.extension
1270 "code_system_name": code_system_name
1274 "date": fDate(encounter.date),
1279 "reason": encounter.encounter_reason
1281 "name": safeTrim(pd.description),
1282 "mood_code": pd.moodCode
1286 function getGoals(pd) {
1289 "name": pd.code_text !== "NULL" ? pd.code_text : "",
1290 "code": cleanCode(pd.code) || "",
1291 "code_system_name": pd.code_type || ""
1294 "identifier": pd.sha_extension,
1295 "extension": pd.extension,
1299 "date": fDate(pd.date),
1303 "type": "observation",
1305 "code": "active", //cleanCode(pd.status)
1307 "author": populateAuthorFromAuthorContainer(pd),
1308 "name": pd.description
1312 function getFunctionalStatus(pd) {
1313 let functionalStatusAuthor = {
1315 "name": all.author.physician_type || '',
1316 "code": all.author.physician_type_code || '',
1317 "code_system": all.author.physician_type_system, "code_system_name": all.author.physician_type_system_name
1321 "date": authorDateTime,
1327 "identifier": all.author.npi ? "2.16.840.1.113883.4.6" : all.author.id,
1328 "extension": all.author.npi ? all.author.npi : 'NI'
1333 "last": all.author.lname,
1334 "first": all.author.fname
1341 "root": oidFacility || "2.16.840.1.113883.4.6",
1342 "extension": npiFacility || ""
1346 all.encounter_provider.facility_name
1353 "status": "completed",
1354 "author": functionalStatusAuthor,
1356 "identifier": "9a6d1bac-17d3-4195-89a4-1121bc809000",
1357 "extension": pd.extension || '',
1362 "name": pd.code_text !== "NULL" ? safeTrim(pd.code_text) : "",
1363 "code": cleanCode(pd.code) || "",
1364 "code_system_name": pd.code_type || "SNOMED-CT"
1367 "identifier": "9a6d1bac-17d3-4195-89a4-1121bc8090ab",
1368 "extension": pd.extension || '',
1372 "date": fDate(pd.date),
1376 "status": "completed",
1377 "author": functionalStatusAuthor
1382 function getMentalStatus(pd) {
1385 "name": pd.code_text !== "NULL" ? pd.code_text : "",
1386 "code": cleanCode(pd.code) || "",
1387 "code_system_name": pd.code_type || ""
1390 "identifier": "9a6d1bac-17d3-4195-89a4-1121bc809ccc",
1391 "extension": pd.extension,
1393 "note": safeTrim(pd.description),
1395 "low": templateDate(pd.date, "day")
1396 //"high": templateDate(pd.date, "day")
1400 "name": all.author.physician_type || '',
1401 "code": all.author.physician_type_code || '',
1402 "code_system": all.author.physician_type_system, "code_system_name": all.author.physician_type_system_name
1406 "date": authorDateTime,
1412 "identifier": all.author.npi ? "2.16.840.1.113883.4.6" : all.author.id,
1413 "extension": all.author.npi ? all.author.npi : 'NI'
1418 "last": all.author.lname,
1419 "first": all.author.fname
1426 "root": oidFacility || "2.16.840.1.113883.4.6",
1427 "extension": npiFacility || ""
1431 all.encounter_provider.facility_name
1439 function getAssessments(pd) {
1441 "description": safeTrim(pd.description),
1442 "author": populateAuthorFromAuthorContainer(pd)
1446 function getHealthConcerns(pd) {
1449 let problems = [], problem = {};
1450 if (countEntities(pd.issues.issue_uuid) !== 0) {
1451 for (let key in pd.issues.issue_uuid) {
1452 issue_uuid = pd.issues.issue_uuid[key];
1458 "identifier": issue_uuid
1461 problems.push(problem);
1465 if (pd.issues.issue_uuid) {
1468 "identifier": pd.issues.issue_uuid
1471 problems.push(problem);
1475 // todo need to make array of health concerns
1477 "text": safeTrim(pd.text),
1479 "name": pd.code_text || "",
1480 "code": cleanCode(pd.code) || "",
1481 "code_system_name": pd.code_type || "SNOMED CT"
1483 "author": populateAuthorFromAuthorContainer(pd),
1485 "identifier": pd.sha_extension,
1486 "extension": pd.extension,
1492 function getReferralReason(pd) {
1494 "reason": safeTrim(pd.text),
1495 "author": populateAuthorFromAuthorContainer(pd)
1499 function populateVital(pd) {
1502 "identifier": pd.sha_extension,
1503 "extension": pd.extension
1505 "status": "completed",
1508 "date": fDate(pd.effectivetime),
1512 // our list of vitals per organizer.
1515 "identifier": pd.sha_extension,
1516 "extension": pd.extension_bps
1519 "name": "Blood Pressure Systolic",
1521 "code_system_name": "LOINC"
1523 "status": "completed",
1526 "date": fDate(pd.effectivetime),
1530 "interpretations": ["Normal"],
1531 "value": parseFloat(pd.bps) || pd.bps,
1533 "author": populateAuthorFromAuthorContainer(pd),
1536 "identifier": pd.sha_extension,
1537 "extension": pd.extension_bpd
1540 "name": "Blood Pressure Diastolic",
1542 "code_system_name": "LOINC"
1544 "status": "completed",
1547 "date": fDate(pd.effectivetime),
1551 "interpretations": ["Normal"],
1552 "value": parseFloat(pd.bpd) || pd.bpd,
1554 "author": populateAuthorFromAuthorContainer(pd),
1557 "identifier": pd.sha_extension,
1558 "extension": pd.extension_height
1563 "code_system_name": "LOINC"
1565 "status": "completed",
1568 "date": fDate(pd.effectivetime),
1572 "interpretations": ["Normal"],
1573 "value": parseFloat(pd.height) || pd.height,
1574 "unit": pd.unit_height,
1575 "author": populateAuthorFromAuthorContainer(pd),
1578 "identifier": pd.sha_extension,
1579 "extension": pd.extension_weight
1582 "name": "Weight Measured",
1584 "code_system_name": "LOINC"
1586 "status": "completed",
1589 "date": fDate(pd.effectivetime),
1593 "interpretations": ["Normal"],
1594 "value": parseFloat(pd.weight) || "",
1595 "unit": pd.unit_weight,
1596 "author": populateAuthorFromAuthorContainer(pd),
1599 "identifier": pd.sha_extension,
1600 "extension": pd.extension_BMI
1603 "name": "BMI (Body Mass Index)",
1605 "code_system_name": "LOINC"
1607 "status": "completed",
1610 "date": fDate(pd.effectivetime),
1614 "interpretations": [pd.BMI_status == 'Overweight' ? 'High' : pd.BMI_status == 'Overweight' ? 'Low' : 'Normal'],
1615 "value": parseFloat(pd.BMI) || "",
1617 "author": populateAuthorFromAuthorContainer(pd),
1620 "identifier": pd.sha_extension,
1621 "extension": pd.extension_pulse
1624 "name": "Heart Rate",
1626 "code_system_name": "LOINC"
1628 "status": "completed",
1631 "date": fDate(pd.effectivetime),
1635 "interpretations": ["Normal"],
1636 "value": parseFloat(pd.pulse) || "",
1638 "author": populateAuthorFromAuthorContainer(pd),
1641 "identifier": "2.16.840.1.113883.3.140.1.0.6.10.14.2",
1642 "extension": pd.extension_breath
1645 "name": "Respiratory Rate",
1647 "code_system_name": "LOINC"
1649 "status": "completed",
1652 "date": fDate(pd.effectivetime),
1656 "interpretations": ["Normal"],
1657 "value": parseFloat(pd.breath) || "",
1659 "author": populateAuthorFromAuthorContainer(pd),
1662 "identifier": "2.16.840.1.113883.3.140.1.0.6.10.14.3",
1663 "extension": pd.extension_temperature
1666 "name": "Body Temperature",
1668 "code_system_name": "LOINC"
1670 "status": "completed",
1673 "date": fDate(pd.effectivetime),
1677 "interpretations": ["Normal"],
1678 "value": parseFloat(pd.temperature) || "",
1679 "unit": pd.unit_temperature,
1680 "author": populateAuthorFromAuthorContainer(pd),
1683 "identifier": pd.sha_extension,
1684 "extension": pd.extension_oxygen_saturation
1687 "name": "O2 % BldC Oximetry",
1689 "code_system_name": "LOINC"
1691 "status": "completed",
1694 "date": fDate(pd.effectivetime),
1698 "interpretations": ["Normal"],
1699 "value": parseFloat(pd.oxygen_saturation) || "",
1701 "author": populateAuthorFromAuthorContainer(pd),
1704 "identifier": pd.sha_extension,
1705 "extension": pd.extension_ped_weight_height
1707 "vital": { // --------------------------------------------------------------------------------
1708 "name": "Weight for Height Percentile",
1710 "code_system_name": "LOINC"
1712 "status": "completed",
1715 "date": fDate(pd.effectivetime),
1719 "interpretations": ["Normal"],
1720 "value": parseFloat(pd.ped_weight_height) || "",
1722 "author": populateAuthorFromAuthorContainer(pd),
1725 "identifier": pd.sha_extension,
1726 "extension": pd.extension_inhaled_oxygen_concentration
1729 "name": "Inhaled Oxygen Concentration",
1731 "code_system_name": "LOINC"
1733 "status": "completed",
1736 "date": fDate(pd.effectivetime),
1740 "interpretations": ["Normal"],
1741 "value": parseFloat(pd.inhaled_oxygen_concentration) || "",
1743 "author": populateAuthorFromAuthorContainer(pd),
1746 "identifier": pd.sha_extension,
1747 "extension": pd.extension_ped_bmi
1750 "name": "BMI Percentile",
1752 "code_system_name": "LOINC"
1754 "status": "completed",
1757 "date": fDate(pd.effectivetime),
1761 "interpretations": ["Normal"],
1762 "value": parseFloat(pd.ped_bmi) || "",
1764 "author": populateAuthorFromAuthorContainer(pd),
1767 "identifier": pd.sha_extension,
1768 "extension": pd.extension_ped_head_circ
1771 "name": "Head Occipital-frontal Circumference Percentile",
1773 "code_system_name": "LOINC"
1775 "status": "completed",
1778 "date": fDate(pd.effectivetime),
1782 "interpretations": ["Normal"],
1783 "value": parseFloat(pd.ped_head_circ) || "",
1785 "author": populateAuthorFromAuthorContainer(pd),
1791 function populateSocialHistory(pd) {
1794 "low": templateDate(pd.date, "day")
1795 //"high": templateDate(pd.date, "day")
1798 "identifier": pd.sha_extension,
1799 "extension": pd.extension
1804 "element": pd.element,
1805 "value": pd.description,
1806 "gender": all.patient.gender,
1809 "name": pd.author.physician_type || '',
1810 "code": pd.author.physician_type_code || '',
1811 "code_system": pd.author.physician_type_system, "code_system_name": pd.author.physician_type_system_name
1815 "date": fDate(pd.author.time),
1821 "identifier": pd.author.npi ? "2.16.840.1.113883.4.6" : pd.author.id,
1822 "extension": pd.author.npi ? pd.author.npi : 'NI'
1827 "last": pd.author.lname,
1828 "first": pd.author.fname
1835 "root": pd.author.facility_oid || "2.16.840.1.113883.4.6",
1836 "extension": pd.author.facility_npi || "NI"
1840 pd.author.facility_name
1845 , "gender_author": {
1847 "name": all.patient.author.physician_type || '',
1848 "code": all.patient.author.physician_type_code || '',
1849 "code_system": all.patient.author.physician_type_system, "code_system_name": all.patient.author.physician_type_system_name
1853 "date": fDate(all.patient.author.time),
1859 "identifier": all.patient.author.npi ? "2.16.840.1.113883.4.6" : all.patient.author.id,
1860 "extension": all.patient.author.npi ? all.patient.author.npi : 'NI'
1865 "last": all.patient.author.lname,
1866 "first": all.patient.author.fname
1873 "root": all.patient.author.facility_oid || "2.16.840.1.113883.4.6",
1874 "extension": all.patient.author.facility_npi || "NI"
1878 all.patient.author.facility_name
1886 function populateImmunization(pd) {
1890 "date": fDate(pd.administered_on),
1895 "identifier": pd.sha_extension,
1896 "extension": pd.extension || ""
1898 "status": "complete",
1901 "name": pd.code_text,
1902 "code": cleanCode(pd.cvx_code),
1903 "code_system_name": "CVX"
1904 /*"translations": [{
1907 "code_system_name": "CVX"
1915 "name": pd.route_of_administration,
1916 "code": cleanCode(pd.route_code) || "",
1917 "code_system_name": "Medication Route FDA"
1926 "identifier": "2.16.840.1.113883.4.6",
1927 "extension": pd.npi || ""
1934 "street_lines": [pd.address],
1942 "identifier": "2.16.840.1.113883.4.6",
1943 "extension": npiFacility || ""
1945 "name": [pd.facility_name]
1950 "name": "immunization education",
1951 "code": "171044003",
1952 "code_system_name": "SNOMED CT"
1954 "free_text": "Needs Attention for more data."
1958 "name": pd.author.physician_type || '',
1959 "code": pd.author.physician_type_code || '',
1960 "code_system": pd.author.physician_type_system, "code_system_name": pd.author.physician_type_system_name
1964 "date": fDate(pd.author.time),
1970 "identifier": pd.author.npi ? "2.16.840.1.113883.4.6" : pd.author.id,
1971 "extension": pd.author.npi ? pd.author.npi : 'NI'
1976 "last": pd.author.lname,
1977 "first": pd.author.fname
1984 "root": pd.author.facility_oid || "2.16.840.1.113883.4.6",
1985 "extension": pd.author.facility_npi || "NI"
1989 pd.author.facility_name
1997 function populatePayer(pd) {
2000 "identifier": "1fe2cdd0-7aad-11db-9fe1-0800200c9a66"
2004 "identifier": "3e676a50-7aac-11db-9fe1-0800200c9a66"
2008 "code_system_name": "HL7 RoleCode"
2013 "code_system_name": "HL7 RoleCode"
2017 "identifier": "2.16.840.1.113883.19"
2020 "street_lines": ["123 Insurance Road"],
2021 "city": "Blue Bell",
2028 "number": "(781)555-1515",
2029 "type": "work place"
2032 "name": ["Good Health Insurance"],
2034 "street_lines": ["123 Insurance Road"],
2035 "city": "Blue Bell",
2042 "number": "(781)555-1515",
2043 "type": "work place"
2048 "code_system_name": "HL7 RoleCode"
2056 "code_system_name": "HL7 Role"
2059 "identifier": "329fcdf0-7ab3-11db-9fe1-0800200c9a66"
2063 "middle": ["Frankie"],
2068 "street_lines": ["17 Daws Rd."],
2069 "city": "Blue Bell",
2073 "use": "primary home"
2076 "number": "(781)555-1212",
2077 "type": "primary home"
2084 "code_system_name": "HL7 Role"
2088 "identifier": "14d4a520-7aae-11db-9fe1-0800200c9a66",
2089 "extension": "1138345"
2092 "street_lines": ["17 Daws Rd."],
2093 "city": "Blue Bell",
2097 "use": "primary home"
2102 "code_system_name": "HL7 Role"
2115 "identifier": "2.16.840.1.113883.19",
2116 "extension": "1138345"
2119 "street_lines": ["17 Daws Rd."],
2120 "city": "Blue Bell",
2124 "use": "primary home"
2130 "identifier": "f4dce790-8328-11db-9fe1-0800200c9a66"
2134 "name": "Colonoscopy",
2136 "code_system_name": "SNOMED CT"
2143 function populateNote(pd) {
2147 "date": fDate(pd.date),
2152 code_system: "2.16.840.1.113883.6.1",
2153 code_system_name: "LOINC",
2154 code: cleanCode(pd.code),
2155 name: pd.code_text || ""
2157 "author": populateAuthorFromAuthorContainer(pd),
2158 "note": safeTrim(pd.description),
2162 function populateParticipant(participant) {
2165 "prefix": participant.prefix || "",
2166 "suffix": participant.suffix || "",
2167 "middle": [participant.mname] || "",
2168 "last": participant.lname || "",
2169 "first": participant.fname || ""
2171 "typeCode": participant.type || "",
2172 "classCode": "ASSIGNED",
2174 "name": participant.organization_taxonomy_description || "",
2175 "code": cleanCode(participant.organization_taxonomy) || "",
2176 "code_system": "2.16.840.1.113883.6.101",
2177 "code_system_name": "NUCC Health Care Provider Taxonomy"
2180 "identifier": participant.organization_npi ? "2.16.840.1.113883.4.6" : participant.organization_id,
2181 "extension": participant.organization_npi ? participant.organization_npi : ''
2185 "date": participant.date_time,
2191 "number": participant.phonew1 || "",
2200 "city": participant.city,
2201 "state": participant.state,
2202 "zip": participant.postalCode,
2203 "country": participant.country || "US",
2204 "use": participant.address_use || "WP"
2210 function populateHeader(pd) {
2211 // default doc type ToC CCD
2212 let name = "Summarization of Episode Note";
2213 let docCode = "34133-9";
2214 let docOid = "2.16.840.1.113883.10.20.22.1.2";
2215 if (pd.doc_type == 'referral') {
2216 name = "Referral Note";
2217 docCode = "57133-1";
2218 docOid = "2.16.840.1.113883.10.20.22.1.14";
2221 if (pd.doc_type == 'unstructured') {
2222 name = "Patient Documents";
2223 docCode = "34133-9";
2224 docOid = "2.16.840.1.113883.10.20.22.1.10";
2230 "identifier": oidFacility,
2231 "extension": "123456"
2237 "code_system_name": "LOINC"
2241 "extension": "2015-08-01"
2246 "date": fDate(pd.created_time_timezone),
2252 "name": all.author.physician_type || '',
2253 "code": all.author.physician_type_code || '',
2254 "code_system": all.author.physician_type_system, "code_system_name": all.author.physician_type_system_name
2258 "date": authorDateTime,
2264 "identifier": all.author.npi ? "2.16.840.1.113883.4.6" : all.author.id,
2265 "extension": all.author.npi ? all.author.npi : 'NI'
2270 "last": all.author.lname,
2271 "first": all.author.fname
2277 all.author.streetAddressLine
2279 "city": all.author.city,
2280 "state": all.author.state,
2281 "zip": all.author.postalCode,
2282 "country": all.author.country || "US",
2288 "number": all.author.telecom || "",
2296 "root": oidFacility || "2.16.840.1.113883.4.6",
2297 "extension": npiFacility || ""
2301 all.encounter_provider.facility_name
2306 all.encounter_provider.facility_street
2308 "city": all.encounter_provider.facility_city,
2309 "state": all.encounter_provider.facility_state,
2310 "zip": all.encounter_provider.facility_postal_code,
2311 "country": all.encounter_provider.facility_country_code || "US",
2317 "number": all.encounter_provider.facility_phone,
2318 "type": "work primary"
2327 "root": "2.16.840.1.113883.4.6",
2328 "extension": npiFacility || ""
2332 pd.custodian.organization || pd.custodian.name
2337 pd.custodian.streetAddressLine
2339 "city": pd.custodian.city,
2340 "state": pd.custodian.state,
2341 "zip": pd.custodian.postalCode,
2342 "country": pd.custodian.country || "US"
2347 "number": pd.custodian.telecom,
2348 "type": "work primary"
2352 "information_recipient": {
2354 "prefix": pd.information_recipient.prefix || "",
2355 "suffix": pd.information_recipient.suffix || "",
2356 "middle": [pd.information_recipient.mname] || "",
2357 "last": pd.information_recipient.lname || "",
2358 "first": pd.information_recipient.fname || ""
2361 "name": pd.information_recipient.organization || "org"
2365 let participants = [];
2366 let docParticipants = pd.document_participants || {participant: []};
2369 count = countEntities(docParticipants.participant);
2374 participants = [populateParticipant(docParticipants.participant)];
2376 // grab the values of our object
2377 participants = Object.values(docParticipants.participant).filter(pcpt => pcpt.type).map(pcpt => populateParticipant(pcpt));
2379 if (participants.length) {
2380 head.participants = participants;
2383 if (countEntities(all.encounter_list.encounter) === 1) {
2384 let primary_care_provider = pd.primary_care_provider || {provider: {}};
2385 head.component_of = {
2388 "identifier": oidFacility || "",
2389 "extension": "PT-" + (pd.patient.id || "")
2393 "name": pd.primary_diagnosis.text || "",
2394 "code": pd.primary_diagnosis.code || "",
2395 "code_system_name": pd.primary_diagnosis.code_type || ""
2399 "date": pd.primary_diagnosis.encounter_date || "",
2403 "date": pd.primary_diagnosis.encounter_end_date || "",
2407 "responsible_party": {
2408 "root": oidFacility,
2410 "last": pd.author.lname,
2411 "first": pd.author.fname
2414 "encounter_participant": {
2415 "root": oidFacility,
2417 "last": primary_care_provider.provider.lname || "",
2418 "first": primary_care_provider.provider.fname || ""
2423 pd.encounter_provider.facility_street
2425 "city": pd.encounter_provider.facility_city,
2426 "state": pd.encounter_provider.facility_state,
2427 "zip": pd.encounter_provider.facility_postal_code,
2428 "country": pd.encounter_provider.facility_country_code || "US",
2434 "number": pd.encounter_provider.facility_phone,
2435 "type": "work primary"
2445 function getMeta(pd) {
2448 "type": pd.doc_type,
2451 "identifier": oidFacility || "NI",
2452 "extension": "OE-DOC-0001"
2455 "confidentiality": "Normal",
2457 "identifier": oidFacility || "NI",
2458 "extension": "sOE-DOC-0001"
2465 / * function generateCcda
2466 /* The main document builder
2467 /* pd array the xml parsed array of data sent from CCM.
2469 function generateCcda(pd) {
2476 let primary_care_provider = all.primary_care_provider || {};
2477 npiProvider = primary_care_provider.provider ? primary_care_provider.provider.npi : "NI";
2478 oidFacility = all.encounter_provider.facility_oid ? all.encounter_provider.facility_oid : "2.16.840.1.113883.19.5.99999.1";
2479 npiFacility = getNpiFacility(pd, false);
2480 webRoot = all.serverRoot;
2481 documentLocation = all.document_location;
2483 authorDateTime = pd.created_time_timezone;
2484 if (pd.author.time.length > 7) {
2485 authorDateTime = pd.author.time;
2486 } else if (all.encounter_list && all.encounter_list.encounter) {
2487 if (countEntities(all.encounter_list.encounter) === 1) {
2488 authorDateTime = all.encounter_list.encounter.date;
2490 authorDateTime = all.encounter_list.encounter[0].date;
2494 authorDateTime = fDate(authorDateTime);
2496 let demographic = populateDemographics(pd, npiFacility);
2497 // This populates documentationOf. We are using providerOrganization also.
2498 if (pd.primary_care_provider) {
2499 Object.assign(demographic, populateProviders(pd));
2501 data.demographics = Object.assign(demographic);
2505 encs.encounters = [];
2507 count = countEntities(pd.encounter_list.encounter);
2512 for (let i in pd.encounter_list.encounter) {
2513 enc[i] = populateEncounter(pd.encounter_list.encounter[i]);
2514 encs.encounters.push(enc[i]);
2516 } else if (count !== 0) {
2517 enc = populateEncounter(pd.encounter_list.encounter);
2518 encs.encounters.push(enc);
2521 data.encounters = Object.assign(encs.encounters);
2528 count = countEntities(pd.history_physical.vitals_list.vitals);
2533 for (let i in pd.history_physical.vitals_list.vitals) {
2534 vitals[i] = populateVital(pd.history_physical.vitals_list.vitals[i]);
2535 vitals.vitals.push(vitals[i]);
2537 } else if (count !== 0) {
2538 vital = populateVital(pd.history_physical.vitals_list.vitals);
2539 vitals.vitals.push(vital);
2542 data.vitals = Object.assign(vitals.vitals);
2547 meds.medications = [];
2549 count = countEntities(pd.medications.medication);
2554 for (let i in pd.medications.medication) {
2555 m[i] = populateMedication(pd.medications.medication[i]);
2556 meds.medications.push(m[i]);
2558 } else if (count !== 0) {
2559 m = populateMedication(pd.medications.medication);
2560 meds.medications.push(m);
2563 data.medications = Object.assign(meds.medications);
2568 allergies.allergies = [];
2570 count = countEntities(pd.allergies.allergy);
2575 for (let i in pd.allergies.allergy) {
2576 allergy[i] = populateAllergy(pd.allergies.allergy[i]);
2577 allergies.allergies.push(allergy[i]);
2579 } else if (count <= 1) {
2580 allergy = populateAllergy(pd.allergies.allergy);
2581 allergies.allergies.push(allergy);
2585 data.allergies = Object.assign(allergies.allergies);
2590 problems.problems = [];
2592 count = countEntities(pd.problem_lists.problem);
2597 for (let i in pd.problem_lists.problem) {
2598 problem[i] = populateProblem(pd.problem_lists.problem[i], pd);
2599 problems.problems.push(problem[i]);
2601 } else if (count !== 0) {
2602 problem = populateProblem(pd.problem_lists.problem);
2603 problems.problems.push(problem);
2606 data.problems = Object.assign(problems.problems);
2611 many.procedures = [];
2613 count = countEntities(pd.procedures.procedure);
2618 for (let i in pd.procedures.procedure) {
2619 theone[i] = populateProcedure(pd.procedures.procedure[i]);
2620 many.procedures.push(theone[i]);
2622 } else if (count !== 0) {
2623 theone = populateProcedure(pd.procedures.procedure);
2624 many.procedures.push(theone);
2627 data.procedures = Object.assign(many.procedures);
2632 many.medical_devices = [];
2634 count = countEntities(pd.medical_devices.device);
2639 for (let i in pd.medical_devices.device) {
2640 theone[i] = populateMedicalDevice(pd.medical_devices.device[i]);
2641 many.medical_devices.push(theone[i]);
2643 } else if (count !== 0) {
2644 theone = populateMedicalDevice(pd.medical_devices.device);
2645 many.medical_devices.push(theone);
2648 data.medical_devices = Object.assign(many.medical_devices);
2652 data.results = Object.assign(getResultSet(pd.results, pd)['results']);
2655 // Referral TODO sjp I'm not happy with this.
2656 // different referral sources. 1st is dynamic with doc gen from CCM.
2657 // 2nd is the latest referral from transactions.
2658 if (pd.referral_reason[0].text !== "") {
2659 data.referral_reason = Object.assign(getReferralReason(pd.referral_reason[0], pd));
2660 } else if (pd.referral_reason[1].text !== "" && typeof pd.referral_reason[1].text !== 'undefined') {
2661 data.referral_reason = Object.assign(getReferralReason(pd.referral_reason[1], pd));
2663 data.referral_reason = {}; // leave as empty so we can get our null flavor section.
2668 many.health_concerns = [];
2670 count = countEntities(pd.health_concerns.concern);
2675 for (let i in pd.health_concerns.concern) {
2676 theone[i] = getHealthConcerns(pd.health_concerns.concern[i]);
2677 many.health_concerns.push(theone[i]);
2680 } else if (count !== 0) {
2681 theone = getHealthConcerns(pd.health_concerns.concern);
2682 many.health_concerns.push(theone);
2685 data.health_concerns = Object.assign(many.health_concerns);
2687 data.health_concerns = {"type": "act"}; // leave it as an empty section that we'll null flavor
2692 many.immunizations = [];
2694 count = countEntities(pd.immunizations.immunization);
2699 for (let i in pd.immunizations.immunization) {
2700 theone[i] = populateImmunization(pd.immunizations.immunization[i]);
2701 many.immunizations.push(theone[i]);
2703 } else if (count !== 0) {
2704 theone = populateImmunization(pd.immunizations.immunization);
2705 many.immunizations.push(theone);
2708 data.immunizations = Object.assign(many.immunizations);
2713 many.plan_of_care = [];
2715 count = countEntities(pd.planofcare.item);
2720 for (let i in pd.planofcare.item) {
2721 if (cleanCode(pd.planofcare.item[i].date) === '') {
2725 theone[i] = getPlanOfCare(pd.planofcare.item[i]);
2727 many.plan_of_care.push(theone[i]);
2730 } else if (count !== 0) {
2731 theone = getPlanOfCare(pd.planofcare.item);
2733 many.plan_of_care.push(theone);
2737 data.plan_of_care = Object.assign(many.plan_of_care);
2744 count = countEntities(pd.goals.item);
2749 for (let i in pd.goals.item) {
2750 theone[i] = getGoals(pd.goals.item[i]);
2751 many.goals.push(theone[i]);
2753 } else if (count !== 0) {
2754 theone = getGoals(pd.goals.item);
2755 many.goals.push(theone);
2758 data.goals = Object.assign(many.goals);
2763 many.clinicalNoteAssessments = [];
2765 count = countEntities(pd.clinical_notes.evaluation_note);
2770 for (let i in pd.clinical_notes.evaluation_note) {
2771 theone[i] = getAssessments(pd.clinical_notes.evaluation_note[i]);
2772 many.clinicalNoteAssessments.push(theone[i]);
2773 break; // for now only one assessment. @todo concat notes to one.
2775 } else if (count !== 0) {
2776 theone = getAssessments(pd.clinical_notes.evaluation_note);
2777 many.clinicalNoteAssessments.push(theone);
2780 data.clinicalNoteAssessments = Object.assign(many.clinicalNoteAssessments);
2783 // Functional Status.
2786 many.functional_status = [];
2788 count = countEntities(pd.functional_status.item);
2793 for (let i in pd.functional_status.item) {
2794 theone[i] = getFunctionalStatus(pd.functional_status.item[i]);
2795 many.functional_status.push(theone[i]);
2797 } else if (count !== 0) {
2798 theone = getFunctionalStatus(pd.functional_status.item);
2799 many.functional_status.push(theone);
2802 data.functional_status = Object.assign(many.functional_status);
2808 many.mental_status = [];
2810 count = countEntities(pd.mental_status.item);
2815 for (let i in pd.mental_status.item) {
2816 theone[i] = getMentalStatus(pd.mental_status.item[i]);
2817 many.mental_status.push(theone[i]);
2819 } else if (count !== 0) {
2820 theone = getMentalStatus(pd.mental_status.item);
2821 many.mental_status.push(theone);
2824 data.mental_status = Object.assign(many.mental_status);
2830 many.social_history = [];
2832 count = countEntities(pd.history_physical.social_history.history_element);
2837 for (let i in pd.history_physical.social_history.history_element) {
2839 theone[i] = populateSocialHistory(pd.history_physical.social_history.history_element[i]);
2840 many.social_history.push(theone[i]);
2842 } else if (count !== 0) {
2843 theone = populateSocialHistory(pd.history_physical.social_history.history_element);
2844 many.social_history.push(theone);
2847 data.social_history = Object.assign(many.social_history);
2850 for (let currentNote in pd.clinical_notes) {
2853 switch (pd.clinical_notes[currentNote].clinical_notes_type) {
2854 case 'evaluation_note':
2856 case 'progress_note':
2858 case 'history_physical':
2859 pd.clinical_notes[currentNote].code_text = "History and Physical";
2863 case 'general_note':
2865 case 'discharge_summary':
2867 case 'procedure_note':
2869 case 'consultation_note':
2871 case 'imaging_narrative':
2873 case 'laboratory_report_narrative':
2875 case 'pathology_report_narrative':
2881 count = countEntities(pd.clinical_notes[currentNote]);
2886 for (let i in pd.clinical_notes[currentNote]) {
2887 theone[i] = populateNote(pd.clinical_notes[currentNote]);
2888 many.push(theone[i]);
2890 } else if (count !== 0) {
2891 theone = populateNote(pd.clinical_notes[currentNote]);
2895 data[currentNote] = Object.assign(many);
2898 // Care Team and members
2899 if (pd.care_team.is_active == 'active') {
2900 data.care_team = Object.assign(populateCareTeamMembers(pd));
2903 // ------------------------------------------ End Sections ---------------------------------------- //
2905 // sections data objects
2906 doc.data = Object.assign(data);
2907 // document meta data and header objects
2908 let meta = getMeta(pd);
2909 let header = populateHeader(pd);
2910 meta.ccda_header = Object.assign(header);
2911 doc.meta = Object.assign(meta);
2913 if (pd.timezone_local_offset) {
2914 populateTimezones(doc, pd.timezone_local_offset, 0);
2917 let xml = bbg.generateCCD(doc);
2920 if (enableDebug === true) {
2921 let place = documentLocation + "/documents/temp/";
2922 if (fs.existsSync(place)) {
2923 fs.writeFile(place + "ccda.json", JSON.stringify(all, null, 4), function (err) {
2925 return console.log(err);
2928 fs.writeFile(place + "ccda.xml", xml, function (err) {
2930 return console.log(err);
2939 let unstructuredTemplate = null;
2941 function generateUnstructured(pd) {
2947 // include unstructured document type oid in header
2948 pd.doc_type = 'unstructured';
2950 let primary_care_provider = all.primary_care_provider || {};
2951 npiProvider = primary_care_provider.provider ? primary_care_provider.provider.npi : "NI";
2952 oidFacility = all.encounter_provider.facility_oid ? all.encounter_provider.facility_oid : "2.16.840.1.113883.19.5.99999.1";
2953 npiFacility = getNpiFacility(pd, true);
2954 webRoot = all.serverRoot;
2955 documentLocation = all.document_location;
2956 authorDateTime = pd.created_time_timezone;
2957 if (pd.author.time.length > 7) {
2958 authorDateTime = pd.author.time;
2959 } else if (all.encounter_list && all.encounter_list.encounter) {
2960 if (countEntities(all.encounter_list.encounter) === 1) {
2961 authorDateTime = all.encounter_list.encounter.date;
2963 authorDateTime = all.encounter_list.encounter[0].date;
2966 authorDateTime = fDate(authorDateTime);
2967 // Demographics is needed in unstructured
2968 let demographic = populateDemographics(pd, npiFacility);
2969 data.demographics = Object.assign(demographic);
2971 if (pd.primary_care_provider) {
2972 Object.assign(demographic, populateProviders(pd));
2974 doc.data = Object.assign(data);
2976 // document meta data and header objects
2977 let meta = getMeta(pd);
2978 let header = populateHeader(pd);
2979 meta.ccda_header = Object.assign(header);
2980 doc.meta = Object.assign(meta);
2982 // set TZ offset for moment
2983 if (pd.timezone_local_offset) {
2984 populateTimezones(doc, pd.timezone_local_offset, 0);
2987 let xml = bbg.generateCCD(doc);
2988 unstructuredTemplate = unstructuredTemplate.trim();
2989 xml = xml.replace(/<\/ClinicalDocument>/g, unstructuredTemplate);
2990 xml += "</ClinicalDocument>" + "\n";
2993 if (enableDebug === true) {
2994 let place = documentLocation + "/documents/temp/";
2995 if (fs.existsSync(place)) {
2996 fs.writeFile(place + "unstructured.xml", xml, function (err) {
2998 return console.log(err);
3007 function processConnection(connection) {
3008 conn = connection; // make it global
3009 let remoteAddress = conn.remoteAddress + ':' + conn.remotePort;
3010 conn.setEncoding('utf8');
3011 //console.log('server remote address ', remoteAddress);
3012 let xml_complete = "";
3014 function eventData(xml) {
3015 xml_complete = xml.toString();
3016 // ensure we have an array start and end
3017 if (xml_complete.match(/^<CCDA/g) && xml_complete.match(/<\/CCDA>$/g)) {
3020 /* eslint-disable-next-line no-control-regex */
3021 xml_complete = xml_complete.replace(/(\u000b\u001c)/gm, "").trim();
3022 xml_complete = xml_complete.replace(/\t\s+/g, " ").trim();
3023 // convert xml data set for document to json array
3024 to_json(xml_complete, function (error, data) {
3027 "toJson error: " + error + "Len: " + xml_complete.length
3029 return "ERROR: Failed json build";
3031 let unstructured = "";
3032 let isUnstruturedData = !!data.CCDA.patient_files;
3033 // extract unstructured documents file component templates. One per file.
3034 if (isUnstruturedData) {
3035 unstructuredTemplate = xml_complete.substring(
3036 xml_complete.lastIndexOf("<patient_files>") + 15,
3037 xml_complete.lastIndexOf("</patient_files>")
3040 // create doc_type document i.e. CCD Referral etc.
3041 if (data.CCDA.doc_type !== "unstructured") {
3042 doc = generateCcda(data.CCDA);
3043 if (data.CCDA.xslUrl) {
3044 xslUrl = data.CCDA.xslUrl || "";
3046 doc = headReplace(doc, xslUrl);
3048 unstructured = generateUnstructured(data.CCDA);
3049 if (data.CCDA.xslUrl) {
3050 xslUrl = data.CCDA.xslUrl || "";
3052 doc = headReplace(unstructured, xslUrl);
3053 // combine the two documents to send back all at once.
3054 doc += unstructured;
3056 // auto build an Unstructured document of supplied embedded files.
3058 data.CCDA.doc_type !== "unstructured" &&
3061 unstructured = generateUnstructured(data.CCDA);
3062 unstructured = headReplace(unstructured, xslUrl);
3063 // combine the two documents to send back all at once.
3064 doc += unstructured;
3067 // send results back to eagerly awaiting CCM for disposal.
3070 /* eslint-disable-next-line no-control-regex */
3071 .replace(/(\u000b\u001c|\r)/gm, "")
3074 let numChunks = Math.ceil(doc.length / 1024);
3075 for (let i = 0, o = 0; i < numChunks; ++i, o += 1024) {
3076 chunk = doc.substring(o, o + 1024);
3079 conn.write(String.fromCharCode(28) + "\r\r" + "");
3084 function eventCloseConn() {
3085 //console.log('connection from %s closed', remoteAddress);
3088 function eventErrorConn(err) {
3089 console.log('Connection %s error: %s', remoteAddress, err.message);
3090 console.log(err.stack);
3094 // Connection Events //
3095 // CCM will send one File Separator characters to mark end of array.
3096 let received = new DataStack(String.fromCharCode(28));
3097 conn.on("data", data => {
3098 received.push(data);
3099 while (!received.endOfCcda() && data.length > 0) {
3101 eventData(received.returnData());
3105 conn.once('close', eventCloseConn);
3106 conn.on('error', eventErrorConn);
3109 function setUp(server) {
3110 server.on('connection', processConnection);
3111 server.listen(6661, '127.0.0.1', function () { // never change port!
3112 //console.log('server listening to ', server.address());
3116 // start up listener for requests from CCM or others.
3119 /* ---------------------------------For future use in header. Do not remove!-------------------------------------------- */
3123 "identifier": "2.16.840.1.113883.4.6",
3124 "extension": "999999943252"
3129 "last": pd.data_enterer.lname,
3130 "first": pd.data_enterer.fname
3136 pd.data_enterer.streetAddressLine
3138 "city": pd.data_enterer.city,
3139 "state": pd.data_enterer.state,
3140 "zip": pd.data_enterer.postalCode,
3141 "country": pd.data_enterer.country
3146 "number": pd.data_enterer.telecom,
3147 "type": "work place"
3154 "identifier": "2.16.840.1.113883.19.5",
3155 "extension": "KP00017"
3160 "last": pd.informer.lname || "",
3161 "first": pd.informer.fname || ""
3167 pd.informer.streetAddressLine || ""
3169 "city": pd.informer.city,
3170 "state": pd.informer.state,
3171 "zip": pd.informer.postalCode,
3172 "country": pd.informer.country
3177 "number": pd.informer.telecom || "",
3178 "type": "work place"
3182 /*"service_event": {
3186 "code_system_name": "SNOMED CT"
3190 "date": "2021-03-11",
3194 "date": pd.created_time,
3204 "identifier": "2.16.840.1.113883.4.6",
3205 "extension": npiProvider
3210 "last": pd.information_recipient.lname || "DAH",
3211 "first": pd.information_recipient.fname || "DAH"
3217 pd.information_recipient.streetAddressLine
3219 "city": pd.information_recipient.city,
3220 "state": pd.information_recipient.state,
3221 "zip": pd.information_recipient.postalCode,
3222 "country": pd.information_recipient.country || "US"
3227 "number": pd.information_recipient.telecom,
3228 "type": "work place"
3235 "identifier": "2.16.840.1.113883.19.5.9999.1393"
3239 pd.encounter_provider.facility_name
3244 pd.encounter_provider.facility_street
3246 "city": pd.encounter_provider.facility_city,
3247 "state": pd.encounter_provider.facility_state,
3248 "zip": pd.encounter_provider.facility_postal_code,
3249 "country": pd.encounter_provider.facility_country_code || "US"
3254 "number": pd.encounter_provider.facility_phone,
3255 "type": "primary work"
3264 "code_system_name": "Provider Codes"
3270 "name": "Primary Performer",
3272 "code_system_name": "Provider Role"