refactor(ccdaservice): test and refactor count entities function (#6805)
[openemr.git] / ccdaservice / serveccda.js
blobb2606b843b63a21ab83624ac96bd43fedc434360
1 /**
2  * @package   OpenEMR CCDAServer
3  * @link      http://www.open-emr.org
4  *
5  * @author    Jerry Padgett <sjpadgett@gmail.com>
6  * @copyright Copyright (c) 2016-2022 Jerry Padgett <sjpadgett@gmail.com>
7  * @license   https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3
8  */
10 "use strict";
12 const enableDebug = true;
14 const net = require('net');
15 const server = net.createServer();
16 const to_json = require('xmljson').to_json;
17 const bbg = require(__dirname + '/oe-blue-button-generate');
18 const fs = require('fs');
19 const { DataStack } = require('./data-stack/data-stack');
20 const { cleanCode } = require('./utils/clean-code/clean-code');
21 const { safeTrim } = require('./utils/safe-trim/safe-trim');
22 const { headReplace } = require('./utils/head-replace/head-replace');
23 const { fDate, templateDate } = require('./utils/date/date');
24 const { countEntities } = require('./utils/count-entities/count-entities');
26 var conn = ''; // make our connection scope global to script
27 var oidFacility = "";
28 var all = "";
29 var npiProvider = "";
30 var npiFacility = "";
31 var webRoot = "";
32 var authorDateTime = '';
33 var documentLocation = '';
35 // do a recursive descent transformation of the node object populating the timezone offset value if we have
36 // a precision property (inside a date) with the value of timezone.
37 function populateTimezones(node, tzOffset, depthCheck) {
38     if (!node || typeof node !== 'object') {
39         return node;
40     }
41     // we should NEVER go farther than 25 recursive loops down in our heirarchy, if we do it means we have an infinite loop
42     if (depthCheck > 25) {
43         console.error("Max depth traversal reached.  Potential infinite loop.  Breaking out of loop")
44         return node;
45     }
47     if (Object.prototype.hasOwnProperty.call(node, 'precision') && node.precision == 'tz' && !Object.prototype.hasOwnProperty.call(node, 'timezoneOffset')) {
48         node.timezoneOffset = tzOffset;
49     } else {
50         for (const [key, value] of Object.entries(node)) {
51             node[key] = populateTimezones(value, tzOffset, depthCheck + 1);
52         }
53     }
54     return node;
57 function fetchPreviousAddresses(pd) {
58     let addressArray = [];
59     let pa = pd.previous_addresses.address;
60     let streetLine = [pd.street[0]];
61     if (pd.street[1].length > 0) {
62         streetLine = [pd.street[0], pd.street[1]];
63     }
64     addressArray.push({
65         "use": "HP",
66         "street_lines": streetLine,
67         "city": pd.city,
68         "state": pd.state,
69         "zip": pd.postalCode,
70         "country": pd.country || "US",
71         "date_time": {
72             // use current date for current residence
73             "low": {
74                 "date": fDate(""),
75                 "precision": "day"
76             }
77         }
78     });
79     let count = countEntities(pa);
80     // how do we ever get here where we just have one object?
81     if (count === 1) {
82         streetLine = [pa.street[0]];
83         if (pa.street[1].length > 0) {
84             streetLine = [pa.street[0], pa.street[1]];
85         }
86         addressArray.push({
87             "use": pa.use,
88             "street_lines": streetLine,
89             "city": pa.city,
90             "state": pa.state,
91             "zip": pa.postalCode,
92             "country": pa.country || "US",
93             "date_time": {
94                 "low": {
95                     "date": fDate(pa.period_start),
96                     "precision": "day"
97                 },
98                 "high": {
99                     "date": fDate(pa.period_end) || fDate(""),
100                     "precision": "day"
101                 }
102             }
103         });
104     } else if (count > 1) {
105         for (let i in pa) {
106             streetLine = [pa[i].street[0]];
107             if (pa[i].street[1].length > 0) {
108                 streetLine = [pa[i].street[0], pa[i].street[1]];
109             }
110             addressArray.push({
111                 "use": pa[i].use,
112                 "street_lines": streetLine,
113                 "city": pa[i].city,
114                 "state": pa[i].state,
115                 "zip": pa[i].postalCode,
116                 "country": pa[i].country || "US",
117                 "date_time": {
118                     "low": {
119                         "date": fDate(pa[i].period_start),
120                         "precision": "day"
121                     },
122                     "high": {
123                         "date": fDate(pa[i].period_end) || fDate(""),
124                         "precision": "day"
125                     }
126                 }
127             });
128         }
129     }
130     return addressArray;
133 function populateDemographic(pd, g) {
134     let first = 'NI';
135     let middle = 'NI';
136     let last = 'NI';
137     const names = g.display_name.split(' ');
138     if (names.length === 2) {
139         first = names[0];
140         last = names[1];
141     }
142     if (names.length === 3) {
143         first = names[0];
144         last = names[2];
145     }
146     let guardian = [{
147         "relation": g.relation,
148         "addresses": [{
149             "street_lines": [g.address],
150             "city": g.city,
151             "state": g.state,
152             "zip": g.postalCode,
153             "country": g.country || "US",
154             "use": "primary home"
155         }],
156         "names": [{
157             "last": last,
158             "first": first
159         }],
160         "phone": [{
161             "number": g.telecom,
162             "type": "primary home"
163         }]
164     }];
165     if (pd.race === 'Declined To Specify' || pd.race === '') {
166         pd.race = "null_flavor";
167     }
168     if (pd.race_group === 'Declined To Specify' || pd.race_group === '') {
169         pd.race_group = "null_flavor";
170     }
171     if (pd.ethnicity === 'Declined To Specify' || pd.ethnicity === '') {
172         pd.ethnicity = "null_flavor";
173     }
174     let addressArray = fetchPreviousAddresses(pd);
175     return {
176         "name": {
177             "prefix": pd.prefix,
178             "suffix": pd.suffix,
179             "middle": [pd.mname] || "",
180             "last": pd.lname,
181             "first": pd.fname
182         },
183         "birth_name": {
184             "middle": pd.birth_mname || "",
185             "last": pd.birth_lname || "",
186             "first": pd.birth_fname || ""
187         },
188         "dob": {
189             "point": {
190                 "date": fDate(pd.dob),
191                 "precision": "day"
192             }
193         },
194         "gender": pd.gender.toUpperCase() || "null_flavor",
195         "identifiers": [{
196             "identifier": oidFacility || npiFacility,
197             "extension": pd.uuid
198         }],
199         "marital_status": pd.status.toUpperCase(),
200         "addresses": addressArray,
201         "phone": [
202             {
203                 "number": pd.phone_home,
204                 "type": "primary home"
205             }, {
206                 "number": pd.phone_mobile,
207                 "type": "primary mobile"
208             }, {
209                 "number": pd.phone_work,
210                 "type": "work place"
211             }, {
212                 "number": pd.phone_emergency,
213                 "type": "emergency contact"
214             },{
215                 "email": pd.email,
216                 "type": "contact_email"
217             }
218         ],
219         "ethnicity": pd.ethnicity || "",
220         "race": pd.race || "null_flavor",
221         "race_additional": pd.race_group || "null_flavor",
222         "languages": [{
223             "language": pd.language === 'English' ? "en-US" : pd.language === 'Spanish' ? "sp-US" : 'en-US',
224             "preferred": true,
225             "mode": "Expressed spoken",
226             "proficiency": "Good"
227         }],
228         //"religion": pd.religion.toUpperCase() || "",
229         /*"birthplace":'', {
230             "city": "",
231             "state": "",
232             "zip": "",
233             "country": ""
234         },*/
235         "attributed_provider": {
236             "identity": [
237                 {
238                     "root": "2.16.840.1.113883.4.6",
239                     "extension": npiFacility || ""
240                 }
241             ],
242             "phone": [{
243                 "number": all.encounter_provider.facility_phone || "",
244             }],
245             "name": [
246                 {
247                     "full": all.encounter_provider.facility_name || ""
248                 }
249             ],
250             "address": [
251                 {
252                     "street_lines": [
253                         all.encounter_provider.facility_street
254                     ],
255                     "city": all.encounter_provider.facility_city,
256                     "state": all.encounter_provider.facility_state,
257                     "zip": all.encounter_provider.facility_postal_code,
258                     "country": all.encounter_provider.facility_country_code || "US",
259                     "use": "work place"
260                 }
261             ],
262         },
263         "guardians": g.display_name ? guardian : '' //not required
264     }
267 function populateProvider(provider) {
268     // The provider role is a maybe and will only be provided for physicians as a
269     // primary care role. All other team members will id via taxonomy only and if not physicians.
270     return {
271         "function_code": provider.physician_type ? "PP" : "",
272         "date_time": {
273             "low": {
274                 "date": provider.provider_since ? fDate(provider.provider_since) : fDate(""),
275                 "precision": "tz"
276             }
277         },
278         "identity": [
279             {
280                 "root": provider.npi ? "2.16.840.1.113883.4.6" : oidFacility,
281                 "extension": provider.npi || provider.table_id || "NI"
282             }
283         ],
284         "type": [
285             {
286                 "name": provider.taxonomy_description || "",
287                 "code": cleanCode(provider.taxonomy) || "",
288                 "code_system": "2.16.840.1.113883.6.101",
289                 "code_system_name": "NUCC Health Care Provider Taxonomy"
290             }
291         ],
292         "name": [
293             {
294                 "last": provider.lname || "",
295                 "first": provider.fname || ""
296             }
297         ],
298         "address": [
299             {
300                 "street_lines": [
301                     all.encounter_provider.facility_street
302                 ],
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 || "US"
307             }
308         ],
310         "phone": [{
311             "number": all.encounter_provider.facility_phone || ""
312         }]
313     }
316 function populateProviders(all) {
317     let providerArray = [];
318     // primary provider
319     let provider = populateProvider(all.primary_care_provider.provider);
320     providerArray.push(provider);
321     let count = countEntities(all.care_team.provider);
322     if (count === 1) {
323         provider = populateProvider(all.care_team.provider);
324         providerArray.push(provider);
325     } else if (count > 1) {
326         for (let i in all.care_team.provider) {
327             provider = populateProvider(all.care_team.provider[i]);
328             providerArray.push(provider);
329         }
330     }
331     return {
332         "providers":
333             {
334                 "date_time": {
335                     "low": {
336                         "date": fDate(all.time_start) || fDate(""),
337                         "precision": "tz"
338                     },
339                     "high": {
340                         "date": fDate(all.time_end) || fDate(""),
341                         "precision": "tz"
342                     }
343                 },
344                 "code": {
345                     "name": all.primary_diagnosis.text || "",
346                     "code": cleanCode(all.primary_diagnosis.code || ""),
347                     "code_system_name": all.primary_diagnosis.code_type || ""
348                 },
349                 "provider": providerArray,
350             }
351     }
355 function populateCareTeamMember(provider) {
356     return {
357         //"function_code": provider.physician_type ? "PP" : "",
358         "function_code": {
359             "xmlns": "urn:hl7-org:sdtc",
360             "name": provider.taxonomy_description || "",
361             "code": cleanCode(provider.taxonomy) || "",
362             "code_system": "2.16.840.1.113883.6.101",
363             "code_system_name": "NUCC Health Care Provider Taxonomy"
364         },
365         "status": "active",
366         "date_time": {
367             "low": {
368                 "date": fDate(provider.provider_since) || fDate(""),
369                 "precision": "tz"
370             }
371         },
372         "identifiers": [
373             {
374                 "identifier": provider.npi ? "2.16.840.1.113883.4.6" : oidFacility,
375                 "extension": provider.npi || provider.table_id
376             }
377         ],
378         "full_name": provider.fname + " " + provider.lname,
379         "name": {
380             "last": provider.lname || "",
381             "first": provider.fname || ""
382         },
383         "address": {
384             "street_lines": [
385                 provider.street
386             ],
387             "city": provider.city,
388             "state": provider.state,
389             "zip": provider.zip,
390             "country": all.encounter_provider.facility_country_code || "US"
391         },
392         "phone": [
393             {
394                 "number": provider.telecom,
395                 "type": "work place"
396             }
397         ]
398     }
401 function populateAuthorFromAuthorContainer(pd) {
402     let author = pd.author || {};
403     return {
404         "code": {
405             "name": author.physician_type || '',
406             "code": author.physician_type_code || '',
407             "code_system": author.physician_type_system,
408             "code_system_name": author.physician_type_system_name
409         },
410         "date_time": {
411             "point": {
412                 "date": fDate(author.time),
413                 "precision": "tz"
414             }
415         },
416         "identifiers": [
417             {
418                 "identifier": author.npi ? "2.16.840.1.113883.4.6" : author.id,
419                 "extension": author.npi ? author.npi : 'NI'
420             }
421         ],
422         "name": [
423             {
424                 "last": author.lname || "",
425                 "first": author.fname || ""
426             }
427         ],
428         "organization": [
429             {
430                 "identity": [
431                     {
432                         "root": author.facility_oid || "2.16.840.1.113883.4.6",
433                         "extension": author.facility_npi || "NI"
434                     }
435                 ],
436                 "name": [
437                     author.facility_name || ""
438                 ]
439             }
440         ]
441     };
444 function populateCareTeamMembers(pd) {
445     let providerArray = [];
446     // primary provider
447     let primaryCareProvider = pd.primary_care_provider || {provider: {}};
448     let providerSince = fDate(primaryCareProvider.provider.provider_since || '');
449     if (pd.primary_care_provider) {
450         let provider = populateCareTeamMember(pd.primary_care_provider.provider);
451         providerArray.push(provider);
452         let count = countEntities(pd.care_team.provider);
453         if (count === 1) {
454             provider = populateCareTeamMember(pd.care_team.provider);
455             providerSince = providerSince || fDate(provider.provider_since);
456             providerArray.push(provider);
457         } else if (count > 1) {
458             for (let i in pd.care_team.provider) {
459                 provider = populateCareTeamMember(pd.care_team.provider[i]);
460                 providerSince = providerSince || fDate(provider.provider_since);
461                 providerArray.push(provider);
462             }
463         }
464     }
465     return {
466         "providers":
467             {
468                 "provider": providerArray,
469             },
470         "status": "active",
471         "date_time": {
472             "low": {
473                 "date": providerSince || fDate(""),
474                 "precision": "tz"
475             }
476         },
477         // we treat this author a bit differently since we are working at the main pd object instead of the sub pd.care_team
478         "author": populateAuthorFromAuthorContainer(pd.care_team)
479     }
482 function populateMedication(pd) {
483     pd.status = 'Completed'; //@todo invoke prescribed
484     return {
485         "date_time": {
486             "low": {
487                 "date": fDate(pd.start_date),
488                 "precision": "tz"
489             }/*,
490             "high": {
491                 "date": fDate(pd.end_date),
492                 "precision": "day"
493             }*/
494         },
495         "identifiers": [{
496             "identifier": pd.sha_extension,
497             "extension": pd.extension || ""
498         }],
499         "status": pd.status,
500         "sig": pd.direction,
501         "product": {
502             "identifiers": [{
503                 "identifier": pd.sha_extension || "2a620155-9d11-439e-92b3-5d9815ff4ee8",
504                 "extension": pd.extension + 1 || ""
505             }],
506             "unencoded_name": pd.drug,
507             "product": {
508                 "name": pd.drug,
509                 "code": cleanCode(pd.rxnorm),
510                 "code_system_name": "RXNORM"
511                 /*"translations": [{
512                     "name": pd.drug,
513                     "code": pd.rxnorm,
514                     "code_system_name": "RXNORM"
515                 }],*/
516             },
517             //"manufacturer": ""
518         },
519         "author": {
520             "code": {
521                 "name": pd.author.physician_type || '',
522                 "code": pd.author.physician_type_code || '',
523                 "code_system": pd.author.physician_type_system, "code_system_name": pd.author.physician_type_system_name
524             },
525             "date_time": {
526                 "point": {
527                     "date": fDate(pd.author.time),
528                     "precision": "tz"
529                 }
530             },
531             "identifiers": [
532                 {
533                     "identifier": pd.author.npi ? "2.16.840.1.113883.4.6" : pd.author.id,
534                     "extension": pd.author.npi ? pd.author.npi : 'NI'
535                 }
536             ],
537             "name": [
538                 {
539                     "last": pd.author.lname,
540                     "first": pd.author.fname
541                 }
542             ],
543             "organization": [
544                 {
545                     "identity": [
546                         {
547                             "root": pd.author.facility_oid || "2.16.840.1.113883.4.6",
548                             "extension": pd.author.facility_npi || "NI"
549                         }
550                     ],
551                     "name": [
552                         pd.author.facility_name
553                     ]
554                 }
555             ]
556         },
557         "supply": {
558             "date_time": {
559                 "low": {
560                     "date": fDate(pd.start_date),
561                     "precision": "day"
562                 },
563                 "high": {
564                     "date": fDate(pd.end_date),
565                     "precision": "day"
566                 }
567             },
568             "repeatNumber": "0",
569             "quantity": "0",
570             "product": {
571                 "identifiers": [{
572                     "identifier": pd.sha_extension || "2a620155-9d11-439e-92b3-5d9815ff4ee8",
573                     "extension": pd.extension + 1 || ""
574                 }],
575                 "unencoded_name": pd.drug,
576                 "product": {
577                     "name": pd.drug,
578                     "code": cleanCode(pd.rxnorm),
579                     /*"translations": [{
580                         "name": pd.drug,
581                         "code": pd.rxnorm,
582                         "code_system_name": "RXNORM"
583                     }],*/
584                     "code_system_name": "RXNORM"
585                 },
586                 //"manufacturer": ""
587             },
588             "author": {
589                 "code": {
590                     "name": all.author.physician_type || '',
591                     "code": all.author.physician_type_code || '',
592                     "code_system": all.author.physician_type_system, "code_system_name": all.author.physician_type_system_name
593                 },
594                 "date_time": {
595                     "point": {
596                         "date": authorDateTime,
597                         "precision": "tz"
598                     }
599                 },
600                 "identifiers": [
601                     {
602                         "identifier": all.author.npi ? "2.16.840.1.113883.4.6" : all.author.id,
603                         "extension": all.author.npi ? all.author.npi : 'NI'
604                     }
605                 ],
606                 "name": [
607                     {
608                         "last": all.author.lname,
609                         "first": all.author.fname
610                     }
611                 ],
612                 "organization": [
613                     {
614                         "identity": [
615                             {
616                                 "root": oidFacility || "2.16.840.1.113883.4.6",
617                                 "extension": npiFacility || ""
618                             }
619                         ],
620                         "name": [
621                             all.encounter_provider.facility_name
622                         ]
623                     }
624                 ]
625             },
626             "instructions": {
627                 "code": {
628                     "name": "instruction",
629                     "code": "409073007",
630                     "code_system_name": "SNOMED CT"
631                 },
632                 "free_text": pd.instructions || "No Instructions"
633             },
634         },
635         "administration": {
636             "route": {
637                 "name": pd.route || "",
638                 "code": cleanCode(pd.route_code) || "",
639                 "code_system_name": "Medication Route FDA"
640             },
641             "form": {
642                 "name": pd.form,
643                 "code": cleanCode(pd.form_code),
644                 "code_system_name": "Medication Route FDA"
645             },
646             "dose": {
647                 "value": parseFloat(pd.size),
648                 "unit": pd.unit,
649             },
650             /*"rate": {
651                 "value": parseFloat(pd.dosage),
652                 "unit": ""
653             },*/
654             "interval": {
655                 "period": {
656                     "value": parseFloat(pd.dosage),
657                     "unit": pd.interval
658                 },
659                 "frequency": true
660             }
661         },
662         "performer": {
663             "identifiers": [{
664                 "identifier": "2.16.840.1.113883.4.6",
665                 "extension": pd.npi || ""
666             }],
667             "organization": [{
668                 "identifiers": [{
669                     "identifier": pd.sha_extension,
670                     "extension": pd.extension || ""
671                 }],
672                 "name": [pd.performer_name]
673             }]
674         },
675         "drug_vehicle": {
676             "name": pd.form,
677             "code": cleanCode(pd.form_code),
678             "code_system_name": "RXNORM"
679         },
680         /*"precondition": {
681             "code": {
682                 "code": "ASSERTION",
683                 "code_system_name": "ActCode"
684             },
685             "value": {
686                 "name": "none",
687                 "code": "none",
688                 "code_system_name": "SNOMED CT"
689             }
690         },
691         "indication": {
692             "identifiers": [{
693                 "identifier": "db734647-fc99-424c-a864-7e3cda82e703",
694                 "extension": "45665"
695             }],
696             "code": {
697                 "name": "Finding",
698                 "code": "404684003",
699                 "code_system_name": "SNOMED CT"
700             },
701             "date_time": {
702                 "low": {
703                     "date": fDate(pd.start_date),
704                     "precision": "day"
705                 }
706             },
707             "value": {
708                 "name": pd.indications,
709                 "code": pd.indications_code,
710                 "code_system_name": "SNOMED CT"
711             }
712         },
713         "dispense": {
714             "identifiers": [{
715                 "identifier": "1.2.3.4.56789.1",
716                 "extension": "cb734647-fc99-424c-a864-7e3cda82e704"
717             }],
718             "performer": {
719                 "identifiers": [{
720                     "identifier": "2.16.840.1.113883.19.5.9999.456",
721                     "extension": "2981823"
722                 }],
723                 "address": [{
724                     "street_lines": [pd.address],
725                     "city": pd.city,
726                     "state": pd.state,
727                     "zip": pd.zip,
728                     "country": "US"
729                 }],
730                 "organization": [{
731                     "identifiers": [{
732                         "identifier": "2.16.840.1.113883.19.5.9999.1393"
733                     }],
734                     "name": [pd.performer_name]
735                 }]
736             },
737             "product": {
738                 "identifiers": [{
739                     "identifier": "2a620155-9d11-439e-92b3-5d9815ff4ee8"
740                 }],
741                 "unencoded_name": pd.drug,
742                 "product": {
743                     "name": pd.drug,
744                     "code": pd.rxnorm,
745                     "translations": [{
746                         "name": pd.drug,
747                         "code": pd.rxnorm,
748                         "code_system_name": "RXNORM"
749                     }],
750                     "code_system_name": "RXNORM"
751                 },
752                 "manufacturer": ""
753             }
754         }*/
755     };
758 function getFinding(pd, problem) {
759     const finding = {
760         "identifiers": [{
761             "identifier": pd.sha_extension,
762             "extension": ''
763         }],
764         "value": {
765             "name": '',
766             "code": '',
767             "code_system_name": ''
768         },
769         "date_time": {
770             "low": {
771                 "date": '',
772                 "precision": "day"
773             }
774         },
775         "status": '',
776         "reason": pd.encounter_reason,
777         "author": {
778             "code": {
779                 "name": all.author.physician_type || '',
780                 "code": all.author.physician_type_code || '',
781                 "code_system": all.author.physician_type_system, "code_system_name": all.author.physician_type_system_name
782             },
783             "date_time": {
784                 "point": {
785                     "date": authorDateTime,
786                     "precision": "tz"
787                 }
788             },
789             "identifiers": [
790                 {
791                     "identifier": all.author.npi ? "2.16.840.1.113883.4.6" : all.author.id,
792                     "extension": all.author.npi ? all.author.npi : 'UNK'
793                 }
794             ],
795             "name": [
796                 {
797                     "last": all.author.lname,
798                     "first": all.author.fname
799                 }
800             ],
801             "organization": [
802                 {
803                     "identity": [
804                         {
805                             "root": oidFacility || "2.16.840.1.113883.4.6",
806                             "extension": npiFacility || ""
807                         }
808                     ],
809                     "name": [
810                         all.encounter_provider.facility_name
811                     ]
812                 }
813             ]
814         },
815     };
817     finding.identifiers["0"].extension = problem.extension;
818     finding.date_time.low.date = fDate(problem.date);
819     finding.value.name = problem.text;
820     finding.value.code = cleanCode(problem.code);
821     finding.value.code_system_name = problem.code_type;
822     finding.status = problem.status;
823     return finding;
826 function populateEncounter(pd) {
827     // just to get diagnosis. for findings..
828     let findingObj = [];
829     let theone = {};
830     let count = 0;
831     try {
832         count = countEntities(pd.encounter_problems.problem);
833     } catch (e) {
834         count = 0;
835     }
836     if (count > 1) {
837         for (let i in pd.encounter_problems.problem) {
838             theone[i] = getFinding(pd, pd.encounter_problems.problem[i]);
839             findingObj.push(theone[i]);
840         }
841     } else if (count !== 0 && pd.encounter_problems.problem.code > '') {
842         let finding = getFinding(pd, pd.encounter_problems.problem);
843         findingObj.push(finding);
844     }
846     return {
847         "encounter": {
848             "name": pd.visit_category ? (pd.visit_category + " | " + pd.encounter_reason) : pd.code_description,
849             "code": pd.code || "185347001",
850             //"code_system": "2.16.840.1.113883.6.96",
851             "code_system_name": pd.code_type || "SNOMED CT",
852             "translations": [{
853                 "name": "Ambulatory",
854                 "code": "AMB",
855                 "code_system_name": "ActCode"
856             }]
857         },
858         "identifiers": [{
859             "identifier": pd.sha_extension,
860             "extension": pd.extension
861         }],
862         "date_time": {
863             "point": {
864                 "date": fDate(pd.date),
865                 "precision": "tz"
866             }
867         },
868         "performers": [{
869             "identifiers": [{
870                 "identifier": "2.16.840.1.113883.4.6",
871                 "extension": pd.npi || ""
872             }],
873             "code": [{
874                 "name": pd.physician_type,
875                 "code": cleanCode(pd.physician_type_code),
876                 "code_system_name": pd.physician_code_type
877             }],
878             "name": [
879                 {
880                     "last": pd.lname || "",
881                     "first": pd.fname || ""
882                 }
883             ],
884             "phone": [
885                 {
886                     "number": pd.work_phone,
887                     "type": "work place"
888                 }
889             ]
890         }],
891         "locations": [{
892             "name": pd.location,
893             "location_type": {
894                 "name": pd.location_details,
895                 "code": "1160-1",
896                 "code_system_name": "HealthcareServiceLocation"
897             },
898             "address": [{
899                 "street_lines": [pd.facility_address],
900                 "city": pd.facility_city,
901                 "state": pd.facility_state,
902                 "zip": pd.facility_zip,
903                 "country": pd.facility_country || "US"
904             }],
905             "phone": [
906                 {
907                     "number": pd.facility_phone,
908                     "type": "work place"
909                 }
910             ]
911         }],
912         "findings": findingObj
913     };
916 function populateAllergy(pd) {
918     if (!pd) {
919         return {
920             "no_know_allergies": "No Known Allergies",
921             "date_time": {
922                 "low": templateDate("", "day"),
923                 //"high": templateDate(pd.enddate, "day")
924             }
925         }
926     }
927     let allergyAuthor = {
928         "code": {
929             "name": pd.author.physician_type || '',
930             "code": pd.author.physician_type_code || '',
931             "code_system": pd.author.physician_type_system, "code_system_name": pd.author.physician_type_system_name
932         },
933         "date_time": {
934             "point": {
935                 "date": fDate(pd.author.time),
936                 "precision": "tz"
937             }
938         },
939         "identifiers": [
940             {
941                 "identifier": pd.author.npi ? "2.16.840.1.113883.4.6" : pd.author.id,
942                 "extension": pd.author.npi ? pd.author.npi : 'NI'
943             }
944         ],
945         "name": [
946             {
947                 "last": pd.author.lname,
948                 "first": pd.author.fname
949             }
950         ],
951         "organization": [
952             {
953                 "identity": [
954                     {
955                         "root": pd.author.facility_oid || "2.16.840.1.113883.4.6",
956                         "extension": pd.author.facility_npi || "NI"
957                     }
958                 ],
959                 "name": [
960                     pd.author.facility_name
961                 ]
962             }
963         ]
964     };
966     return {
967         "identifiers": [{
968             "identifier": pd.sha_id,
969             "extension": pd.id || ""
970         }],
971         "date_time": {
972             "low": templateDate(pd.startdate, "day"),
973             //"high": templateDate(pd.enddate, "day")
974         },
975         "author": allergyAuthor,
976         "observation": {
977             "identifiers": [{
978                 "identifier": pd.sha_extension || "2a620155-9d11-439e-92b3-5d9815ff4ee8",
979                 "extension": pd.id + 1 || ""
980             }],
981             "author": allergyAuthor,
982             "allergen": {
983                 "name": pd.title || "",
984                 "code": pd.rxnorm_code_text ? cleanCode(pd.rxnorm_code) : pd.snomed_code_text ? cleanCode(pd.snomed_code) : cleanCode(""),
985                 "code_system_name": pd.rxnorm_code_text ? "RXNORM" : pd.snomed_code_text ? "SNOMED CT" : ""
986             },
987             "date_time": {
988                 "low": {
989                     "date": fDate(pd.startdate) || fDate(""),
990                     "precision": "day"
991                 }
992             },
993             "intolerance": {
994                 "name": "Propensity to adverse reactions to drug",
995                 "code": "420134006",
996                 "code_system_name": "SNOMED CT"
997             },
998             "severity": {
999                 "code": {
1000                     "name": pd.outcome || "",
1001                     "code": cleanCode(pd.outcome_code) || "",
1002                     "code_system_name": "SNOMED CT"
1003                 }
1004             },
1005             "status": {
1006                 "name": pd.status_table || "",
1007                 "code": cleanCode(pd.status_code),
1008                 "code_system_name": "SNOMED CT"
1009             },
1010             "reactions": [{
1011                 "identifiers": [{
1012                     "identifier": "4adc1020-7b14-11db-9fe1-0800200c9a64"
1013                 }],
1014                 "date_time": {
1015                     "low": templateDate(pd.startdate, "day"),
1016                     "high": templateDate(pd.enddate, "day")
1017                 },
1018                 "reaction": {
1019                     "name": pd.reaction_text,
1020                     "code": cleanCode(pd.reaction_code) || "",
1021                     "code_system_name": pd.reaction_code_type || "SNOMED CT"
1022                 },
1023                 "severity": {
1024                     "code": {
1025                         "name": pd.outcome || "",
1026                         "code": cleanCode(pd.outcome_code),
1027                         "code_system_name": "SNOMED CT"
1028                     }
1029                 }
1030             }]
1031         }
1032     }
1035 function populateProblem(pd) {
1036     let primary_care_provider = all.primary_care_provider || {provider: {}};
1037     return {
1038         "date_time": {
1039             "low": {
1040                 "date": fDate(pd.start_date_table),
1041                 "precision": "day"
1042             },
1043             /*"high": {
1044                 "date": fDate(pd.end_date),
1045                 "precision": "day"
1046             }*/
1047         },
1048         "identifiers": [{
1049             "identifier": pd.sha_extension,
1050             "extension": pd.extension || ""
1051         }],
1052         "translations": [{
1053             "name": "Condition",
1054             "code": "75323-6",
1055             "code_system_name": "LOINC"
1056         }],
1057         "problem": {
1058             "code": {
1059                 "name": safeTrim(pd.title),
1060                 "code": cleanCode(pd.code),
1061                 "code_system_name": safeTrim(pd.code_type)
1062             },
1063             "date_time": {
1064                 "low": {
1065                     "date": fDate(pd.start_date),
1066                     "precision": "day"
1067                 },
1068                 /*"high": {
1069                     "date": fDate(pd.end_date),
1070                     "precision": getPrecision()
1071                 }*/
1072             }
1073         },
1074         "author": {
1075             "code": {
1076                 "name": pd.author.physician_type || '',
1077                 "code": pd.author.physician_type_code || '',
1078                 "code_system": pd.author.physician_type_system, "code_system_name": pd.author.physician_type_system_name
1079             },
1080             "date_time": {
1081                 "point": {
1082                     "date": fDate(pd.author.time),
1083                     "precision": "tz"
1084                 }
1085             },
1086             "identifiers": [
1087                 {
1088                     "identifier": pd.author.npi ? "2.16.840.1.113883.4.6" : pd.author.id,
1089                     "extension": pd.author.npi ? pd.author.npi : 'NI'
1090                 }
1091             ],
1092             "name": [
1093                 {
1094                     "last": pd.author.lname,
1095                     "first": pd.author.fname
1096                 }
1097             ],
1098             "organization": [
1099                 {
1100                     "identity": [
1101                         {
1102                             "root": pd.author.facility_oid || "2.16.840.1.113883.4.6",
1103                             "extension": pd.author.facility_npi || "NI"
1104                         }
1105                     ],
1106                     "name": [
1107                         pd.author.facility_name
1108                     ]
1109                 }
1110             ]
1111         },
1112         "performer": [
1113             {
1114                 "identifiers": [
1115                     {
1116                         "identifier": "2.16.840.1.113883.4.6",
1117                         "extension": primary_care_provider.provider.npi || ""
1118                     }
1119                 ],
1120                 "name": [
1121                     {
1122                         "last": primary_care_provider.provider.lname || "",
1123                         "first": primary_care_provider.provider.fname || ""
1124                     }
1125                 ]
1126             }],
1127         "onset_age": pd.age,
1128         "onset_age_unit": "Year",
1129         "status": {
1130             "name": pd.status_table,
1131             "date_time": {
1132                 "low": {
1133                     "date": fDate(pd.start_date),
1134                     "precision": "day"
1135                 },
1136                 /*"high": {
1137                     "date": fDate(pd.end_date),
1138                     "precision": getPrecision()
1139                 }*/
1140             }
1141         },
1142         "patient_status": pd.observation,
1143         "source_list_identifiers": [{
1144             "identifier": pd.sha_extension,
1145             "extension": pd.extension || ""
1146         }]
1147     };
1151 function populateProcedure(pd) {
1152     return {
1153         "procedure": {
1154             "name": pd.description,
1155             "code": cleanCode(pd.code),
1156             //"code_system": "2.16.840.1.113883.6.12",
1157             "code_system_name": pd.code_type
1158         },
1159         "identifiers": [{
1160             "identifier": "d68b7e32-7810-4f5b-9cc2-acd54b0fd85d",
1161             "extension": pd.extension
1162         }],
1163         "status": "completed",
1164         "date_time": {
1165             "point": {
1166                 "date": fDate(pd.date),
1167                 "precision": "day"
1168             }
1169         },
1170         /*"body_sites": [{
1171             "name": "",
1172             "code": "",
1173             "code_system_name": ""
1174         }],
1175         "specimen": {
1176             "identifiers": [{
1177                 "identifier": "c2ee9ee9-ae31-4628-a919-fec1cbb58683"
1178             }],
1179             "code": {
1180                 "name": "",
1181                 "code": "",
1182                 "code_system_name": "SNOMED CT"
1183             }
1184         },*/
1185         "performers": [{
1186             "identifiers": [{
1187                 "identifier": "2.16.840.1.113883.4.6",
1188                 "extension": pd.npi || ""
1189             }],
1190             "address": [{
1191                 "street_lines": [pd.address],
1192                 "city": pd.city,
1193                 "state": pd.state,
1194                 "zip": pd.zip,
1195                 "country": "US"
1196             }],
1197             "phone": [{
1198                 "number": pd.work_phone,
1199                 "type": "work place"
1200             }],
1201             "organization": [{
1202                 "identifiers": [{
1203                     "identifier": pd.facility_sha_extension,
1204                     "extension": pd.facility_extension
1205                 }],
1206                 "name": [pd.facility_name],
1207                 "address": [{
1208                     "street_lines": [pd.facility_address],
1209                     "city": pd.facility_city,
1210                     "state": pd.facility_state,
1211                     "zip": pd.facility_zip,
1212                     "country": pd.facility_country || "US"
1213                 }],
1214                 "phone": [{
1215                     "number": pd.facility_phone,
1216                     "type": "work place"
1217                 }]
1218             }]
1219         }],
1220         "author": populateAuthorFromAuthorContainer(pd),
1221         "procedure_type": "procedure"
1222     };
1225 function populateMedicalDevice(pd) {
1226     return {
1227         "identifiers": [{
1228             "identifier": pd.sha_extension,
1229             "extension": pd.extension
1230         }],
1231         "date_time": {
1232             "low": {
1233                 "date": fDate(pd.start_date),
1234                 "precision": "day"
1235             }/*,
1236         "high": {
1237             "date": fDate(pd.end_date),
1238             "precision": "day"
1239         }*/
1240         },
1241         "device_type": "UDI",
1242         "device": {
1243             "name": pd.code_text,
1244             "code": cleanCode(pd.code),
1245             "code_system_name": "SNOMED CT",
1246             "identifiers": [{
1247                 "identifier": "2.16.840.1.113883.3.3719",
1248                 "extension": pd.udi
1249             }],
1250             "status": "completed",
1251             "body_sites": [{
1252                 "name": "",
1253                 "code": "",
1254                 "code_system_name": ""
1255             }],
1256             "udi": pd.udi
1257         },
1258         "author": {
1259             "code": {
1260                 "name": pd.author.physician_type || '',
1261                 "code": pd.author.physician_type_code || '',
1262                 "code_system": pd.author.physician_type_system, "code_system_name": pd.author.physician_type_system_name
1263             },
1264             "date_time": {
1265                 "point": {
1266                     "date": fDate(pd.author.time),
1267                     "precision": "tz"
1268                 }
1269             },
1270             "identifiers": [
1271                 {
1272                     "identifier": pd.author.npi ? "2.16.840.1.113883.4.6" : pd.author.id,
1273                     "extension": pd.author.npi ? pd.author.npi : 'NI'
1274                 }
1275             ],
1276             "name": [
1277                 {
1278                     "last": pd.author.lname,
1279                     "first": pd.author.fname
1280                 }
1281             ],
1282             "organization": [
1283                 {
1284                     "identity": [
1285                         {
1286                             "root": pd.author.facility_oid || "2.16.840.1.113883.4.6",
1287                             "extension": pd.author.facility_npi || "NI"
1288                         }
1289                     ],
1290                     "name": [
1291                         pd.author.facility_name
1292                     ]
1293                 }
1294             ]
1295         }
1296     }
1299 function populateResult(pd) {
1300     let icode = pd.subtest.abnormal_flag;
1301     let value = parseFloat(pd.subtest.result_value) || pd.subtest.result_value || "";
1302     let type = isNaN(value) ? "ST" : "PQ";
1303     type = !pd.subtest.unit ? "ST" : type;
1304     value += "";
1305     let range_type = pd.subtest.range.toUpperCase() == "NEGATIVE" ? "CO" : type;
1306     type = value.toUpperCase() == "NEGATIVE" ? "CO" : type;
1308     switch (pd.subtest.abnormal_flag.toUpperCase()) {
1309         case "NO":
1310             icode = "Normal";
1311             break;
1312         case "YES":
1313             icode = "Abnormal";
1314             break;
1315         case "":
1316             icode = "";
1317             break;
1318     }
1319     let result = {
1320         "identifiers": [{
1321             "identifier": pd.subtest.root,
1322             "extension": pd.subtest.extension
1323         }],
1324         "result": {
1325             "name": pd.title,
1326             "code": cleanCode(pd.subtest.result_code) || "",
1327             "code_system_name": "LOINC"
1328         },
1329         "date_time": {
1330             "point": {
1331                 "date": fDate(pd.date_ordered),
1332                 "precision": "day"
1333             }
1334         },
1335         "status": pd.order_status,
1336         "reference_range": {
1337             "low": pd.subtest.low,
1338             "high": pd.subtest.high,
1339             "unit": pd.subtest.unit,
1340             "type": type,
1341             "range_type": range_type
1342         },
1343         "value": value + "",
1344         "unit": pd.subtest.unit,
1345         "type": type,
1346         "range": pd.subtest.range,
1347         "range_type": range_type
1348     };
1349     // interpretation cannot be an empty value so we skip it if it is
1350     // empty as Observation.interpretationCode is [0..*]
1351     if (icode !== "") {
1352         result["interpretations"] = [icode];
1353     }
1354     return result;
1357 function getResultSet(results) {
1359     if (!results) return '';
1361     // not sure if the result set should be grouped better on the backend as the author information needs to be more nuanced here
1362     let tResult = results.result[0] || results.result;
1363     let resultSet = {
1364         "identifiers": [{
1365             "identifier": tResult.root,
1366             "extension": tResult.extension
1367         }],
1368         "author": populateAuthorFromAuthorContainer(tResult),
1369         "result_set": {
1370             "name": tResult.test_name,
1371             "code": cleanCode(tResult.test_code),
1372             "code_system_name": "LOINC"
1373         }
1374     };
1375     let rs = [];
1376     let many = [];
1377     let theone = {};
1378     let count = 0;
1379     many.results = [];
1380     try {
1381         count = countEntities(results.result);
1382     } catch (e) {
1383         count = 0;
1384     }
1385     if (count > 1) {
1386         for (let i in results.result) {
1387             theone[i] = populateResult(results.result[i]);
1388             many.results.push(theone[i]);
1389         }
1390     } else if (count !== 0) {
1391         theone = populateResult(results.result);
1392         many.results.push(theone);
1393     }
1394     rs.results = Object.assign(resultSet);
1395     rs.results.results = Object.assign(many.results);
1396     return rs;
1399 function getPlanOfCare(pd) {
1400     let name = '';
1401     let code = '';
1402     let code_system_name = "";
1403     let status = "Active";
1404     let one = true;
1405     let encounter;
1407     let planType = "observation";
1408     switch (pd.care_plan_type) {
1409         case 'plan_of_care':
1410             planType = "observation"; // mood code INT. sets code in template
1411             break;
1412         case 'test_or_order':
1413             planType = "observation"; // mood code RQO
1414             break;
1415         case 'procedure':
1416             planType = "procedure";
1417             break;
1418         case 'appointments':
1419             planType = "encounter";
1420             break;
1421         case 'instructions':
1422             planType = "instructions";
1423             break;
1424         case 'referral':
1425             planType = ""; // for now exclude. unsure how to template.
1426             break;
1427         default:
1428             planType = "observation";
1429     }
1430     if (pd.code_type === 'RXCUI') {
1431         pd.code_type = 'RXNORM';
1432     }
1433     if (pd.code_type === 'RXNORM') {
1434         planType = "substanceAdministration";
1435     }
1436     if (planType === "") {
1437         return false;
1438     }
1440     for (let key in all.encounter_list.encounter) {
1441         // skip loop if the property is from prototype
1442         if (!Object.prototype.hasOwnProperty.call(all.encounter_list.encounter, key)) {
1443             continue;
1444         }
1445         encounter = all.encounter_list.encounter[key];
1446         if (pd.encounter == encounter.encounter_id) {
1447             one = false;
1448             name = encounter.encounter_diagnosis.text;
1449             code = cleanCode(encounter.encounter_diagnosis.code);
1450             code_system_name = encounter.encounter_diagnosis.code_type;
1451             status = encounter.encounter_diagnosis.status;
1452             encounter = all.encounter_list.encounter[key]; // to be sure.
1453             break;
1454         }
1455     }
1456     if (one) {
1457         let value = "";
1458         if (all.encounter_list && all.encounter_list.encounter && all.encounter_list.encounter.encounter_diagnosis) {
1459             value = all.encounter_list.encounter.encounter_diagnosis;
1460         }
1461         name = value.text;
1462         code = cleanCode(value.code);
1463         code_system_name = value.code_type;
1464         status = value.status;
1465         encounter = all.encounter_list.encounter;
1466     }
1468     return {
1469         "plan": {
1470             "name": pd.code_text || "",
1471             "code": cleanCode(pd.code) || "",
1472             "code_system_name": pd.code_type || "SNOMED CT"
1473         },
1474         "identifiers": [{
1475             "identifier": pd.sha_extension,
1476             "extension": pd.extension || ""
1477         }],
1478         "goal": {
1479             "code": cleanCode(pd.code) || "",
1480             "name": safeTrim(pd.description) || ""
1481         },
1482         "date_time": {
1483             "point": {
1484                 "date": fDate(pd.date),
1485                 "precision": "day"
1486             }
1487         },
1488         "type": planType,
1489         "status": {
1490             "code": cleanCode(pd.status)
1491         },
1492         "author": populateAuthorFromAuthorContainer(pd),
1493         "performers": [{
1494             "identifiers": [{
1495                 "identifier": "2.16.840.1.113883.4.6",
1496                 "extension": encounter.npi || ""
1497             }],
1498             "code": [{
1499                 "name": encounter.physician_type,
1500                 "code": cleanCode(encounter.physician_type_code),
1501                 "code_system_name": "SNOMED CT"
1502             }],
1503             "name": [
1504                 {
1505                     "last": encounter.lname || "",
1506                     "first": encounter.fname || ""
1507                 }
1508             ],
1509             "phone": [
1510                 {
1511                     "number": encounter.work_phone,
1512                     "type": "work place"
1513                 }
1514             ]
1515         }],
1516         "locations": [{
1517             "name": encounter.location,
1518             "location_type": {
1519                 "name": encounter.location_details,
1520                 "code": "1160-1",
1521                 "code_system_name": "HealthcareServiceLocation"
1522             },
1523             "address": [{
1524                 "street_lines": [encounter.facility_address],
1525                 "city": encounter.facility_city,
1526                 "state": encounter.facility_state,
1527                 "zip": encounter.facility_zip,
1528                 "country": encounter.facility_country || "US"
1529             }],
1530             "phone": [
1531                 {
1532                     "number": encounter.facility_phone,
1533                     "type": "work place"
1534                 }
1535             ]
1536         }],
1537         "findings": [{
1538             "identifiers": [{
1539                 "identifier": encounter.sha_extension,
1540                 "extension": encounter.extension
1541             }],
1542             "value": {
1543                 "name": name,
1544                 "code": code,
1545                 "code_system_name": code_system_name
1546             },
1547             "date_time": {
1548                 "low": {
1549                     "date": fDate(encounter.date),
1550                     "precision": "day"
1551                 }
1552             },
1553             "status": status,
1554             "reason": encounter.encounter_reason
1555         }],
1556         "name": safeTrim(pd.description),
1557         "mood_code": pd.moodCode
1558     };
1561 function getGoals(pd) {
1562     return {
1563         "goal_code": {
1564             "name": pd.code_text !== "NULL" ? pd.code_text : "",
1565             "code": cleanCode(pd.code) || "",
1566             "code_system_name": pd.code_type || ""
1567         },
1568         "identifiers": [{
1569             "identifier": pd.sha_extension,
1570             "extension": pd.extension,
1571         }],
1572         "date_time": {
1573             "point": {
1574                 "date": fDate(pd.date),
1575                 "precision": "day"
1576             }
1577         },
1578         "type": "observation",
1579         "status": {
1580             "code": "active", //cleanCode(pd.status)
1581         },
1582         "author": populateAuthorFromAuthorContainer(pd),
1583         "name": pd.description
1584     };
1587 function getFunctionalStatus(pd) {
1588     let functionalStatusAuthor = {
1589         "code": {
1590             "name": all.author.physician_type || '',
1591             "code": all.author.physician_type_code || '',
1592             "code_system": all.author.physician_type_system, "code_system_name": all.author.physician_type_system_name
1593         },
1594         "date_time": {
1595             "point": {
1596                 "date": authorDateTime,
1597                 "precision": "tz"
1598             }
1599         },
1600         "identifiers": [
1601             {
1602                 "identifier": all.author.npi ? "2.16.840.1.113883.4.6" : all.author.id,
1603                 "extension": all.author.npi ? all.author.npi : 'NI'
1604             }
1605         ],
1606         "name": [
1607             {
1608                 "last": all.author.lname,
1609                 "first": all.author.fname
1610             }
1611         ],
1612         "organization": [
1613             {
1614                 "identity": [
1615                     {
1616                         "root": oidFacility || "2.16.840.1.113883.4.6",
1617                         "extension": npiFacility || ""
1618                     }
1619                 ],
1620                 "name": [
1621                     all.encounter_provider.facility_name
1622                 ]
1623             }
1624         ]
1625     };
1627     return {
1628         "status": "completed",
1629         "author": functionalStatusAuthor,
1630         "identifiers": [{
1631             "identifier": "9a6d1bac-17d3-4195-89a4-1121bc809000",
1632             "extension": pd.extension || '',
1633         }],
1635         "observation": {
1636             "value": {
1637                 "name": pd.code_text !== "NULL" ? safeTrim(pd.code_text) : "",
1638                 "code": cleanCode(pd.code) || "",
1639                 "code_system_name": pd.code_type || "SNOMED-CT"
1640             },
1641             "identifiers": [{
1642                 "identifier": "9a6d1bac-17d3-4195-89a4-1121bc8090ab",
1643                 "extension": pd.extension || '',
1644             }],
1645             "date_time": {
1646                 "point": {
1647                     "date": fDate(pd.date),
1648                     "precision": "day"
1649                 }
1650             },
1651             "status": "completed",
1652             "author": functionalStatusAuthor
1653         }
1654     };
1657 function getMentalStatus(pd) {
1658     return {
1659         "value": {
1660             "name": pd.code_text !== "NULL" ? pd.code_text : "",
1661             "code": cleanCode(pd.code) || "",
1662             "code_system_name": pd.code_type || ""
1663         },
1664         "identifiers": [{
1665             "identifier": "9a6d1bac-17d3-4195-89a4-1121bc809ccc",
1666             "extension": pd.extension,
1667         }],
1668         "note": safeTrim(pd.description),
1669         "date_time": {
1670             "low": templateDate(pd.date, "day")
1671             //"high": templateDate(pd.date, "day")
1672         },
1673         "author": {
1674             "code": {
1675                 "name": all.author.physician_type || '',
1676                 "code": all.author.physician_type_code || '',
1677                 "code_system": all.author.physician_type_system, "code_system_name": all.author.physician_type_system_name
1678             },
1679             "date_time": {
1680                 "point": {
1681                     "date": authorDateTime,
1682                     "precision": "tz"
1683                 }
1684             },
1685             "identifiers": [
1686                 {
1687                     "identifier": all.author.npi ? "2.16.840.1.113883.4.6" : all.author.id,
1688                     "extension": all.author.npi ? all.author.npi : 'NI'
1689                 }
1690             ],
1691             "name": [
1692                 {
1693                     "last": all.author.lname,
1694                     "first": all.author.fname
1695                 }
1696             ],
1697             "organization": [
1698                 {
1699                     "identity": [
1700                         {
1701                             "root": oidFacility || "2.16.840.1.113883.4.6",
1702                             "extension": npiFacility || ""
1703                         }
1704                     ],
1705                     "name": [
1706                         all.encounter_provider.facility_name
1707                     ]
1708                 }
1709             ]
1710         }
1711     };
1714 function getAssessments(pd) {
1715     return {
1716         "description": safeTrim(pd.description),
1717         "author": populateAuthorFromAuthorContainer(pd)
1718     };
1721 function getHealthConcerns(pd) {
1722     let one = true;
1723     let issue_uuid;
1724     let problems = [], problem = {};
1725     if (countEntities(pd.issues.issue_uuid) !== 0) {
1726         for (let key in pd.issues.issue_uuid) {
1727             issue_uuid = pd.issues.issue_uuid[key];
1728             if (issue_uuid) {
1729                 one = false;
1730             }
1731             problem = {
1732                 "identifiers": [{
1733                     "identifier": issue_uuid
1734                 }]
1735             };
1736             problems.push(problem);
1737         }
1738     }
1739     if (one) {
1740         if (pd.issues.issue_uuid) {
1741             problem = {
1742                 "identifiers": [{
1743                     "identifier": pd.issues.issue_uuid
1744                 }]
1745             };
1746             problems.push(problem);
1747         }
1748     }
1749     return {
1750         // todo need to make array of health concerns
1751         "type": "act",
1752         "text": safeTrim(pd.text),
1753         "value": {
1754             "name": pd.code_text || "",
1755             "code": cleanCode(pd.code) || "",
1756             "code_system_name": pd.code_type || "SNOMED CT"
1757         },
1758         "author": populateAuthorFromAuthorContainer(pd),
1759         "identifiers": [{
1760             "identifier": pd.sha_extension,
1761             "extension": pd.extension,
1762         }],
1763         problems: problems
1764     }
1767 function getReferralReason(pd) {
1768     return {
1769         "reason": safeTrim(pd.text),
1770         "author": populateAuthorFromAuthorContainer(pd)
1771     };
1774 function populateVital(pd) {
1775     return {
1776         "identifiers": [{
1777             "identifier": pd.sha_extension,
1778             "extension": pd.extension
1779         }],
1780         "status": "completed",
1781         "date_time": {
1782             "point": {
1783                 "date": fDate(pd.effectivetime),
1784                 "precision": "day"
1785             }
1786         },
1787         // our list of vitals per organizer.
1788         "vital_list": [{
1789             "identifiers": [{
1790                 "identifier": pd.sha_extension,
1791                 "extension": pd.extension_bps
1792             }],
1793             "vital": {
1794                 "name": "Blood Pressure Systolic",
1795                 "code": "8480-6",
1796                 "code_system_name": "LOINC"
1797             },
1798             "status": "completed",
1799             "date_time": {
1800                 "point": {
1801                     "date": fDate(pd.effectivetime),
1802                     "precision": "day"
1803                 }
1804             },
1805             "interpretations": ["Normal"],
1806             "value": parseFloat(pd.bps) || pd.bps,
1807             "unit": "mm[Hg]",
1808             "author": populateAuthorFromAuthorContainer(pd),
1809         }, {
1810             "identifiers": [{
1811                 "identifier": pd.sha_extension,
1812                 "extension": pd.extension_bpd
1813             }],
1814             "vital": {
1815                 "name": "Blood Pressure Diastolic",
1816                 "code": "8462-4",
1817                 "code_system_name": "LOINC"
1818             },
1819             "status": "completed",
1820             "date_time": {
1821                 "point": {
1822                     "date": fDate(pd.effectivetime),
1823                     "precision": "day"
1824                 }
1825             },
1826             "interpretations": ["Normal"],
1827             "value": parseFloat(pd.bpd) || pd.bpd,
1828             "unit": "mm[Hg]",
1829             "author": populateAuthorFromAuthorContainer(pd),
1830         }, {
1831             "identifiers": [{
1832                 "identifier": pd.sha_extension,
1833                 "extension": pd.extension_height
1834             }],
1835             "vital": {
1836                 "name": "Height",
1837                 "code": "8302-2",
1838                 "code_system_name": "LOINC"
1839             },
1840             "status": "completed",
1841             "date_time": {
1842                 "point": {
1843                     "date": fDate(pd.effectivetime),
1844                     "precision": "day"
1845                 }
1846             },
1847             "interpretations": ["Normal"],
1848             "value": parseFloat(pd.height) || pd.height,
1849             "unit": pd.unit_height,
1850             "author": populateAuthorFromAuthorContainer(pd),
1851         }, {
1852             "identifiers": [{
1853                 "identifier": pd.sha_extension,
1854                 "extension": pd.extension_weight
1855             }],
1856             "vital": {
1857                 "name": "Weight Measured",
1858                 "code": "29463-7",
1859                 "code_system_name": "LOINC"
1860             },
1861             "status": "completed",
1862             "date_time": {
1863                 "point": {
1864                     "date": fDate(pd.effectivetime),
1865                     "precision": "day"
1866                 }
1867             },
1868             "interpretations": ["Normal"],
1869             "value": parseFloat(pd.weight) || "",
1870             "unit": pd.unit_weight,
1871             "author": populateAuthorFromAuthorContainer(pd),
1872         }, {
1873             "identifiers": [{
1874                 "identifier": pd.sha_extension,
1875                 "extension": pd.extension_BMI
1876             }],
1877             "vital": {
1878                 "name": "BMI (Body Mass Index)",
1879                 "code": "39156-5",
1880                 "code_system_name": "LOINC"
1881             },
1882             "status": "completed",
1883             "date_time": {
1884                 "point": {
1885                     "date": fDate(pd.effectivetime),
1886                     "precision": "day"
1887                 }
1888             },
1889             "interpretations": [pd.BMI_status == 'Overweight' ? 'High' : pd.BMI_status == 'Overweight' ? 'Low' : 'Normal'],
1890             "value": parseFloat(pd.BMI) || "",
1891             "unit": "kg/m2",
1892             "author": populateAuthorFromAuthorContainer(pd),
1893         }, {
1894             "identifiers": [{
1895                 "identifier": pd.sha_extension,
1896                 "extension": pd.extension_pulse
1897             }],
1898             "vital": {
1899                 "name": "Heart Rate",
1900                 "code": "8867-4",
1901                 "code_system_name": "LOINC"
1902             },
1903             "status": "completed",
1904             "date_time": {
1905                 "point": {
1906                     "date": fDate(pd.effectivetime),
1907                     "precision": "day"
1908                 }
1909             },
1910             "interpretations": ["Normal"],
1911             "value": parseFloat(pd.pulse) || "",
1912             "unit": "/min",
1913             "author": populateAuthorFromAuthorContainer(pd),
1914         }, {
1915             "identifiers": [{
1916                 "identifier": "2.16.840.1.113883.3.140.1.0.6.10.14.2",
1917                 "extension": pd.extension_breath
1918             }],
1919             "vital": {
1920                 "name": "Respiratory Rate",
1921                 "code": "9279-1",
1922                 "code_system_name": "LOINC"
1923             },
1924             "status": "completed",
1925             "date_time": {
1926                 "point": {
1927                     "date": fDate(pd.effectivetime),
1928                     "precision": "day"
1929                 }
1930             },
1931             "interpretations": ["Normal"],
1932             "value": parseFloat(pd.breath) || "",
1933             "unit": "/min",
1934             "author": populateAuthorFromAuthorContainer(pd),
1935         }, {
1936             "identifiers": [{
1937                 "identifier": "2.16.840.1.113883.3.140.1.0.6.10.14.3",
1938                 "extension": pd.extension_temperature
1939             }],
1940             "vital": {
1941                 "name": "Body Temperature",
1942                 "code": "8310-5",
1943                 "code_system_name": "LOINC"
1944             },
1945             "status": "completed",
1946             "date_time": {
1947                 "point": {
1948                     "date": fDate(pd.effectivetime),
1949                     "precision": "day"
1950                 }
1951             },
1952             "interpretations": ["Normal"],
1953             "value": parseFloat(pd.temperature) || "",
1954             "unit": pd.unit_temperature,
1955             "author": populateAuthorFromAuthorContainer(pd),
1956         }, {
1957             "identifiers": [{
1958                 "identifier": pd.sha_extension,
1959                 "extension": pd.extension_oxygen_saturation
1960             }],
1961             "vital": {
1962                 "name": "O2 % BldC Oximetry",
1963                 "code": "59408-5",
1964                 "code_system_name": "LOINC"
1965             },
1966             "status": "completed",
1967             "date_time": {
1968                 "point": {
1969                     "date": fDate(pd.effectivetime),
1970                     "precision": "day"
1971                 }
1972             },
1973             "interpretations": ["Normal"],
1974             "value": parseFloat(pd.oxygen_saturation) || "",
1975             "unit": "%",
1976             "author": populateAuthorFromAuthorContainer(pd),
1977         }, {
1978             "identifiers": [{
1979                 "identifier": pd.sha_extension,
1980                 "extension": pd.extension_ped_weight_height
1981             }],
1982             "vital": { // --------------------------------------------------------------------------------
1983                 "name": "Weight for Height Percentile",
1984                 "code": "77606-2",
1985                 "code_system_name": "LOINC"
1986             },
1987             "status": "completed",
1988             "date_time": {
1989                 "point": {
1990                     "date": fDate(pd.effectivetime),
1991                     "precision": "day"
1992                 }
1993             },
1994             "interpretations": ["Normal"],
1995             "value": parseFloat(pd.ped_weight_height) || "",
1996             "unit": "%",
1997             "author": populateAuthorFromAuthorContainer(pd),
1998         }, {
1999             "identifiers": [{
2000                 "identifier": pd.sha_extension,
2001                 "extension": pd.extension_inhaled_oxygen_concentration
2002             }],
2003             "vital": {
2004                 "name": "Inhaled Oxygen Concentration",
2005                 "code": "3150-0",
2006                 "code_system_name": "LOINC"
2007             },
2008             "status": "completed",
2009             "date_time": {
2010                 "point": {
2011                     "date": fDate(pd.effectivetime),
2012                     "precision": "day"
2013                 }
2014             },
2015             "interpretations": ["Normal"],
2016             "value": parseFloat(pd.inhaled_oxygen_concentration) || "",
2017             "unit": "%",
2018             "author": populateAuthorFromAuthorContainer(pd),
2019         }, {
2020             "identifiers": [{
2021                 "identifier": pd.sha_extension,
2022                 "extension": pd.extension_ped_bmi
2023             }],
2024             "vital": {
2025                 "name": "BMI Percentile",
2026                 "code": "59576-9",
2027                 "code_system_name": "LOINC"
2028             },
2029             "status": "completed",
2030             "date_time": {
2031                 "point": {
2032                     "date": fDate(pd.effectivetime),
2033                     "precision": "day"
2034                 }
2035             },
2036             "interpretations": ["Normal"],
2037             "value": parseFloat(pd.ped_bmi) || "",
2038             "unit": "%",
2039             "author": populateAuthorFromAuthorContainer(pd),
2040         }, {
2041             "identifiers": [{
2042                 "identifier": pd.sha_extension,
2043                 "extension": pd.extension_ped_head_circ
2044             }],
2045             "vital": {
2046                 "name": "Head Occipital-frontal Circumference Percentile",
2047                 "code": "8289-1",
2048                 "code_system_name": "LOINC"
2049             },
2050             "status": "completed",
2051             "date_time": {
2052                 "point": {
2053                     "date": fDate(pd.effectivetime),
2054                     "precision": "day"
2055                 }
2056             },
2057             "interpretations": ["Normal"],
2058             "value": parseFloat(pd.ped_head_circ) || "",
2059             "unit": "%",
2060             "author": populateAuthorFromAuthorContainer(pd),
2061         }
2062         ]
2063     }
2066 function populateSocialHistory(pd) {
2067     return {
2068         "date_time": {
2069             "low": templateDate(pd.date, "day")
2070             //"high": templateDate(pd.date, "day")
2071         },
2072         "identifiers": [{
2073             "identifier": pd.sha_extension,
2074             "extension": pd.extension
2075         }],
2076         "code": {
2077             "name": pd.code
2078         },
2079         "element": pd.element,
2080         "value": pd.description,
2081         "gender": all.patient.gender,
2082         "author": {
2083             "code": {
2084                 "name": pd.author.physician_type || '',
2085                 "code": pd.author.physician_type_code || '',
2086                 "code_system": pd.author.physician_type_system, "code_system_name": pd.author.physician_type_system_name
2087             },
2088             "date_time": {
2089                 "point": {
2090                     "date": fDate(pd.author.time),
2091                     "precision": "tz"
2092                 }
2093             },
2094             "identifiers": [
2095                 {
2096                     "identifier": pd.author.npi ? "2.16.840.1.113883.4.6" : pd.author.id,
2097                     "extension": pd.author.npi ? pd.author.npi : 'NI'
2098                 }
2099             ],
2100             "name": [
2101                 {
2102                     "last": pd.author.lname,
2103                     "first": pd.author.fname
2104                 }
2105             ],
2106             "organization": [
2107                 {
2108                     "identity": [
2109                         {
2110                             "root": pd.author.facility_oid || "2.16.840.1.113883.4.6",
2111                             "extension": pd.author.facility_npi || "NI"
2112                         }
2113                     ],
2114                     "name": [
2115                         pd.author.facility_name
2116                     ]
2117                 }
2118             ]
2119         }
2120         , "gender_author": {
2121             "code": {
2122                 "name": all.patient.author.physician_type || '',
2123                 "code": all.patient.author.physician_type_code || '',
2124                 "code_system": all.patient.author.physician_type_system, "code_system_name": all.patient.author.physician_type_system_name
2125             },
2126             "date_time": {
2127                 "point": {
2128                     "date": fDate(all.patient.author.time),
2129                     "precision": "tz"
2130                 }
2131             },
2132             "identifiers": [
2133                 {
2134                     "identifier": all.patient.author.npi ? "2.16.840.1.113883.4.6" : all.patient.author.id,
2135                     "extension": all.patient.author.npi ? all.patient.author.npi : 'NI'
2136                 }
2137             ],
2138             "name": [
2139                 {
2140                     "last": all.patient.author.lname,
2141                     "first": all.patient.author.fname
2142                 }
2143             ],
2144             "organization": [
2145                 {
2146                     "identity": [
2147                         {
2148                             "root": all.patient.author.facility_oid || "2.16.840.1.113883.4.6",
2149                             "extension": all.patient.author.facility_npi || "NI"
2150                         }
2151                     ],
2152                     "name": [
2153                         all.patient.author.facility_name
2154                     ]
2155                 }
2156             ]
2157         }
2158     };
2161 function populateImmunization(pd) {
2162     return {
2163         "date_time": {
2164             "low": {
2165                 "date": fDate(pd.administered_on),
2166                 "precision": "day"
2167             }
2168         },
2169         "identifiers": [{
2170             "identifier": pd.sha_extension,
2171             "extension": pd.extension || ""
2172         }],
2173         "status": "complete",
2174         "product": {
2175             "product": {
2176                 "name": pd.code_text,
2177                 "code": cleanCode(pd.cvx_code),
2178                 "code_system_name": "CVX"
2179                 /*"translations": [{
2180                     "name": "",
2181                     "code": "",
2182                     "code_system_name": "CVX"
2183                 }]*/
2184             },
2185             "lot_number": "",
2186             "manufacturer": ""
2187         },
2188         "administration": {
2189             "route": {
2190                 "name": pd.route_of_administration,
2191                 "code": cleanCode(pd.route_code) || "",
2192                 "code_system_name": "Medication Route FDA"
2193             }/*,
2194         "dose": {
2195             "value": 50,
2196             "unit": "mcg"
2197         }*/
2198         },
2199         "performer": {
2200             "identifiers": [{
2201                 "identifier": "2.16.840.1.113883.4.6",
2202                 "extension": pd.npi || ""
2203             }],
2204             "name": [{
2205                 "last": pd.lname,
2206                 "first": pd.fname
2207             }],
2208             "address": [{
2209                 "street_lines": [pd.address],
2210                 "city": pd.city,
2211                 "state": pd.state,
2212                 "zip": pd.zip,
2213                 "country": "US"
2214             }],
2215             "organization": [{
2216                 "identifiers": [{
2217                     "identifier": "2.16.840.1.113883.4.6",
2218                     "extension": npiFacility || ""
2219                 }],
2220                 "name": [pd.facility_name]
2221             }]
2222         },
2223         "instructions": {
2224             "code": {
2225                 "name": "immunization education",
2226                 "code": "171044003",
2227                 "code_system_name": "SNOMED CT"
2228             },
2229             "free_text": "Needs Attention for more data."
2230         },
2231         "author": {
2232             "code": {
2233                 "name": pd.author.physician_type || '',
2234                 "code": pd.author.physician_type_code || '',
2235                 "code_system": pd.author.physician_type_system, "code_system_name": pd.author.physician_type_system_name
2236             },
2237             "date_time": {
2238                 "point": {
2239                     "date": fDate(pd.author.time),
2240                     "precision": "tz"
2241                 }
2242             },
2243             "identifiers": [
2244                 {
2245                     "identifier": pd.author.npi ? "2.16.840.1.113883.4.6" : pd.author.id,
2246                     "extension": pd.author.npi ? pd.author.npi : 'NI'
2247                 }
2248             ],
2249             "name": [
2250                 {
2251                     "last": pd.author.lname,
2252                     "first": pd.author.fname
2253                 }
2254             ],
2255             "organization": [
2256                 {
2257                     "identity": [
2258                         {
2259                             "root": pd.author.facility_oid || "2.16.840.1.113883.4.6",
2260                             "extension": pd.author.facility_npi || "NI"
2261                         }
2262                     ],
2263                     "name": [
2264                         pd.author.facility_name
2265                     ]
2266                 }
2267             ]
2268         }
2269     };
2272 function populatePayer(pd) {
2273     return {
2274         "identifiers": [{
2275             "identifier": "1fe2cdd0-7aad-11db-9fe1-0800200c9a66"
2276         }],
2277         "policy": {
2278             "identifiers": [{
2279                 "identifier": "3e676a50-7aac-11db-9fe1-0800200c9a66"
2280             }],
2281             "code": {
2282                 "code": "SELF",
2283                 "code_system_name": "HL7 RoleCode"
2284             },
2285             "insurance": {
2286                 "code": {
2287                     "code": "PAYOR",
2288                     "code_system_name": "HL7 RoleCode"
2289                 },
2290                 "performer": {
2291                     "identifiers": [{
2292                         "identifier": "2.16.840.1.113883.19"
2293                     }],
2294                     "address": [{
2295                         "street_lines": ["123 Insurance Road"],
2296                         "city": "Blue Bell",
2297                         "state": "MA",
2298                         "zip": "02368",
2299                         "country": "US",
2300                         "use": "work place"
2301                     }],
2302                     "phone": [{
2303                         "number": "(781)555-1515",
2304                         "type": "work place"
2305                     }],
2306                     "organization": [{
2307                         "name": ["Good Health Insurance"],
2308                         "address": [{
2309                             "street_lines": ["123 Insurance Road"],
2310                             "city": "Blue Bell",
2311                             "state": "MA",
2312                             "zip": "02368",
2313                             "country": "US",
2314                             "use": "work place"
2315                         }],
2316                         "phone": [{
2317                             "number": "(781)555-1515",
2318                             "type": "work place"
2319                         }]
2320                     }],
2321                     "code": [{
2322                         "code": "PAYOR",
2323                         "code_system_name": "HL7 RoleCode"
2324                     }]
2325                 }
2326             }
2327         },
2328         "guarantor": {
2329             "code": {
2330                 "code": "GUAR",
2331                 "code_system_name": "HL7 Role"
2332             },
2333             "identifiers": [{
2334                 "identifier": "329fcdf0-7ab3-11db-9fe1-0800200c9a66"
2335             }],
2336             "name": [{
2337                 "prefix": "Mr.",
2338                 "middle": ["Frankie"],
2339                 "last": "Everyman",
2340                 "first": "Adam"
2341             }],
2342             "address": [{
2343                 "street_lines": ["17 Daws Rd."],
2344                 "city": "Blue Bell",
2345                 "state": "MA",
2346                 "zip": "02368",
2347                 "country": "US",
2348                 "use": "primary home"
2349             }],
2350             "phone": [{
2351                 "number": "(781)555-1212",
2352                 "type": "primary home"
2353             }]
2354         },
2355         "participant": {
2356             "code": {
2357                 "name": "Self",
2358                 "code": "SELF",
2359                 "code_system_name": "HL7 Role"
2360             },
2361             "performer": {
2362                 "identifiers": [{
2363                     "identifier": "14d4a520-7aae-11db-9fe1-0800200c9a66",
2364                     "extension": "1138345"
2365                 }],
2366                 "address": [{
2367                     "street_lines": ["17 Daws Rd."],
2368                     "city": "Blue Bell",
2369                     "state": "MA",
2370                     "zip": "02368",
2371                     "country": "US",
2372                     "use": "primary home"
2373                 }],
2374                 "code": [{
2375                     "name": "Self",
2376                     "code": "SELF",
2377                     "code_system_name": "HL7 Role"
2378                 }]
2379             },
2380             "name": [{
2381                 "prefix": "Mr.",
2382                 "middle": ["A."],
2383                 "last": "Everyman",
2384                 "first": "Frank"
2385             }]
2386         },
2387         "policy_holder": {
2388             "performer": {
2389                 "identifiers": [{
2390                     "identifier": "2.16.840.1.113883.19",
2391                     "extension": "1138345"
2392                 }],
2393                 "address": [{
2394                     "street_lines": ["17 Daws Rd."],
2395                     "city": "Blue Bell",
2396                     "state": "MA",
2397                     "zip": "02368",
2398                     "country": "US",
2399                     "use": "primary home"
2400                 }]
2401             }
2402         },
2403         "authorization": {
2404             "identifiers": [{
2405                 "identifier": "f4dce790-8328-11db-9fe1-0800200c9a66"
2406             }],
2407             "procedure": {
2408                 "code": {
2409                     "name": "Colonoscopy",
2410                     "code": "73761001",
2411                     "code_system_name": "SNOMED CT"
2412                 }
2413             }
2414         }
2415     };
2418 function populateNote(pd) {
2419     return {
2420         "date_time": {
2421             "point": {
2422                 "date": fDate(pd.date),
2423                 "precision": "day"
2424             }
2425         },
2426         "translations": {
2427             code_system: "2.16.840.1.113883.6.1",
2428             code_system_name: "LOINC",
2429             code: cleanCode(pd.code),
2430             name: pd.code_text || ""
2431         },
2432         "author": populateAuthorFromAuthorContainer(pd),
2433         "note": safeTrim(pd.description),
2434     };
2437 function populateParticipant(participant) {
2438     return {
2439         "name": {
2440             "prefix": participant.prefix || "",
2441             "suffix": participant.suffix || "",
2442             "middle": [participant.mname] || "",
2443             "last": participant.lname || "",
2444             "first": participant.fname || ""
2445         },
2446         "typeCode": participant.type || "",
2447         "classCode": "ASSIGNED",
2448         "code": {
2449             "name": participant.organization_taxonomy_description || "",
2450             "code": cleanCode(participant.organization_taxonomy) || "",
2451             "code_system": "2.16.840.1.113883.6.101",
2452             "code_system_name": "NUCC Health Care Provider Taxonomy"
2453         },
2454         "identifiers": [{
2455             "identifier": participant.organization_npi ? "2.16.840.1.113883.4.6" : participant.organization_id,
2456             "extension": participant.organization_npi ? participant.organization_npi : ''
2457         }],
2458         "date_time": {
2459             "point": {
2460                 "date": participant.date_time,
2461                 "precision": "tz"
2462             }
2463         },
2464         "phone": [
2465             {
2466                 "number": participant.phonew1 || "",
2467                 "type": "WP"
2468             }
2469         ],
2470         "address": [
2471             {
2472                 "street_lines": [
2473                     participant.street
2474                 ],
2475                 "city": participant.city,
2476                 "state": participant.state,
2477                 "zip": participant.postalCode,
2478                 "country": participant.country || "US",
2479                 "use": participant.address_use || "WP"
2480             }
2481         ],
2482     }
2485 function populateHeader(pd) {
2486     // default doc type ToC CCD
2487     let name = "Summarization of Episode Note";
2488     let docCode = "34133-9";
2489     let docOid = "2.16.840.1.113883.10.20.22.1.2";
2490     if (pd.doc_type == 'referral') {
2491         name = "Referral Note";
2492         docCode = "57133-1";
2493         docOid = "2.16.840.1.113883.10.20.22.1.14";
2494     }
2496     if (pd.doc_type == 'unstructured') {
2497         name = "Patient Documents";
2498         docCode = "34133-9";
2499         docOid = "2.16.840.1.113883.10.20.22.1.10";
2500     }
2502     const head = {
2503         "identifiers": [
2504             {
2505                 "identifier": oidFacility,
2506                 "extension": "123456"
2507             }
2508         ],
2509         "code": {
2510             "name": name,
2511             "code": docCode,
2512             "code_system_name": "LOINC"
2513         },
2514         "template": {
2515             "root": docOid,
2516             "extension": "2015-08-01"
2517         },
2518         "title": name,
2519         "date_time": {
2520             "point": {
2521                 "date": fDate(pd.created_time_timezone),
2522                 "precision": "tz"
2523             }
2524         },
2525         "author": {
2526             "code": {
2527                 "name": all.author.physician_type || '',
2528                 "code": all.author.physician_type_code || '',
2529                 "code_system": all.author.physician_type_system, "code_system_name": all.author.physician_type_system_name
2530             },
2531             "date_time": {
2532                 "point": {
2533                     "date": authorDateTime,
2534                     "precision": "tz"
2535                 }
2536             },
2537             "identifiers": [
2538                 {
2539                     "identifier": all.author.npi ? "2.16.840.1.113883.4.6" : all.author.id,
2540                     "extension": all.author.npi ? all.author.npi : 'NI'
2541                 }
2542             ],
2543             "name": [
2544                 {
2545                     "last": all.author.lname,
2546                     "first": all.author.fname
2547                 }
2548             ],
2549             "address": [
2550                 {
2551                     "street_lines": [
2552                         all.author.streetAddressLine
2553                     ],
2554                     "city": all.author.city,
2555                     "state": all.author.state,
2556                     "zip": all.author.postalCode,
2557                     "country": all.author.country || "US",
2558                     "use": "work place"
2559                 }
2560             ],
2561             "phone": [
2562                 {
2563                     "number": all.author.telecom || "",
2564                     "type": "WP"
2565                 }
2566             ],
2567             "organization": [
2568                 {
2569                     "identity": [
2570                         {
2571                             "root": oidFacility || "2.16.840.1.113883.4.6",
2572                             "extension": npiFacility || ""
2573                         }
2574                     ],
2575                     "name": [
2576                         all.encounter_provider.facility_name
2577                     ],
2578                     "address": [
2579                         {
2580                             "street_lines": [
2581                                 all.encounter_provider.facility_street
2582                             ],
2583                             "city": all.encounter_provider.facility_city,
2584                             "state": all.encounter_provider.facility_state,
2585                             "zip": all.encounter_provider.facility_postal_code,
2586                             "country": all.encounter_provider.facility_country_code || "US",
2587                             "use": "work place"
2588                         }
2589                     ],
2590                     "phone": [
2591                         {
2592                             "number": all.encounter_provider.facility_phone,
2593                             "type": "work primary"
2594                         }
2595                     ]
2596                 }
2597             ]
2598         },
2599         "custodian": {
2600             "identity": [
2601                 {
2602                     "root": "2.16.840.1.113883.4.6",
2603                     "extension": npiFacility || ""
2604                 }
2605             ],
2606             "name": [
2607                 pd.custodian.organization || pd.custodian.name
2608             ],
2609             "address": [
2610                 {
2611                     "street_lines": [
2612                         pd.custodian.streetAddressLine
2613                     ],
2614                     "city": pd.custodian.city,
2615                     "state": pd.custodian.state,
2616                     "zip": pd.custodian.postalCode,
2617                     "country": pd.custodian.country || "US"
2618                 }
2619             ],
2620             "phone": [
2621                 {
2622                     "number": pd.custodian.telecom,
2623                     "type": "work primary"
2624                 }
2625             ]
2626         },
2627         "information_recipient": {
2628             "name": {
2629                 "prefix": pd.information_recipient.prefix || "",
2630                 "suffix": pd.information_recipient.suffix || "",
2631                 "middle": [pd.information_recipient.mname] || "",
2632                 "last": pd.information_recipient.lname || "",
2633                 "first": pd.information_recipient.fname || ""
2634             },
2635             "organization": {
2636                 "name": pd.information_recipient.organization || "org"
2637             },
2638         }
2639     };
2640     let participants = [];
2641     let docParticipants = pd.document_participants || {participant: []};
2642     let count = 0;
2643     try {
2644         count = countEntities(docParticipants.participant);
2645     } catch (e) {
2646         count = 0
2647     }
2648     if (count === 1) {
2649         participants = [populateParticipant(docParticipants.participant)];
2650     } else {
2651         // grab the values of our object
2652         participants = Object.values(docParticipants.participant).filter(pcpt => pcpt.type).map(pcpt => populateParticipant(pcpt));
2653     }
2654     if (participants.length) {
2655         head.participants = participants;
2656     }
2658     if (countEntities(all.encounter_list.encounter) === 1) {
2659         let primary_care_provider = pd.primary_care_provider || {provider: {}};
2660         head.component_of = {
2661             "identifiers": [
2662                 {
2663                     "identifier": oidFacility || "",
2664                     "extension": "PT-" + (pd.patient.id || "")
2665                 }
2666             ],
2667             "code": {
2668                 "name": pd.primary_diagnosis.text || "",
2669                 "code": pd.primary_diagnosis.code || "",
2670                 "code_system_name": pd.primary_diagnosis.code_type || ""
2671             },
2672             "date_time": {
2673                 "low": {
2674                     "date": pd.primary_diagnosis.encounter_date || "",
2675                     "precision": "tz"
2676                 },
2677                 "high": {
2678                     "date": pd.primary_diagnosis.encounter_end_date || "",
2679                     "precision": "tz"
2680                 }
2681             },
2682             "responsible_party": {
2683                 "root": oidFacility,
2684                 "name": {
2685                     "last": pd.author.lname,
2686                     "first": pd.author.fname
2687                 },
2688             },
2689             "encounter_participant": {
2690                 "root": oidFacility,
2691                 "name": {
2692                     "last": primary_care_provider.provider.lname || "",
2693                     "first": primary_care_provider.provider.fname || ""
2694                 },
2695                 "address": [
2696                     {
2697                         "street_lines": [
2698                             pd.encounter_provider.facility_street
2699                         ],
2700                         "city": pd.encounter_provider.facility_city,
2701                         "state": pd.encounter_provider.facility_state,
2702                         "zip": pd.encounter_provider.facility_postal_code,
2703                         "country": pd.encounter_provider.facility_country_code || "US",
2704                         "use": "work place"
2705                     }
2706                 ],
2707                 "phone": [
2708                     {
2709                         "number": pd.encounter_provider.facility_phone,
2710                         "type": "work primary"
2711                     }
2712                 ]
2713             }
2714         }
2715     }
2717     return head;
2720 function getMeta(pd) {
2721     let meta = {};
2722     meta = {
2723         "type": pd.doc_type,
2724         "identifiers": [
2725             {
2726                 "identifier": oidFacility || "NI",
2727                 "extension": "TT988"
2728             }
2729         ],
2730         "confidentiality": "Normal",
2731         "set_id": {
2732             "identifier": oidFacility || "NI",
2733             "extension": "sTT988"
2734         }
2735     }
2736     return meta;
2740  / * function generateCcda
2741  /* The main document builder
2742  /* pd array the xml parsed array of data sent from CCM.
2743  */
2744 function generateCcda(pd) {
2745     let doc = {};
2746     let data = {};
2747     let count = 0;
2748     let many = [];
2749     let theone = {};
2750     all = pd;
2751     let primary_care_provider = all.primary_care_provider || {};
2752     npiProvider = primary_care_provider.provider ? primary_care_provider.provider.npi : "NI";
2753     oidFacility = all.encounter_provider.facility_oid ? all.encounter_provider.facility_oid : "2.16.840.1.113883.19.5.99999.1";
2754     npiFacility = all.encounter_provider.facility_npi;
2755     webRoot = all.serverRoot;
2756     documentLocation = all.document_location;
2758     authorDateTime = pd.created_time_timezone;
2759     if (pd.author.time.length > 7) {
2760         authorDateTime = pd.author.time;
2761     } else if (all.encounter_list && all.encounter_list.encounter) {
2762         if (countEntities(all.encounter_list.encounter) === 1) {
2763             authorDateTime = all.encounter_list.encounter.date;
2764         } else {
2765             authorDateTime = all.encounter_list.encounter[0].date;
2766         }
2767     }
2769     authorDateTime = fDate(authorDateTime);
2770 // Demographics
2771     let demographic = populateDemographic(pd.patient, pd.guardian, pd);
2772 // This populates documentationOf. We are using providerOrganization also.
2773     if (pd.primary_care_provider) {
2774         Object.assign(demographic, populateProviders(pd));
2775     }
2776     data.demographics = Object.assign(demographic);
2777 // Encounters
2778     let encs = [];
2779     let enc = {};
2780     encs.encounters = [];
2781     try {
2782         count = countEntities(pd.encounter_list.encounter);
2783     } catch (e) {
2784         count = 0
2785     }
2786     if (count > 1) {
2787         for (let i in pd.encounter_list.encounter) {
2788             enc[i] = populateEncounter(pd.encounter_list.encounter[i]);
2789             encs.encounters.push(enc[i]);
2790         }
2791     } else if (count !== 0) {
2792         enc = populateEncounter(pd.encounter_list.encounter);
2793         encs.encounters.push(enc);
2794     }
2795     if (count !== 0) {
2796         data.encounters = Object.assign(encs.encounters);
2797     }
2798 // vitals
2799     let vitals = [];
2800     let vital = {};
2801     vitals.vitals = [];
2802     try {
2803         count = countEntities(pd.history_physical.vitals_list.vitals);
2804     } catch (e) {
2805         count = 0
2806     }
2807     if (count > 1) {
2808         for (let i in pd.history_physical.vitals_list.vitals) {
2809             vitals[i] = populateVital(pd.history_physical.vitals_list.vitals[i]);
2810             vitals.vitals.push(vitals[i]);
2811         }
2812     } else if (count !== 0) {
2813         vital = populateVital(pd.history_physical.vitals_list.vitals);
2814         vitals.vitals.push(vital);
2815     }
2816     if (count !== 0) {
2817         data.vitals = Object.assign(vitals.vitals);
2818     }
2819 // Medications
2820     let meds = [];
2821     let m = {};
2822     meds.medications = [];
2823     try {
2824         count = countEntities(pd.medications.medication);
2825     } catch (e) {
2826         count = 0
2827     }
2828     if (count > 1) {
2829         for (let i in pd.medications.medication) {
2830             m[i] = populateMedication(pd.medications.medication[i]);
2831             meds.medications.push(m[i]);
2832         }
2833     } else if (count !== 0) {
2834         m = populateMedication(pd.medications.medication);
2835         meds.medications.push(m);
2836     }
2837     if (count !== 0) {
2838         data.medications = Object.assign(meds.medications);
2839     }
2840 // Allergies
2841     let allergies = [];
2842     let allergy = {};
2843     allergies.allergies = [];
2844     try {
2845         count = countEntities(pd.allergies.allergy);
2846     } catch (e) {
2847         count = 0
2848     }
2849     if (count > 1) {
2850         for (let i in pd.allergies.allergy) {
2851             allergy[i] = populateAllergy(pd.allergies.allergy[i]);
2852             allergies.allergies.push(allergy[i]);
2853         }
2854     } else if (count <= 1) {
2855         allergy = populateAllergy(pd.allergies.allergy);
2856         allergies.allergies.push(allergy);
2857         count = 1;
2858     }
2859     if (count !== 0) {
2860         data.allergies = Object.assign(allergies.allergies);
2861     }
2862 // Problems
2863     let problems = [];
2864     let problem = {};
2865     problems.problems = [];
2866     try {
2867         count = countEntities(pd.problem_lists.problem);
2868     } catch (e) {
2869         count = 0
2870     }
2871     if (count > 1) {
2872         for (let i in pd.problem_lists.problem) {
2873             problem[i] = populateProblem(pd.problem_lists.problem[i], pd);
2874             problems.problems.push(problem[i]);
2875         }
2876     } else if (count !== 0) {
2877         problem = populateProblem(pd.problem_lists.problem);
2878         problems.problems.push(problem);
2879     }
2880     if (count !== 0) {
2881         data.problems = Object.assign(problems.problems);
2882     }
2883 // Procedures
2884     many = [];
2885     theone = {};
2886     many.procedures = [];
2887     try {
2888         count = countEntities(pd.procedures.procedure);
2889     } catch (e) {
2890         count = 0
2891     }
2892     if (count > 1) {
2893         for (let i in pd.procedures.procedure) {
2894             theone[i] = populateProcedure(pd.procedures.procedure[i]);
2895             many.procedures.push(theone[i]);
2896         }
2897     } else if (count !== 0) {
2898         theone = populateProcedure(pd.procedures.procedure);
2899         many.procedures.push(theone);
2900     }
2901     if (count !== 0) {
2902         data.procedures = Object.assign(many.procedures);
2903     }
2904 // Medical Devices
2905     many = [];
2906     theone = {};
2907     many.medical_devices = [];
2908     try {
2909         count = countEntities(pd.medical_devices.device);
2910     } catch (e) {
2911         count = 0
2912     }
2913     if (count > 1) {
2914         for (let i in pd.medical_devices.device) {
2915             theone[i] = populateMedicalDevice(pd.medical_devices.device[i]);
2916             many.medical_devices.push(theone[i]);
2917         }
2918     } else if (count !== 0) {
2919         theone = populateMedicalDevice(pd.medical_devices.device);
2920         many.medical_devices.push(theone);
2921     }
2922     if (count !== 0) {
2923         data.medical_devices = Object.assign(many.medical_devices);
2924     }
2925 // Results
2926     if (pd.results) {
2927         data.results = Object.assign(getResultSet(pd.results, pd)['results']);
2928     }
2930 // Referral TODO sjp I'm not happy with this.
2931     // different referral sources. 1st is dynamic with doc gen from CCM.
2932     // 2nd is the latest referral from transactions.
2933     if (pd.referral_reason[0].text !== "") {
2934         data.referral_reason = Object.assign(getReferralReason(pd.referral_reason[0], pd));
2935     } else if (pd.referral_reason[1].text !== "" && typeof pd.referral_reason[1].text !== 'undefined') {
2936         data.referral_reason = Object.assign(getReferralReason(pd.referral_reason[1], pd));
2937     } else {
2938         data.referral_reason = {}; // leave as empty so we can get our null flavor section.
2939     }
2940 // Health Concerns
2941     many = [];
2942     theone = {};
2943     many.health_concerns = [];
2944     try {
2945         count = countEntities(pd.health_concerns.concern);
2946     } catch (e) {
2947         count = 0
2948     }
2949     if (count > 1) {
2950         for (let i in pd.health_concerns.concern) {
2951             theone[i] = getHealthConcerns(pd.health_concerns.concern[i]);
2952             many.health_concerns.push(theone[i]);
2953             break;
2954         }
2955     } else if (count !== 0) {
2956         theone = getHealthConcerns(pd.health_concerns.concern);
2957         many.health_concerns.push(theone);
2958     }
2959     if (count !== 0) {
2960         data.health_concerns = Object.assign(many.health_concerns);
2961     } else {
2962         data.health_concerns = {"type": "act"}; // leave it as an empty section that we'll null flavor
2963     }
2964 // Immunizations
2965     many = [];
2966     theone = {};
2967     many.immunizations = [];
2968     try {
2969         count = countEntities(pd.immunizations.immunization);
2970     } catch (e) {
2971         count = 0;
2972     }
2973     if (count > 1) {
2974         for (let i in pd.immunizations.immunization) {
2975             theone[i] = populateImmunization(pd.immunizations.immunization[i]);
2976             many.immunizations.push(theone[i]);
2977         }
2978     } else if (count !== 0) {
2979         theone = populateImmunization(pd.immunizations.immunization);
2980         many.immunizations.push(theone);
2981     }
2982     if (count !== 0) {
2983         data.immunizations = Object.assign(many.immunizations);
2984     }
2985 // Plan of Care
2986     many = [];
2987     theone = {};
2988     many.plan_of_care = [];
2989     try {
2990         count = countEntities(pd.planofcare.item);
2991     } catch (e) {
2992         count = 0
2993     }
2994     if (count > 1) {
2995         for (let i in pd.planofcare.item) {
2996             if (cleanCode(pd.planofcare.item[i].date) === '') {
2997                 i--;
2998                 continue;
2999             }
3000             theone[i] = getPlanOfCare(pd.planofcare.item[i]);
3001             if (theone[i]) {
3002                 many.plan_of_care.push(theone[i]);
3003             }
3004         }
3005     } else if (count !== 0) {
3006         theone = getPlanOfCare(pd.planofcare.item);
3007         if (theone) {
3008             many.plan_of_care.push(theone);
3009         }
3010     }
3011     if (count !== 0) {
3012         data.plan_of_care = Object.assign(many.plan_of_care);
3013     }
3014 // Goals
3015     many = [];
3016     theone = {};
3017     many.goals = [];
3018     try {
3019         count = countEntities(pd.goals.item);
3020     } catch (e) {
3021         count = 0
3022     }
3023     if (count > 1) {
3024         for (let i in pd.goals.item) {
3025             theone[i] = getGoals(pd.goals.item[i]);
3026             many.goals.push(theone[i]);
3027         }
3028     } else if (count !== 0) {
3029         theone = getGoals(pd.goals.item);
3030         many.goals.push(theone);
3031     }
3032     if (count !== 0) {
3033         data.goals = Object.assign(many.goals);
3034     }
3035 // Assessments.
3036     many = [];
3037     theone = {};
3038     many.clinicalNoteAssessments = [];
3039     try {
3040         count = countEntities(pd.clinical_notes.evaluation_note);
3041     } catch (e) {
3042         count = 0
3043     }
3044     if (count > 1) {
3045         for (let i in pd.clinical_notes.evaluation_note) {
3046             theone[i] = getAssessments(pd.clinical_notes.evaluation_note[i]);
3047             many.clinicalNoteAssessments.push(theone[i]);
3048             break; // for now only one assessment. @todo concat notes to one.
3049         }
3050     } else if (count !== 0) {
3051         theone = getAssessments(pd.clinical_notes.evaluation_note);
3052         many.clinicalNoteAssessments.push(theone);
3053     }
3054     if (count !== 0) {
3055         data.clinicalNoteAssessments = Object.assign(many.clinicalNoteAssessments);
3056     }
3058 // Functional Status.
3059     many = [];
3060     theone = {};
3061     many.functional_status = [];
3062     try {
3063         count = countEntities(pd.functional_status.item);
3064     } catch (e) {
3065         count = 0
3066     }
3067     if (count > 1) {
3068         for (let i in pd.functional_status.item) {
3069             theone[i] = getFunctionalStatus(pd.functional_status.item[i]);
3070             many.functional_status.push(theone[i]);
3071         }
3072     } else if (count !== 0) {
3073         theone = getFunctionalStatus(pd.functional_status.item);
3074         many.functional_status.push(theone);
3075     }
3076     if (count !== 0) {
3077         data.functional_status = Object.assign(many.functional_status);
3078     }
3080 // Mental Status.
3081     many = [];
3082     theone = {};
3083     many.mental_status = [];
3084     try {
3085         count = countEntities(pd.mental_status.item);
3086     } catch (e) {
3087         count = 0
3088     }
3089     if (count > 1) {
3090         for (let i in pd.mental_status.item) {
3091             theone[i] = getMentalStatus(pd.mental_status.item[i]);
3092             many.mental_status.push(theone[i]);
3093         }
3094     } else if (count !== 0) {
3095         theone = getMentalStatus(pd.mental_status.item);
3096         many.mental_status.push(theone);
3097     }
3098     if (count !== 0) {
3099         data.mental_status = Object.assign(many.mental_status);
3100     }
3102 // Social History
3103     many = [];
3104     theone = {};
3105     many.social_history = [];
3106     try {
3107         count = countEntities(pd.history_physical.social_history.history_element);
3108     } catch (e) {
3109         count = 0
3110     }
3111     if (count > 1) {
3112         for (let i in pd.history_physical.social_history.history_element) {
3113             if (i > 0) break;
3114             theone[i] = populateSocialHistory(pd.history_physical.social_history.history_element[i]);
3115             many.social_history.push(theone[i]);
3116         }
3117     } else if (count !== 0) {
3118         theone = populateSocialHistory(pd.history_physical.social_history.history_element);
3119         many.social_history.push(theone);
3120     }
3121     if (count !== 0) {
3122         data.social_history = Object.assign(many.social_history);
3123     }
3124 // Notes
3125     for (let currentNote in pd.clinical_notes) {
3126         many = [];
3127         theone = {};
3128         switch (pd.clinical_notes[currentNote].clinical_notes_type) {
3129             case 'evaluation_note':
3130                 continue;
3131             case 'progress_note':
3132                 break;
3133             case 'history_physical':
3134                 pd.clinical_notes[currentNote].code_text = "History and Physical";
3135                 break;
3136             case 'nurse_note':
3137                 break;
3138             case 'general_note':
3139                 break;
3140             case 'discharge_summary':
3141                 break;
3142             case 'procedure_note':
3143                 break;
3144             case 'consultation_note':
3145                 break;
3146             case 'imaging_narrative':
3147                 break;
3148             case 'laboratory_report_narrative':
3149                 break;
3150             case 'pathology_report_narrative':
3151                 break;
3152             default:
3153                 continue;
3154         }
3155         try {
3156             count = countEntities(pd.clinical_notes[currentNote]);
3157         } catch (e) {
3158             count = 0
3159         }
3160         if (count > 1) {
3161             for (let i in pd.clinical_notes[currentNote]) {
3162                 theone[i] = populateNote(pd.clinical_notes[currentNote]);
3163                 many.push(theone[i]);
3164             }
3165         } else if (count !== 0) {
3166             theone = populateNote(pd.clinical_notes[currentNote]);
3167             many.push(theone);
3168         }
3169         if (count !== 0) {
3170             data[currentNote] = Object.assign(many);
3171         }
3172     }
3173 // Care Team and members
3174     if (pd.care_team.is_active == 'active') {
3175         data.care_team = Object.assign(populateCareTeamMembers(pd));
3176     }
3178 // ------------------------------------------ End Sections ---------------------------------------- //
3180     // sections data objects
3181     doc.data = Object.assign(data);
3182     // document meta data and header objects
3183     let meta = getMeta(pd);
3184     let header = populateHeader(pd);
3185     meta.ccda_header = Object.assign(header);
3186     doc.meta = Object.assign(meta);
3188     if (pd.timezone_local_offset) {
3189         populateTimezones(doc, pd.timezone_local_offset, 0);
3190     }
3191     // build to cda
3192     let xml = bbg.generateCCD(doc);
3194     /* Debug */
3195     if (enableDebug === true) {
3196         let place = documentLocation + "/documents/temp/";
3197         if (fs.existsSync(place)) {
3198             fs.writeFile(place + "ccda.json", JSON.stringify(all, null, 4), function (err) {
3199                 if (err) {
3200                     return console.log(err);
3201                 }
3202             });
3203             fs.writeFile(place + "ccda.xml", xml, function (err) {
3204                 if (err) {
3205                     return console.log(err);
3206                 }
3207             });
3208         }
3209     }
3211     return xml;
3214 let unstructuredTemplate = null;
3216 function generateUnstructured(pd) {
3217     let doc = {};
3218     let data = {};
3219     let count = 0;
3220     let many = [];
3221     let theone = {};
3222     // include unstructured document type oid in header
3223     pd.doc_type = 'unstructured';
3224     all = pd;
3225     let primary_care_provider = all.primary_care_provider || {};
3226     npiProvider = primary_care_provider.provider ? primary_care_provider.provider.npi : "NI";
3227     oidFacility = all.encounter_provider.facility_oid ? all.encounter_provider.facility_oid : "2.16.840.1.113883.19.5.99999.1";
3228     npiFacility = all.encounter_provider.facility_npi || "NI";
3229     webRoot = all.serverRoot;
3230     documentLocation = all.document_location;
3231     authorDateTime = pd.created_time_timezone;
3232     if (pd.author.time.length > 7) {
3233         authorDateTime = pd.author.time;
3234     } else if (all.encounter_list && all.encounter_list.encounter) {
3235         if (countEntities(all.encounter_list.encounter) === 1) {
3236             authorDateTime = all.encounter_list.encounter.date;
3237         } else {
3238             authorDateTime = all.encounter_list.encounter[0].date;
3239         }
3240     }
3241     authorDateTime = fDate(authorDateTime);
3242 // Demographics is needed in unstructured
3243     let demographic = populateDemographic(pd.patient, pd.guardian, pd);
3244     data.demographics = Object.assign(demographic);
3246     if (pd.primary_care_provider) {
3247         Object.assign(demographic, populateProviders(pd));
3248     }
3249     doc.data = Object.assign(data);
3251     // document meta data and header objects
3252     let meta = getMeta(pd);
3253     let header = populateHeader(pd);
3254     meta.ccda_header = Object.assign(header);
3255     doc.meta = Object.assign(meta);
3257     // set TZ offset for moment
3258     if (pd.timezone_local_offset) {
3259         populateTimezones(doc, pd.timezone_local_offset, 0);
3260     }
3261     // build to cda
3262     let xml = bbg.generateCCD(doc);
3263     unstructuredTemplate = unstructuredTemplate.trim();
3264     xml = xml.replace(/<\/ClinicalDocument>/g, unstructuredTemplate);
3265     xml += "</ClinicalDocument>" + "\n";
3267     /* Debug */
3268     if (enableDebug === true) {
3269         let place = documentLocation + "/documents/temp/";
3270         if (fs.existsSync(place)) {
3271             fs.writeFile(place + "unstructured.xml", xml, function (err) {
3272                 if (err) {
3273                     return console.log(err);
3274                 }
3275             });
3276         }
3277     }
3279     return xml;
3282 function processConnection(connection) {
3283     conn = connection; // make it global
3284     let remoteAddress = conn.remoteAddress + ':' + conn.remotePort;
3285     conn.setEncoding('utf8');
3286     //console.log('server remote address ', remoteAddress);
3287     let xml_complete = "";
3289     function eventData(xml) {
3290         xml_complete = xml.toString();
3291         // ensure we have an array start and end
3292         if (xml_complete.match(/^<CCDA/g) && xml_complete.match(/<\/CCDA>$/g)) {
3293             let doc = "";
3294             let xslUrl = "";
3295             /* eslint-disable-next-line no-control-regex */
3296             xml_complete = xml_complete.replace(/(\u000b\u001c)/gm, "").trim();
3297             xml_complete = xml_complete.replace(/\t\s+/g, " ").trim();
3298             // convert xml data set for document to json array
3299             to_json(xml_complete, function (error, data) {
3300                 if (error) {
3301                     console.log(
3302                         "toJson error: " + error + "Len: " + xml_complete.length
3303                     );
3304                     return "ERROR: Failed json build";
3305                 }
3306                 let unstructured = "";
3307                 let isUnstruturedData = !!data.CCDA.patient_files;
3308                 // extract unstructured documents file component templates. One per file.
3309                 if (isUnstruturedData) {
3310                     unstructuredTemplate = xml_complete.substring(
3311                         xml_complete.lastIndexOf("<patient_files>") + 15,
3312                         xml_complete.lastIndexOf("</patient_files>")
3313                     );
3314                 }
3315                 // create doc_type document i.e. CCD Referral etc.
3316                 if (data.CCDA.doc_type !== "unstructured") {
3317                     doc = generateCcda(data.CCDA);
3318                     if (data.CCDA.xslUrl) {
3319                         xslUrl = data.CCDA.xslUrl || "";
3320                     }
3321                     doc = headReplace(doc, xslUrl);
3322                 } else {
3323                     unstructured = generateUnstructured(data.CCDA);
3324                     if (data.CCDA.xslUrl) {
3325                         xslUrl = data.CCDA.xslUrl || "";
3326                     }
3327                     doc = headReplace(unstructured, xslUrl);
3328                     // combine the two documents to send back all at once.
3329                     doc += unstructured;
3330                 }
3331                 // auto build an Unstructured document of supplied embedded files.
3332                 if (
3333                     data.CCDA.doc_type !== "unstructured" &&
3334                     isUnstruturedData
3335                 ) {
3336                     unstructured = generateUnstructured(data.CCDA);
3337                     unstructured = headReplace(unstructured, xslUrl);
3338                     // combine the two documents to send back all at once.
3339                     doc += unstructured;
3340                 }
3341             });
3342             // send results back to eagerly awaiting CCM for disposal.
3343             doc = doc
3344                 .toString()
3345                 /* eslint-disable-next-line no-control-regex */
3346                 .replace(/(\u000b\u001c|\r)/gm, "")
3347                 .trim();
3348             let chunk = "";
3349             let numChunks = Math.ceil(doc.length / 1024);
3350             for (let i = 0, o = 0; i < numChunks; ++i, o += 1024) {
3351                 chunk = doc.substring(o, o + 1024);
3352                 conn.write(chunk);
3353             }
3354             conn.write(String.fromCharCode(28) + "\r\r" + "");
3355             conn.end();
3356         }
3357     }
3359     function eventCloseConn() {
3360         //console.log('connection from %s closed', remoteAddress);
3361     }
3363     function eventErrorConn(err) {
3364         console.log('Connection %s error: %s', remoteAddress, err.message);
3365         console.log(err.stack);
3366         conn.destroy();
3367     }
3369 // Connection Events //
3370     // CCM will send one File Separator characters to mark end of array.
3371     let received = new DataStack(String.fromCharCode(28));
3372     conn.on("data", data => {
3373         received.push(data);
3374         while (!received.endOfCcda() && data.length > 0) {
3375             data = "";
3376             eventData(received.returnData());
3377         }
3378     });
3380     conn.once('close', eventCloseConn);
3381     conn.on('error', eventErrorConn);
3384 function setUp(server) {
3385     server.on('connection', processConnection);
3386     server.listen(6661, '127.0.0.1', function () { // never change port!
3387         //console.log('server listening to ', server.address());
3388     });
3391 // start up listener for requests from CCM or others.
3392 setUp(server);
3394 /* ---------------------------------For future use in header. Do not remove!-------------------------------------------- */
3395 /*"data_enterer": {
3396     "identifiers": [
3397         {
3398             "identifier": "2.16.840.1.113883.4.6",
3399             "extension": "999999943252"
3400         }
3401     ],
3402     "name": [
3403         {
3404             "last": pd.data_enterer.lname,
3405             "first": pd.data_enterer.fname
3406         }
3407     ],
3408     "address": [
3409         {
3410             "street_lines": [
3411                 pd.data_enterer.streetAddressLine
3412             ],
3413             "city": pd.data_enterer.city,
3414             "state": pd.data_enterer.state,
3415             "zip": pd.data_enterer.postalCode,
3416             "country": pd.data_enterer.country
3417         }
3418     ],
3419     "phone": [
3420         {
3421             "number": pd.data_enterer.telecom,
3422             "type": "work place"
3423         }
3424     ]
3426 "informant": {
3427     "identifiers": [
3428         {
3429             "identifier": "2.16.840.1.113883.19.5",
3430             "extension": "KP00017"
3431         }
3432     ],
3433     "name": [
3434         {
3435             "last": pd.informer.lname || "",
3436             "first": pd.informer.fname || ""
3437         }
3438     ],
3439     "address": [
3440         {
3441             "street_lines": [
3442                 pd.informer.streetAddressLine || ""
3443             ],
3444             "city": pd.informer.city,
3445             "state": pd.informer.state,
3446             "zip": pd.informer.postalCode,
3447             "country": pd.informer.country
3448         }
3449     ],
3450     "phone": [
3451         {
3452             "number": pd.informer.telecom || "",
3453             "type": "work place"
3454         }
3455     ]
3456 },*/
3457 /*"service_event": {
3458     "code": {
3459         "name": "",
3460         "code": "",
3461         "code_system_name": "SNOMED CT"
3462     },
3463     "date_time": {
3464         "low": {
3465             "date": "2021-03-11",
3466             "precision": "day"
3467         },
3468         "high": {
3469             "date": pd.created_time,
3470             "precision": "day"
3471         }
3472     },
3473     "performer": [
3474         {
3475             "performer": [
3476                 {
3477                     "identifiers": [
3478                         {
3479                             "identifier": "2.16.840.1.113883.4.6",
3480                             "extension": npiProvider
3481                         }
3482                     ],
3483                     "name": [
3484                         {
3485                             "last": pd.information_recipient.lname || "DAH",
3486                             "first": pd.information_recipient.fname || "DAH"
3487                         }
3488                     ],
3489                     "address": [
3490                         {
3491                             "street_lines": [
3492                                 pd.information_recipient.streetAddressLine
3493                             ],
3494                             "city": pd.information_recipient.city,
3495                             "state": pd.information_recipient.state,
3496                             "zip": pd.information_recipient.postalCode,
3497                             "country": pd.information_recipient.country || "US"
3498                         }
3499                     ],
3500                     "phone": [
3501                         {
3502                             "number": pd.information_recipient.telecom,
3503                             "type": "work place"
3504                         }
3505                     ],
3506                     "organization": [
3507                         {
3508                             "identifiers": [
3509                                 {
3510                                     "identifier": "2.16.840.1.113883.19.5.9999.1393"
3511                                 }
3512                             ],
3513                             "name": [
3514                                 pd.encounter_provider.facility_name
3515                             ],
3516                             "address": [
3517                                 {
3518                                     "street_lines": [
3519                                         pd.encounter_provider.facility_street
3520                                     ],
3521                                     "city": pd.encounter_provider.facility_city,
3522                                     "state": pd.encounter_provider.facility_state,
3523                                     "zip": pd.encounter_provider.facility_postal_code,
3524                                     "country": pd.encounter_provider.facility_country_code || "US"
3525                                 }
3526                             ],
3527                             "phone": [
3528                                 {
3529                                     "number": pd.encounter_provider.facility_phone,
3530                                     "type": "primary work"
3531                                 }
3532                             ]
3533                         }
3534                     ],
3535                     "code": [
3536                         {
3537                             "name": "",
3538                             "code": "",
3539                             "code_system_name": "Provider Codes"
3540                         }
3541                     ]
3542                 }
3543             ],
3544             "code": {
3545                 "name": "Primary Performer",
3546                 "code": "PP",
3547                 "code_system_name": "Provider Role"
3548             }
3549         }
3550     ]