3 * Copyright (C) 2016-2018 Jerry Padgett <sjpadgett@gmail.com>
5 * LICENSE: This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Affero General Public License as
7 * published by the Free Software Foundation, either version 3 of the
8 * License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Affero General Public License for more details.
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 * @author Jerry Padgett <sjpadgett@gmail.com>
20 * @link http://www.open-emr.org
24 var net = require('net');
25 var to_json = require('xmljson').to_json;
26 var bbg = require('oe-blue-button-generate');
27 //var bbm = require('blue-button-model'); //for use set global-not needed here
28 //var fs = require('fs');
29 //var bb = require('blue-button');
30 var fs = require('fs');
32 var server = net.createServer();
33 var conn = ''; // make our connection scope global to script
39 // some useful routines for populating template sections
40 function validate(toValidate, ref, retObj) {
42 if (typeof ref[p].dataType === "undefined") {
44 if (!toValidate[p]) toValidate[p] = {};
45 validate(toValidate[p], ref[p], retObj[p]);
47 if (typeof toValidate === "undefined") toValidate = {};
48 var trimmed = trim(toValidate[p]);
49 retObj[p] = typeEnforcer(ref[p].dataType, trimmed);
55 function typeEnforcer(type, val) {
59 if (typeof val === "string") {
60 validVal = val.toLowerCase() === "true";
66 if ((val === null) || (val === "undefined") || (typeof val === "undefined")) {
68 } else if (typeof val == "object") {
71 validVal = trim(String(val));
75 if (typeof val === 'undefined' || val === null) {
77 } else if (Array.isArray(val)) {
79 val.forEach(function (v) {
80 validVal.push(trim(v));
83 validVal = [trim(val)];
87 var asInt = parseInt(val, 10);
88 if (isNaN(asInt)) asInt = 0;
92 var asNum = parseFloat(val);
93 if (isNaN(asNum)) asNum = 0;
101 if (typeof s === 'string') return s.trim();
106 return trim(s).toLowerCase().replace(/[^a-zA-Z0-9]+/g, '-').replace(/\-+$/, '');
109 function fDate(str) {
110 str = String(str); // at least ensure string so cast it...
112 if (Number(str) === 0) {
115 if (str.length === 8 || (str.length === 14 && (1 * str.substring(12, 14)) === 0)) {
116 return [str.slice(0, 4), str.slice(4, 6), str.slice(6, 8)].join('-')
117 } else if (str.length === 10 && (1 * str.substring(0, 2)) <= 12) {
118 // case mm/dd/yyyy or mm-dd-yyyy
119 return [str.slice(6, 10), str.slice(0, 2), str.slice(3, 5)].join('-')
120 } else if (str.length === 14 && (1 * str.substring(12, 14)) > 0) {
121 // maybe a real time so parse
126 function getPrecision(str) {
130 if (Number(str) === 0) {
133 if (str.length > 8) {
136 if (str.length > 12) {
143 function templateDate(date, precision) {
147 return [{'date': date, 'precision': precision}]
150 function cleanCode(code) {
151 return code.replace(/[.#]/, "");
154 function isOne(who) {
156 if (who !== null && typeof who === 'object') {
157 return who.hasOwnProperty('extension') || who.hasOwnProperty('id') ? 1 : Object.keys(who).length;
166 function headReplace(content) {
167 var r = '<?xml version="1.0" encoding="UTF-8"?>\n' +
168 '<ClinicalDocument xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:hl7-org:v3 http://xreg2.nist.gov:8080/hitspValidation/schema/cdar2c32/infrastructure/cda/C32_CDA.xsd"' +
169 ' xmlns="urn:hl7-org:v3" xmlns:mif="urn:hl7-org:v3/mif">\n';
170 r += content.substr(content.search(/<realmCode/i));
174 // Data model for Blue Button
175 function populateDemographic(pd, g) {
177 "relation": g.relation,
179 "street_lines": [g.address],
183 "country": g.country,
184 "use": "primary home"
187 "last": g.display_name, //@todo parse first/last
188 "first": g.display_name
192 "type": "primary home"
198 "middle": [pd.mname],
204 "date": fDate(pd.dob),
208 "gender": pd.gender.toUpperCase(),
210 "identifier": oidFacility,
211 "extension": "PT-" + pd.id
213 "marital_status": pd.status.toUpperCase() || "U",
215 "street_lines": [pd.street],
218 "zip": pd.postalCode,
219 "country": pd.country,
220 "use": "primary home"
223 "number": pd.phone_home,
224 "type": "primary home"
227 "ethnicity": pd.ethnicity || "U",
229 "language": pd.language,
231 "mode": "Expressed spoken",
232 "proficiency": "Good"
234 "religion": pd.religion.toUpperCase() || "",
241 "attributed_provider": {
244 "root": oidFacility || "2.16.840.1.113883.4.6",
245 "extension": npiFacility || "UNK"
249 "number": all.encounter_provider.facility_phone || "",
253 "full": all.encounter_provider.facility_name || ""
257 "guardians": g.display_name ? guardian : ''
262 function populateProviders() {
268 "root": "2.16.840.1.113883.4.6",
269 "extension": all.primary_care_provider.provider[0].npi || "UNK"
274 "name": all.primary_care_provider.provider[0].physician_type || "Not Avail",
275 "code": all.primary_care_provider.provider[0].physician_type_code || "",
276 "code_system_name": "Provider Codes"
281 "last": all.primary_care_provider.provider[0].lname || "",
282 "first": all.primary_care_provider.provider[0].fname || ""
288 all.encounter_provider.facility_street
290 "city": all.encounter_provider.facility_city,
291 "state": all.encounter_provider.facility_state,
292 "zip": all.encounter_provider.facility_postal_code,
293 "country": all.encounter_provider.facility_country_code
299 "number": all.encounter_provider.facility_phone || "",
307 "identifier": "2.16.840.1.113883.19.5.9999.1393" //@todo need facility oid
311 all.encounter_provider.facility_name
316 all.encounter_provider.facility_street
318 "city": all.encounter_provider.facility_city,
319 "state": all.encounter_provider.facility_state,
320 "zip": all.encounter_provider.facility_postal_code,
321 "country": all.encounter_provider.facility_country_code
326 "number": all.encounter_provider.facility_phone,
327 "type": "primary work"
340 function populateMedication(pd) {
341 pd.status = 'Completed'; //@todo invoke prescribed
345 "date": fDate(pd.start_date),
349 "date": pd.end_date ? fDate(pd.end_date) : "",
354 "identifier": pd.sha_extension,
355 "extension": pd.extension || "UNK"
361 "identifier": "2a620155-9d11-439e-92b3-5d9815ff4ee8",
364 "unencoded_name": pd.drug,
371 "code_system_name": "RXNORM"
373 "code_system_name": "RXNORM"
380 "date": fDate(pd.start_date),
389 "date": fDate(pd.start_date),
390 "precision": getPrecision(fDate(pd.start_date))
394 "identifier": "2.16.840.1.113883.4.6",
395 "extension": pd.npi || "UNK"
405 "name": "instruction",
407 "code_system_name": "SNOMED CT"
409 "free_text": pd.instructions || "None Available"
414 "name": pd.route || "",
415 "code": pd.route_code || "",
416 "code_system_name": "Medication Route FDA"
420 "code": pd.form_code,
421 "code_system_name": "Medication Route FDA"
424 "value": parseFloat(pd.size),
428 "value": parseFloat(pd.dosage),
433 "value": parseFloat(pd.dosage),
441 "identifier": "2.16.840.1.113883.4.6",
442 "extension": pd.npi || "UNK"
446 "identifier": pd.sha_extension,
447 "extension": pd.extension || "UNK"
449 "name": [pd.performer_name]
454 "code": pd.form_code,
455 "code_system_name": "RXNORM"
460 "code_system_name": "ActCode"
465 "code_system_name": "SNOMED CT"
470 "identifier": "db734647-fc99-424c-a864-7e3cda82e703",
476 "code_system_name": "SNOMED CT"
480 "date": fDate(pd.start_date),
485 "name": pd.indications,
486 "code": pd.indications_code,
487 "code_system_name": "SNOMED CT"
492 "identifier": "1.2.3.4.56789.1",
493 "extension": "cb734647-fc99-424c-a864-7e3cda82e704"
497 "identifier": "2.16.840.1.113883.19.5.9999.456",
498 "extension": "2981823"
501 "street_lines": [pd.address],
509 "identifier": "2.16.840.1.113883.19.5.9999.1393"
511 "name": [pd.performer_name]
516 "identifier": "2a620155-9d11-439e-92b3-5d9815ff4ee8"
518 "unencoded_name": pd.drug,
525 "code_system_name": "RXNORM"
527 "code_system_name": "RXNORM"
535 function populateEncounter(pd) {
538 "name": pd.visit_category ? pd.visit_category : '',
539 "code": pd.encounter_procedures ? pd.encounter_procedures.procedures.code : '',
540 "code_system_name": "CPT4",
542 "name": "Ambulatory",
544 "code_system_name": "ActCode"
548 "identifier": pd.sha_extension,
549 "extension": pd.extension
553 "date": fDate(pd.date_formatted),
554 "precision": getPrecision(fDate(pd.date_formatted))
559 "identifier": "2.16.840.1.113883.4.6",
560 "extension": pd.npi || "UNK"
563 "name": pd.physician_type,
564 "code": pd.physician_type_code,
565 "code_system_name": "SNOMED CT"
569 "last": pd.lname || "",
570 "first": pd.fname || ""
575 "number": pd.work_phone,
583 "name": pd.location_details,
585 "code_system_name": "HealthcareServiceLocation"
588 "street_lines": [pd.facility_address],
589 "city": pd.facility_city,
590 "state": pd.facility_state,
591 "zip": pd.facility_zip,
592 "country": pd.facility_country
597 "identifier": pd.sha_extension,
598 "extension": pd.extension
601 "name": pd.encounter_reason,
603 "code_system_name": "SNOMED CT"
607 "date": fDate(pd.date),
615 function populateAllergy(pd) {
618 "identifier": "36e3e930-7b14-11db-9fe1-0800200c9a66",
619 "extension": pd.id || "UNK"
623 "date": fDate(pd.startdate),
629 "identifier": "4adc1020-7b14-11db-9fe1-0800200c9a66",
630 "extension": pd.extension || "UNK"
634 "code": pd.rxnorm_code === 0 ? "" : pd.rxnorm_code,
635 "code_system_name": "RXNORM"
638 "name": "Propensity to adverse reactions to drug",
639 "code": pd.snomed_code || "420134006",
640 "code_system_name": "SNOMED CT"
644 "date": fDate(pd.startdate),
649 "name": pd.allergy_status,
650 "code": pd.status_code === 0 ? "" : pd.status_code,
651 "code_system_name": "SNOMED CT"
655 "identifier": "4adc1020-7b14-11db-9fe1-0800200c9a64"
658 "low": templateDate(pd.startdate, "day"),
659 "high": templateDate(pd.enddate, "day")
662 "name": pd.reaction_text,
663 "code": pd.reaction_code === 0 ? "" : pd.reaction_code,
664 "code_system_name": "SNOMED CT"
669 "code": pd.outcome_code === 0 ? "" : pd.outcome_code,
670 "code_system_name": "SNOMED CT"
672 /*"interpretation": {
675 "code_system_name": "Observation Interpretation"
682 "code": pd.outcome_code === 0 ? "" : pd.outcome_code,
683 "code_system_name": "SNOMED CT"
685 /*"interpretation": {
688 "code_system_name": "Observation Interpretation"
695 function populateProblem(pd) {
699 "date": fDate(pd.start_date_table),
703 "date": fDate(pd.end_date),
708 "identifier": pd.sha_extension,
709 "extension": pd.extension || "UNK"
713 "name": trim(pd.title),
714 "code": cleanCode(pd.code),
715 "code_system_name": "ICD10"
719 "date": fDate(pd.start_date),
723 "date": fDate(pd.end_date),
724 "precision": getPrecision()
732 "identifier": "2.16.840.1.113883.4.6",
733 "extension": all.primary_care_provider.provider[0].npi || "UNK"
738 "last": all.primary_care_provider.provider[0].lname || "",
739 "first": all.primary_care_provider.provider[0].fname || ""
744 "onset_age_unit": "Year",
749 "date": fDate(pd.start_date),
753 "date": fDate(pd.end_date),
754 "precision": getPrecision()
758 "patient_status": pd.observation,
759 "source_list_identifiers": [{
760 "identifier": pd.sha_extension,
761 "extension": pd.extension || "UNK"
767 function populateProcedure(pd) {
770 "name": pd.description,
772 "code_system_name": "CPT4"
775 "identifier": "d68b7e32-7810-4f5b-9cc2-acd54b0fd85d",
776 "extension": pd.extension
778 "status": "completed",
781 "date": fDate(pd.date),
788 "code_system_name": ""
792 "identifier": "c2ee9ee9-ae31-4628-a919-fec1cbb58683"
797 "code_system_name": "SNOMED CT"
802 "identifier": "2.16.840.1.113883.4.6",
803 "extension": pd.npi || "UNK"
806 "street_lines": [pd.address],
813 "number": pd.work_phone,
818 "identifier": pd.facility_sha_extension,
819 "extension": pd.facility_extension
821 "name": [pd.facility_name],
823 "street_lines": [pd.facility_address],
824 "city": pd.facility_city,
825 "state": pd.facility_state,
826 "zip": pd.facility_zip,
827 "country": pd.facility_country
830 "number": pd.facility_phone,
835 "procedure_type": "procedure"
839 function populateResult(pd) {
840 let icode = pd.subtest.abnormal_flag;
841 switch (pd.subtest.abnormal_flag.toUpperCase()) {
854 "identifier": pd.subtest.root,
855 "extension": pd.subtest.extension
859 "code": pd.subtest.result_code || "",
860 "code_system_name": "LOINC"
864 "date": fDate(pd.date_ordered),
868 "status": pd.order_status,
870 "range": pd.subtest.range //OpenEMR doesn't have high/low so show range as text.
872 /*"reference_range": {
873 "low": pd.subtest.range,
874 "high": pd.subtest.range,
875 "unit": pd.subtest.unit
877 "interpretations": [icode],
878 "value": parseFloat(pd.subtest.result_value) || pd.subtest.result_value || "",
879 "unit": pd.subtest.unit
883 function getResultSet(results) {
885 if (!results) return '';
887 let tResult = results.result[0] || results.result;
890 "identifier": tResult.root,
891 "extension": tResult.extension
897 "date": fDate(tResult.date_ordered),
898 "precision": getPrecision(fDate(tResult.date_ordered))
903 "identifier": "2.16.840.1.113883.4.6",
904 "extension": all.primary_care_provider.provider.npi || "UNK"
909 "last": all.primary_care_provider.provider.lname || "",
910 "first": all.primary_care_provider.provider.fname || ""
915 "name": tResult.test_name,
916 "code": tResult.test_code,
917 "code_system_name": "LOINC"
926 count = isOne(results.result);
931 for (let i in results.result) {
932 theone[i] = populateResult(results.result[i]);
933 many.results.push(theone[i]);
935 } else if (count !== 0) {
936 theone = populateResult(results.result);
937 many.results.push(theone);
939 rs.results = Object.assign(resultSet);
940 rs.results.results = Object.assign(many.results);
944 function getPlanOfCare(pd) {
947 "name": pd.code_text,
949 "code_system_name": "SNOMED CT"
952 "identifier": "9a6d1bac-17d3-4195-89a4-1121bc809b4a"
956 "date": fDate(pd.date),
960 "type": "observation",
964 "subType": pd.description
968 function populateVital(pd) {
971 "identifier": "2.16.840.1.113883.3.140.1.0.6.10.14.1",
972 "extension": pd.extension_bps
975 "name": "Blood Pressure Systolic",
977 "code_system_name": "LOINC"
979 "status": "completed",
982 "date": fDate(pd.effectivetime),
983 "precision": getPrecision(fDate(pd.effectivetime))
986 "value": parseFloat(pd.bps),
990 "identifier": "2.16.840.1.113883.3.140.1.0.6.10.14.1",
991 "extension": pd.extension_bpd
994 "name": "Blood Pressure Diastolic",
996 "code_system_name": "LOINC"
998 "status": "completed",
1001 "date": fDate(pd.effectivetime),
1002 "precision": getPrecision(fDate(pd.effectivetime))
1005 "interpretations": ["Normal"],
1006 "value": parseFloat(pd.bpd),
1010 "identifier": "2.16.840.1.113883.3.140.1.0.6.10.14.1",
1011 "extension": pd.extension_pulse
1014 "name": "Heart Rate",
1016 "code_system_name": "LOINC"
1018 "status": "completed",
1021 "date": fDate(pd.effectivetime),
1022 "precision": getPrecision(fDate(pd.effectivetime))
1025 "interpretations": ["Normal"],
1026 "value": parseFloat(pd.pulse),
1030 "identifier": "2.16.840.1.113883.3.140.1.0.6.10.14.1",
1031 "extension": pd.extension_breath
1034 "name": "Respiratory Rate",
1036 "code_system_name": "LOINC"
1038 "status": "completed",
1041 "date": fDate(pd.effectivetime),
1042 "precision": getPrecision(fDate(pd.effectivetime))
1045 "interpretations": ["Normal"],
1046 "value": parseFloat(pd.breath),
1050 "identifier": "2.16.840.1.113883.3.140.1.0.6.10.14.1",
1051 "extension": pd.extension_temperature
1054 "name": "Body Temperature",
1056 "code_system_name": "LOINC"
1058 "status": "completed",
1061 "date": fDate(pd.effectivetime),
1062 "precision": getPrecision(fDate(pd.effectivetime))
1065 "interpretations": ["Normal"],
1066 "value": parseFloat(pd.temperature),
1070 "identifier": "2.16.840.1.113883.3.140.1.0.6.10.14.1",
1071 "extension": pd.extension_height
1076 "code_system_name": "LOINC"
1078 "status": "completed",
1081 "date": fDate(pd.effectivetime),
1082 "precision": getPrecision(fDate(pd.effectivetime))
1085 "interpretations": ["Normal"],
1086 "value": parseFloat(pd.height),
1087 "unit": pd.unit_height
1090 "identifier": "2.16.840.1.113883.3.140.1.0.6.10.14.1",
1091 "extension": pd.extension_weight
1094 "name": "Weight Measured",
1096 "code_system_name": "LOINC"
1098 "status": "completed",
1101 "date": fDate(pd.effectivetime),
1102 "precision": getPrecision(fDate(pd.effectivetime))
1105 "interpretations": ["Normal"],
1106 "value": parseFloat(pd.weight),
1107 "unit": pd.unit_weight
1110 "identifier": "2.16.840.1.113883.3.140.1.0.6.10.14.1",
1111 "extension": pd.extension_BMI
1114 "name": "BMI (Body Mass Index)",
1116 "code_system_name": "LOINC"
1118 "status": "completed",
1121 "date": fDate(pd.effectivetime),
1122 "precision": getPrecision(fDate(pd.effectivetime))
1125 "interpretations": ["Normal"],
1126 "value": parseFloat(pd.BMI),
1131 function populateSocialHistory(pd) {
1134 "low": templateDate(pd.date_formatted, "day")
1135 //"high": templateDate(pd.date_formatted, "day")
1138 "identifier": pd.sha_extension,
1139 "extension": pd.extension
1144 "value": pd.description
1148 function populateImmunization(pd) {
1152 "date": fDate(pd.administered_on),
1153 "precision": "month"
1157 "identifier": "e6f1ba43-c0ed-4b9b-9f12-f435d8ad8f92",
1158 "extension": pd.extension || "UNK"
1160 "status": "complete",
1163 "name": pd.code_text,
1164 "code": pd.cvx_code,
1165 "code_system_name": "CVX"
1166 /*"translations": [{
1169 "code_system_name": "CVX"
1173 "manufacturer": "UNK"
1177 "name": pd.route_of_administration,
1178 "code": pd.route_code,
1179 "code_system_name": "Medication Route FDA"
1188 "identifier": "2.16.840.1.113883.4.6",
1189 "extension": npiProvider
1196 "street_lines": [pd.address],
1204 "identifier": oidFacility,
1205 "extension": npiFacility
1207 "name": [pd.facility_name]
1212 "name": "immunization education",
1213 "code": "171044003",
1214 "code_system_name": "SNOMED CT"
1216 "free_text": "Needs Attention for more data."
1221 function populatePayer(pd) {
1224 "identifier": "1fe2cdd0-7aad-11db-9fe1-0800200c9a66"
1228 "identifier": "3e676a50-7aac-11db-9fe1-0800200c9a66"
1232 "code_system_name": "HL7 RoleCode"
1237 "code_system_name": "HL7 RoleCode"
1241 "identifier": "2.16.840.1.113883.19"
1244 "street_lines": ["123 Insurance Road"],
1245 "city": "Blue Bell",
1252 "number": "(781)555-1515",
1253 "type": "work place"
1256 "name": ["Good Health Insurance"],
1258 "street_lines": ["123 Insurance Road"],
1259 "city": "Blue Bell",
1266 "number": "(781)555-1515",
1267 "type": "work place"
1272 "code_system_name": "HL7 RoleCode"
1280 "code_system_name": "HL7 Role"
1283 "identifier": "329fcdf0-7ab3-11db-9fe1-0800200c9a66"
1287 "middle": ["Frankie"],
1292 "street_lines": ["17 Daws Rd."],
1293 "city": "Blue Bell",
1297 "use": "primary home"
1300 "number": "(781)555-1212",
1301 "type": "primary home"
1308 "code_system_name": "HL7 Role"
1312 "identifier": "14d4a520-7aae-11db-9fe1-0800200c9a66",
1313 "extension": "1138345"
1316 "street_lines": ["17 Daws Rd."],
1317 "city": "Blue Bell",
1321 "use": "primary home"
1326 "code_system_name": "HL7 Role"
1339 "identifier": "2.16.840.1.113883.19",
1340 "extension": "1138345"
1343 "street_lines": ["17 Daws Rd."],
1344 "city": "Blue Bell",
1348 "use": "primary home"
1354 "identifier": "f4dce790-8328-11db-9fe1-0800200c9a66"
1358 "name": "Colonoscopy",
1360 "code_system_name": "SNOMED CT"
1367 function populateHeader(pd) {
1371 "identifier": "2.16.840.1.113883.19.5.99999.1",
1372 "extension": "TT988"
1376 "name": "Continuity of Care Document",
1378 "code_system_name": "LOINC"
1381 "2.16.840.1.113883.10.20.22.1.1",
1382 "2.16.840.1.113883.10.20.22.1.2"
1384 "title": "Clinical Health Summary",
1387 "date": fDate(pd.created_time) || "",
1388 "precision": getPrecision(fDate(pd.created_time))
1396 "identifier": "2.16.840.1.113883.4.6",
1397 "extension": pd.author.npi || "UNK"
1402 "last": pd.author.lname,
1403 "first": pd.author.fname
1409 pd.author.streetAddressLine
1411 "city": pd.author.city,
1412 "state": pd.author.state,
1413 "zip": pd.author.postalCode,
1414 "country": pd.author.country
1419 "number": pd.author.telecom,
1420 "type": "work place"
1433 "root": "2.16.840.1.113883.19.5.9999.1393",
1434 "extension": pd.encounter_provider.facility_id || "UNK"
1438 pd.encounter_provider.facility_name
1443 pd.encounter_provider.facility_street
1445 "city": pd.encounter_provider.facility_city,
1446 "state": pd.encounter_provider.facility_state,
1447 "zip": pd.encounter_provider.facility_postal_code,
1448 "country": pd.encounter_provider.facility_country_code
1453 "number": pd.encounter_provider.facility_phone,
1454 "type": "primary work"
1465 "identifier": "2.16.840.1.113883.4.6",
1466 "extension": "999999943252"
1471 "last": pd.data_enterer.lname,
1472 "first": pd.data_enterer.fname
1478 pd.data_enterer.streetAddressLine
1480 "city": pd.data_enterer.city,
1481 "state": pd.data_enterer.state,
1482 "zip": pd.data_enterer.postalCode,
1483 "country": pd.data_enterer.country
1488 "number": pd.data_enterer.telecom,
1489 "type": "work place"
1496 "identifier": "2.16.840.1.113883.19.5",
1497 "extension": "KP00017"
1502 "last": pd.informer.lname || "",
1503 "first": pd.informer.fname || ""
1509 pd.informer.streetAddressLine || ""
1511 "city": pd.informer.city,
1512 "state": pd.informer.state,
1513 "zip": pd.informer.postalCode,
1514 "country": pd.informer.country
1519 "number": pd.informer.telecom || "",
1520 "type": "work place"
1524 "service_event": { // @todo maybe move this to attributed or write z-schema template
1528 "code_system_name": "SNOMED CT"
1533 "precision": getPrecision()
1536 "date": pd.created_time,
1537 "precision": getPrecision()
1546 "identifier": "2.16.840.1.113883.4.6",
1547 "extension": "PseudoMD-1"
1552 "last": pd.information_recipient.lname,
1553 "first": pd.information_recipient.fname
1559 pd.information_recipient.streetAddressLine
1561 "city": pd.information_recipient.city,
1562 "state": pd.information_recipient.state,
1563 "zip": pd.information_recipient.postalCode,
1564 "country": pd.information_recipient.country
1569 "number": pd.information_recipient.telecom,
1570 "type": "work place"
1577 "identifier": "2.16.840.1.113883.19.5.9999.1393"
1581 pd.encounter_provider.facility_name
1586 pd.encounter_provider.facility_street
1588 "city": pd.encounter_provider.facility_city,
1589 "state": pd.encounter_provider.facility_state,
1590 "zip": pd.encounter_provider.facility_postal_code,
1591 "country": pd.encounter_provider.facility_country_code
1596 "number": pd.encounter_provider.facility_phone,
1597 "type": "primary work"
1606 "code_system_name": "Provider Codes"
1612 "name": "Primary Performer",
1614 "code_system_name": "Provider Role"
1623 function getMeta(pd) {
1629 "identifier": "2.16.840.1.113883.19.5.99999.1",
1630 "extension": "TT988"
1633 "confidentiality": "Normal",
1635 "identifier": "2.16.840.1.113883.19.5.99999.19",
1636 "extension": "sTT988"
1642 function genCcda(pd) {
1650 npiProvider = all.primary_care_provider.provider[0].npi;
1651 oidFacility = all.encounter_provider.facility_oid;
1652 npiFacility = all.encounter_provider.facility_npi;
1655 let demographic = populateDemographic(pd.patient, pd.guardian, pd);
1656 Object.assign(demographic, populateProviders());
1657 data.demographics = Object.assign(demographic);
1662 count = isOne(pd.history_physical.vitals_list.vitals);
1667 for (let i in pd.history_physical.vitals_list.vitals) {
1668 theone = populateVital(pd.history_physical.vitals_list.vitals[i]);
1669 many.vitals.push.apply(many.vitals, theone);
1671 } else if (count === 1) {
1672 theone = populateVital(pd.history_physical.vitals_list.vitals);
1673 many.vitals.push(theone);
1675 data.vitals = Object.assign(many.vitals);
1679 meds.medications = [];
1681 count = isOne(pd.medications.medication);
1686 for (let i in pd.medications.medication) {
1687 m[i] = populateMedication(pd.medications.medication[i]);
1688 meds.medications.push(m[i]);
1690 } else if (count !== 0) {
1691 m = populateMedication(pd.medications.medication);
1692 meds.medications.push(m);
1694 data.medications = Object.assign(meds.medications);
1698 encs.encounters = [];
1700 count = isOne(pd.encounter_list.encounter);
1705 for (let i in pd.encounter_list.encounter) {
1706 enc[i] = populateEncounter(pd.encounter_list.encounter[i]);
1707 encs.encounters.push(enc[i]);
1709 } else if (count !== 0) {
1710 enc = populateEncounter(pd.encounter_list.encounter);
1711 encs.encounters.push(enc);
1713 data.encounters = Object.assign(encs.encounters);
1718 allergies.allergies = [];
1720 count = isOne(pd.allergies.allergy);
1725 for (let i in pd.allergies.allergy) {
1726 allergy[i] = populateAllergy(pd.allergies.allergy[i]);
1727 allergies.allergies.push(allergy[i]);
1729 } else if (count !== 0) {
1730 allergy = populateAllergy(pd.allergies.allergy);
1731 allergies.allergies.push(allergy);
1733 data.allergies = Object.assign(allergies.allergies);
1738 problems.problems = [];
1740 count = isOne(pd.problem_lists.problem);
1745 for (let i in pd.problem_lists.problem) {
1746 problem[i] = populateProblem(pd.problem_lists.problem[i], pd);
1747 problems.problems.push(problem[i]);
1749 } else if (count !== 0) {
1750 problem = populateProblem(pd.problem_lists.problem);
1751 problems.problems.push(problem);
1753 data.problems = Object.assign(problems.problems);
1757 many.procedures = [];
1759 count = isOne(pd.procedures.procedure);
1764 for (let i in pd.procedures.procedure) {
1765 theone[i] = populateProcedure(pd.procedures.procedure[i]);
1766 many.procedures.push(theone[i]);
1768 } else if (count !== 0) {
1769 theone = populateProcedure(pd.procedures.procedure);
1770 many.procedures.push(theone);
1773 data.procedures = Object.assign(many.procedures);
1776 data.results = Object.assign(getResultSet(pd.results, pd)['results']);
1781 many.immunizations = [];
1783 count = isOne(pd.immunizations.immunization);
1788 for (let i in pd.immunizations.immunization) {
1789 theone[i] = populateImmunization(pd.immunizations.immunization[i]);
1790 many.immunizations.push(theone[i]);
1792 } else if (count !== 0) {
1793 theone = populateImmunization(pd.immunizations.immunization);
1794 many.immunizations.push(theone);
1796 data.immunizations = Object.assign(many.immunizations);
1800 many.plan_of_care = [];
1802 count = isOne(pd.planofcare); // ccm doesn't send array of items
1807 for (let i in pd.planofcare.item) {
1808 theone[i] = getPlanOfCare(pd.planofcare.item[i]);
1809 many.plan_of_care.push(theone[i]);
1811 } else if (count !== 0) {
1812 theone = getPlanOfCare(pd.planofcare.item);
1813 many.plan_of_care.push(theone);
1816 data.plan_of_care = Object.assign(many.plan_of_care);
1820 many.social_history = [];
1822 count = isOne(pd.history_physical.social_history.history_element);
1827 for (let i in pd.history_physical.social_history.history_element) {
1829 theone[i] = populateSocialHistory(pd.history_physical.social_history.history_element[i]);
1830 many.social_history.push(theone[i]);
1832 } else if (count !== 0) {
1833 theone = populateSocialHistory(pd.history_physical.social_history.history_element);
1834 many.social_history.push(theone);
1837 data.social_history = Object.assign(many.social_history);
1839 // ------------------------------------------ End Sections ----------------------------------------//
1841 doc.data = Object.assign(data);
1842 let meta = getMeta(pd);
1843 let header = populateHeader(pd);
1845 meta.ccda_header = Object.assign(header);
1846 doc.meta = Object.assign(meta);
1847 let xml = bbg.generateCCD(doc);
1850 /*fs.writeFile("bbtest.json", JSON.stringify(doc, null, 4), function (err) {
1852 return console.log(err);
1854 console.log("Json saved!");
1856 fs.writeFile("bbtest.xml", xml, function (err) {
1858 return console.log(err);
1860 console.log("Xml saved!");
1866 function processConnection(connection) {
1867 conn = connection; // make it global
1868 var remoteAddress = conn.remoteAddress + ':' + conn.remotePort;
1869 console.log(remoteAddress);
1870 conn.setEncoding('utf8');
1872 function eventData(xml) {
1873 xml = xml.replace(/(\u000b|\u001c)/gm, "").trim();
1874 // Sanity check from service manager
1875 if (xml === 'status' || xml.length < 80) {
1876 conn.write("statusok" + String.fromCharCode(28) + "\r\r");
1881 // ---------------------start--------------------------------
1883 xml = xml.toString().replace(/\t\s+/g, ' ').trim();
1885 to_json(xml, function (error, data) {
1886 // console.log(JSON.stringify(data, null, 4));
1887 if (error) { // need try catch
1888 console.log('toJson error: ' + error + 'Len: ' + xml.length);
1891 doc = genCcda(data.CCDA);
1894 doc = headReplace(doc);
1895 doc = doc.toString().replace(/(\u000b|\u001c|\r)/gm, "").trim();
1898 let numChunks = Math.ceil(doc.length / 1024);
1899 for (let i = 0, o = 0; i < numChunks; ++i, o += 1024) {
1900 chunk = doc.substr(o, 1024);
1904 conn.write(String.fromCharCode(28) + "\r\r" + '');
1909 function eventCloseConn() {
1910 //console.log('connection from %s closed', remoteAddress);
1913 function eventErrorConn(err) {
1914 //console.log('Connection %s error: %s', remoteAddress, err.message);
1917 // Connection Events //
1918 conn.on('data', eventData);
1919 conn.once('close', eventCloseConn);
1920 conn.on('error', eventErrorConn);
1923 function setUp(server) {
1924 server.on('connection', processConnection);
1925 server.listen(6661, 'localhost', function () {
1926 //console.log('server listening to %j', server.address());