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('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');
32 var server = net.createServer();
33 var conn = ''; // make our connection scope global to script
35 // some useful routines for populating template sections
36 function validate(toValidate, ref, retObj) {
38 if (typeof ref[p].dataType === "undefined") {
40 if (!toValidate[p]) toValidate[p] = {};
41 validate(toValidate[p], ref[p], retObj[p]);
43 if (typeof toValidate === "undefined") toValidate = {};
44 var trimmed = trim(toValidate[p]);
45 retObj[p] = typeEnforcer(ref[p].dataType, trimmed);
51 function typeEnforcer(type, val) {
55 if (typeof val === "string") {
56 validVal = val.toLowerCase() === "true";
62 if ((val === null) || (val === "undefined") || (typeof val === "undefined")) {
64 } else if (typeof val == "object") {
67 validVal = trim(String(val));
71 if (typeof val === 'undefined' || val === null) {
73 } else if (Array.isArray(val)) {
75 val.forEach(function (v) {
76 validVal.push(trim(v));
79 validVal = [trim(val)];
83 var asInt = parseInt(val, 10);
84 if (isNaN(asInt)) asInt = 0;
88 var asNum = parseFloat(val);
89 if (isNaN(asNum)) asNum = 0;
97 if (typeof s === 'string') return s.trim();
102 return trim(s).toLowerCase().replace(/[^a-zA-Z0-9]+/g, '-').replace(/\-+$/, '');
105 function fDate(str) {
107 * Format dates to js required yyyy-mm-dd + zero hundred hours Yes I freely
110 str = String(str); // at least ensure string so cast it...
111 if (Number(str) === 0) {
112 return "0000-01-01T00:00:00.000Z"
114 if (str.length === 8 || (str.length === 14 && (1 * str.substring(12, 14)) === 0)) {
115 return [str.slice(0, 4), str.slice(4, 6), str.slice(6, 8)].join('-') + 'T00:00:00.000Z';
116 } else if (str.length === 10 && (1 * str.substring(0, 2)) <= 12) {
117 // case mm/dd/yyyy or mm-dd-yyyy
118 return [str.slice(6, 10), str.slice(0, 2), str.slice(3, 5)].join('-') + 'T00:00:00.000Z';
119 } else if (str.length === 14 && (1 * str.substring(12, 14)) > 0) {
120 // maybe a real time so parse
122 return str + 'T00:00:00.000Z';
125 function cleanCode(code) {
126 return code.replace(/[.#]/, "");
129 function isOne(who) {
131 if (who !== null && typeof who === 'object') {
132 return who.hasOwnProperty('extension') ? 1 : Object.keys(who).length;
141 function headReplace(content) {
142 var r = '<?xml version="1.0" encoding="UTF-8"?>\n' +
143 '<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"' +
144 ' xmlns="urn:hl7-org:v3" xmlns:mif="urn:hl7-org:v3/mif">\n';
145 r += content.substr(content.search(/<realmCode/i));
149 // Data model for Blue Button
150 function populateDemographic(pd, g, all) {
152 "relation": g.relation,
154 "street_lines": [g.address],
158 "country": g.country,
159 "use": "primary home"
162 "last": g.display_name, //@todo parse first/last
163 "first": g.display_name
167 "type": "primary home"
173 "middle": [pd.mname],
179 "date": fDate(pd.dob),
185 "identifier": "2.16.840.1.113883.19.5.99999.2",
186 "extension": "998991"
188 "identifier": "2.16.840.1.113883.4.1",
191 "marital_status": pd.status || "",
193 "street_lines": [pd.street],
196 "zip": pd.postalCode,
197 "country": pd.country,
198 "use": "primary home"
201 "number": pd.phone_home,
202 "type": "primary home"
205 "ethnicity": pd.ethnicity,
207 "language": pd.language,
209 "mode": "Expressed spoken",
210 "proficiency": "Good"
212 "religion": pd.religion,
219 "guardians": g.display_name ? guardian : '',
220 "attributed_provider": {
223 "root": "2.16.840.1.113883.4.6",
224 //"extension": "KP00017"
228 "number": all.encounter_provider.facility_phone || "",
232 "full": all.encounter_provider.facility_name || ""
240 function populateProviders(all) {
246 "root": "2.16.840.1.113883.4.6",
247 "extension": "MD-Provider-1"
252 "name": all.primary_care_provider.provider[0].physician_type || "Not Avail",
253 "code": all.primary_care_provider.provider[0].physician_type_code || "",
254 "code_system_name": "Provider Codes"
259 "last": all.primary_care_provider.provider[0].lname || "",
260 "first": all.primary_care_provider.provider[0].fname || ""
266 all.encounter_provider.facility_street
268 "city": all.encounter_provider.facility_city,
269 "state": all.encounter_provider.facility_state,
270 "zip": all.encounter_provider.facility_postal_code,
271 "country": all.encounter_provider.facility_country_code
277 "number": all.encounter_provider.facility_phone || "",
285 "identifier": "2.16.840.1.113883.19.5.9999.1393" //@todo need facility oid
289 all.encounter_provider.facility_name
294 all.encounter_provider.facility_street
296 "city": all.encounter_provider.facility_city,
297 "state": all.encounter_provider.facility_state,
298 "zip": all.encounter_provider.facility_postal_code,
299 "country": all.encounter_provider.facility_country_code
304 "number": all.encounter_provider.facility_phone,
305 "type": "primary work"
320 function populateMedication(pd) {
324 "date": fDate(pd.start_date),
328 "date": pd.end_date ? fDate(pd.end_date) : "",
333 "identifier": pd.sha_extension
339 "identifier": "2a620155-9d11-439e-92b3-5d9815ff4ee8"
341 "unencoded_name": pd.drug,
348 "code_system_name": "RXNORM"
350 "code_system_name": "RXNORM"
357 "date": fDate(pd.start_date),
365 "identifier": "2a620155-9d11-439e-92b3-5d9815fe4de8"
375 "name": "instruction",
377 "code_system_name": "SNOMED CT"
379 "free_text": pd.instructions || "None Available"
384 "name": pd.route || "",
385 "code": pd.route_code || "",
386 "code_system_name": "Medication Route FDA"
390 "code": pd.form_code,
391 "code_system_name": "Medication Route FDA"
394 "value": parseFloat(pd.size),
398 "value": parseFloat(pd.dosage),
403 "value": parseFloat(pd.dosage),
412 "identifier": "2.16.840.1.113883.19.5.9999.1393"
414 "name": [pd.performer_name]
419 "code": pd.form_code,
420 "code_system_name": "RXNORM"
425 "code_system_name": "ActCode"
430 "code_system_name": "SNOMED CT"
435 "identifier": "db734647-fc99-424c-a864-7e3cda82e703",
441 "code_system_name": "SNOMED CT"
445 "date": fDate(pd.start_date),
450 "name": pd.indications,
451 "code": pd.indications_code,
452 "code_system_name": "SNOMED CT"
457 "identifier": "1.2.3.4.56789.1",
458 "extension": "cb734647-fc99-424c-a864-7e3cda82e704"
462 "identifier": "2.16.840.1.113883.19.5.9999.456",
463 "extension": "2981823"
466 "street_lines": [pd.address],
474 "identifier": "2.16.840.1.113883.19.5.9999.1393"
476 "name": [pd.performer_name]
481 "identifier": "2a620155-9d11-439e-92b3-5d9815ff4ee8"
483 "unencoded_name": pd.drug,
490 "code_system_name": "RXNORM"
492 "code_system_name": "RXNORM"
500 function populateEncounter(pd) {
503 "name": pd.visit_category ? pd.visit_category : '',
504 "code": pd.encounter_procedures ? pd.encounter_procedures.procedures.code : '',
505 "code_system_name": "CPT",
507 "name": "Ambulatory",
509 "code_system_name": "ActCode"
513 "identifier": pd.sha_extension
517 "date": fDate(pd.date_formatted),
518 "precision": "second"
523 "identifier": pd.facility_sha_extension
526 "name": pd.physician_type,
527 "code": pd.physician_type_code,
528 "code_system_name": "SNOMED CT"
534 "name": pd.location_details,
536 "code_system_name": "HealthcareServiceLocation"
539 "street_lines": [pd.facility_address],
540 "city": pd.facility_city,
541 "state": pd.facility_state,
542 "zip": pd.facility_zip,
543 "country": pd.facility_country
552 "name": pd.encounter_reason,
554 "code_system_name": "SNOMED CT"
558 "date": fDate(pd.date_formatted),
566 function populateAllergy(pd) {
569 "identifier": "36e3e930-7b14-11db-9fe1-0800200c9a66"
573 "date": fDate(pd.startdate),
579 "identifier": "4adc1020-7b14-11db-9fe1-0800200c9a66"
583 "code": pd.rxnorm_code,
584 "code_system_name": "RXNORM"
587 "name": "Propensity to adverse reactions to drug",
588 "code": pd.snomed_code,
589 "code_system_name": "SNOMED CT"
593 "date": fDate(pd.startdate),
598 "name": pd.allergy_status,
599 "code": pd.status_code,
600 "code_system_name": "SNOMED CT"
604 "identifier": "4adc1020-7b14-11db-9fe1-0800200c9a64"
608 "date": fDate(pd.startdate),
612 "date": fDate(pd.enddate),
617 "name": pd.reaction_text,
618 "code": pd.reaction_code,
619 "code_system_name": "SNOMED CT"
624 "code": pd.outcome_code,
625 "code_system_name": "SNOMED CT"
627 /*"interpretation": {
630 "code_system_name": "Observation Interpretation"
637 "code": pd.outcome_code,
638 "code_system_name": "SNOMED CT"
640 /*"interpretation": {
643 "code_system_name": "Observation Interpretation"
650 function populateProblem(pd) {
654 "date": fDate(pd.start_date_table),
658 "date": fDate(pd.end_date),
663 "identifier": pd.sha_extension,
664 "extension": pd.extension
668 "name": trim(pd.title),
669 "code": cleanCode(pd.code),
670 "code_system_name": "ICD10"
674 "date": fDate(pd.start_date),
678 "date": fDate(pd.end_date),
679 "precision": "second"
684 "onset_age_unit": "Year",
689 "date": fDate(pd.start_date),
693 "date": fDate(pd.end_date),
694 "precision": "second"
698 "patient_status": pd.observation,
699 "source_list_identifiers": [{
700 "identifier": pd.sha_extension
706 function populateProcedure(pd) {
709 "name": pd.description,
711 "code_system_name": "CPT"
714 "identifier": "d68b7e32-7810-4f5b-9cc2-acd54b0fd85d"
719 "date": fDate(pd.date),
726 "code_system_name": ""
730 "identifier": "c2ee9ee9-ae31-4628-a919-fec1cbb58683"
735 "code_system_name": "SNOMED CT"
740 "identifier": "2.16.840.1.113883.19.5.9999.456",
741 "extension": "2981823"
744 "street_lines": [pd.address],
751 "number": pd.work_phone,
756 "identifier": "2.16.840.1.113883.19.5.9999.1393"
758 "name": [pd.facility_name],
760 "street_lines": [pd.facility_address],
761 "city": pd.facility_city,
762 "state": pd.facility_state,
763 "zip": pd.facility_zip,
764 "country": pd.facility_country
767 "number": pd.facility_phone,
772 "procedure_type": "procedure"
776 function populateResult(pd) {
779 "identifier": "107c2dc0-67a5-11db-bd13-0800200c9a66"
783 "code": pd.test_code || "",
784 "code_system_name": "LOINC"
788 "date": fDate(pd.date_ordered_table),
792 "status": pd.order_status,
794 "low": pd.subtest.range,
795 "high": pd.subtest.range,
796 "unit": pd.subtest.unit
798 "interpretations": [pd.subtest.result_value],
799 "value": parseFloat(pd.subtest.result_value),
800 "unit": pd.subtest.unit
804 function getResultSet(results) {
807 "identifier": "7d5a02b0-67a4-11db-bd13-0800200c9a66"
810 "name": "Get this data.",
812 "code_system_name": "SNOMED CT"
821 count = isOne(results.result);
826 for (let i in results.result) {
827 theone[i] = populateResult(results.result[i]);
828 many.results.push(theone[i]);
830 } else if (count !== 0) {
831 theone = populateResult(results.result);
832 many.results.push(theone);
834 rs.results = Object.assign(resultSet);
835 rs.results.results = Object.assign(many.results);
839 function getPlanOfCare(pd) {
842 "name": pd.code_text,
844 "code_system_name": "SNOMED CT"
847 "identifier": "9a6d1bac-17d3-4195-89a4-1121bc809b4a"
851 "date": fDate(pd.date),
855 "type": "observation",
859 "subType": pd.description
863 function populateVital(pd) {
866 "identifier": "2.16.840.1.113883.3.140.1.0.6.10.14.1",
867 "extension": pd.extension_bps
870 "name": "Blood Pressure Systolic",
872 "code_system_name": "LOINC"
874 "status": "completed",
877 "date": fDate(pd.effectivetime),
878 "precision": "second"
881 "value": parseFloat(pd.bps),
885 "identifier": "2.16.840.1.113883.3.140.1.0.6.10.14.1",
886 "extension": pd.extension_bpd
889 "name": "Blood Pressure Diastolic",
891 "code_system_name": "LOINC"
893 "status": "completed",
896 "date": fDate(pd.effectivetime),
897 "precision": "second"
900 "interpretations": ["UNK"],
901 "value": parseFloat(pd.bpd),
905 "identifier": "2.16.840.1.113883.3.140.1.0.6.10.14.1",
906 "extension": pd.extension_pulse
909 "name": "Heart Rate",
911 "code_system_name": "LOINC"
913 "status": "completed",
916 "date": fDate(pd.effectivetime),
917 "precision": "second"
920 "interpretations": ["UNK"],
921 "value": parseFloat(pd.pulse),
925 "identifier": "2.16.840.1.113883.3.140.1.0.6.10.14.1",
926 "extension": pd.extension_breath
929 "name": "Respiratory Rate",
931 "code_system_name": "LOINC"
933 "status": "completed",
936 "date": fDate(pd.effectivetime),
937 "precision": "second"
940 "interpretations": ["UNK"],
941 "value": parseFloat(pd.breath),
945 "identifier": "2.16.840.1.113883.3.140.1.0.6.10.14.1",
946 "extension": pd.extension_temperature
949 "name": "Body Temperature",
951 "code_system_name": "LOINC"
953 "status": "completed",
956 "date": fDate(pd.effectivetime),
957 "precision": "second"
960 "interpretations": ["UNK"],
961 "value": parseFloat(pd.temperature),
965 "identifier": "2.16.840.1.113883.3.140.1.0.6.10.14.1",
966 "extension": pd.extension_height
971 "code_system_name": "LOINC"
973 "status": "completed",
976 "date": fDate(pd.effectivetime),
977 "precision": "second"
980 "interpretations": ["UNK"],
981 "value": parseFloat(pd.height),
982 "unit": pd.unit_height
985 "identifier": "2.16.840.1.113883.3.140.1.0.6.10.14.1",
986 "extension": pd.extension_weight
989 "name": "Weight Measured",
991 "code_system_name": "LOINC"
993 "status": "completed",
996 "date": fDate(pd.effectivetime),
997 "precision": "second"
1000 "interpretations": ["UNK"],
1001 "value": parseFloat(pd.weight),
1002 "unit": pd.unit_weight
1005 "identifier": "2.16.840.1.113883.3.140.1.0.6.10.14.1",
1006 "extension": pd.extension_BMI
1009 "name": "BMI (Body Mass Index)",
1011 "code_system_name": "LOINC"
1013 "status": "completed",
1016 "date": fDate(pd.effectivetime),
1017 "precision": "second"
1020 "interpretations": ["UNK"],
1021 "value": parseFloat(pd.BMI),
1026 function populateSocialHistory(pd) {
1030 "date": fDate(pd.date),
1034 "date": fDate(pd.date),
1035 "precision": "second"
1039 "identifier": pd.sha_extension,
1040 "extension": pd.extension
1045 "value": pd.description
1049 function populateImmunization(pd) {
1053 "date": fDate(pd.administered_on),
1054 "precision": "month"
1058 "identifier": "e6f1ba43-c0ed-4b9b-9f12-f435d8ad8f92"
1060 "status": "complete",
1063 "name": pd.code_text,
1064 "code": pd.cvx_code,
1065 "code_system_name": "CVX",
1069 "code_system_name": "CVX"
1073 "manufacturer": "UNK"
1077 "name": pd.route_of_administration,
1078 "code": pd.route_code,
1079 "code_system_name": "Medication Route FDA"
1088 "identifier": "2.16.840.1.113883.19.5.9999.456",
1089 "extension": "2981824"
1096 "street_lines": [pd.address],
1104 "identifier": "2.16.840.1.113883.19.5.9999.1394"
1106 "name": [pd.facility_name]
1111 "name": "immunization education",
1112 "code": "171044003",
1113 "code_system_name": "SNOMED CT"
1115 "free_text": "Needs Attention for more data."
1120 function populatePayer(pd) {
1123 "identifier": "1fe2cdd0-7aad-11db-9fe1-0800200c9a66"
1127 "identifier": "3e676a50-7aac-11db-9fe1-0800200c9a66"
1131 "code_system_name": "HL7 RoleCode"
1136 "code_system_name": "HL7 RoleCode"
1140 "identifier": "2.16.840.1.113883.19"
1143 "street_lines": ["123 Insurance Road"],
1144 "city": "Blue Bell",
1151 "number": "(781)555-1515",
1152 "type": "work place"
1155 "name": ["Good Health Insurance"],
1157 "street_lines": ["123 Insurance Road"],
1158 "city": "Blue Bell",
1165 "number": "(781)555-1515",
1166 "type": "work place"
1171 "code_system_name": "HL7 RoleCode"
1179 "code_system_name": "HL7 Role"
1182 "identifier": "329fcdf0-7ab3-11db-9fe1-0800200c9a66"
1186 "middle": ["Frankie"],
1191 "street_lines": ["17 Daws Rd."],
1192 "city": "Blue Bell",
1196 "use": "primary home"
1199 "number": "(781)555-1212",
1200 "type": "primary home"
1207 "code_system_name": "HL7 Role"
1211 "identifier": "14d4a520-7aae-11db-9fe1-0800200c9a66",
1212 "extension": "1138345"
1215 "street_lines": ["17 Daws Rd."],
1216 "city": "Blue Bell",
1220 "use": "primary home"
1225 "code_system_name": "HL7 Role"
1238 "identifier": "2.16.840.1.113883.19",
1239 "extension": "1138345"
1242 "street_lines": ["17 Daws Rd."],
1243 "city": "Blue Bell",
1247 "use": "primary home"
1253 "identifier": "f4dce790-8328-11db-9fe1-0800200c9a66"
1257 "name": "Colonoscopy",
1259 "code_system_name": "SNOMED CT"
1266 function populateHeader(pd) {
1270 "identifier": "2.16.840.1.113883.19.5.99999.1",
1271 "extension": "TT988"
1275 "name": "Continuity of Care Document",
1277 "code_system_name": "LOINC"
1280 "2.16.840.1.113883.10.20.22.1.1",
1281 "2.16.840.1.113883.10.20.22.1.2"
1283 "title": "Clinical Health Summary",
1286 "date": pd.created_time_timezone || "",
1287 "precision": "minute"
1295 "identifier": "2.16.840.1.113883.4.6",
1296 "extension": "99999999"
1301 "last": pd.author.lname,
1302 "first": pd.author.fname
1308 pd.author.streetAddressLine
1310 "city": pd.author.city,
1311 "state": pd.author.state,
1312 "zip": pd.author.postalCode,
1313 "country": pd.author.country
1318 "number": pd.author.telecom,
1319 "type": "work place"
1332 "root": "2.16.840.1.113883.19.5.9999.1393" // @todo oid
1336 pd.encounter_provider.facility_name
1341 pd.encounter_provider.facility_street
1343 "city": pd.encounter_provider.facility_city,
1344 "state": pd.encounter_provider.facility_state,
1345 "zip": pd.encounter_provider.facility_postal_code,
1346 "country": pd.encounter_provider.facility_country_code
1351 "number": pd.encounter_provider.facility_phone,
1352 "type": "primary work"
1363 "identifier": "2.16.840.1.113883.4.6",
1364 "extension": "999999943252"
1369 "last": pd.data_enterer.lname,
1370 "first": pd.data_enterer.fname
1376 pd.data_enterer.streetAddressLine
1378 "city": pd.data_enterer.city,
1379 "state": pd.data_enterer.state,
1380 "zip": pd.data_enterer.postalCode,
1381 "country": pd.data_enterer.country
1386 "number": pd.data_enterer.telecom,
1387 "type": "work place"
1394 "identifier": "2.16.840.1.113883.19.5",
1395 "extension": "KP00017"
1400 "last": pd.informer.lname || "",
1401 "first": pd.informer.fname || ""
1407 pd.informer.streetAddressLine || ""
1409 "city": pd.informer.city,
1410 "state": pd.informer.state,
1411 "zip": pd.informer.postalCode,
1412 "country": pd.informer.country
1417 "number": pd.informer.telecom || "",
1418 "type": "work place"
1422 "service_event": { // @todo maybe move this to attributed or write z-schema template
1426 "code_system_name": "SNOMED CT"
1431 "precision": "minute"
1434 "date": pd.created_time_timezone,
1435 "precision": "minute"
1444 "identifier": "2.16.840.1.113883.4.6",
1445 "extension": "PseudoMD-1"
1450 "last": pd.information_recipient.lname,
1451 "first": pd.information_recipient.fname
1457 pd.information_recipient.streetAddressLine
1459 "city": pd.information_recipient.city,
1460 "state": pd.information_recipient.state,
1461 "zip": pd.information_recipient.postalCode,
1462 "country": pd.information_recipient.country
1467 "number": pd.information_recipient.telecom,
1468 "type": "work place"
1475 "identifier": "2.16.840.1.113883.19.5.9999.1393"
1479 pd.encounter_provider.facility_name
1484 pd.encounter_provider.facility_street
1486 "city": pd.encounter_provider.facility_city,
1487 "state": pd.encounter_provider.facility_state,
1488 "zip": pd.encounter_provider.facility_postal_code,
1489 "country": pd.encounter_provider.facility_country_code
1494 "number": pd.encounter_provider.facility_phone,
1495 "type": "primary work"
1504 "code_system_name": "Provider Codes"
1510 "name": "Primary Performer",
1512 "code_system_name": "Provider Role"
1521 function getMeta(pd) {
1527 "identifier": "2.16.840.1.113883.19.5.99999.1",
1528 "extension": "TT988"
1531 "confidentiality": "Normal",
1533 "identifier": "2.16.840.1.113883.19.5.99999.19",
1534 "extension": "sTT988"
1540 function genCcda(pd) {
1549 let demographic = populateDemographic(pd.patient, pd.guardian, pd);
1550 Object.assign(demographic, populateProviders(pd));
1551 data.demographics = Object.assign(demographic);
1556 count = isOne(pd.history_physical.vitals_list.vitals);
1561 for (let i in pd.history_physical.vitals_list.vitals) {
1562 theone = populateVital(pd.history_physical.vitals_list.vitals[i]);
1563 many.vitals.push.apply(many.vitals, theone);
1565 } else if (count === 1) {
1566 theone = populateVital(pd.history_physical.vitals_list.vitals);
1567 many.vitals.push(theone);
1569 data.vitals = Object.assign(many.vitals);
1573 meds.medications = [];
1575 count = isOne(pd.medications.medication);
1580 for (let i in pd.medications.medication) {
1581 m[i] = populateMedication(pd.medications.medication[i]);
1582 meds.medications.push(m[i]);
1584 } else if (count !== 0) {
1585 m = populateMedication(pd.medications.medication);
1586 meds.medications.push(m);
1588 data.medications = Object.assign(meds.medications);
1592 encs.encounters = [];
1594 count = isOne(pd.encounter_list.encounter);
1599 for (let i in pd.encounter_list.encounter) {
1600 enc[i] = populateEncounter(pd.encounter_list.encounter[i]);
1601 encs.encounters.push(enc[i]);
1603 } else if (count !== 0) {
1604 enc = populateEncounter(pd.encounter_list.encounter);
1605 encs.encounters.push(enc);
1607 data.encounters = Object.assign(encs.encounters);
1612 allergies.allergies = [];
1614 count = isOne(pd.allergies.allergy);
1619 for (let i in pd.allergies.allergy) {
1620 allergy[i] = populateAllergy(pd.allergies.allergy[i]);
1621 allergies.allergies.push(allergy[i]);
1623 } else if (count !== 0) {
1624 allergy = populateAllergy(pd.allergies.allergy);
1625 allergies.allergies.push(allergy);
1627 data.allergies = Object.assign(allergies.allergies);
1632 problems.problems = [];
1634 count = isOne(pd.problem_lists.problem);
1639 for (let i in pd.problem_lists.problem) {
1640 problem[i] = populateProblem(pd.problem_lists.problem[i]);
1641 problems.problems.push(problem[i]);
1643 } else if (count !== 0) {
1644 problem = populateProblem(pd.problem_lists.problem);
1645 problems.problems.push(problem);
1647 data.problems = Object.assign(problems.problems);
1651 many.procedures = [];
1653 count = isOne(pd.procedures.procedure);
1658 for (let i in pd.procedures.procedure) {
1659 theone[i] = populateProcedure(pd.procedures.procedure[i]);
1660 many.procedures.push(theone[i]);
1662 } else if (count !== 0) {
1663 theone = populateProcedure(pd.procedures.procedure);
1664 many.procedures.push(theone);
1667 data.procedures = Object.assign(many.procedures);
1669 data.results = Object.assign(getResultSet(pd.results)['results']);
1673 many.immunizations = [];
1675 count = isOne(pd.immunizations.immunization);
1680 for (let i in pd.immunizations.immunization) {
1681 theone[i] = populateImmunization(pd.immunizations.immunization[i]);
1682 many.immunizations.push(theone[i]);
1684 } else if (count !== 0) {
1685 theone = populateImmunization(pd.immunizations.immunization);
1686 many.immunizations.push(theone);
1688 data.immunizations = Object.assign(many.immunizations);
1692 many.plan_of_care = [];
1694 count = isOne(pd.planofcare); // ccm doesn't send array of items
1699 for (let i in pd.planofcare.item) {
1700 theone[i] = getPlanOfCare(pd.planofcare.item[i]);
1701 many.plan_of_care.push(theone[i]);
1703 } else if (count !== 0) {
1704 theone = getPlanOfCare(pd.planofcare.item);
1705 many.plan_of_care.push(theone);
1708 data.plan_of_care = Object.assign(many.plan_of_care);
1712 many.social_history = [];
1714 count = isOne(pd.history_physical.social_history.history_element);
1719 for (let i in pd.history_physical.social_history.history_element) {
1720 theone[i] = populateSocialHistory(pd.history_physical.social_history.history_element[i]);
1721 many.social_history.push(theone[i]);
1723 } else if (count !== 0) {
1724 theone = populateSocialHistory(pd.history_physical.social_history.history_element);
1725 many.social_history.push(theone);
1728 data.social_history = Object.assign(many.social_history);
1730 // ------------------------------------------ End Sections ----------------------------------------//
1732 doc.data = Object.assign(data);
1733 let meta = getMeta(pd);
1734 let header = populateHeader(pd);
1736 meta.ccda_header = Object.assign(header);
1737 doc.meta = Object.assign(meta);
1738 let xml = bbg.generateCCD(doc);
1741 /*fs.writeFile("bbtest.json", JSON.stringify(doc, null, 4), function (err) {
1743 return console.log(err);
1745 console.log("Json saved!");
1747 fs.writeFile("bbtest.xml", xml, function (err) {
1749 return console.log(err);
1751 console.log("Xml saved!");
1757 function processConnection(connection) {
1758 conn = connection; // make it global
1759 var remoteAddress = conn.remoteAddress + ':' + conn.remotePort;
1760 console.log(remoteAddress);
1761 conn.setEncoding('utf8');
1763 function eventData(xml) {
1764 xml = xml.replace(/(\u000b|\u001c)/gm, "").trim();
1765 // Sanity check from service manager
1766 if (xml === 'status' || xml.length < 80) {
1767 conn.write("statusok" + String.fromCharCode(28) + "\r\r");
1772 // ---------------------start--------------------------------
1774 xml = xml.toString().replace(/\t\s+/g, ' ').trim();
1775 //xml = xml.toString().replace(/\t/g,'');
1776 //console.log(xml + "\n\rLen: "+ xml.length);
1777 to_json(xml, function (error, data) {
1778 // console.log(JSON.stringify(data, null, 4));
1779 if (error) { // need try catch
1780 console.log('toJson error: ' + error + 'Len: ' + xml.length);
1783 doc = genCcda(data.CCDA);
1786 doc = headReplace(doc);
1787 doc = doc.toString().replace(/(\u000b|\u001c|\r)/gm, "").trim();
1790 let numChunks = Math.ceil(doc.length / 1024);
1791 for (let i = 0, o = 0; i < numChunks; ++i, o += 1024) {
1792 chunk = doc.substr(o, 1024);
1796 conn.write(String.fromCharCode(28) + "\r\r" + '');
1801 function eventCloseConn() {
1802 //console.log('connection from %s closed', remoteAddress);
1805 function eventErrorConn(err) {
1806 //console.log('Connection %s error: %s', remoteAddress, err.message);
1809 // Connection Events //
1810 conn.on('data', eventData);
1811 conn.once('close', eventCloseConn);
1812 conn.on('error', eventErrorConn);
1815 function setUp(server) {
1816 server.on('connection', processConnection);
1817 server.listen(6661, 'localhost', function () {
1818 //console.log('server listening to %j', server.address());