5 * @link https://www.open-emr.org
6 * @author Jerry Padgett <sjpadgett@gmail.com>
7 * @copyright Copyright (c) 2016-2017 Jerry Padgett <sjpadgett@gmail.com>
8 * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3
12 var net = require('net');
13 var to_json = require('xmljson').to_json;
14 var bbg = require('oe-blue-button-generate');
15 //var bbm = require('blue-button-model'); //for use set global-not needed here
16 //var fs = require('fs');
17 //var bb = require('blue-button');
18 var fs = require('fs');
20 var server = net.createServer();
21 var conn = ''; // make our connection scope global to script
27 // some useful routines for populating template sections
28 function validate(toValidate, ref, retObj) {
30 if (typeof ref[p].dataType === "undefined") {
32 if (!toValidate[p]) toValidate[p] = {};
33 validate(toValidate[p], ref[p], retObj[p]);
35 if (typeof toValidate === "undefined") toValidate = {};
36 var trimmed = trim(toValidate[p]);
37 retObj[p] = typeEnforcer(ref[p].dataType, trimmed);
43 function typeEnforcer(type, val) {
47 if (typeof val === "string") {
48 validVal = val.toLowerCase() === "true";
54 if ((val === null) || (val === "undefined") || (typeof val === "undefined")) {
56 } else if (typeof val == "object") {
59 validVal = trim(String(val));
63 if (typeof val === 'undefined' || val === null) {
65 } else if (Array.isArray(val)) {
67 val.forEach(function (v) {
68 validVal.push(trim(v));
71 validVal = [trim(val)];
75 var asInt = parseInt(val, 10);
76 if (isNaN(asInt)) asInt = 0;
80 var asNum = parseFloat(val);
81 if (isNaN(asNum)) asNum = 0;
89 if (typeof s === 'string') return s.trim();
94 return trim(s).toLowerCase().replace(/[^a-zA-Z0-9]+/g, '-').replace(/\-+$/, '');
98 str = String(str); // at least ensure string so cast it...
100 if (Number(str) === 0) {
101 return (new Date()).toISOString().slice(0,10).replace(/-/g,"");
103 if (str.length === 8 || (str.length === 14 && (1 * str.substring(12, 14)) === 0)) {
104 return [str.slice(0, 4), str.slice(4, 6), str.slice(6, 8)].join('-')
105 } else if (str.length === 10 && (1 * str.substring(0, 2)) <= 12) {
106 // case mm/dd/yyyy or mm-dd-yyyy
107 return [str.slice(6, 10), str.slice(0, 2), str.slice(3, 5)].join('-')
108 } else if (str.length === 14 && (1 * str.substring(12, 14)) > 0) {
109 // maybe a real time so parse
114 function getPrecision(str) {
118 if (Number(str) === 0) {
121 if (str.length > 8) {
124 if (str.length > 12) {
131 function templateDate(date, precision) {
132 return [{'date': fDate(date), 'precision': precision}]
135 function cleanCode(code) {
136 return code.replace(/[.#]/, "");
139 function isOne(who) {
141 if (who !== null && typeof who === 'object') {
142 return who.hasOwnProperty('extension') || who.hasOwnProperty('id') ? 1 : Object.keys(who).length;
151 function headReplace(content) {
152 var r = '<?xml version="1.0" encoding="UTF-8"?>\n' +
153 '<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"' +
154 ' xmlns="urn:hl7-org:v3" xmlns:mif="urn:hl7-org:v3/mif">\n';
155 r += content.substr(content.search(/<realmCode/i));
159 // Data model for Blue Button
160 function populateDemographic(pd, g) {
162 "relation": g.relation,
164 "street_lines": [g.address],
168 "country": g.country,
169 "use": "primary home"
172 "last": g.display_name, //@todo parse first/last
173 "first": g.display_name
177 "type": "primary home"
183 "middle": [pd.mname],
189 "date": fDate(pd.dob),
193 "gender": pd.gender.toUpperCase(),
195 "identifier": oidFacility,
196 "extension": "PT-" + pd.id
198 "marital_status": pd.status.toUpperCase() || "U",
200 "street_lines": [pd.street],
203 "zip": pd.postalCode,
204 "country": pd.country,
205 "use": "primary home"
208 "number": pd.phone_home,
209 "type": "primary home"
212 "ethnicity": pd.ethnicity || "U",
214 "language": pd.language,
216 "mode": "Expressed spoken",
217 "proficiency": "Good"
219 "religion": pd.religion.toUpperCase() || "",
226 "attributed_provider": {
229 "root": "2.16.840.1.113883.4.6",
230 "extension": npiFacility || "UNK"
234 "number": all.encounter_provider.facility_phone || "",
238 "full": all.encounter_provider.facility_name || ""
242 "guardians": g.display_name ? guardian : ''
247 function populateProviders() {
253 "root": "2.16.840.1.113883.4.6",
254 "extension": all.primary_care_provider.provider[0].npi || "UNK"
259 "name": all.primary_care_provider.provider[0].physician_type || "Not Avail",
260 "code": all.primary_care_provider.provider[0].physician_type_code || "",
261 "code_system_name": "Provider Codes"
266 "last": all.primary_care_provider.provider[0].lname || "",
267 "first": all.primary_care_provider.provider[0].fname || ""
273 all.encounter_provider.facility_street
275 "city": all.encounter_provider.facility_city,
276 "state": all.encounter_provider.facility_state,
277 "zip": all.encounter_provider.facility_postal_code,
278 "country": all.encounter_provider.facility_country_code
284 "number": all.encounter_provider.facility_phone || "",
292 "identifier": "2.16.840.1.113883.19.5.9999.1393" //@todo need facility oid
296 all.encounter_provider.facility_name
301 all.encounter_provider.facility_street
303 "city": all.encounter_provider.facility_city,
304 "state": all.encounter_provider.facility_state,
305 "zip": all.encounter_provider.facility_postal_code,
306 "country": all.encounter_provider.facility_country_code
311 "number": all.encounter_provider.facility_phone,
312 "type": "primary work"
325 function populateMedication(pd) {
326 pd.status = 'Completed'; //@todo invoke prescribed
330 "date": fDate(pd.start_date),
334 "date": pd.end_date ? fDate(pd.end_date) : "",
339 "identifier": pd.sha_extension,
340 "extension": pd.extension || "UNK"
346 "identifier": "2a620155-9d11-439e-92b3-5d9815ff4ee8",
349 "unencoded_name": pd.drug,
356 "code_system_name": "RXNORM"
358 "code_system_name": "RXNORM"
365 "date": fDate(pd.start_date),
374 "date": fDate(pd.start_date),
375 "precision": getPrecision(fDate(pd.start_date))
379 "identifier": "2.16.840.1.113883.4.6",
380 "extension": pd.npi || "UNK"
390 "name": "instruction",
392 "code_system_name": "SNOMED CT"
394 "free_text": pd.instructions || "None Available"
399 "name": pd.route || "",
400 "code": pd.route_code || "",
401 "code_system_name": "Medication Route FDA"
405 "code": pd.form_code,
406 "code_system_name": "Medication Route FDA"
409 "value": parseFloat(pd.size),
413 "value": parseFloat(pd.dosage),
418 "value": parseFloat(pd.dosage),
426 "identifier": "2.16.840.1.113883.4.6",
427 "extension": pd.npi || "UNK"
431 "identifier": pd.sha_extension,
432 "extension": pd.extension || "UNK"
434 "name": [pd.performer_name]
439 "code": pd.form_code,
440 "code_system_name": "RXNORM"
445 "code_system_name": "ActCode"
450 "code_system_name": "SNOMED CT"
455 "identifier": "db734647-fc99-424c-a864-7e3cda82e703",
461 "code_system_name": "SNOMED CT"
465 "date": fDate(pd.start_date),
470 "name": pd.indications,
471 "code": pd.indications_code,
472 "code_system_name": "SNOMED CT"
477 "identifier": "1.2.3.4.56789.1",
478 "extension": "cb734647-fc99-424c-a864-7e3cda82e704"
482 "identifier": "2.16.840.1.113883.19.5.9999.456",
483 "extension": "2981823"
486 "street_lines": [pd.address],
494 "identifier": "2.16.840.1.113883.19.5.9999.1393"
496 "name": [pd.performer_name]
501 "identifier": "2a620155-9d11-439e-92b3-5d9815ff4ee8"
503 "unencoded_name": pd.drug,
510 "code_system_name": "RXNORM"
512 "code_system_name": "RXNORM"
520 function populateEncounter(pd) {
523 "name": pd.visit_category ? pd.visit_category : '',
524 "code": pd.encounter_procedures ? pd.encounter_procedures.procedures.code : '',
525 "code_system_name": "CPT4",
527 "name": "Ambulatory",
529 "code_system_name": "ActCode"
533 "identifier": pd.sha_extension,
534 "extension": pd.extension
538 "date": fDate(pd.date),
539 "precision": "second" //getPrecision(fDate(pd.date_formatted))
544 "identifier": "2.16.840.1.113883.4.6",
545 "extension": pd.npi || "UNK"
548 "name": pd.physician_type,
549 "code": pd.physician_type_code,
550 "code_system_name": "SNOMED CT"
554 "last": pd.lname || "",
555 "first": pd.fname || ""
560 "number": pd.work_phone,
568 "name": pd.location_details,
570 "code_system_name": "HealthcareServiceLocation"
573 "street_lines": [pd.facility_address],
574 "city": pd.facility_city,
575 "state": pd.facility_state,
576 "zip": pd.facility_zip,
577 "country": pd.facility_country
582 "identifier": pd.sha_extension,
583 "extension": pd.extension
586 "name": pd.encounter_reason,
588 "code_system_name": "SNOMED CT"
592 "date": fDate(pd.date),
600 function populateAllergy(pd) {
603 "identifier": "36e3e930-7b14-11db-9fe1-0800200c9a66",
604 "extension": pd.id || "UNK"
608 "date": fDate(pd.startdate),
614 "identifier": "4adc1020-7b14-11db-9fe1-0800200c9a66",
615 "extension": pd.extension || "UNK"
619 "code": pd.rxnorm_code === 0 ? "" : pd.rxnorm_code,
620 "code_system_name": "RXNORM"
623 "name": "Propensity to adverse reactions to drug",
624 "code": pd.snomed_code || "420134006",
625 "code_system_name": "SNOMED CT"
629 "date": fDate(pd.startdate),
634 "name": pd.allergy_status,
635 "code": pd.status_code === 0 ? "" : pd.status_code,
636 "code_system_name": "SNOMED CT"
640 "identifier": "4adc1020-7b14-11db-9fe1-0800200c9a64"
643 "low": templateDate(pd.startdate, "day"),
644 "high": templateDate(pd.enddate, "day")
647 "name": pd.reaction_text,
648 "code": pd.reaction_code === 0 ? "" : pd.reaction_code,
649 "code_system_name": "SNOMED CT"
654 "code": pd.outcome_code === 0 ? "" : pd.outcome_code,
655 "code_system_name": "SNOMED CT"
657 /*"interpretation": {
660 "code_system_name": "Observation Interpretation"
667 "code": pd.outcome_code === 0 ? "" : pd.outcome_code,
668 "code_system_name": "SNOMED CT"
670 /*"interpretation": {
673 "code_system_name": "Observation Interpretation"
680 function populateProblem(pd) {
684 "date": fDate(pd.start_date_table),
688 "date": fDate(pd.end_date),
693 "identifier": pd.sha_extension,
694 "extension": pd.extension || "UNK"
698 "name": trim(pd.title),
699 "code": cleanCode(pd.code),
700 "code_system_name": "ICD10"
704 "date": fDate(pd.start_date),
708 "date": fDate(pd.end_date),
709 "precision": getPrecision()
717 "identifier": "2.16.840.1.113883.4.6",
718 "extension": all.primary_care_provider.provider[0].npi || "UNK"
723 "last": all.primary_care_provider.provider[0].lname || "",
724 "first": all.primary_care_provider.provider[0].fname || ""
729 "onset_age_unit": "Year",
734 "date": fDate(pd.start_date),
738 "date": fDate(pd.end_date),
739 "precision": getPrecision()
743 "patient_status": pd.observation,
744 "source_list_identifiers": [{
745 "identifier": pd.sha_extension,
746 "extension": pd.extension || "UNK"
752 function populateProcedure(pd) {
755 "name": pd.description,
757 "code_system_name": "CPT4"
760 "identifier": "d68b7e32-7810-4f5b-9cc2-acd54b0fd85d",
761 "extension": pd.extension
763 "status": "completed",
766 "date": fDate(pd.date),
773 "code_system_name": ""
777 "identifier": "c2ee9ee9-ae31-4628-a919-fec1cbb58683"
782 "code_system_name": "SNOMED CT"
787 "identifier": "2.16.840.1.113883.4.6",
788 "extension": pd.npi || "UNK"
791 "street_lines": [pd.address],
798 "number": pd.work_phone,
803 "identifier": pd.facility_sha_extension,
804 "extension": pd.facility_extension
806 "name": [pd.facility_name],
808 "street_lines": [pd.facility_address],
809 "city": pd.facility_city,
810 "state": pd.facility_state,
811 "zip": pd.facility_zip,
812 "country": pd.facility_country
815 "number": pd.facility_phone,
820 "procedure_type": "procedure"
824 function populateResult(pd) {
825 let icode = pd.subtest.abnormal_flag;
826 switch (pd.subtest.abnormal_flag.toUpperCase()) {
839 "identifier": pd.subtest.root,
840 "extension": pd.subtest.extension
844 "code": pd.subtest.result_code || "",
845 "code_system_name": "LOINC"
849 "date": fDate(pd.date_ordered),
853 "status": pd.order_status,
855 "range": pd.subtest.range //OpenEMR doesn't have high/low so show range as text.
857 /*"reference_range": {
858 "low": pd.subtest.range,
859 "high": pd.subtest.range,
860 "unit": pd.subtest.unit
862 "interpretations": [icode],
863 "value": parseFloat(pd.subtest.result_value) || pd.subtest.result_value || "",
864 "unit": pd.subtest.unit
868 function getResultSet(results) {
870 if (!results) return '';
872 let tResult = results.result[0] || results.result;
875 "identifier": tResult.root,
876 "extension": tResult.extension
882 "date": fDate(tResult.date_ordered),
883 "precision": getPrecision(fDate(tResult.date_ordered))
888 "identifier": "2.16.840.1.113883.4.6",
889 "extension": all.primary_care_provider.provider.npi || "UNK"
894 "last": all.primary_care_provider.provider.lname || "",
895 "first": all.primary_care_provider.provider.fname || ""
900 "name": tResult.test_name,
901 "code": tResult.test_code,
902 "code_system_name": "LOINC"
911 count = isOne(results.result);
916 for (let i in results.result) {
917 theone[i] = populateResult(results.result[i]);
918 many.results.push(theone[i]);
920 } else if (count !== 0) {
921 theone = populateResult(results.result);
922 many.results.push(theone);
924 rs.results = Object.assign(resultSet);
925 rs.results.results = Object.assign(many.results);
929 function getPlanOfCare(pd) {
932 "name": pd.code_text,
934 "code_system_name": "SNOMED CT"
937 "identifier": "9a6d1bac-17d3-4195-89a4-1121bc809b4a"
941 "date": fDate(pd.date),
945 "type": "observation",
949 "subType": pd.description
953 function populateVital(pd) {
956 "identifier": "2.16.840.1.113883.3.140.1.0.6.10.14.1",
957 "extension": pd.extension_bps
960 "name": "Blood Pressure Systolic",
962 "code_system_name": "LOINC"
964 "status": "completed",
967 "date": fDate(pd.effectivetime),
968 "precision": getPrecision(fDate(pd.effectivetime))
971 "value": parseFloat(pd.bps),
975 "identifier": "2.16.840.1.113883.3.140.1.0.6.10.14.1",
976 "extension": pd.extension_bpd
979 "name": "Blood Pressure Diastolic",
981 "code_system_name": "LOINC"
983 "status": "completed",
986 "date": fDate(pd.effectivetime),
987 "precision": getPrecision(fDate(pd.effectivetime))
990 "interpretations": ["Normal"],
991 "value": parseFloat(pd.bpd),
995 "identifier": "2.16.840.1.113883.3.140.1.0.6.10.14.1",
996 "extension": pd.extension_pulse
999 "name": "Heart Rate",
1001 "code_system_name": "LOINC"
1003 "status": "completed",
1006 "date": fDate(pd.effectivetime),
1007 "precision": getPrecision(fDate(pd.effectivetime))
1010 "interpretations": ["Normal"],
1011 "value": parseFloat(pd.pulse),
1015 "identifier": "2.16.840.1.113883.3.140.1.0.6.10.14.1",
1016 "extension": pd.extension_breath
1019 "name": "Respiratory Rate",
1021 "code_system_name": "LOINC"
1023 "status": "completed",
1026 "date": fDate(pd.effectivetime),
1027 "precision": getPrecision(fDate(pd.effectivetime))
1030 "interpretations": ["Normal"],
1031 "value": parseFloat(pd.breath),
1035 "identifier": "2.16.840.1.113883.3.140.1.0.6.10.14.1",
1036 "extension": pd.extension_temperature
1039 "name": "Body Temperature",
1041 "code_system_name": "LOINC"
1043 "status": "completed",
1046 "date": fDate(pd.effectivetime),
1047 "precision": getPrecision(fDate(pd.effectivetime))
1050 "interpretations": ["Normal"],
1051 "value": parseFloat(pd.temperature),
1055 "identifier": "2.16.840.1.113883.3.140.1.0.6.10.14.1",
1056 "extension": pd.extension_height
1061 "code_system_name": "LOINC"
1063 "status": "completed",
1066 "date": fDate(pd.effectivetime),
1067 "precision": getPrecision(fDate(pd.effectivetime))
1070 "interpretations": ["Normal"],
1071 "value": parseFloat(pd.height),
1072 "unit": pd.unit_height
1075 "identifier": "2.16.840.1.113883.3.140.1.0.6.10.14.1",
1076 "extension": pd.extension_weight
1079 "name": "Weight Measured",
1081 "code_system_name": "LOINC"
1083 "status": "completed",
1086 "date": fDate(pd.effectivetime),
1087 "precision": getPrecision(fDate(pd.effectivetime))
1090 "interpretations": ["Normal"],
1091 "value": parseFloat(pd.weight),
1092 "unit": pd.unit_weight
1095 "identifier": "2.16.840.1.113883.3.140.1.0.6.10.14.1",
1096 "extension": pd.extension_BMI
1099 "name": "BMI (Body Mass Index)",
1101 "code_system_name": "LOINC"
1103 "status": "completed",
1106 "date": fDate(pd.effectivetime),
1107 "precision": getPrecision(fDate(pd.effectivetime))
1110 "interpretations": ["Normal"],
1111 "value": parseFloat(pd.BMI),
1116 function populateSocialHistory(pd) {
1119 "low": templateDate(pd.date, "day")
1120 //"high": templateDate(pd.date, "day")
1123 "identifier": pd.sha_extension,
1124 "extension": pd.extension
1129 "value": pd.description
1133 function populateImmunization(pd) {
1137 "date": fDate(pd.administered_on),
1138 "precision": "month"
1142 "identifier": "e6f1ba43-c0ed-4b9b-9f12-f435d8ad8f92",
1143 "extension": pd.extension || "UNK"
1145 "status": "complete",
1148 "name": pd.code_text,
1149 "code": pd.cvx_code,
1150 "code_system_name": "CVX"
1151 /*"translations": [{
1154 "code_system_name": "CVX"
1158 "manufacturer": "UNK"
1162 "name": pd.route_of_administration,
1163 "code": pd.route_code,
1164 "code_system_name": "Medication Route FDA"
1173 "identifier": "2.16.840.1.113883.4.6",
1174 "extension": npiProvider
1181 "street_lines": [pd.address],
1189 "identifier": "2.16.840.1.113883.4.6",
1190 "extension": npiFacility
1192 "name": [pd.facility_name]
1197 "name": "immunization education",
1198 "code": "171044003",
1199 "code_system_name": "SNOMED CT"
1201 "free_text": "Needs Attention for more data."
1206 function populatePayer(pd) {
1209 "identifier": "1fe2cdd0-7aad-11db-9fe1-0800200c9a66"
1213 "identifier": "3e676a50-7aac-11db-9fe1-0800200c9a66"
1217 "code_system_name": "HL7 RoleCode"
1222 "code_system_name": "HL7 RoleCode"
1226 "identifier": "2.16.840.1.113883.19"
1229 "street_lines": ["123 Insurance Road"],
1230 "city": "Blue Bell",
1237 "number": "(781)555-1515",
1238 "type": "work place"
1241 "name": ["Good Health Insurance"],
1243 "street_lines": ["123 Insurance Road"],
1244 "city": "Blue Bell",
1251 "number": "(781)555-1515",
1252 "type": "work place"
1257 "code_system_name": "HL7 RoleCode"
1265 "code_system_name": "HL7 Role"
1268 "identifier": "329fcdf0-7ab3-11db-9fe1-0800200c9a66"
1272 "middle": ["Frankie"],
1277 "street_lines": ["17 Daws Rd."],
1278 "city": "Blue Bell",
1282 "use": "primary home"
1285 "number": "(781)555-1212",
1286 "type": "primary home"
1293 "code_system_name": "HL7 Role"
1297 "identifier": "14d4a520-7aae-11db-9fe1-0800200c9a66",
1298 "extension": "1138345"
1301 "street_lines": ["17 Daws Rd."],
1302 "city": "Blue Bell",
1306 "use": "primary home"
1311 "code_system_name": "HL7 Role"
1324 "identifier": "2.16.840.1.113883.19",
1325 "extension": "1138345"
1328 "street_lines": ["17 Daws Rd."],
1329 "city": "Blue Bell",
1333 "use": "primary home"
1339 "identifier": "f4dce790-8328-11db-9fe1-0800200c9a66"
1343 "name": "Colonoscopy",
1345 "code_system_name": "SNOMED CT"
1352 function populateHeader(pd) {
1356 "identifier": oidFacility,
1357 "extension": "TT988"
1361 "name": "Continuity of Care Document",
1363 "code_system_name": "LOINC"
1366 "2.16.840.1.113883.10.20.22.1.1",
1367 "2.16.840.1.113883.10.20.22.1.2"
1369 "title": "Clinical Health Summary",
1372 "date": fDate(pd.created_time) || "",
1373 "precision": getPrecision(fDate(pd.created_time))
1381 "identifier": "2.16.840.1.113883.4.6",
1382 "extension": pd.author.npi || "UNK"
1387 "last": pd.author.lname,
1388 "first": pd.author.fname
1394 pd.author.streetAddressLine
1396 "city": pd.author.city,
1397 "state": pd.author.state,
1398 "zip": pd.author.postalCode,
1399 "country": pd.author.country
1404 "number": pd.author.telecom,
1418 "root": "2.16.840.1.113883.4.6",
1419 "extension": npiFacility || "UNK"
1423 pd.encounter_provider.facility_name
1428 pd.encounter_provider.facility_street
1430 "city": pd.encounter_provider.facility_city,
1431 "state": pd.encounter_provider.facility_state,
1432 "zip": pd.encounter_provider.facility_postal_code,
1433 "country": pd.encounter_provider.facility_country_code
1438 "number": pd.encounter_provider.facility_phone,
1439 "type": "work primary"
1450 "root": "2.16.840.1.113883.4.6",
1451 "extension": npiFacility || "UNK"
1455 pd.encounter_provider.facility_name
1460 pd.encounter_provider.facility_street
1462 "city": pd.encounter_provider.facility_city,
1463 "state": pd.encounter_provider.facility_state,
1464 "zip": pd.encounter_provider.facility_postal_code,
1465 "country": pd.encounter_provider.facility_country_code
1470 "number": pd.encounter_provider.facility_phone,
1471 "type": "work primary"
1478 "identifier": "2.16.840.1.113883.4.6",
1479 "extension": "999999943252"
1484 "last": pd.data_enterer.lname,
1485 "first": pd.data_enterer.fname
1491 pd.data_enterer.streetAddressLine
1493 "city": pd.data_enterer.city,
1494 "state": pd.data_enterer.state,
1495 "zip": pd.data_enterer.postalCode,
1496 "country": pd.data_enterer.country
1501 "number": pd.data_enterer.telecom,
1502 "type": "work place"
1509 "identifier": "2.16.840.1.113883.19.5",
1510 "extension": "KP00017"
1515 "last": pd.informer.lname || "",
1516 "first": pd.informer.fname || ""
1522 pd.informer.streetAddressLine || ""
1524 "city": pd.informer.city,
1525 "state": pd.informer.state,
1526 "zip": pd.informer.postalCode,
1527 "country": pd.informer.country
1532 "number": pd.informer.telecom || "",
1533 "type": "work place"
1537 "service_event": { // @todo maybe move this to attributed or write z-schema template
1541 "code_system_name": "SNOMED CT"
1546 "precision": getPrecision()
1549 "date": pd.created_time,
1550 "precision": getPrecision()
1559 "identifier": "2.16.840.1.113883.4.6",
1560 "extension": "PseudoMD-1"
1565 "last": pd.information_recipient.lname,
1566 "first": pd.information_recipient.fname
1572 pd.information_recipient.streetAddressLine
1574 "city": pd.information_recipient.city,
1575 "state": pd.information_recipient.state,
1576 "zip": pd.information_recipient.postalCode,
1577 "country": pd.information_recipient.country
1582 "number": pd.information_recipient.telecom,
1583 "type": "work place"
1590 "identifier": "2.16.840.1.113883.19.5.9999.1393"
1594 pd.encounter_provider.facility_name
1599 pd.encounter_provider.facility_street
1601 "city": pd.encounter_provider.facility_city,
1602 "state": pd.encounter_provider.facility_state,
1603 "zip": pd.encounter_provider.facility_postal_code,
1604 "country": pd.encounter_provider.facility_country_code
1609 "number": pd.encounter_provider.facility_phone,
1610 "type": "primary work"
1619 "code_system_name": "Provider Codes"
1625 "name": "Primary Performer",
1627 "code_system_name": "Provider Role"
1636 function getMeta(pd) {
1642 "identifier": "2.16.840.1.113883.19.5.99999.1",
1643 "extension": "TT988"
1646 "confidentiality": "Normal",
1648 "identifier": "2.16.840.1.113883.19.5.99999.19",
1649 "extension": "sTT988"
1655 function genCcda(pd) {
1663 npiProvider = all.primary_care_provider.provider[0].npi;
1664 oidFacility = all.encounter_provider.facility_oid;
1665 npiFacility = all.encounter_provider.facility_npi;
1668 let demographic = populateDemographic(pd.patient, pd.guardian, pd);
1669 Object.assign(demographic, populateProviders());
1670 data.demographics = Object.assign(demographic);
1675 count = isOne(pd.history_physical.vitals_list.vitals);
1680 for (let i in pd.history_physical.vitals_list.vitals) {
1681 theone = populateVital(pd.history_physical.vitals_list.vitals[i]);
1682 many.vitals.push.apply(many.vitals, theone);
1684 } else if (count === 1) {
1685 theone = populateVital(pd.history_physical.vitals_list.vitals);
1686 many.vitals.push(theone);
1688 data.vitals = Object.assign(many.vitals);
1692 meds.medications = [];
1694 count = isOne(pd.medications.medication);
1699 for (let i in pd.medications.medication) {
1700 m[i] = populateMedication(pd.medications.medication[i]);
1701 meds.medications.push(m[i]);
1703 } else if (count !== 0) {
1704 m = populateMedication(pd.medications.medication);
1705 meds.medications.push(m);
1707 data.medications = Object.assign(meds.medications);
1711 encs.encounters = [];
1713 count = isOne(pd.encounter_list.encounter);
1718 for (let i in pd.encounter_list.encounter) {
1719 enc[i] = populateEncounter(pd.encounter_list.encounter[i]);
1720 encs.encounters.push(enc[i]);
1722 } else if (count !== 0) {
1723 enc = populateEncounter(pd.encounter_list.encounter);
1724 encs.encounters.push(enc);
1726 data.encounters = Object.assign(encs.encounters);
1731 allergies.allergies = [];
1733 count = isOne(pd.allergies.allergy);
1738 for (let i in pd.allergies.allergy) {
1739 allergy[i] = populateAllergy(pd.allergies.allergy[i]);
1740 allergies.allergies.push(allergy[i]);
1742 } else if (count !== 0) {
1743 allergy = populateAllergy(pd.allergies.allergy);
1744 allergies.allergies.push(allergy);
1746 data.allergies = Object.assign(allergies.allergies);
1751 problems.problems = [];
1753 count = isOne(pd.problem_lists.problem);
1758 for (let i in pd.problem_lists.problem) {
1759 problem[i] = populateProblem(pd.problem_lists.problem[i], pd);
1760 problems.problems.push(problem[i]);
1762 } else if (count !== 0) {
1763 problem = populateProblem(pd.problem_lists.problem);
1764 problems.problems.push(problem);
1766 data.problems = Object.assign(problems.problems);
1770 many.procedures = [];
1772 count = isOne(pd.procedures.procedure);
1777 for (let i in pd.procedures.procedure) {
1778 theone[i] = populateProcedure(pd.procedures.procedure[i]);
1779 many.procedures.push(theone[i]);
1781 } else if (count !== 0) {
1782 theone = populateProcedure(pd.procedures.procedure);
1783 many.procedures.push(theone);
1786 data.procedures = Object.assign(many.procedures);
1789 data.results = Object.assign(getResultSet(pd.results, pd)['results']);
1794 many.immunizations = [];
1796 count = isOne(pd.immunizations.immunization);
1801 for (let i in pd.immunizations.immunization) {
1802 theone[i] = populateImmunization(pd.immunizations.immunization[i]);
1803 many.immunizations.push(theone[i]);
1805 } else if (count !== 0) {
1806 theone = populateImmunization(pd.immunizations.immunization);
1807 many.immunizations.push(theone);
1809 data.immunizations = Object.assign(many.immunizations);
1813 many.plan_of_care = [];
1815 count = isOne(pd.planofcare); // ccm doesn't send array of items
1820 for (let i in pd.planofcare.item) {
1821 theone[i] = getPlanOfCare(pd.planofcare.item[i]);
1822 many.plan_of_care.push(theone[i]);
1824 } else if (count !== 0) {
1825 theone = getPlanOfCare(pd.planofcare.item);
1826 many.plan_of_care.push(theone);
1829 data.plan_of_care = Object.assign(many.plan_of_care);
1833 many.social_history = [];
1835 count = isOne(pd.history_physical.social_history.history_element);
1840 for (let i in pd.history_physical.social_history.history_element) {
1842 theone[i] = populateSocialHistory(pd.history_physical.social_history.history_element[i]);
1843 many.social_history.push(theone[i]);
1845 } else if (count !== 0) {
1846 theone = populateSocialHistory(pd.history_physical.social_history.history_element);
1847 many.social_history.push(theone);
1850 data.social_history = Object.assign(many.social_history);
1852 // ------------------------------------------ End Sections ----------------------------------------//
1854 doc.data = Object.assign(data);
1855 let meta = getMeta(pd);
1856 let header = populateHeader(pd);
1858 meta.ccda_header = Object.assign(header);
1859 doc.meta = Object.assign(meta);
1860 let xml = bbg.generateCCD(doc);
1863 /*fs.writeFile("bbtest.json", JSON.stringify(doc, null, 4), function (err) {
1865 return console.log(err);
1867 console.log("Json saved!");
1869 fs.writeFile("bbtest.xml", xml, function (err) {
1871 return console.log(err);
1873 console.log("Xml saved!");
1879 function processConnection(connection) {
1880 conn = connection; // make it global
1881 var remoteAddress = conn.remoteAddress + ':' + conn.remotePort;
1882 //console.log(remoteAddress);
1883 conn.setEncoding('utf8');
1885 // patched out re: buffer overrun fix. maintain for history.
1886 /*function eventData(xml) {
1887 xml = xml.replace(/(\u000b|\u001c)/gm, "").trim();
1888 // Sanity check from service manager
1889 if (xml === 'status' || xml.length < 80) {
1890 conn.write("statusok" + String.fromCharCode(28) + "\r\r");
1895 // ---------------------start--------------------------------
1897 xml = xml.toString().replace(/\t\s+/g, ' ').trim();
1899 to_json(xml, function (error, data) {
1900 // console.log(JSON.stringify(data, null, 4));
1901 if (error) { // need try catch
1902 console.log('toJson error: ' + error + 'Len: ' + xml.length);
1905 doc = genCcda(data.CCDA);
1908 doc = headReplace(doc);
1909 doc = doc.toString().replace(/(\u000b|\u001c|\r)/gm, "").trim();
1912 let numChunks = Math.ceil(doc.length / 1024);
1913 for (let i = 0, o = 0; i < numChunks; ++i, o += 1024) {
1914 chunk = doc.substr(o, 1024);
1918 conn.write(String.fromCharCode(28) + "\r\r" + '');
1923 var xml_complete = "";
1925 function eventData(xml) {
1926 xml = xml.replace(/(\u000b|\u001c)/gm, "").trim();
1927 // Sanity check from service manager
1928 if (xml === 'status' || xml.length < 80) {
1929 conn.write("statusok" + String.fromCharCode(28) + "\r\r");
1933 xml_complete += xml.toString();
1934 if (xml.toString().match(/\<\/CCDA\>$/g)) {
1935 // ---------------------start--------------------------------
1937 xml_complete = xml_complete.replace(/\t\s+/g, ' ').trim();
1939 to_json(xml_complete, function (error, data) {
1940 // console.log(JSON.stringify(data, null, 4));
1941 if (error) { // need try catch
1942 console.log('toJson error: ' + error + 'Len: ' + xml_complete.length);
1945 doc = genCcda(data.CCDA);
1948 doc = headReplace(doc);
1949 doc = doc.toString().replace(/(\u000b|\u001c|\r)/gm, "").trim();
1952 let numChunks = Math.ceil(doc.length / 1024);
1953 for (let i = 0, o = 0; i < numChunks; ++i, o += 1024) {
1954 chunk = doc.substr(o, 1024);
1957 conn.write(String.fromCharCode(28) + "\r\r" + '');
1962 function eventCloseConn() {
1963 //console.log('connection from %s closed', remoteAddress);
1966 function eventErrorConn(err) {
1967 //console.log('Connection %s error: %s', remoteAddress, err.message);
1970 // Connection Events //
1971 conn.on('data', eventData);
1972 conn.once('close', eventCloseConn);
1973 conn.on('error', eventErrorConn);
1976 function setUp(server) {
1977 server.on('connection', processConnection);
1978 server.listen(6661, 'localhost', function () {
1979 //console.log('server listening to %j', server.address());