3 * Copyright (C) 2016-2017 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 bb = require('blue-button'); //for use set global-not needed here
27 var bbg = require('blue-button-generate');
28 //var bbm = require('blue-button-model'); //for use set global-not needed here
30 var server = net.createServer();
31 var conn = ''; // make our connection scope global to script
32 // some useful routines for populating template sections
33 function validate(toValidate, ref, retObj) {
35 if (typeof ref[p].dataType === "undefined") {
37 if (!toValidate[p]) toValidate[p] = {};
38 validate(toValidate[p], ref[p], retObj[p]);
40 if (typeof toValidate === "undefined") toValidate = {};
41 var trimmed = trim(toValidate[p]);
42 retObj[p] = typeEnforcer(ref[p].dataType, trimmed);
47 function typeEnforcer(type, val){
51 if (typeof val === "string") {
52 validVal = val.toLowerCase() === "true";
58 if ( (val === null) || (val === "undefined") || (typeof val === "undefined") ) {
60 } else if (typeof val == "object") {
63 validVal = trim(String(val));
67 if (typeof val === 'undefined' || val === null) {
69 } else if (Array.isArray(val)) {
71 val.forEach(function(v) {
72 validVal.push(trim(v));
75 validVal = [trim(val)];
79 var asInt = parseInt(val, 10);
80 if (isNaN(asInt)) asInt = 0;
84 var asNum = parseFloat(val);
85 if (isNaN(asNum)) asNum = 0;
92 if (typeof s === 'string') return s.trim();
96 return trim(s).toLowerCase().replace(/[^a-zA-Z0-9]+/g, '-').replace(/\-+$/, '');
100 * Format dates to js required yyyy-mm-dd + zero hundred hours Yes I freely
103 str = String(str); // at least ensure string so cast it...
104 if (Number(str) === 0){return "0000-01-01T00:00:00.000Z";}
105 if (str.length === 8 || (str.length === 14 && (1 * str.substring(12, 14)) === 0)) {
106 // case yyyymmdd or yyyymmdd000000 called effectivetime by ccm.
107 return [ str.slice(0, 4), str.slice(4, 6), str.slice(6, 8) ].join('-') + 'T00:00:00.000Z';
108 } else if (str.length === 10 && (1 * str.substring(0, 2)) <= 12) {
109 // case mm/dd/yyyy or mm-dd-yyyy
110 return [ str.slice(6, 10), str.slice(0, 2), str.slice(3, 5) ].join('-') + 'T00:00:00.000Z';
111 } else if (str.length === 14 && (1 * str.substring(12, 14)) > 0) {
112 // maybe a real time so parse
114 return str + 'T00:00:00.000Z';
116 function isOne(who) {
118 if (who !== null && typeof who === 'object') {
119 return who.hasOwnProperty('extension') ? 1 : Object.keys(who).length;
122 catch(e){ return false;}
125 function headReplace(content) {
126 var r = '<?xml version="1.0" encoding="UTF-8"?>\n' +
127 '<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"' +
128 ' xmlns="urn:hl7-org:v3" xmlns:mif="urn:hl7-org:v3/mif">\n';
129 r += content.substr(194);
132 // Data model for Blue Button
133 function populateDemographic(pd, g) {
136 "middle" : [ pd.mname ],
142 "date" : fDate(pd.dob),
146 "gender" : pd.gender,
148 "identifier" : "2.16.840.1.113883.19.5.99999.2",
149 "extension" : "998991"
151 "identifier" : "2.16.840.1.113883.4.1",
154 "marital_status" : pd.status,
156 "street_lines" : [ pd.street ],
159 "zip" : pd.postalCode,
160 "country" : pd.country,
161 "use" : "primary home"
164 "number" : pd.phone_home,
165 "type" : "primary home"
168 "ethnicity" : pd.ethnicity,
170 "language" : pd.language,
172 "mode" : "Expressed spoken",
173 "proficiency" : "Good"
175 "religion" : pd.religion,
183 "relation" : g.relation,
185 "street_lines" : [ g.address ],
188 "zip" : g.postalCode,
189 "country" : g.country,
190 "use" : "primary home"
198 "number" : g.telecom,
199 "type" : "primary home"
205 function populateMedication(pd) {
209 "date" : fDate(pd.start_date),
213 "date" : fDate(pd.end_date),
218 "identifier" : "cdbd33f0-6cde-11db-9fe1-0800200c9a66"
220 "status" : pd.status,
221 "sig" : pd.direction,
224 "identifier" : "2a620155-9d11-439e-92b3-5d9815ff4ee8"
226 "unencoded_name" : pd.drug,
233 "code_system_name" : "RXNORM"
235 "code_system_name" : "RXNORM"
237 "manufacturer" : "UNK"
242 "date" : fDate(pd.start_date),
246 "repeatNumber" : "NI",
250 "identifier" : "2a620155-9d11-439e-92b3-5d9815fe4de8"
262 "code" : pd.route_code,
263 "code_system_name" : "Medication Route FDA"
267 "code" : pd.form_code,
268 "code_system_name" : "Medication Route FDA"
271 "value" : parseFloat(pd.dosage),
280 "value" : parseFloat(pd.dosage),
289 "identifier" : "2.16.840.1.113883.19.5.9999.1393"
291 "name" : [ pd.performer_name ]
296 "code" : pd.form_code,
297 "code_system_name" : "RXNORM"
301 "code" : "ASSERTION",
302 "code_system_name" : "ActCode"
307 "code_system_name" : "SNOMED CT"
312 "identifier" : "db734647-fc99-424c-a864-7e3cda82e703",
313 "extension" : "45665"
317 "code" : "404684003",
318 "code_system_name" : "SNOMED CT"
322 "date" : fDate(pd.start_date),
327 "name" : pd.indications,
329 "code_system_name" : "SNOMED CT"
334 "identifier" : "1.2.3.4.56789.1",
335 "extension" : "cb734647-fc99-424c-a864-7e3cda82e704"
339 "identifier" : "2.16.840.1.113883.19.5.9999.456",
340 "extension" : "2981823"
343 "street_lines" : [ pd.address ],
351 "identifier" : "2.16.840.1.113883.19.5.9999.1393"
353 "name" : [ pd.performer_name ]
360 function populateEncounter(pd) {
363 "name" : pd.encounter_procedures ? pd.encounter_procedures.procedures.text : 'NI',
364 "code" : pd.encounter_procedures ? pd.encounter_procedures.procedures.code : 'NI',
365 "code_system_name" : "CPT",
369 "code_system_name" : "NI"
373 "identifier" : pd.sha_extension
377 "date" : fDate(pd.date_formatted),
378 "precision" : "second"
383 "identifier" : pd.facility_sha_extension
386 "name" : pd.physician_type,
388 "code_system_name" : "SNOMED CT"
392 "name" : pd.location,
394 "name" : pd.location_details,
396 "code_system_name" : "HealthcareServiceLocation"
399 "street_lines" : [ pd.facility_address ],
400 "city" : pd.facility_city,
401 "state" : pd.facility_state,
402 "zip" : pd.facility_zip,
403 "country" : pd.facility_country
412 "name" : pd.encounter_reason,
414 "code_system_name" : "SNOMED CT"
418 "date" : fDate(pd.date_formatted),
426 function populateAllergy(pd) {
429 "identifier" : "36e3e930-7b14-11db-9fe1-0800200c9a66"
433 "date" : fDate(pd.startdate),
439 "identifier" : "4adc1020-7b14-11db-9fe1-0800200c9a66"
443 "code" : pd.rxnorm_code,
444 "code_system_name" : "RXNORM"
447 "name" : "Propensity to adverse reactions to drug",
448 "code" : pd.snomed_code,
449 "code_system_name" : "SNOMED CT"
453 "date" : fDate(pd.startdate),
458 "name" : pd.allergy_status,
459 "code" : pd.status_code,
460 "code_system_name" : "SNOMED CT"
464 "identifier" : "4adc1020-7b14-11db-9fe1-0800200c9a64"
468 "date" : fDate(pd.startdate),
472 "date" : fDate(pd.enddate),
477 "name" : pd.reaction_text,
478 "code" : pd.reaction_code,
479 "code_system_name" : "SNOMED CT"
484 "code" : pd.outcome_code,
485 "code_system_name" : "SNOMED CT"
490 "code_system_name" : "Observation Interpretation"
497 "code" : pd.outcome_code,
498 "code_system_name" : "SNOMED CT"
503 "code_system_name" : "Observation Interpretation"
510 function populateProblem(pd) {
514 "date" : fDate(pd.start_date_table),
518 "date" : fDate(pd.end_date),
523 "identifier" : pd.sha_extension
529 "code_system_name" : "ICD10"
533 "date" : fDate(pd.start_date_table),
537 "date" : fDate(pd.end_date),
538 "precision" : "second"
542 "onset_age" : pd.age,
543 "onset_age_unit" : "Year",
545 "name" : pd.status_table,
548 "date" : fDate(pd.start_date_table),
552 "date" : fDate(pd.end_date),
553 "precision" : "second"
557 "patient_status" : pd.observation,
558 "source_list_identifiers" : [ {
559 "identifier" : "ec8a6ff8-ed4b-4f7e-82c3-e98e58b45de7"
565 function populateProcedure(pd) {
568 "name" : pd.description,
570 "code_system_name" : "CPT"
573 "identifier" : "d68b7e32-7810-4f5b-9cc2-acd54b0fd85d"
578 "date" : fDate(pd.date),
585 "code_system_name" : ""
589 "identifier" : "c2ee9ee9-ae31-4628-a919-fec1cbb58683"
594 "code_system_name" : "SNOMED CT"
599 "identifier" : "2.16.840.1.113883.19.5.9999.456",
600 "extension" : "2981823"
603 "street_lines" : [ pd.address ],
610 "number" : pd.work_phone,
611 "type" : "work place"
615 "identifier" : "2.16.840.1.113883.19.5.9999.1393"
617 "name" : [ pd.facility_name ],
619 "street_lines" : [ pd.facility_address ],
620 "city" : pd.facility_city,
621 "state" : pd.facility_state,
622 "zip" : pd.facility_zip,
623 "country" : pd.facility_country
626 "number" : pd.facility_phone,
627 "type" : "work place"
631 "procedure_type" : "procedure"
635 function populateResult(pd) {
638 "identifier" : "107c2dc0-67a5-11db-bd13-0800200c9a66"
642 "code" : pd.test_code || "NI",
643 "code_system_name" : "LOINC"
647 "date" : fDate(pd.date_ordered_table),
651 "status" : pd.order_status,
652 "reference_range" : {
653 "low" : pd.subtest.range,
654 "high" : pd.subtest.range,
655 "unit" : pd.subtest.unit
657 "interpretations" : [ pd.subtest.result_value ],
658 "value" : parseFloat(pd.subtest.result_value),
659 "unit" : pd.subtest.unit
663 function getResultSet(results) {
666 "identifier" : "7d5a02b0-67a4-11db-bd13-0800200c9a66"
669 "name" : "Get this data.",
671 "code_system_name" : "SNOMED CT"
680 count = isOne(results.result);
681 }catch(e){count = 0;}
683 for ( var i in results.result) {
684 theone[i] = populateResult(results.result[i]);
685 many.results.push(theone[i]);
687 } else if (count !== 0) {
688 theone = populateResult(results.result);
689 many.results.push(theone);
691 rs.results = Object.assign(resultSet);
692 rs.results.results = Object.assign(many.results);
696 function getPlanOfCare(pd) {
699 "name" : pd.code_text,
701 "code_system_name" : "SNOMED CT"
704 "identifier" : "9a6d1bac-17d3-4195-89a4-1121bc809b4a"
708 "date" : fDate(pd.date),
712 "type" : "observation",
716 "subType" : pd.description
720 function populateVital(pd) {
723 "identifier" : "2.16.840.1.113883.3.140.1.0.6.10.14.1",
724 "extension" : pd.extension_bps
727 "name" : "Blood Pressure Systolic",
729 "code_system_name" : "LOINC"
731 "status" : "completed",
734 "date" : fDate(pd.effectivetime),
735 "precision" : "second"
738 "value" : parseFloat(pd.bps),
742 "identifier" : "2.16.840.1.113883.3.140.1.0.6.10.14.1",
743 "extension" : pd.extension_bpd
746 "name" : "Blood Pressure Diastolic",
748 "code_system_name" : "LOINC"
750 "status" : "completed",
753 "date" : fDate(pd.effectivetime),
754 "precision" : "second"
757 "interpretations" : [ "UNK" ],
758 "value" : parseFloat(pd.bpd),
762 "identifier" : "2.16.840.1.113883.3.140.1.0.6.10.14.1",
763 "extension" : pd.extension_pulse
766 "name" : "Heart Rate",
768 "code_system_name" : "LOINC"
770 "status" : "completed",
773 "date" : fDate(pd.effectivetime),
774 "precision" : "second"
777 "interpretations" : [ "UNK" ],
778 "value" : parseFloat(pd.pulse),
782 "identifier" : "2.16.840.1.113883.3.140.1.0.6.10.14.1",
783 "extension" : pd.extension_breath
786 "name" : "Respiratory Rate",
788 "code_system_name" : "LOINC"
790 "status" : "completed",
793 "date" : fDate(pd.effectivetime),
794 "precision" : "second"
797 "interpretations" : [ "UNK" ],
798 "value" : parseFloat(pd.breath),
802 "identifier" : "2.16.840.1.113883.3.140.1.0.6.10.14.1",
803 "extension" : pd.extension_temperature
806 "name" : "Body Temperature",
808 "code_system_name" : "LOINC"
810 "status" : "completed",
813 "date" : fDate(pd.effectivetime),
814 "precision" : "second"
817 "interpretations" : [ "UNK" ],
818 "value" : parseFloat(pd.temperature),
822 "identifier" : "2.16.840.1.113883.3.140.1.0.6.10.14.1",
823 "extension" : pd.extension_height
828 "code_system_name" : "LOINC"
830 "status" : "completed",
833 "date" : fDate(pd.effectivetime),
834 "precision" : "second"
837 "interpretations" : [ "UNK" ],
838 "value" : parseFloat(pd.height),
839 "unit" : pd.unit_height
842 "identifier" : "2.16.840.1.113883.3.140.1.0.6.10.14.1",
843 "extension" : pd.extension_weight
846 "name" : "Weight Measured",
848 "code_system_name" : "LOINC"
850 "status" : "completed",
853 "date" : fDate(pd.effectivetime),
854 "precision" : "second"
857 "interpretations" : [ "UNK" ],
858 "value" : parseFloat(pd.weight),
859 "unit" : pd.unit_weight
862 "identifier" : "2.16.840.1.113883.3.140.1.0.6.10.14.1",
863 "extension" : pd.extension_BMI
866 "name" : "BMI (Body Mass Index)",
868 "code_system_name" : "LOINC"
870 "status" : "completed",
873 "date" : fDate(pd.effectivetime),
874 "precision" : "second"
877 "interpretations" : [ "UNK" ],
878 "value" : parseFloat(pd.BMI),
883 function populateSocialHistory(pd) {
887 "date" : fDate(pd.date),
891 "date" : fDate(pd.date),
892 "precision" : "second"
896 "identifier" : pd.sha_extension,
897 "extension" : pd.extension
902 "value" : pd.description
906 function populateImmunization(pd) {
910 "date" : fDate(pd.administered_on),
911 "precision" : "month"
915 "identifier" : "e6f1ba43-c0ed-4b9b-9f12-f435d8ad8f92"
917 "status" : "complete",
920 "name" : pd.code_text,
921 "code" : pd.cvx_code,
922 "code_system_name" : "CVX",
926 "code_system_name" : "CVX"
930 "manufacturer" : "UNK"
934 "name" : pd.route_of_administration,
935 "code" : pd.route_code,
936 "code_system_name" : "Medication Route FDA"
945 "identifier" : "2.16.840.1.113883.19.5.9999.456",
946 "extension" : "2981824"
953 "street_lines" : [ pd.address ],
961 "identifier" : "2.16.840.1.113883.19.5.9999.1394"
963 "name" : [ pd.facility_name ]
968 "name" : "immunization education",
969 "code" : "171044003",
970 "code_system_name" : "SNOMED CT"
972 "free_text" : "Needs Attention for more data."
977 function populatePayer(pd) {
980 "identifier" : "1fe2cdd0-7aad-11db-9fe1-0800200c9a66"
984 "identifier" : "3e676a50-7aac-11db-9fe1-0800200c9a66"
988 "code_system_name" : "HL7 RoleCode"
993 "code_system_name" : "HL7 RoleCode"
997 "identifier" : "2.16.840.1.113883.19"
1000 "street_lines" : [ "123 Insurance Road" ],
1001 "city" : "Blue Bell",
1005 "use" : "work place"
1008 "number" : "(781)555-1515",
1009 "type" : "work place"
1011 "organization" : [ {
1012 "name" : [ "Good Health Insurance" ],
1014 "street_lines" : [ "123 Insurance Road" ],
1015 "city" : "Blue Bell",
1019 "use" : "work place"
1022 "number" : "(781)555-1515",
1023 "type" : "work place"
1028 "code_system_name" : "HL7 RoleCode"
1036 "code_system_name" : "HL7 Role"
1039 "identifier" : "329fcdf0-7ab3-11db-9fe1-0800200c9a66"
1043 "middle" : [ "Frankie" ],
1044 "last" : "Everyman",
1048 "street_lines" : [ "17 Daws Rd." ],
1049 "city" : "Blue Bell",
1053 "use" : "primary home"
1056 "number" : "(781)555-1212",
1057 "type" : "primary home"
1064 "code_system_name" : "HL7 Role"
1068 "identifier" : "14d4a520-7aae-11db-9fe1-0800200c9a66",
1069 "extension" : "1138345"
1072 "street_lines" : [ "17 Daws Rd." ],
1073 "city" : "Blue Bell",
1077 "use" : "primary home"
1082 "code_system_name" : "HL7 Role"
1087 "middle" : [ "A." ],
1088 "last" : "Everyman",
1095 "identifier" : "2.16.840.1.113883.19",
1096 "extension" : "1138345"
1099 "street_lines" : [ "17 Daws Rd." ],
1100 "city" : "Blue Bell",
1104 "use" : "primary home"
1110 "identifier" : "f4dce790-8328-11db-9fe1-0800200c9a66"
1114 "name" : "Colonoscopy",
1115 "code" : "73761001",
1116 "code_system_name" : "SNOMED CT"
1123 function populateHeader(pd){
1127 "identifier": "2.16.840.1.113883.19.5.99999.1",
1128 "extension": "TT988"
1131 "confidentiality_code": {
1134 "code_system_name": "Confidentiality Code"
1137 "name": "Continuity of Care Document",
1139 "code_system_name": "LOINC"
1142 "2.16.840.1.113883.10.20.22.1.1",
1143 "2.16.840.1.113883.10.20.22.1.2"
1145 "title": "Clinical: Health Summary",
1148 "date": pd.created_time_timezone,
1149 "precision": "minute"
1157 "identifier": "2.16.840.1.113883.4.6",
1158 "extension": "99999999"
1163 "last": pd.author.lname,
1164 "first": pd.author.fname
1170 pd.author.streetAddressLine
1172 "city": pd.author.city,
1173 "state": pd.author.state,
1174 "zip": pd.author.postalCode,
1175 "country": pd.author.country
1180 "number": pd.author.telecom,
1181 "type": "work place"
1195 "precision": "second"
1202 "identifier": "2.16.840.1.113883.4.6",
1203 "extension": "999999943252"
1208 "last": pd.data_enterer.lname,
1209 "first": pd.data_enterer.fname
1215 pd.data_enterer.streetAddressLine
1217 "city": pd.data_enterer.city,
1218 "state": pd.data_enterer.state,
1219 "zip": pd.data_enterer.postalCode,
1220 "country": pd.data_enterer.country
1225 "number": pd.data_enterer.telecom,
1226 "type": "work place"
1233 "identifier": "2.16.840.1.113883.19.5",
1234 "extension": "KP00017"
1239 "last": pd.informer.lname,
1240 "first": pd.informer.fname
1246 pd.informer.streetAddressLine
1248 "city": pd.informer.city,
1249 "state": pd.informer.state,
1250 "zip": pd.informer.postalCode,
1251 "country": pd.informer.country
1256 "number": pd.informer.telecom,
1257 "type": "work place"
1265 "code_system_name": "SNOMED CT"
1270 "precision": "minute"
1273 "date": pd.created_time_timezone,
1274 "precision": "minute"
1283 "identifier": "2.16.840.1.113883.4.6",
1284 "extension": "PseudoMD-1"
1287 "name": [ // Most likely not right - maybe should be provider
1289 "last": pd.information_recipient.lname,
1290 "first": pd.information_recipient.fname
1296 pd.information_recipient.streetAddressLine
1298 "city": pd.information_recipient.city,
1299 "state": pd.information_recipient.state,
1300 "zip": pd.information_recipient.postalCode,
1301 "country": pd.information_recipient.country
1306 "number": pd.information_recipient.telecom,
1307 "type": "work place"
1314 "identifier": "2.16.840.1.113883.19.5.9999.1393"
1318 pd.encounter_provider.facility_name
1323 pd.encounter_provider.facility_street
1325 "city": pd.encounter_provider.facility_city,
1326 "state": pd.encounter_provider.facility_state,
1327 "zip": pd.encounter_provider.facility_postal_code,
1328 "country": pd.encounter_provider.facility_country_code
1333 "number": pd.encounter_provider.facility_phone,
1334 "type": "primary work"
1343 "code_system_name": "Provider Codes"
1349 "name": "Primary Performer",
1351 "code_system_name": "Provider Role"
1360 function genCcda(pd) {
1367 // Header - @todo pd may be too large- break down to subsections
1368 var head = populateHeader(pd);
1369 data.head = Object.assign(head);
1371 var demographic = populateDemographic(pd.patient, pd.guardian);
1372 data.demographics = Object.assign(demographic);
1376 count = isOne(pd.history_physical.vitals_list.vitals);
1377 }catch(e){count = 0}
1379 for ( var i in pd.history_physical.vitals_list.vitals) {
1380 theone = populateVital(pd.history_physical.vitals_list.vitals[i]);
1381 many.vitals.push.apply(many.vitals, theone);
1383 } else if (count === 1) {
1384 theone = populateVital(pd.history_physical.vitals_list.vitals);
1385 many.vitals.push(theone);
1387 data.vitals = Object.assign(many.vitals);
1391 meds.medications = [];
1393 count = isOne(pd.medications.medication);
1394 }catch(e){count = 0}
1396 for ( var i in pd.medications.medication) {
1397 m[i] = populateMedication(pd.medications.medication[i]);
1398 meds.medications.push(m[i]);
1400 } else if (count !== 0) {
1401 m = populateMedication(pd.medications.medication);
1402 meds.medications.push(m);
1404 data.medications = Object.assign(meds.medications);
1408 encs.encounters = [];
1410 count = isOne(pd.encounter_list.encounter);
1411 }catch(e){count = 0}
1413 for ( var i in pd.encounter_list.encounter) {
1414 enc[i] = populateEncounter(pd.encounter_list.encounter[i]);
1415 encs.encounters.push(enc[i]);
1417 } else if (count !== 0) {
1418 enc = populateEncounter(pd.encounter_list.encounter);
1419 encs.encounters.push(enc);
1421 data.encounters = Object.assign(encs.encounters);
1425 allergies.allergies = [];
1427 count = isOne(pd.allergies.allergy);
1428 }catch(e){count = 0}
1430 for ( var i in pd.allergies.allergy) {
1431 allergy[i] = populateAllergy(pd.allergies.allergy[i]);
1432 allergies.allergies.push(allergy[i]);
1434 } else if (count !== 0) {
1435 allergy = populateAllergy(pd.allergies.allergy);
1436 allergies.allergies.push(allergy);
1438 data.allergies = Object.assign(allergies.allergies);
1442 problems.problems = [];
1444 count = isOne(pd.problem_lists.problem);
1445 }catch(e){count = 0}
1447 for ( var i in pd.problem_lists.problem) {
1448 problem[i] = populateProblem(pd.problem_lists.problem[i]);
1449 problems.problems.push(problem[i]);
1451 } else if (count !== 0) {
1452 problem = populateProblem(pd.problem_lists.problem);
1453 problems.problems.push(problem);
1455 data.problems = Object.assign(problems.problems);
1459 many.procedures = [];
1461 count = isOne(pd.procedures.procedure);
1462 }catch(e){count = 0}
1464 for ( var i in pd.procedures.procedure) {
1465 theone[i] = populateProcedure(pd.procedures.procedure[i]);
1466 many.procedures.push(theone[i]);
1468 } else if (count !== 0) {
1469 theone = populateProcedure(pd.procedures.procedure);
1470 many.procedures.push(theone);
1472 data.procedures = Object.assign(many.procedures);
1474 data.results = Object.assign(getResultSet(pd.results)['results']);
1478 many.immunizations = [];
1480 count = isOne(pd.immunizations.immunization);
1481 }catch(e){count = 0;}
1483 for ( var i in pd.immunizations.immunization) {
1484 theone[i] = populateImmunization(pd.immunizations.immunization[i]);
1485 many.immunizations.push(theone[i]);
1487 } else if (count !== 0) {
1488 theone = populateImmunization(pd.immunizations.immunization);
1489 many.immunizations.push(theone);
1491 data.immunizations = Object.assign(many.immunizations);
1495 many.plan_of_care = [];
1497 count = isOne(pd.planofcare); // ccm doesn't send array of items
1498 }catch(e){count = 0}
1500 for ( var i in pd.planofcare.item) {
1501 theone[i] = getPlanOfCare(pd.planofcare.item[i]);
1502 many.plan_of_care.push(theone[i]);
1504 } else if (count !== 0) {
1505 theone = getPlanOfCare(pd.planofcare.item);
1506 many.plan_of_care.push(theone);
1508 data.plan_of_care = Object.assign(many.plan_of_care);
1512 many.social_history = [];
1514 count = isOne(pd.history_physical.social_history.history_element);
1515 }catch(e){count = 0}
1517 for ( var i in pd.history_physical.social_history.history_element) {
1518 theone[i] = populateSocialHistory(pd.history_physical.social_history.history_element[i]);
1519 many.social_history.push(theone[i]);
1521 } else if (count !== 0) {
1522 theone = populateSocialHistory(pd.history_physical.social_history.history_element);
1523 many.social_history.push(theone);
1525 data.social_history = Object.assign(many.social_history);
1527 // ------------------------------------------ End Sections -------------------------------------------------//
1529 doc.data = Object.assign(data);
1531 var xml = bbg.generateCCD(doc);
1536 function processConnection( connection ) {
1537 conn = connection; // make it global
1538 var remoteAddress = conn.remoteAddress + ':' + conn.remotePort;
1539 console.log(remoteAddress);
1540 conn.setEncoding('utf8');
1541 function eventData(xml) {
1542 xml = xml.replace(/(\u000b|\u001c)/gm, "").trim();
1543 // Sanity check from service manager
1544 if (xml === 'status' || xml.length < 80) {
1545 conn.write("statusok" + String.fromCharCode(28) + "\r\r");
1550 to_json(xml, function(error, data) {
1551 // console.log(JSON.stringify(data, null, 4));
1552 if (error) { // need try catch
1556 doc = genCcda( data.CCDA );
1558 doc = headReplace(doc);
1559 doc = doc.toString().replace(/(\u000b|\u001c|\r)/gm, "").trim();
1561 // var justuwait = new Date(new Date().getTime() + 1 * 1000);
1562 // while(justuwait > new Date()){}
1563 // Document length is anywhere from 50k to 125K so lets not flood poor ole php's socket buffer
1564 // not really throttled though...
1566 var numChunks = Math.ceil(doc.length / 1024);
1567 for (var i = 0, o = 0; i < numChunks; ++i, o += 1024) {
1568 chunk = doc.substr(o, 1024);
1571 // Php is sensitive to ending read stream and CCM chops last three bytes
1572 // empty string ends read and char 28 is xfer end (php doesn't care but CCM used so, I will also).
1573 conn.write(String.fromCharCode(28) + "\r\r" + '');
1575 /*By parsing doc just created we can see any nullFlavors or missing parts
1576 console.log("total=" + conn.bytesWritten);
1577 remove for production - test validation of doc just created
1578 var data = bb.parse(doc);
1579 console.log(JSON.stringify(data.errors, null, 4));*/
1581 function eventCloseConn() {
1582 //console.log('connection from %s closed', remoteAddress);
1584 function eventErrorConn(err) {
1585 //console.log('Connection %s error: %s', remoteAddress, err.message);
1587 // Connection Events //
1588 conn.on('data', eventData);
1589 conn.once('close', eventCloseConn);
1590 conn.on('error', eventErrorConn);
1592 function setUp(server) {
1593 server.on('connection', processConnection);
1594 server.listen(6661, 'localhost', function() {
1595 //console.log('server listening to %j', server.address());