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